1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/dnd.cpp
3 // Purpose: wxDropTarget class
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_DRAG_AND_DROP
13
14 #include "wx/dnd.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/intl.h"
18 #include "wx/log.h"
19 #include "wx/app.h"
20 #include "wx/utils.h"
21 #include "wx/window.h"
22 #include "wx/gdicmn.h"
23 #endif
24
25 #include "wx/scopeguard.h"
26
27 #include "wx/gtk/private/wrapgtk.h"
28
29 //----------------------------------------------------------------------------
30 // global data
31 //----------------------------------------------------------------------------
32
33 extern bool g_blockEventsOnDrag;
34
35 // the flags used for the last DoDragDrop()
36 static long gs_flagsForDrag = 0;
37
38 // the trace mask we use with wxLogTrace() - call
39 // wxLog::AddTraceMask(TRACE_DND) to enable the trace messages from here
40 // (there are quite a few of them, so don't enable this by default)
41 #define TRACE_DND "dnd"
42
43 // global variables because GTK+ DnD want to have the
44 // mouse event that caused it
45 extern GdkEvent *g_lastMouseEvent;
46 extern int g_lastButtonNumber;
47
48 //----------------------------------------------------------------------------
49 // standard icons
50 //----------------------------------------------------------------------------
51
52 /* Copyright (c) Julian Smart */
53 static const char * page_xpm[] = {
54 /* columns rows colors chars-per-pixel */
55 "32 32 37 1",
56 "5 c #7198D9",
57 ", c #769CDA",
58 "2 c #DCE6F6",
59 "i c #FFFFFF",
60 "e c #779DDB",
61 ": c #9AB6E4",
62 "9 c #EAF0FA",
63 "- c #B1C7EB",
64 "$ c #6992D7",
65 "y c #F7F9FD",
66 "= c #BED0EE",
67 "q c #F0F5FC",
68 "; c #A8C0E8",
69 "@ c #366BC2",
70 " c None",
71 "u c #FDFEFF",
72 "8 c #5987D3",
73 "* c #C4D5F0",
74 "7 c #7CA0DC",
75 "O c #487BCE",
76 "< c #6B94D7",
77 "& c #CCDAF2",
78 "> c #89A9DF",
79 "3 c #5584D1",
80 "w c #82A5DE",
81 "1 c #3F74CB",
82 "+ c #3A70CA",
83 ". c #3569BF",
84 "% c #D2DFF4",
85 "# c #3366BB",
86 "r c #F5F8FD",
87 "0 c #FAFCFE",
88 "4 c #DFE8F7",
89 "X c #5E8AD4",
90 "o c #5282D0",
91 "t c #B8CCEC",
92 "6 c #E5EDF9",
93 /* pixels */
94 " ",
95 " ",
96 " ",
97 " ",
98 " ",
99 " .XXXooOO++@# ",
100 " $%&*=-;::>,<1 ",
101 " $2%&*=-;::><:3 ",
102 " $42%&*=-;::<&:3 ",
103 " 56477<<<<8<<9&:X ",
104 " 59642%&*=-;<09&:5 ",
105 " 5q9642%&*=-<<<<<# ",
106 " 5qqw777<<<<<88:>+ ",
107 " erqq9642%&*=t;::+ ",
108 " eyrqq9642%&*=t;:O ",
109 " eyywwww777<<<<t;O ",
110 " e0yyrqq9642%&*=to ",
111 " e00yyrqq9642%&*=o ",
112 " eu0wwwwwww777<&*X ",
113 " euu00yyrqq9642%&X ",
114 " eiuu00yyrqq9642%X ",
115 " eiiwwwwwwwwww742$ ",
116 " eiiiuu00yyrqq964$ ",
117 " eiiiiuu00yyrqq96$ ",
118 " eiiiiiuu00yyrqq95 ",
119 " eiiiiiiuu00yyrqq5 ",
120 " eeeeeeeeeeeeee55e ",
121 " ",
122 " ",
123 " ",
124 " ",
125 " "
126 };
127
128
129 // ============================================================================
130 // private functions
131 // ============================================================================
132
133 // ----------------------------------------------------------------------------
134 // convert between GTK+ and wxWidgets DND constants
135 // ----------------------------------------------------------------------------
136
ConvertFromGTK(long action)137 static wxDragResult ConvertFromGTK(long action)
138 {
139 switch ( action )
140 {
141 case GDK_ACTION_COPY:
142 return wxDragCopy;
143
144 case GDK_ACTION_LINK:
145 return wxDragLink;
146
147 case GDK_ACTION_MOVE:
148 return wxDragMove;
149 }
150
151 return wxDragNone;
152 }
153
154 // ----------------------------------------------------------------------------
155 // "drag_leave"
156 // ----------------------------------------------------------------------------
157
158 extern "C" {
target_drag_leave(GtkWidget * WXUNUSED (widget),GdkDragContext * context,guint WXUNUSED (time),wxDropTarget * drop_target)159 static void target_drag_leave( GtkWidget *WXUNUSED(widget),
160 GdkDragContext *context,
161 guint WXUNUSED(time),
162 wxDropTarget *drop_target )
163 {
164 /* inform the wxDropTarget about the current GdkDragContext.
165 this is only valid for the duration of this call */
166 drop_target->GTKSetDragContext( context );
167
168 /* we don't need return values. this event is just for
169 information */
170 drop_target->OnLeave();
171
172 /* this has to be done because GDK has no "drag_enter" event */
173 drop_target->m_firstMotion = true;
174
175 /* after this, invalidate the drop_target's GdkDragContext */
176 drop_target->GTKSetDragContext( NULL );
177 }
178 }
179
180 // ----------------------------------------------------------------------------
181 // "drag_motion"
182 // ----------------------------------------------------------------------------
183
184 extern "C" {
target_drag_motion(GtkWidget * WXUNUSED (widget),GdkDragContext * context,gint x,gint y,guint time,wxDropTarget * drop_target)185 static gboolean target_drag_motion( GtkWidget *WXUNUSED(widget),
186 GdkDragContext *context,
187 gint x,
188 gint y,
189 guint time,
190 wxDropTarget *drop_target )
191 {
192 /* Owen Taylor: "if the coordinates not in a drop zone,
193 return FALSE, otherwise call gtk_drag_status() and
194 return TRUE" */
195
196 #if 0
197 wxPrintf( "motion\n" );
198 GList *tmp_list;
199 for (tmp_list = context->targets; tmp_list; tmp_list = tmp_list->next)
200 {
201 wxString atom = wxString::FromAscii( gdk_atom_name (GDK_POINTER_TO_ATOM (tmp_list->data)) );
202 wxPrintf( "Atom: %s\n", atom );
203 }
204 #endif
205
206 // Inform the wxDropTarget about the current GdkDragContext.
207 // This is only valid for the duration of this call.
208 drop_target->GTKSetDragContext( context );
209
210 // Does the source actually accept the data type?
211 if (drop_target->GTKGetMatchingPair() == (GdkAtom) 0)
212 {
213 drop_target->GTKSetDragContext( NULL );
214 return FALSE;
215 }
216
217 wxDragResult suggested_action = drop_target->GTKFigureOutSuggestedAction();
218
219 wxDragResult result = wxDragNone;
220
221 if (drop_target->m_firstMotion)
222 {
223 // the first "drag_motion" event substitutes a "drag_enter" event
224 result = drop_target->OnEnter( x, y, suggested_action );
225 }
226 else
227 {
228 // give program a chance to react (i.e. to say no by returning FALSE)
229 result = drop_target->OnDragOver( x, y, suggested_action );
230 }
231
232 GdkDragAction result_action = GDK_ACTION_DEFAULT;
233 if (result == wxDragCopy)
234 result_action = GDK_ACTION_COPY;
235 else if (result == wxDragLink)
236 result_action = GDK_ACTION_LINK;
237 else
238 result_action = GDK_ACTION_MOVE;
239
240 // is result action actually supported
241 bool ret = (result_action != GDK_ACTION_DEFAULT) &&
242 (gdk_drag_context_get_actions(context) & result_action);
243
244 if (ret)
245 gdk_drag_status( context, result_action, time );
246
247 // after this, invalidate the drop_target's GdkDragContext
248 drop_target->GTKSetDragContext( NULL );
249
250 // this has to be done because GDK has no "drag_enter" event
251 drop_target->m_firstMotion = false;
252
253 return ret;
254 }
255 }
256
257 // ----------------------------------------------------------------------------
258 // "drag_drop"
259 // ----------------------------------------------------------------------------
260
261 extern "C" {
target_drag_drop(GtkWidget * widget,GdkDragContext * context,gint x,gint y,guint time,wxDropTarget * drop_target)262 static gboolean target_drag_drop( GtkWidget *widget,
263 GdkDragContext *context,
264 gint x,
265 gint y,
266 guint time,
267 wxDropTarget *drop_target )
268 {
269 /* Owen Taylor: "if the drop is not in a drop zone,
270 return FALSE, otherwise, if you aren't accepting
271 the drop, call gtk_drag_finish() with success == FALSE
272 otherwise call gtk_drag_data_get()" */
273
274 /* inform the wxDropTarget about the current GdkDragContext.
275 this is only valid for the duration of this call */
276 drop_target->GTKSetDragContext( context );
277
278 // Does the source actually accept the data type?
279 if (drop_target->GTKGetMatchingPair() == (GdkAtom) 0)
280 {
281 // cancel the whole thing
282 gtk_drag_finish( context,
283 FALSE, // no success
284 FALSE, // don't delete data on dropping side
285 time );
286
287 drop_target->GTKSetDragContext( NULL );
288
289 drop_target->m_firstMotion = true;
290
291 return FALSE;
292 }
293
294 /* inform the wxDropTarget about the current drag widget.
295 this is only valid for the duration of this call */
296 drop_target->GTKSetDragWidget( widget );
297
298 /* inform the wxDropTarget about the current drag time.
299 this is only valid for the duration of this call */
300 drop_target->GTKSetDragTime( time );
301
302 /* reset the block here as someone might very well
303 show a dialog as a reaction to a drop and this
304 wouldn't work without events */
305 g_blockEventsOnDrag = false;
306
307 bool ret = drop_target->OnDrop( x, y );
308
309 if (!ret)
310 {
311 wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned FALSE") );
312
313 /* cancel the whole thing */
314 gtk_drag_finish( context,
315 FALSE, /* no success */
316 FALSE, /* don't delete data on dropping side */
317 time );
318 }
319 else
320 {
321 wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned true") );
322
323 GdkAtom format = drop_target->GTKGetMatchingPair();
324
325 // this does happen somehow, see bug 555111
326 wxCHECK_MSG( format, FALSE, wxT("no matching GdkAtom for format?") );
327
328 /* this should trigger an "drag_data_received" event */
329 gtk_drag_get_data( widget,
330 context,
331 format,
332 time );
333 }
334
335 /* after this, invalidate the drop_target's GdkDragContext */
336 drop_target->GTKSetDragContext( NULL );
337
338 /* after this, invalidate the drop_target's drag widget */
339 drop_target->GTKSetDragWidget( NULL );
340
341 /* this has to be done because GDK has no "drag_enter" event */
342 drop_target->m_firstMotion = true;
343
344 return ret;
345 }
346 }
347
348 // ----------------------------------------------------------------------------
349 // "drag_data_received"
350 // ----------------------------------------------------------------------------
351
352 extern "C" {
target_drag_data_received(GtkWidget * WXUNUSED (widget),GdkDragContext * context,gint x,gint y,GtkSelectionData * data,guint WXUNUSED (info),guint time,wxDropTarget * drop_target)353 static void target_drag_data_received( GtkWidget *WXUNUSED(widget),
354 GdkDragContext *context,
355 gint x,
356 gint y,
357 GtkSelectionData *data,
358 guint WXUNUSED(info),
359 guint time,
360 wxDropTarget *drop_target )
361 {
362 /* Owen Taylor: "call gtk_drag_finish() with
363 success == TRUE" */
364
365 if (gtk_selection_data_get_length(data) <= 0 || gtk_selection_data_get_format(data) != 8)
366 {
367 /* negative data length and non 8-bit data format
368 qualifies for junk */
369 gtk_drag_finish (context, FALSE, FALSE, time);
370
371 return;
372 }
373
374 wxLogTrace(TRACE_DND, wxT( "Drop target: data received event") );
375
376 /* inform the wxDropTarget about the current GtkSelectionData.
377 this is only valid for the duration of this call */
378 drop_target->GTKSetDragData( data );
379
380 wxDragResult result = ConvertFromGTK(gdk_drag_context_get_selected_action(context));
381
382 if ( wxIsDragResultOk( drop_target->OnData( x, y, result ) ) )
383 {
384 wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned true") );
385
386 /* tell GTK that data transfer was successful */
387 gtk_drag_finish( context, TRUE, FALSE, time );
388 }
389 else
390 {
391 wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned FALSE") );
392
393 /* tell GTK that data transfer was not successful */
394 gtk_drag_finish( context, FALSE, FALSE, time );
395 }
396
397 /* after this, invalidate the drop_target's drag data */
398 drop_target->GTKSetDragData( NULL );
399 }
400 }
401
402 //----------------------------------------------------------------------------
403 // wxDropTarget
404 //----------------------------------------------------------------------------
405
wxDropTarget(wxDataObject * data)406 wxDropTarget::wxDropTarget( wxDataObject *data )
407 : wxDropTargetBase( data )
408 {
409 m_firstMotion = true;
410 m_dragContext = NULL;
411 m_dragWidget = NULL;
412 m_dragData = NULL;
413 m_dragTime = 0;
414 }
415
OnDragOver(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y),wxDragResult def)416 wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
417 wxCoord WXUNUSED(y),
418 wxDragResult def )
419 {
420 return def;
421 }
422
OnDrop(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y))423 bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
424 {
425 return true;
426 }
427
OnData(wxCoord WXUNUSED (x),wxCoord WXUNUSED (y),wxDragResult def)428 wxDragResult wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
429 wxDragResult def )
430 {
431 return GetData() ? def : wxDragNone;
432 }
433
GTKFigureOutSuggestedAction()434 wxDragResult wxDropTarget::GTKFigureOutSuggestedAction()
435 {
436 if (!m_dragContext)
437 return wxDragError;
438
439 // GTK+ always supposes that we want to copy the data by default while we
440 // might want to move it, so examine not only suggested_action - which is
441 // only good if we don't have our own preferences - but also the actions
442 // field
443 wxDragResult suggested_action = wxDragNone;
444 const GdkDragAction actions = gdk_drag_context_get_actions(m_dragContext);
445 if (GetDefaultAction() == wxDragNone)
446 {
447 // use default action set by wxDropSource::DoDragDrop()
448 if ( (gs_flagsForDrag & wxDrag_DefaultMove) == wxDrag_DefaultMove &&
449 (actions & GDK_ACTION_MOVE))
450 {
451 // move is requested by the program and allowed by GTK+ - do it, even
452 // though suggested_action may be currently wxDragCopy
453 suggested_action = wxDragMove;
454 }
455 else // use whatever GTK+ says we should
456 {
457 suggested_action = ConvertFromGTK(gdk_drag_context_get_suggested_action(m_dragContext));
458
459 #if 0
460 // RR: I don't understand the code below: if the drag comes from
461 // a different app, the gs_flagsForDrag is invalid; if it
462 // comes from the same wx app, then GTK+ hopefully won't
463 // suggest something we didn't allow in the frist place
464 // in DoDrop()
465 if ( (suggested_action == wxDragMove) && !(gs_flagsForDrag & wxDrag_AllowMove) )
466 {
467 // we're requested to move but we can't
468 suggested_action = wxDragCopy;
469 }
470 #endif
471 }
472 }
473 else if (GetDefaultAction() == wxDragMove &&
474 (actions & GDK_ACTION_MOVE))
475 {
476
477 suggested_action = wxDragMove;
478 }
479 else
480 {
481 if (actions & GDK_ACTION_COPY)
482 suggested_action = wxDragCopy;
483 else if (actions & GDK_ACTION_MOVE)
484 suggested_action = wxDragMove;
485 else if (actions & GDK_ACTION_LINK)
486 suggested_action = wxDragLink;
487 else
488 suggested_action = wxDragNone;
489 }
490
491 return suggested_action;
492 }
493
GetMatchingPair()494 wxDataFormat wxDropTarget::GetMatchingPair()
495 {
496 return wxDataFormat( GTKGetMatchingPair() );
497 }
498
GTKGetMatchingPair(bool quiet)499 GdkAtom wxDropTarget::GTKGetMatchingPair(bool quiet)
500 {
501 if (!m_dataObject)
502 return (GdkAtom) 0;
503
504 if (!m_dragContext)
505 return (GdkAtom) 0;
506
507 const GList* child = gdk_drag_context_list_targets(m_dragContext);
508 while (child)
509 {
510 GdkAtom formatAtom = (GdkAtom)(child->data);
511 wxDataFormat format( formatAtom );
512
513 if ( !quiet )
514 {
515 wxLogTrace(TRACE_DND, wxT("Drop target: drag has format: %s"),
516 format.GetId().c_str());
517 }
518
519 if (m_dataObject->IsSupportedFormat( format ))
520 return formatAtom;
521
522 child = child->next;
523 }
524
525 return (GdkAtom) 0;
526 }
527
GetData()528 bool wxDropTarget::GetData()
529 {
530 if (!m_dragData)
531 return false;
532
533 if (!m_dataObject)
534 return false;
535
536 wxDataFormat dragFormat(gtk_selection_data_get_target(m_dragData));
537
538 if (!m_dataObject->IsSupportedFormat( dragFormat ))
539 return false;
540
541 m_dataObject->SetData(dragFormat,
542 (size_t)gtk_selection_data_get_length(m_dragData),
543 (const void*)gtk_selection_data_get_data(m_dragData));
544
545 return true;
546 }
547
GtkUnregisterWidget(GtkWidget * widget)548 void wxDropTarget::GtkUnregisterWidget( GtkWidget *widget )
549 {
550 wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") );
551
552 gtk_drag_dest_unset( widget );
553
554 g_signal_handlers_disconnect_by_func (widget,
555 (gpointer) target_drag_leave, this);
556 g_signal_handlers_disconnect_by_func (widget,
557 (gpointer) target_drag_motion, this);
558 g_signal_handlers_disconnect_by_func (widget,
559 (gpointer) target_drag_drop, this);
560 g_signal_handlers_disconnect_by_func (widget,
561 (gpointer) target_drag_data_received, this);
562 }
563
GtkRegisterWidget(GtkWidget * widget)564 void wxDropTarget::GtkRegisterWidget( GtkWidget *widget )
565 {
566 wxCHECK_RET( widget != NULL, wxT("register widget is NULL") );
567
568 /* gtk_drag_dest_set() determines what default behaviour we'd like
569 GTK to supply. we don't want to specify out targets (=formats)
570 or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and
571 not GTK_DEST_DEFAULT_DROP). instead we react individually to
572 "drag_motion" and "drag_drop" events. this makes it possible
573 to allow dropping on only a small area. we should set
574 GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice
575 highlighting if dragging over standard controls, but this
576 seems to be broken without the other two. */
577
578 gtk_drag_dest_set( widget,
579 (GtkDestDefaults) 0, /* no default behaviour */
580 NULL, /* we don't supply any formats here */
581 0, /* number of targets = 0 */
582 (GdkDragAction) 0 ); /* we don't supply any actions here */
583
584 g_signal_connect (widget, "drag_leave",
585 G_CALLBACK (target_drag_leave), this);
586
587 g_signal_connect (widget, "drag_motion",
588 G_CALLBACK (target_drag_motion), this);
589
590 g_signal_connect (widget, "drag_drop",
591 G_CALLBACK (target_drag_drop), this);
592
593 g_signal_connect (widget, "drag_data_received",
594 G_CALLBACK (target_drag_data_received), this);
595 }
596
597 //----------------------------------------------------------------------------
598 // "drag_data_get"
599 //----------------------------------------------------------------------------
600
601 extern "C" {
602 static void
source_drag_data_get(GtkWidget * WXUNUSED (widget),GdkDragContext * context,GtkSelectionData * selection_data,guint WXUNUSED (info),guint WXUNUSED (time),wxDropSource * drop_source)603 source_drag_data_get (GtkWidget *WXUNUSED(widget),
604 GdkDragContext *context,
605 GtkSelectionData *selection_data,
606 guint WXUNUSED(info),
607 guint WXUNUSED(time),
608 wxDropSource *drop_source )
609 {
610 wxDataFormat format(gtk_selection_data_get_target(selection_data));
611
612 wxLogTrace(TRACE_DND, wxT("Drop source: format requested: %s"),
613 format.GetId().c_str());
614
615 drop_source->m_retValue = wxDragError;
616
617 wxDataObject *data = drop_source->GetDataObject();
618
619 if (!data)
620 {
621 wxLogTrace(TRACE_DND, wxT("Drop source: no data object") );
622 return;
623 }
624
625 if (!data->IsSupportedFormat(format))
626 {
627 wxLogTrace(TRACE_DND, wxT("Drop source: unsupported format") );
628 return;
629 }
630
631 if (data->GetDataSize(format) == 0)
632 {
633 wxLogTrace(TRACE_DND, wxT("Drop source: empty data") );
634 return;
635 }
636
637 size_t size = data->GetDataSize(format);
638
639 // printf( "data size: %d.\n", (int)data_size );
640
641 guchar *d = new guchar[size];
642
643 if (!data->GetDataHere( format, (void*)d ))
644 {
645 delete[] d;
646 return;
647 }
648
649 drop_source->m_retValue = ConvertFromGTK(gdk_drag_context_get_selected_action(context));
650
651 gtk_selection_data_set( selection_data,
652 gtk_selection_data_get_target(selection_data),
653 8, // 8-bit
654 d,
655 size );
656
657 delete[] d;
658 }
659 }
660
661 //----------------------------------------------------------------------------
662 // "drag_end"
663 //----------------------------------------------------------------------------
664
665 extern "C" {
source_drag_end(GtkWidget * WXUNUSED (widget),GdkDragContext * WXUNUSED (context),wxDropSource * drop_source)666 static void source_drag_end( GtkWidget *WXUNUSED(widget),
667 GdkDragContext *WXUNUSED(context),
668 wxDropSource *drop_source )
669 {
670 drop_source->m_waiting = false;
671 }
672 }
673
674 //-----------------------------------------------------------------------------
675 // "configure_event" from m_iconWindow
676 //-----------------------------------------------------------------------------
677
678 extern "C" {
679 static gint
gtk_dnd_window_configure_callback(GtkWidget * WXUNUSED (widget),GdkEventConfigure * WXUNUSED (event),wxDropSource * source)680 gtk_dnd_window_configure_callback( GtkWidget *WXUNUSED(widget), GdkEventConfigure *WXUNUSED(event), wxDropSource *source )
681 {
682 source->GiveFeedback(ConvertFromGTK(gdk_drag_context_get_selected_action(source->m_dragContext)));
683
684 return 0;
685 }
686 }
687
688 //---------------------------------------------------------------------------
689 // wxDropSource
690 //---------------------------------------------------------------------------
691
wxDropSource(wxWindow * win,const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)692 wxDropSource::wxDropSource(wxWindow *win,
693 const wxIcon &iconCopy,
694 const wxIcon &iconMove,
695 const wxIcon &iconNone)
696 {
697 m_waiting = true;
698
699 m_iconWindow = NULL;
700
701 m_window = win;
702 m_widget = win->m_widget;
703 if (win->m_wxwindow) m_widget = win->m_wxwindow;
704
705 m_retValue = wxDragNone;
706
707 SetIcons(iconCopy, iconMove, iconNone);
708 }
709
wxDropSource(wxDataObject & data,wxWindow * win,const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)710 wxDropSource::wxDropSource(wxDataObject& data,
711 wxWindow *win,
712 const wxIcon &iconCopy,
713 const wxIcon &iconMove,
714 const wxIcon &iconNone)
715 {
716 m_waiting = true;
717
718 SetData( data );
719
720 m_iconWindow = NULL;
721
722 m_window = win;
723 m_widget = win->m_widget;
724 if (win->m_wxwindow) m_widget = win->m_wxwindow;
725
726 m_retValue = wxDragNone;
727
728 SetIcons(iconCopy, iconMove, iconNone);
729 }
730
SetIcons(const wxIcon & iconCopy,const wxIcon & iconMove,const wxIcon & iconNone)731 void wxDropSource::SetIcons(const wxIcon &iconCopy,
732 const wxIcon &iconMove,
733 const wxIcon &iconNone)
734 {
735 m_iconCopy = iconCopy;
736 m_iconMove = iconMove;
737 m_iconNone = iconNone;
738
739 if ( !m_iconCopy.IsOk() )
740 m_iconCopy = wxIcon(page_xpm);
741 if ( !m_iconMove.IsOk() )
742 m_iconMove = m_iconCopy;
743 if ( !m_iconNone.IsOk() )
744 m_iconNone = m_iconCopy;
745 }
746
~wxDropSource()747 wxDropSource::~wxDropSource()
748 {
749 }
750
PrepareIcon(int action,GdkDragContext * context)751 void wxDropSource::PrepareIcon( int action, GdkDragContext *context )
752 {
753 // get the right icon to display
754 wxIcon *icon = NULL;
755 if ( action & GDK_ACTION_MOVE )
756 icon = &m_iconMove;
757 else if ( action & GDK_ACTION_COPY )
758 icon = &m_iconCopy;
759 else
760 icon = &m_iconNone;
761
762 #ifndef __WXGTK3__
763 GdkBitmap *mask;
764 if ( icon->GetMask() )
765 mask = *icon->GetMask();
766 else
767 mask = NULL;
768
769 GdkPixmap *pixmap = icon->GetPixmap();
770
771 GdkColormap *colormap = gtk_widget_get_colormap( m_widget );
772 gtk_widget_push_colormap (colormap);
773 #endif
774
775 m_iconWindow = gtk_window_new (GTK_WINDOW_POPUP);
776 gtk_widget_set_events (m_iconWindow, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
777 gtk_widget_set_app_paintable (m_iconWindow, TRUE);
778
779 #ifdef __WXGTK3__
780 gtk_widget_set_visual(m_iconWindow, gtk_widget_get_visual(m_widget));
781 #else
782 gtk_widget_pop_colormap ();
783 #endif
784
785 gtk_widget_set_size_request (m_iconWindow, icon->GetWidth(), icon->GetHeight());
786 gtk_widget_realize (m_iconWindow);
787
788 g_signal_connect (m_iconWindow, "configure_event",
789 G_CALLBACK (gtk_dnd_window_configure_callback), this);
790
791 #ifdef __WXGTK3__
792 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(m_iconWindow));
793 icon->SetSourceSurface(cr, 0, 0);
794 cairo_pattern_t* pattern = cairo_get_source(cr);
795 gdk_window_set_background_pattern(gtk_widget_get_window(m_iconWindow), pattern);
796 cairo_destroy(cr);
797 cairo_surface_t* mask = NULL;
798 if (icon->GetMask())
799 mask = *icon->GetMask();
800 if (mask)
801 {
802 cairo_region_t* region = gdk_cairo_region_create_from_surface(mask);
803 gtk_widget_shape_combine_region(m_iconWindow, region);
804 cairo_region_destroy(region);
805 }
806 #else
807 gdk_window_set_back_pixmap(gtk_widget_get_window(m_iconWindow), pixmap, false);
808
809 if (mask)
810 gtk_widget_shape_combine_mask (m_iconWindow, mask, 0, 0);
811 #endif
812
813 gtk_drag_set_icon_widget( context, m_iconWindow, 0, 0 );
814 }
815
DoDragDrop(int flags)816 wxDragResult wxDropSource::DoDragDrop(int flags)
817 {
818 wxCHECK_MSG( m_data && m_data->GetFormatCount(), wxDragNone,
819 wxT("Drop source: no data") );
820
821 // still in drag
822 if (g_blockEventsOnDrag)
823 return wxDragNone;
824
825 // don't start dragging if no button is down
826 if (g_lastButtonNumber == 0)
827 return wxDragNone;
828
829 // we can only start a drag after a mouse event
830 if (g_lastMouseEvent == NULL)
831 return wxDragNone;
832
833 GTKConnectDragSignals();
834 wxON_BLOCK_EXIT_OBJ0(*this, wxDropSource::GTKDisconnectDragSignals);
835
836 m_waiting = true;
837
838 GtkTargetList *target_list = gtk_target_list_new( NULL, 0 );
839
840 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
841 m_data->GetAllFormats( array );
842 size_t count = m_data->GetFormatCount();
843 for (size_t i = 0; i < count; i++)
844 {
845 GdkAtom atom = array[i];
846 wxLogTrace(TRACE_DND, wxT("Drop source: Supported atom %s"),
847 gdk_atom_name( atom ));
848 gtk_target_list_add( target_list, atom, 0, 0 );
849 }
850 delete[] array;
851
852 int allowed_actions = GDK_ACTION_COPY;
853 if ( flags & wxDrag_AllowMove )
854 allowed_actions |= GDK_ACTION_MOVE;
855
856 // VZ: as we already use g_blockEventsOnDrag it shouldn't be that bad
857 // to use a global to pass the flags to the drop target but I'd
858 // surely prefer a better way to do it
859 gs_flagsForDrag = flags;
860
861 m_retValue = wxDragCancel;
862
863 GdkDragContext *context = gtk_drag_begin( m_widget,
864 target_list,
865 (GdkDragAction)allowed_actions,
866 g_lastButtonNumber, // number of mouse button which started drag
867 (GdkEvent*) g_lastMouseEvent );
868
869 if ( !context )
870 {
871 // this can happen e.g. if gdk_pointer_grab() failed
872 return wxDragError;
873 }
874
875 m_dragContext = context;
876
877 PrepareIcon( allowed_actions, context );
878
879 while (m_waiting)
880 gtk_main_iteration();
881
882 g_signal_handlers_disconnect_by_func (m_iconWindow,
883 (gpointer) gtk_dnd_window_configure_callback, this);
884
885 return m_retValue;
886 }
887
GTKConnectDragSignals()888 void wxDropSource::GTKConnectDragSignals()
889 {
890 if (!m_widget)
891 return;
892
893 g_blockEventsOnDrag = true;
894
895 g_signal_connect (m_widget, "drag_data_get",
896 G_CALLBACK (source_drag_data_get), this);
897 g_signal_connect (m_widget, "drag_end",
898 G_CALLBACK (source_drag_end), this);
899
900 }
901
GTKDisconnectDragSignals()902 void wxDropSource::GTKDisconnectDragSignals()
903 {
904 if (!m_widget)
905 return;
906
907 g_blockEventsOnDrag = false;
908
909 g_signal_handlers_disconnect_by_func (m_widget,
910 (gpointer) source_drag_data_get,
911 this);
912 g_signal_handlers_disconnect_by_func (m_widget,
913 (gpointer) source_drag_end,
914 this);
915 }
916
917 #endif
918 // wxUSE_DRAG_AND_DROP
919