зеркало из https://github.com/mozilla/gecko-dev.git
b=497498 schedule event dispatch in response to GTK drag target signals to avoid running the event loop at unexpected times r=roc
--HG-- extra : rebase_source : 881ad7c0efa85174347059a9f53b3a5bd4c76696
This commit is contained in:
Родитель
3eb6a4c206
Коммит
c42f5a3887
|
@ -127,6 +127,7 @@ invisibleSourceDragDataGet(GtkWidget *aWidget,
|
|||
gpointer aData);
|
||||
|
||||
nsDragService::nsDragService()
|
||||
: mTaskSource(0)
|
||||
{
|
||||
// We have to destroy the hidden widget before the event loop stops
|
||||
// running.
|
||||
|
@ -162,9 +163,6 @@ nsDragService::nsDragService()
|
|||
sDragLm = PR_NewLogModule("nsDragService");
|
||||
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService"));
|
||||
mGrabWidget = 0;
|
||||
mTargetWidget = 0;
|
||||
mTargetDragContext = 0;
|
||||
mTargetTime = 0;
|
||||
mCanDrop = false;
|
||||
mTargetDragDataReceived = false;
|
||||
mTargetDragData = 0;
|
||||
|
@ -174,6 +172,9 @@ nsDragService::nsDragService()
|
|||
nsDragService::~nsDragService()
|
||||
{
|
||||
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::~nsDragService"));
|
||||
if (mTaskSource)
|
||||
g_source_remove(mTaskSource);
|
||||
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED2(nsDragService, nsBaseDragService,
|
||||
|
@ -1131,7 +1132,8 @@ nsDragService::GetTargetDragData(GdkAtom aFlavor)
|
|||
{
|
||||
PR_LOG(sDragLm, PR_LOG_DEBUG, ("getting data flavor %d\n", aFlavor));
|
||||
PR_LOG(sDragLm, PR_LOG_DEBUG, ("mLastWidget is %p and mLastContext is %p\n",
|
||||
mTargetWidget, mTargetDragContext));
|
||||
mTargetWidget.get(),
|
||||
mTargetDragContext.get()));
|
||||
// reset our target data areas
|
||||
TargetResetData();
|
||||
gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
|
||||
|
@ -1702,3 +1704,231 @@ invisibleSourceDragEnd(GtkWidget *aWidget,
|
|||
dragService->SourceEndDragSession(aContext, MOZ_GTK_DRAG_RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
// The following methods handle responding to GTK drag destination signals and
|
||||
// tracking state between these signals.
|
||||
//
|
||||
// In general, GTK does not expect us to run the event loop while handling its
|
||||
// drag destination signals, however our drag event handlers may run the
|
||||
// event loop, most often to fetch information about the drag data.
|
||||
//
|
||||
// GTK, for example, uses the return value from drag-motion signals to
|
||||
// determine whether drag-leave signals should be sent. If an event loop is
|
||||
// run during drag-motion the XdndLeave message can get processed but when GTK
|
||||
// receives the message it does not yet know that it needs to send the
|
||||
// drag-leave signal to our widget.
|
||||
//
|
||||
// After a drag-drop signal, we need to reply with gtk_drag_finish().
|
||||
// However, gtk_drag_finish should happen after the drag-drop signal handler
|
||||
// returns so that when the Motif drag protocol is used, the
|
||||
// XmTRANSFER_SUCCESS during gtk_drag_finish is sent after the XmDROP_START
|
||||
// reply sent on return from the drag-drop signal handler.
|
||||
//
|
||||
// Therefore we reply to the signals immediately and schedule a task to
|
||||
// dispatch the Gecko events, which may run the event loop.
|
||||
//
|
||||
// Action in response to drag-leave signals is also delayed until the event
|
||||
// loop runs again so that we find out whether a drag-drop signal follows.
|
||||
//
|
||||
// A single task is scheduled to manage responses to all three GTK signals.
|
||||
// If further signals are received while the task is scheduled, the scheduled
|
||||
// response is updated, sometimes effectively compressing successive signals.
|
||||
//
|
||||
// No Gecko drag events are dispatched (during nested event loops) while other
|
||||
// Gecko drag events are in flight. This helps event handlers that may not
|
||||
// expect nested events, while accessing an event's dataTransfer for example.
|
||||
|
||||
gboolean
|
||||
nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint, guint aTime)
|
||||
{
|
||||
if (mScheduledTask == eDragTaskMotion) {
|
||||
// The drag source has sent another motion message before we've
|
||||
// replied to the previous. That shouldn't happen with Xdnd. The
|
||||
// spec for Motif drags is less clear, but we'll just update the
|
||||
// scheduled task with the new position reply only to the most
|
||||
// recent message.
|
||||
NS_WARNING("Drag Motion message received before previous reply was sent");
|
||||
}
|
||||
|
||||
// Returning TRUE means we'll reply with a status message, unless we first
|
||||
// get a leave.
|
||||
return Schedule(eDragTaskMotion, aWindow, aDragContext,
|
||||
aWindowPoint, aTime);
|
||||
}
|
||||
|
||||
void
|
||||
nsDragService::ScheduleLeaveEvent()
|
||||
{
|
||||
// We don't know at this stage whether a drop signal will immediately
|
||||
// follow. If the drop signal gets sent it will happen before we return
|
||||
// to the main loop and the scheduled leave task will be replaced.
|
||||
if (!Schedule(eDragTaskLeave, nsnull, NULL, nsIntPoint(), 0)) {
|
||||
NS_WARNING("Drag leave after drop");
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsDragService::ScheduleDropEvent(nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint, guint aTime)
|
||||
{
|
||||
if (!Schedule(eDragTaskDrop, aWindow,
|
||||
aDragContext, aWindowPoint, aTime)) {
|
||||
NS_WARNING("Additional drag drop ignored");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
|
||||
|
||||
// We'll reply with gtk_drag_finish().
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint, guint aTime)
|
||||
{
|
||||
// If we haven't yet run a scheduled drop task, just say that
|
||||
// we are not ready to receive another drop.
|
||||
if (mScheduledTask == eDragTaskDrop)
|
||||
return FALSE;
|
||||
|
||||
// If there is an existing leave or motion task scheduled, then that
|
||||
// will be replaced. When the new task is run, it will dispatch
|
||||
// any necessary leave or motion events.
|
||||
|
||||
mScheduledTask = aTask;
|
||||
mPendingWindow = aWindow;
|
||||
mPendingDragContext = aDragContext;
|
||||
mPendingWindowPoint = aWindowPoint;
|
||||
mPendingTime = aTime;
|
||||
|
||||
if (!mTaskSource) {
|
||||
// High priority is used here because the native events involved have
|
||||
// already waited at default priority. Perhaps a lower than default
|
||||
// priority could be used for motion tasks because there is a chance
|
||||
// that a leave or drop is waiting, but managing different priorities
|
||||
// may not be worth the effort. Motion tasks shouldn't queue up as
|
||||
// they should be throttled based on replies.
|
||||
mTaskSource =
|
||||
g_idle_add_full(G_PRIORITY_HIGH, TaskDispatchCallback, this, NULL);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsDragService::TaskDispatchCallback(gpointer data)
|
||||
{
|
||||
nsRefPtr<nsDragService> dragService = static_cast<nsDragService*>(data);
|
||||
return dragService->RunScheduledTask();
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsDragService::RunScheduledTask()
|
||||
{
|
||||
if (mTargetWindow && mTargetWindow != mPendingWindow) {
|
||||
mTargetWindow->OnDragLeave();
|
||||
}
|
||||
|
||||
// It is possible that the pending state has been updated during dispatch
|
||||
// of the leave event. That's fine.
|
||||
|
||||
// Now we collect the pending state because, from this point on, we want
|
||||
// to use the same state for all events dispatched. All state is updated
|
||||
// so that when other tasks are scheduled during dispatch here, this
|
||||
// task is considered to have already been run.
|
||||
bool positionHasChanged =
|
||||
mPendingWindow != mTargetWindow ||
|
||||
mPendingWindowPoint != mTargetWindowPoint;
|
||||
DragTask task = mScheduledTask;
|
||||
mScheduledTask = eDragTaskNone;
|
||||
mTargetWindow = mPendingWindow.forget();
|
||||
mTargetWindowPoint = mPendingWindowPoint;
|
||||
|
||||
if (task == eDragTaskLeave) {
|
||||
// Nothing more to do
|
||||
// Returning false removes the task source from the event loop.
|
||||
mTaskSource = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// This may be the start of a destination drag session.
|
||||
StartDragSession();
|
||||
|
||||
// mTargetWidget may be NULL if the window has been destroyed.
|
||||
// (The leave event is not scheduled if a drop task is still scheduled.)
|
||||
// We still reply appropriately to indicate that the drop will or didn't
|
||||
// succeeed.
|
||||
mTargetWidget = mTargetWindow->GetMozContainerWidget();
|
||||
mTargetDragContext.steal(mPendingDragContext);
|
||||
mTargetTime = mPendingTime;
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
|
||||
// (as at 27 December 2010) indicates that a "drop" event should only be
|
||||
// fired (at the current target element) if the current drag operation is
|
||||
// not none. The current drag operation will only be set to a non-none
|
||||
// value during a "dragover" event.
|
||||
//
|
||||
// If the user has ended the drag before any dragover events have been
|
||||
// sent, then the spec recommends skipping the drop (because the current
|
||||
// drag operation is none). However, here we assume that, by releasing
|
||||
// the mouse button, the user has indicated that they want to drop, so we
|
||||
// proceed with the drop where possible.
|
||||
//
|
||||
// In order to make the events appear to content in the same way as if the
|
||||
// spec is being followed we make sure to dispatch a "dragover" event with
|
||||
// appropriate coordinates and check canDrop before the "drop" event.
|
||||
//
|
||||
// When the Xdnd protocol is used for source/destination communication (as
|
||||
// should be the case with GTK source applications) a dragover event
|
||||
// should have already been sent during the drag-motion signal, which
|
||||
// would have already been received because XdndDrop messages do not
|
||||
// contain a position. However, we can't assume the same when the Motif
|
||||
// protocol is used.
|
||||
if (task == eDragTaskMotion || positionHasChanged) {
|
||||
nsWindow::UpdateDragStatus(mTargetDragContext, this);
|
||||
mTargetWindow->
|
||||
DispatchDragMotionEvents(this, mTargetWindowPoint, mTargetTime);
|
||||
|
||||
if (task == eDragTaskMotion) {
|
||||
// Reply to tell the source whether we can drop and what
|
||||
// action would be taken.
|
||||
TargetEndDragMotion(mTargetWidget, mTargetDragContext, mTargetTime);
|
||||
}
|
||||
}
|
||||
|
||||
if (task == eDragTaskDrop) {
|
||||
gboolean success = mTargetWindow->
|
||||
DispatchDragDropEvent(this, mTargetWindowPoint, mTargetTime);
|
||||
|
||||
// Perhaps we should set the del parameter to TRUE when the drag
|
||||
// action is move, but we don't know whether the data was successfully
|
||||
// transferred.
|
||||
gtk_drag_finish(mTargetDragContext, success,
|
||||
/* del = */ FALSE, mTargetTime);
|
||||
|
||||
// This drag is over, so clear out our reference to the previous
|
||||
// window.
|
||||
mTargetWindow = nsnull;
|
||||
// Make sure to end the drag session. If this drag started in a
|
||||
// different app, we won't get a drag_end signal to end it from.
|
||||
EndDragSession(true);
|
||||
}
|
||||
|
||||
// We're done with the drag context.
|
||||
mTargetWidget = NULL;
|
||||
mTargetDragContext = NULL;
|
||||
|
||||
// If we got another drag signal while running the sheduled task, that
|
||||
// must have happened while running a nested event loop. Leave the task
|
||||
// source on the event loop.
|
||||
if (mScheduledTask != eDragTaskNone)
|
||||
return TRUE;
|
||||
|
||||
// We have no task scheduled.
|
||||
// Returning false removes the task source from the event loop.
|
||||
mTaskSource = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,30 @@
|
|||
#include "nsIObserver.h"
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
class nsWindow;
|
||||
|
||||
#ifndef HAVE_NSGOBJECTREFTRAITS
|
||||
#define HAVE_NSGOBJECTREFTRAITS
|
||||
template <class T>
|
||||
class nsGObjectRefTraits : public nsPointerRefTraits<T> {
|
||||
public:
|
||||
static void Release(T *aPtr) { g_object_unref(aPtr); }
|
||||
static void AddRef(T *aPtr) { g_object_ref(aPtr); }
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_NSAUTOREFTRAITS_GTKWIDGET
|
||||
#define HAVE_NSAUTOREFTRAITS_GTKWIDGET
|
||||
template <>
|
||||
class nsAutoRefTraits<GtkWidget> : public nsGObjectRefTraits<GtkWidget> { };
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_NSAUTOREFTRAITS_GDKDRAGCONTEXT
|
||||
#define HAVE_NSAUTOREFTRAITS_GDKDRAGCONTEXT
|
||||
template <>
|
||||
class nsAutoRefTraits<GdkDragContext> :
|
||||
public nsGObjectRefTraits<GdkDragContext> { };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Native GTK DragService wrapper
|
||||
|
@ -100,6 +124,25 @@ public:
|
|||
|
||||
static nsDragService* GetInstance();
|
||||
|
||||
// Methods called from nsWindow to handle responding to GTK drag
|
||||
// destination signals
|
||||
|
||||
gboolean ScheduleMotionEvent(nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint,
|
||||
guint aTime);
|
||||
void ScheduleLeaveEvent();
|
||||
gboolean ScheduleDropEvent(nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint,
|
||||
guint aTime);
|
||||
|
||||
nsWindow* GetMostRecentDestWindow()
|
||||
{
|
||||
return mScheduledTask == eDragTaskNone ? mTargetWindow
|
||||
: mPendingWindow;
|
||||
}
|
||||
|
||||
// END PUBLIC API
|
||||
|
||||
// These methods are public only so that they can be called from functions
|
||||
|
@ -118,14 +161,47 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
// target side vars
|
||||
// mScheduledTask indicates what signal has been received from GTK and
|
||||
// so what needs to be dispatched when the scheduled task is run. It is
|
||||
// eDragTaskNone when there is no task scheduled (but the
|
||||
// previous task may still not have finished running).
|
||||
enum DragTask {
|
||||
eDragTaskNone,
|
||||
eDragTaskMotion,
|
||||
eDragTaskLeave,
|
||||
eDragTaskDrop
|
||||
};
|
||||
DragTask mScheduledTask;
|
||||
// mTaskSource is the GSource id for the task that is either scheduled
|
||||
// or currently running. It is 0 if no task is scheduled or running.
|
||||
guint mTaskSource;
|
||||
|
||||
// the last widget that was the target of a drag
|
||||
GtkWidget *mTargetWidget;
|
||||
GdkDragContext *mTargetDragContext;
|
||||
// target/destination side vars
|
||||
// These variables keep track of the state of the current drag.
|
||||
|
||||
// mPendingWindow, mPendingWindowPoint, mPendingDragContext, and
|
||||
// mPendingTime, carry information from the GTK signal that will be used
|
||||
// when the scheduled task is run. mPendingWindow and mPendingDragContext
|
||||
// will be NULL if the scheduled task is eDragTaskLeave.
|
||||
nsRefPtr<nsWindow> mPendingWindow;
|
||||
nsIntPoint mPendingWindowPoint;
|
||||
nsCountedRef<GdkDragContext> mPendingDragContext;
|
||||
guint mPendingTime;
|
||||
|
||||
// mTargetWindow and mTargetWindowPoint record the position of the last
|
||||
// eDragTaskMotion or eDragTaskDrop task that was run or is still running.
|
||||
// mTargetWindow is cleared once the drag has completed or left.
|
||||
nsRefPtr<nsWindow> mTargetWindow;
|
||||
nsIntPoint mTargetWindowPoint;
|
||||
// mTargetWidget and mTargetDragContext are set only while dispatching
|
||||
// motion or drop events. mTime records the corresponding timestamp.
|
||||
nsCountedRef<GtkWidget> mTargetWidget;
|
||||
nsCountedRef<GdkDragContext> mTargetDragContext;
|
||||
guint mTargetTime;
|
||||
|
||||
// is it OK to drop on us?
|
||||
bool mCanDrop;
|
||||
|
||||
// have we received our drag data?
|
||||
bool mTargetDragDataReceived;
|
||||
// last data received and its length
|
||||
|
@ -161,6 +237,13 @@ private:
|
|||
PRInt32 aYOffset,
|
||||
const nsIntRect &dragRect);
|
||||
|
||||
gboolean Schedule(DragTask aTask, nsWindow *aWindow,
|
||||
GdkDragContext *aDragContext,
|
||||
nsIntPoint aWindowPoint, guint aTime);
|
||||
|
||||
// Callback for g_idle_add_full() to run mScheduledTask.
|
||||
static gboolean TaskDispatchCallback(gpointer data);
|
||||
gboolean RunScheduledTask();
|
||||
};
|
||||
|
||||
#endif // nsDragService_h__
|
||||
|
|
|
@ -272,9 +272,6 @@ static void drag_data_received_event_cb(GtkWidget *aWidget,
|
|||
/* initialization static functions */
|
||||
static nsresult initialize_prefs (void);
|
||||
|
||||
// this is the last window that had a drag event happen on it.
|
||||
nsWindow *nsWindow::sLastDragMotionWindow = NULL;
|
||||
|
||||
// Time of the last button release event. We use it to detect when the
|
||||
// drag ended before we could properly setup drag and drop.
|
||||
static guint32 sLastButtonReleaseTime = 0;
|
||||
|
@ -433,9 +430,6 @@ nsWindow::nsWindow()
|
|||
nsWindow::~nsWindow()
|
||||
{
|
||||
LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
|
||||
if (sLastDragMotionWindow == this) {
|
||||
sLastDragMotionWindow = NULL;
|
||||
}
|
||||
|
||||
delete[] mTransparencyBitmap;
|
||||
mTransparencyBitmap = nsnull;
|
||||
|
@ -687,6 +681,11 @@ nsWindow::Destroy(void)
|
|||
gRollupListener = nsnull;
|
||||
}
|
||||
|
||||
nsDragService *dragService = nsDragService::GetInstance();
|
||||
if (this == dragService->GetMostRecentDestWindow()) {
|
||||
dragService->ScheduleLeaveEvent();
|
||||
}
|
||||
|
||||
NativeShow(false);
|
||||
|
||||
if (mIMModule) {
|
||||
|
@ -715,11 +714,6 @@ nsWindow::Destroy(void)
|
|||
// the surface after its X Window.
|
||||
mThebesSurface = nsnull;
|
||||
|
||||
if (mDragLeaveTimer) {
|
||||
mDragLeaveTimer->Cancel();
|
||||
mDragLeaveTimer = nsnull;
|
||||
}
|
||||
|
||||
GtkWidget *owningWidget = GetMozContainerWidget();
|
||||
if (mShell) {
|
||||
gtk_widget_destroy(mShell);
|
||||
|
@ -3309,35 +3303,6 @@ nsWindow::ThemeChanged()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::CheckNeedDragLeave(nsWindow* aInnerMostWidget,
|
||||
nsIDragService* aDragService,
|
||||
GdkDragContext *aDragContext,
|
||||
nscoord aX, nscoord aY)
|
||||
{
|
||||
// check to see if there was a drag motion window already in place
|
||||
if (sLastDragMotionWindow) {
|
||||
// same as the last window so no need for dragleave event
|
||||
if (sLastDragMotionWindow == aInnerMostWidget) {
|
||||
UpdateDragStatus(aDragContext, aDragService);
|
||||
return;
|
||||
}
|
||||
|
||||
// send a dragleave event to the last window that got a motion event
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip = sLastDragMotionWindow;
|
||||
sLastDragMotionWindow->OnDragLeave();
|
||||
}
|
||||
|
||||
// Make sure that the drag service knows we're now dragging
|
||||
aDragService->StartDragSession();
|
||||
|
||||
// update our drag status
|
||||
UpdateDragStatus(aDragContext, aDragService);
|
||||
|
||||
// set the last window to the innerMostWidget
|
||||
sLastDragMotionWindow = aInnerMostWidget;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::DispatchDragMotionEvents(nsDragService *aDragService,
|
||||
const nsIntPoint& aWindowPoint, guint aTime)
|
||||
|
@ -3386,170 +3351,6 @@ nsWindow::DispatchDragEvent(PRUint32 aMsg, const nsIntPoint& aRefPoint,
|
|||
DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsWindow::OnDragMotionEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
gint aX,
|
||||
gint aY,
|
||||
guint aTime,
|
||||
gpointer aData)
|
||||
{
|
||||
LOGDRAG(("nsWindow::OnDragMotionSignal\n"));
|
||||
|
||||
// get our drag context
|
||||
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
|
||||
nsDragService *dragServiceGTK =
|
||||
static_cast<nsDragService*>(dragService.get());
|
||||
|
||||
// first, figure out which internal widget this drag motion actually
|
||||
// happened on
|
||||
nscoord retx = 0;
|
||||
nscoord rety = 0;
|
||||
|
||||
GdkWindow *innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
|
||||
&retx, &rety);
|
||||
nsRefPtr<nsWindow> innerMostWidget = get_window_for_gdk_window(innerWindow);
|
||||
|
||||
if (!innerMostWidget)
|
||||
innerMostWidget = this;
|
||||
|
||||
// clear any drag leave timer that might be pending so that it
|
||||
// doesn't get processed when we actually go out to get data.
|
||||
if (mDragLeaveTimer) {
|
||||
mDragLeaveTimer->Cancel();
|
||||
mDragLeaveTimer = nsnull;
|
||||
}
|
||||
|
||||
CheckNeedDragLeave(innerMostWidget, dragService, aDragContext, retx, rety);
|
||||
|
||||
// update the drag context
|
||||
dragServiceGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
|
||||
|
||||
innerMostWidget->
|
||||
DispatchDragMotionEvents(dragServiceGTK, nsIntPoint(retx, rety), aTime);
|
||||
|
||||
// Reply to tell the source whether we can drop and what action would be
|
||||
// taken.
|
||||
dragServiceGTK->TargetEndDragMotion(aWidget, aDragContext, aTime);
|
||||
|
||||
// and unset our context
|
||||
dragServiceGTK->TargetSetLastContext(0, 0, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnDragLeaveEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
guint aTime,
|
||||
gpointer aData)
|
||||
{
|
||||
// XXX Do we want to pass this on only if the event's subwindow is null?
|
||||
|
||||
LOGDRAG(("nsWindow::OnDragLeaveSignal(%p)\n", (void*)this));
|
||||
|
||||
if (mDragLeaveTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
// create a fast timer - we're delaying the drag leave until the
|
||||
// next mainloop in hopes that we might be able to get a drag drop
|
||||
// signal
|
||||
mDragLeaveTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
NS_ASSERTION(mDragLeaveTimer, "Failed to create drag leave timer!");
|
||||
// fire this baby asafp, but not too quickly... see bug 216800 ;-)
|
||||
mDragLeaveTimer->InitWithFuncCallback(DragLeaveTimerCallback,
|
||||
(void *)this,
|
||||
20, nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsWindow::OnDragDropEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
gint aX,
|
||||
gint aY,
|
||||
guint aTime,
|
||||
gpointer aData)
|
||||
|
||||
{
|
||||
LOGDRAG(("nsWindow::OnDragDropSignal\n"));
|
||||
|
||||
// get our drag context
|
||||
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
|
||||
nsDragService *dragServiceGTK = static_cast<nsDragService*>(dragService.get());
|
||||
|
||||
dragServiceGTK->SetDragEndPoint(nsIntPoint(aX, aY) + WidgetToScreenOffset());
|
||||
|
||||
nscoord retx = 0;
|
||||
nscoord rety = 0;
|
||||
|
||||
GdkWindow *innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
|
||||
&retx, &rety);
|
||||
nsRefPtr<nsWindow> innerMostWidget = get_window_for_gdk_window(innerWindow);
|
||||
|
||||
if (!innerMostWidget)
|
||||
innerMostWidget = this;
|
||||
|
||||
// clear any drag leave timer that might be pending so that it
|
||||
// doesn't get processed when we actually go out to get data.
|
||||
if (mDragLeaveTimer) {
|
||||
mDragLeaveTimer->Cancel();
|
||||
mDragLeaveTimer = nsnull;
|
||||
}
|
||||
|
||||
CheckNeedDragLeave(innerMostWidget, dragService, aDragContext, retx, rety);
|
||||
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
|
||||
// (as at 27 December 2010) indicates that a "drop" event should only be
|
||||
// fired (at the current target element) if the current drag operation is
|
||||
// not none. The current drag operation will only be set to a non-none
|
||||
// value during a "dragover" event.
|
||||
//
|
||||
// If the user has ended the drag before any dragover events have been
|
||||
// sent, then the spec recommends skipping the drop (because the current
|
||||
// drag operation is none). However, here we assume that, by releasing
|
||||
// the mouse button, the user has indicated that they want to drop, so we
|
||||
// proceed with the drop where possible.
|
||||
//
|
||||
// In order to make the events appear to content in the same way as if the
|
||||
// spec is being followed we make sure to dispatch a "dragover" event with
|
||||
// appropriate coordinates and check canDrop before the "drop" event.
|
||||
//
|
||||
// When the Xdnd protocol is used for source/destination communication (as
|
||||
// should be the case with GTK source applications) a dragover event
|
||||
// should have already been sent during the drag-motion signal, which
|
||||
// would have already been received because XdndDrop messages do not
|
||||
// contain a position. However, we can't assume the same when the Motif
|
||||
// protocol is used.
|
||||
|
||||
dragServiceGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
|
||||
|
||||
innerMostWidget->
|
||||
DispatchDragMotionEvents(dragServiceGTK, nsIntPoint(retx, rety), aTime);
|
||||
|
||||
gboolean success = innerMostWidget->
|
||||
DispatchDragDropEvent(dragServiceGTK, nsIntPoint(retx, rety), aTime);
|
||||
|
||||
// before we unset the context we need to do a drop_finish
|
||||
|
||||
gdk_drop_finish(aDragContext, success, aTime);
|
||||
|
||||
// after a drop takes place we need to make sure that the drag
|
||||
// service doesn't think that it still has a context. if the other
|
||||
// way ( besides the drop ) to end a drag event is during the leave
|
||||
// event and and that case is handled in that handler.
|
||||
dragServiceGTK->TargetSetLastContext(0, 0, 0);
|
||||
|
||||
// clear the sLastDragMotion window
|
||||
sLastDragMotionWindow = 0;
|
||||
|
||||
// Make sure to end the drag session. If this drag started in a
|
||||
// different app, we won't get a drag_end signal to end it from.
|
||||
dragService->EndDragSession(true);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
|
@ -5910,7 +5711,7 @@ nsWindow::InitDragEvent(nsDragEvent &aEvent)
|
|||
// drag context. Gtk gets this from a combination of the key settings
|
||||
// and what the source is offering.
|
||||
|
||||
void
|
||||
/* static */ void
|
||||
nsWindow::UpdateDragStatus(GdkDragContext *aDragContext,
|
||||
nsIDragService *aDragService)
|
||||
{
|
||||
|
@ -5973,9 +5774,24 @@ drag_motion_event_cb(GtkWidget *aWidget,
|
|||
}
|
||||
}
|
||||
|
||||
return window->OnDragMotionEvent(aWidget,
|
||||
aDragContext,
|
||||
aX, aY, aTime, aData);
|
||||
// figure out which internal widget this drag motion actually happened on
|
||||
nscoord retx = 0;
|
||||
nscoord rety = 0;
|
||||
|
||||
GdkWindow *innerWindow =
|
||||
get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
|
||||
&retx, &rety);
|
||||
nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
|
||||
|
||||
if (!innerMostWindow) {
|
||||
innerMostWindow = window;
|
||||
}
|
||||
|
||||
LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
|
||||
|
||||
return nsDragService::GetInstance()->
|
||||
ScheduleMotionEvent(innerMostWindow, aDragContext,
|
||||
nsIntPoint(aX, aY), aTime);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -5988,7 +5804,27 @@ drag_leave_event_cb(GtkWidget *aWidget,
|
|||
if (!window)
|
||||
return;
|
||||
|
||||
window->OnDragLeaveEvent(aWidget, aDragContext, aTime, aData);
|
||||
nsDragService *dragService = nsDragService::GetInstance();
|
||||
|
||||
nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
|
||||
if (!mostRecentDragWindow) {
|
||||
NS_WARNING("Spurious drag leave signal");
|
||||
return;
|
||||
}
|
||||
|
||||
GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
|
||||
if (aWidget != mozContainer)
|
||||
{
|
||||
// When the drag moves between widgets, GTK can send leave signal for
|
||||
// the old widget after the motion or drop signal for the new widget.
|
||||
// We'll send the leave event when the motion or drop event is run.
|
||||
return;
|
||||
}
|
||||
|
||||
LOGDRAG(("nsWindow drag-leave signal for %p\n",
|
||||
(void*)mostRecentDragWindow));
|
||||
|
||||
dragService->ScheduleLeaveEvent();
|
||||
}
|
||||
|
||||
|
||||
|
@ -6004,9 +5840,24 @@ drag_drop_event_cb(GtkWidget *aWidget,
|
|||
if (!window)
|
||||
return FALSE;
|
||||
|
||||
return window->OnDragDropEvent(aWidget,
|
||||
aDragContext,
|
||||
aX, aY, aTime, aData);
|
||||
// figure out which internal widget this drag motion actually happened on
|
||||
nscoord retx = 0;
|
||||
nscoord rety = 0;
|
||||
|
||||
GdkWindow *innerWindow =
|
||||
get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
|
||||
&retx, &rety);
|
||||
nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
|
||||
|
||||
if (!innerMostWindow) {
|
||||
innerMostWindow = window;
|
||||
}
|
||||
|
||||
LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
|
||||
|
||||
return nsDragService::GetInstance()->
|
||||
ScheduleDropEvent(innerMostWindow, aDragContext,
|
||||
nsIntPoint(aX, aY), aTime);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -6041,30 +5892,6 @@ initialize_prefs(void)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::FireDragLeaveTimer(void)
|
||||
{
|
||||
LOGDRAG(("nsWindow::FireDragLeaveTimer(%p)\n", (void*)this));
|
||||
|
||||
mDragLeaveTimer = nsnull;
|
||||
|
||||
// clean up any pending drag motion window info
|
||||
if (sLastDragMotionWindow) {
|
||||
nsRefPtr<nsWindow> kungFuDeathGrip = sLastDragMotionWindow;
|
||||
// send our leave signal
|
||||
sLastDragMotionWindow->OnDragLeave();
|
||||
sLastDragMotionWindow = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsWindow::DragLeaveTimerCallback(nsITimer *aTimer, void *aClosure)
|
||||
{
|
||||
nsRefPtr<nsWindow> window = static_cast<nsWindow *>(aClosure);
|
||||
window->FireDragLeaveTimer();
|
||||
}
|
||||
|
||||
static GdkWindow *
|
||||
get_inner_gdk_window (GdkWindow *aWindow,
|
||||
gint x, gint y,
|
||||
|
|
|
@ -237,22 +237,6 @@ public:
|
|||
GdkEventVisibility *aEvent);
|
||||
void OnWindowStateEvent(GtkWidget *aWidget,
|
||||
GdkEventWindowState *aEvent);
|
||||
gboolean OnDragMotionEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
gint aX,
|
||||
gint aY,
|
||||
guint aTime,
|
||||
gpointer aData);
|
||||
void OnDragLeaveEvent(GtkWidget * aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
guint aTime,
|
||||
gpointer aData);
|
||||
gboolean OnDragDropEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
gint aX,
|
||||
gint aY,
|
||||
guint aTime,
|
||||
gpointer aData);
|
||||
void OnDragDataReceivedEvent(GtkWidget *aWidget,
|
||||
GdkDragContext *aDragContext,
|
||||
gint aX,
|
||||
|
@ -297,11 +281,6 @@ public:
|
|||
|
||||
void ThemeChanged(void);
|
||||
|
||||
void CheckNeedDragLeave(nsWindow* aInnerMostWidget,
|
||||
nsIDragService* aDragService,
|
||||
GdkDragContext *aDragContext,
|
||||
nscoord aX, nscoord aY);
|
||||
|
||||
#ifdef MOZ_X11
|
||||
Window mOldFocusWindow;
|
||||
#endif /* MOZ_X11 */
|
||||
|
@ -312,6 +291,9 @@ public:
|
|||
NS_IMETHOD BeginMoveDrag(nsMouseEvent* aEvent);
|
||||
|
||||
MozContainer* GetMozContainer() { return mContainer; }
|
||||
// GetMozContainerWidget returns the MozContainer even for undestroyed
|
||||
// descendant windows
|
||||
GtkWidget* GetMozContainerWidget();
|
||||
GdkWindow* GetGdkWindow() { return mGdkWindow; }
|
||||
bool IsDestroyed() { return mIsDestroyed; }
|
||||
|
||||
|
@ -324,6 +306,8 @@ public:
|
|||
gboolean DispatchDragDropEvent(nsDragService *aDragService,
|
||||
const nsIntPoint& aWindowPoint,
|
||||
guint aTime);
|
||||
static void UpdateDragStatus (GdkDragContext *aDragContext,
|
||||
nsIDragService *aDragService);
|
||||
// If this dispatched the keydown event actually, this returns TRUE,
|
||||
// otherwise, FALSE.
|
||||
bool DispatchKeyDownEvent(GdkEventKey *aEvent,
|
||||
|
@ -393,7 +377,6 @@ protected:
|
|||
private:
|
||||
void DestroyChildWindows();
|
||||
void GetToplevelWidget(GtkWidget **aWidget);
|
||||
GtkWidget *GetMozContainerWidget();
|
||||
nsWindow *GetContainerWindow();
|
||||
void SetUrgencyHint(GtkWidget *top_window, bool state);
|
||||
void *SetupPluginPort(void);
|
||||
|
@ -493,13 +476,8 @@ private:
|
|||
gchar* mTransparencyBitmap;
|
||||
|
||||
// all of our DND stuff
|
||||
// this is the last window that had a drag event happen on it.
|
||||
static nsWindow *sLastDragMotionWindow;
|
||||
void InitDragEvent (nsDragEvent &aEvent);
|
||||
void UpdateDragStatus (GdkDragContext *aDragContext,
|
||||
nsIDragService *aDragService);
|
||||
|
||||
nsCOMPtr<nsITimer> mDragLeaveTimer;
|
||||
float mLastMotionPressure;
|
||||
|
||||
// Remember the last sizemode so that we can restore it when
|
||||
|
@ -508,9 +486,6 @@ private:
|
|||
|
||||
static bool DragInProgress(void);
|
||||
|
||||
void FireDragLeaveTimer (void);
|
||||
static void DragLeaveTimerCallback (nsITimer *aTimer, void *aClosure);
|
||||
|
||||
void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче