b=495343 reimplement periodic dragover events such that the source expects feedback from the destination r+a=roc

--HG--
extra : transplant_source : %5D%2C%F8%7E%14%EA8%D6%B4W%92%18%C2%D1%C4%3A9%B0i%EE
This commit is contained in:
Karl Tomlinson 2011-01-27 14:58:55 +13:00
Родитель d3d16bf377
Коммит 356adb2767
2 изменённых файлов: 120 добавлений и 0 удалений

Просмотреть файл

@ -82,7 +82,19 @@ enum {
MOZ_GTK_DRAG_RESULT_NO_TARGET MOZ_GTK_DRAG_RESULT_NO_TARGET
}; };
// Some gobject functions expect functions for gpointer arguments.
// gpointer is void* but C++ doesn't like casting functions to void*.
template<class T> static inline gpointer
FuncToGpointer(T aFunction)
{
return reinterpret_cast<gpointer>
(reinterpret_cast<uintptr_t>
// This cast just provides a warning if T is not a function.
(reinterpret_cast<void (*)()>(aFunction)));
}
static PRLogModuleInfo *sDragLm = NULL; static PRLogModuleInfo *sDragLm = NULL;
static guint sMotionEventTimerID;
static const char gMimeListType[] = "application/x-moz-internal-item-list"; static const char gMimeListType[] = "application/x-moz-internal-item-list";
static const char gMozUrlType[] = "_NETSCAPE_URL"; static const char gMozUrlType[] = "_NETSCAPE_URL";
@ -141,6 +153,7 @@ nsDragService::nsDragService()
if (!sDragLm) if (!sDragLm)
sDragLm = PR_NewLogModule("nsDragService"); sDragLm = PR_NewLogModule("nsDragService");
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService")); PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService"));
mGrabWidget = 0;
mTargetWidget = 0; mTargetWidget = 0;
mTargetDragContext = 0; mTargetDragContext = 0;
mTargetTime = 0; mTargetTime = 0;
@ -180,6 +193,88 @@ nsDragService::Observe(nsISupports *aSubject, const char *aTopic,
return NS_OK; return NS_OK;
} }
// Support for periodic drag events
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
// and the Xdnd protocol both recommend that drag events are sent periodically,
// but GTK does not normally provide this.
//
// Here GTK is periodically stimulated by copies of the most recent mouse
// motion events so as to send drag position messages to the destination when
// appropriate (after it has received a status event from the previous
// message).
//
// (If events were sent only on the destination side then the destination
// would have no message to which it could reply with a drag status. Without
// sending a drag status to the source, the destination would not be able to
// change its feedback re whether it could accept the drop, and so the
// source's behavior on drop will not be consistent.)
struct MotionEventData {
MotionEventData(GtkWidget *aWidget, GdkEvent *aEvent)
: mWidget(aWidget), mEvent(gdk_event_copy(aEvent))
{
MOZ_COUNT_CTOR(MotionEventData);
g_object_ref(mWidget);
}
~MotionEventData()
{
MOZ_COUNT_DTOR(MotionEventData);
g_object_unref(mWidget);
gdk_event_free(mEvent);
}
GtkWidget *mWidget;
GdkEvent *mEvent;
};
static void
DestroyMotionEventData(gpointer data)
{
delete static_cast<MotionEventData*>(data);
}
static gboolean
DispatchMotionEventCopy(gpointer aData)
{
MotionEventData *data = static_cast<MotionEventData*>(aData);
// Clear the timer id before OnSourceGrabEventAfter is called during event dispatch.
sMotionEventTimerID = 0;
// If there is no longer a grab on the widget, then the drag is over and
// there is no need to continue drag motion.
if (gtk_grab_get_current() == data->mWidget) {
gtk_propagate_event(data->mWidget, data->mEvent);
}
// Cancel this timer;
// We've already started another if the motion event was dispatched.
return FALSE;
}
static void
OnSourceGrabEventAfter(GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
if (event->type != GDK_MOTION_NOTIFY)
return;
if (sMotionEventTimerID) {
g_source_remove(sMotionEventTimerID);
}
MotionEventData *data = new MotionEventData(widget, event);
// G_PRIORITY_DEFAULT_IDLE is lower priority than GDK's redraw idle source
// and lower than GTK's idle source that sends drag position messages after
// motion-notify signals.
//
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
// recommends an interval of 350ms +/- 200ms.
sMotionEventTimerID =
g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 350,
DispatchMotionEventCopy, data, DestroyMotionEventData);
}
// nsIDragService // nsIDragService
NS_IMETHODIMP NS_IMETHODIMP
@ -270,6 +365,16 @@ nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
if (needsFallbackIcon) if (needsFallbackIcon)
gtk_drag_set_icon_default(context); gtk_drag_set_icon_default(context);
// GTK uses another hidden window for receiving mouse events.
mGrabWidget = gtk_grab_get_current();
if (mGrabWidget) {
g_object_ref(mGrabWidget);
// Only motion events are required but connect to
// "event-after" as this is never blocked by other handlers.
g_signal_connect(mGrabWidget, "event-after",
G_CALLBACK(OnSourceGrabEventAfter), NULL);
}
} }
gtk_target_list_unref(sourceList); gtk_target_list_unref(sourceList);
@ -341,6 +446,19 @@ nsDragService::EndDragSession(PRBool aDoneDrag)
{ {
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession %d", PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession %d",
aDoneDrag)); aDoneDrag));
if (mGrabWidget) {
g_signal_handlers_disconnect_by_func(mGrabWidget,
FuncToGpointer(OnSourceGrabEventAfter), NULL);
g_object_unref(mGrabWidget);
mGrabWidget = NULL;
if (sMotionEventTimerID) {
g_source_remove(sMotionEventTimerID);
sMotionEventTimerID = 0;
}
}
// unset our drag action // unset our drag action
SetDragAction(DRAGDROP_ACTION_NONE); SetDragAction(DRAGDROP_ACTION_NONE);
return nsBaseDragService::EndDragSession(aDoneDrag); return nsBaseDragService::EndDragSession(aDoneDrag);

Просмотреть файл

@ -138,6 +138,8 @@ private:
// the source of our drags // the source of our drags
GtkWidget *mHiddenWidget; GtkWidget *mHiddenWidget;
// the widget receiving mouse events
GtkWidget *mGrabWidget;
// our source data items // our source data items
nsCOMPtr<nsISupportsArray> mSourceDataItems; nsCOMPtr<nsISupportsArray> mSourceDataItems;
// get a list of the sources in gtk's format // get a list of the sources in gtk's format