зеркало из https://github.com/mozilla/pjs.git
210 строки
7.0 KiB
C
210 строки
7.0 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
|
|
dragdrop.c --- Very simplistic drag and drop support.
|
|
Created: Terry Weissman <terry@netscape.com>, 27-Jun-95.
|
|
*/
|
|
|
|
#include "mozilla.h"
|
|
#include "xfe.h"
|
|
#include "dragdrop.h"
|
|
|
|
#include <Xfe/Xfe.h> /* for xfe widgets and utilities */
|
|
|
|
typedef struct DropSite {
|
|
Widget widget;
|
|
fe_dnd_DropFunc func;
|
|
void* closure;
|
|
struct DropSite* next;
|
|
} DropSite;
|
|
|
|
static DropSite* FirstDropSite = NULL;
|
|
|
|
static void
|
|
fe_dnd_dropsite_destroyed(Widget widget, XtPointer closure,
|
|
XtPointer call_data)
|
|
{
|
|
DropSite* site = (DropSite*) closure;
|
|
DropSite** tmp;
|
|
assert(widget == site->widget);
|
|
for (tmp = &FirstDropSite ; *tmp ; tmp = &((*tmp)->next)) {
|
|
if (*tmp == site) {
|
|
*tmp = site->next;
|
|
XP_FREE(site);
|
|
return;
|
|
}
|
|
}
|
|
abort();
|
|
}
|
|
|
|
void
|
|
fe_dnd_CreateDrop(Widget widget, fe_dnd_DropFunc func, void* closure)
|
|
{
|
|
DropSite* tmp = XP_NEW_ZAP(DropSite);
|
|
tmp->widget = widget;
|
|
tmp->func = func;
|
|
tmp->closure = closure;
|
|
tmp->next = FirstDropSite;
|
|
FirstDropSite = tmp;
|
|
|
|
XtAddCallback(widget, XmNdestroyCallback, fe_dnd_dropsite_destroyed, tmp);
|
|
}
|
|
|
|
|
|
|
|
void
|
|
fe_dnd_DoDrag(fe_dnd_Source* source, XEvent* event, fe_dnd_Event type)
|
|
{
|
|
Display* dpy = XtDisplay(source->widget);
|
|
int x = event->xbutton.x_root - source->hotx;
|
|
int y = event->xbutton.y_root - source->hoty;
|
|
DropSite* site;
|
|
Window dropwindow = None;
|
|
XtRealizeWidget(source->widget);
|
|
XMoveWindow(dpy, XtWindow(source->widget), x, y);
|
|
if (type == FE_DND_START) {
|
|
XtPopup(source->widget, XtGrabNone);
|
|
}
|
|
for (site = FirstDropSite ; site ; site = site->next) {
|
|
XP_Bool callit = TRUE;
|
|
if (type == FE_DND_DROP) {
|
|
/* Only call drops if it is actually on this widget. */
|
|
Position rootx;
|
|
Position rooty;
|
|
XtTranslateCoords(site->widget, 0, 0, &rootx, &rooty);
|
|
if (event->xbutton.x_root < rootx || event->xbutton.y_root < rooty) {
|
|
callit = FALSE;
|
|
} else {
|
|
rootx += XfeWidth(site->widget);
|
|
rooty += XfeHeight(site->widget);
|
|
if (event->xbutton.x_root >= rootx || event->xbutton.y_root >= rooty) {
|
|
callit = FALSE;
|
|
} else {
|
|
/* Huh. Well, it looks like we're over this widget, but we have more
|
|
tests to try. First, make sure that this widget and all of its
|
|
ancesters are managed at the moment. (For some reason, the
|
|
uppermost widget, the Shell widget, is generally not managed even
|
|
when visible, so we stop checking once we've hit a shell.)
|
|
|
|
We might also want to check for mapped, as well as managed, but
|
|
that seems a bit awkward to do right now, and is not strictly
|
|
necessary. Such checks would be nice to do during dragging as
|
|
well as dropping; it would help prevent us from drawing into
|
|
iconified windows. */
|
|
Widget widget;
|
|
Window toplevelwindow = 0;
|
|
for (widget = site->widget;
|
|
widget && !XtIsShell(widget);
|
|
widget = XtParent(widget)) {
|
|
if (!XtIsManaged(widget)) {
|
|
callit = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (callit) {
|
|
/* OK, the final acid test: is the mouse really on the window in
|
|
question? I am trusting that we don't have any layout where the
|
|
widget is obscured by some other widget within the same toplevel
|
|
window; I'm more worried about the case where there is another
|
|
toplevel window above this widget's toplevel window and below
|
|
the mouse. (There maybe ought to be a grab on during some of
|
|
this.) */
|
|
if (dropwindow == None) {
|
|
/* Figure out what toplevel window we are pointing at. Done
|
|
only once; this does require a round trip to the X server. */
|
|
Window rootreturn;
|
|
int rootx, rooty, winx, winy;
|
|
unsigned int mask;
|
|
XUnmapWindow(dpy, XtWindow(source->widget));
|
|
if (!XQueryPointer(dpy, event->xbutton.root, &rootreturn,
|
|
&dropwindow, &rootx, &rooty, &winx, &winy,
|
|
&mask)) {
|
|
/* This is not very likely -- the cursor has moved to another
|
|
screen since we got the drop event. Whatever.*/
|
|
dropwindow = None;
|
|
}
|
|
if (dropwindow == None) {
|
|
/* Well, it appears that the drop didn't even happen on a
|
|
window at all; the mouse is pointing at the root window.
|
|
How bizarre, especially since the rectangle check showed
|
|
we were within a widget. I guess it could happen if we
|
|
iconified a window, and then dropped where it was when
|
|
uniconified.
|
|
|
|
At any rate, we shouldn't deliver a drop event to anywhere.
|
|
Break out of the loop where we've been looking for a
|
|
destination. */
|
|
break;
|
|
}
|
|
}
|
|
/* Find out what X window is the topmost container for this
|
|
site, so we can test if it's the same that XQueryWindow
|
|
returned. Note that we can't easily cache this result; it
|
|
can change if the window manager decides to reparent the
|
|
shell to another window. Which can happen, for example, if
|
|
we unmap this window and map it later.
|
|
|
|
Anyway, finding this toplevel window can take several round
|
|
trips to the server. First we find the uppermost window we can
|
|
from the widget hierarchy, and then we work up our way up
|
|
the X window tree. */
|
|
for (widget = site->widget; widget; widget = XtParent(widget)) {
|
|
Window w = XtWindow(widget);
|
|
if (w) toplevelwindow = w;
|
|
if (XtIsShell(widget)) {
|
|
/* Sometimes a shell has another shell as a parent (I guess
|
|
for trasient-for and stuff), so be sure we stop before
|
|
going to that other shell. */
|
|
break;
|
|
}
|
|
}
|
|
if (toplevelwindow) {
|
|
Window parent;
|
|
Window root;
|
|
Window* children;
|
|
unsigned int numchildren;
|
|
while (toplevelwindow != dropwindow) {
|
|
/* There oughta be a better call then XQueryTree... */
|
|
if (!XQueryTree(dpy, toplevelwindow, &root, &parent,
|
|
&children, &numchildren)) {
|
|
/* The call failed. I dunno how it can do that. Uh, uh,
|
|
well, we'll just not drop in there, shall we. ### */
|
|
XP_ASSERT(0);
|
|
break;
|
|
}
|
|
if (children) XFree(children);
|
|
if (parent == root) break;
|
|
toplevelwindow = parent;
|
|
}
|
|
}
|
|
if (dropwindow != toplevelwindow) {
|
|
callit = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (callit) {
|
|
(*site->func)(site->widget, site->closure, type, source, event);
|
|
if (type == FE_DND_DROP) {
|
|
/* Never drop on more than one dropsite. */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|