]>
Commit | Line | Data |
---|---|---|
c801d85f KB |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Name: dnd.cpp | |
3 | // Purpose: wxDropTarget class | |
4 | // Author: Robert Roebling | |
a81258be | 5 | // Id: $Id$ |
01111366 | 6 | // Copyright: (c) 1998 Robert Roebling |
f03fc89f | 7 | // Licence: wxWindows licence |
c801d85f KB |
8 | /////////////////////////////////////////////////////////////////////////////// |
9 | ||
10 | #ifdef __GNUG__ | |
11 | #pragma implementation "dnd.h" | |
12 | #endif | |
13 | ||
14 | #include "wx/dnd.h" | |
ac57418f | 15 | |
06cfab17 | 16 | #if wxUSE_DRAG_AND_DROP |
ac57418f | 17 | |
c801d85f KB |
18 | #include "wx/window.h" |
19 | #include "wx/app.h" | |
20 | #include "wx/gdicmn.h" | |
b527aac5 RR |
21 | #include "wx/intl.h" |
22 | #include "wx/utils.h" | |
c801d85f | 23 | |
83624f79 RR |
24 | #include "gdk/gdk.h" |
25 | #include "gtk/gtk.h" | |
c801d85f KB |
26 | #include "gdk/gdkprivate.h" |
27 | ||
8a126fcc RR |
28 | #include "gtk/gtkdnd.h" |
29 | #include "gtk/gtkselection.h" | |
c801d85f | 30 | |
5549fa65 RR |
31 | //----------------------------------------------------------------------------- |
32 | // idle system | |
33 | //----------------------------------------------------------------------------- | |
34 | ||
35 | extern void wxapp_install_idle_handler(); | |
36 | extern bool g_isIdle; | |
37 | ||
b453e1b2 RR |
38 | //----------------------------------------------------------------------------- |
39 | // thread system | |
40 | //----------------------------------------------------------------------------- | |
41 | ||
42 | #if wxUSE_THREADS | |
43 | extern void wxapp_install_thread_wakeup(); | |
44 | extern void wxapp_uninstall_thread_wakeup(); | |
45 | #endif | |
46 | ||
22d5903e RR |
47 | //---------------------------------------------------------------------------- |
48 | // global data | |
49 | //---------------------------------------------------------------------------- | |
c801d85f KB |
50 | |
51 | extern bool g_blockEventsOnDrag; | |
52 | ||
22d5903e RR |
53 | //---------------------------------------------------------------------------- |
54 | // standard icons | |
55 | //---------------------------------------------------------------------------- | |
56 | ||
57 | /* XPM */ | |
58 | static char * gv_xpm[] = { | |
59 | "40 34 3 1", | |
f03fc89f VZ |
60 | " s None c None", |
61 | ". c black", | |
62 | "X c white", | |
22d5903e RR |
63 | " ", |
64 | " ", | |
65 | " ...... ", | |
66 | " ..XXXXXX.. ", | |
67 | " .XXXXXXXXXX. ", | |
68 | " .XXXXXXXXXXXX. ", | |
69 | " .XXXXXXXXXXXX. ", | |
70 | " .XXXXXXXXXXXXXX. ", | |
71 | " .XXX..XXXX..XXX. ", | |
72 | " ....XX....XX....XX. ", | |
73 | " .XXX.XXX..XXXX..XXX.... ", | |
74 | " .XXXXXXXXXXXXXXXXXXX.XXX. ", | |
75 | " .XXXXXXXXXXXXXXXXXXXXXXXX. ", | |
76 | " .XXXXXXXXXXXXXXXXXXXXXXXX. ", | |
77 | " ..XXXXXXXXXXXXXXXXXXXXXX. ", | |
78 | " .XXXXXXXXXXXXXXXXXX... ", | |
79 | " ..XXXXXXXXXXXXXXXX. ", | |
80 | " .XXXXXXXXXXXXXXXX. ", | |
81 | " .XXXXXXXXXXXXXXXX. ", | |
82 | " .XXXXXXXXXXXXXXXXX. ", | |
83 | " .XXXXXXXXXXXXXXXXX. ", | |
84 | " .XXXXXXXXXXXXXXXXXX. ", | |
85 | " .XXXXXXXXXXXXXXXXXXX. ", | |
86 | " .XXXXXXXXXXXXXXXXXXXXX. ", | |
87 | " .XXXXXXXXXXXXXX.XXXXXXX. ", | |
88 | " .XXXXXXX.XXXXXXX.XXXXXXX. ", | |
89 | " .XXXXXXXX.XXXXXXX.XXXXXXX. ", | |
90 | " .XXXXXXX...XXXXX...XXXXX. ", | |
91 | " .XXXXXXX. ..... ..... ", | |
92 | " ..XXXX.. ", | |
93 | " .... ", | |
94 | " ", | |
95 | " ", | |
96 | " "}; | |
f03fc89f | 97 | |
22d5903e RR |
98 | /* XPM */ |
99 | static char * page_xpm[] = { | |
100 | /* width height ncolors chars_per_pixel */ | |
101 | "32 32 5 1", | |
102 | /* colors */ | |
f03fc89f VZ |
103 | " s None c None", |
104 | ". c black", | |
105 | "X c wheat", | |
106 | "o c tan", | |
107 | "O c #6699FF", | |
22d5903e RR |
108 | /* pixels */ |
109 | " ................... ", | |
110 | " .XXXXXXXXXXXXXXXXX.. ", | |
111 | " .XXXXXXXXXXXXXXXXX.o. ", | |
112 | " .XXXXXXXXXXXXXXXXX.oo. ", | |
113 | " .XXXXXXXXXXXXXXXXX.ooo. ", | |
114 | " .XXXXXXXXXXXXXXXXX.oooo. ", | |
115 | " .XXXXXXXXXXXXXXXXX....... ", | |
116 | " .XXXXXOOOOOOOOOOXXXooooo. ", | |
117 | " .XXXXXXXXXXXXXXXXXXooooo. ", | |
118 | " .XXXXXOOOOOOOOOOXXXXXXXX. ", | |
119 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
120 | " .XXXXXXXOOOOOOOOOXXXXXXX. ", | |
121 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
122 | " .XXXXXXOOOOOOOOOOXXXXXXX. ", | |
123 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
124 | " .XXXXXOOOOOOOOOOXXXXXXXX. ", | |
125 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
126 | " .XXXXXXXOOOOOOOOOXXXXXXX. ", | |
127 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
128 | " .XXXXXXOOOOOOOOOOXXXXXXX. ", | |
129 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
130 | " .XXXXXOOOOOOOOOOXXXXXXXX. ", | |
131 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
132 | " .XXXXXXOOOOOOOOOOXXXXXXX. ", | |
133 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
134 | " .XXXXXOOOOOOOXXXXXXXXXXX. ", | |
135 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
136 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
137 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
138 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
139 | " .XXXXXXXXXXXXXXXXXXXXXXX. ", | |
140 | " ......................... "}; | |
f03fc89f VZ |
141 | |
142 | ||
4ba47b40 | 143 | |
33a5bc52 RR |
144 | // ---------------------------------------------------------------------------- |
145 | // "drag_leave" | |
146 | // ---------------------------------------------------------------------------- | |
147 | ||
148 | static void target_drag_leave( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
149 | GdkDragContext *context, |
150 | guint WXUNUSED(time), | |
151 | wxDropTarget *drop_target ) | |
33a5bc52 | 152 | { |
5549fa65 RR |
153 | if (g_isIdle) wxapp_install_idle_handler(); |
154 | ||
829e3e8d RR |
155 | /* inform the wxDropTarget about the current GdkDragContext. |
156 | this is only valid for the duration of this call */ | |
157 | drop_target->SetDragContext( context ); | |
f03fc89f | 158 | |
829e3e8d RR |
159 | /* we don't need return values. this event is just for |
160 | information */ | |
161 | drop_target->OnLeave(); | |
f03fc89f | 162 | |
829e3e8d RR |
163 | /* this has to be done because GDK has no "drag_enter" event */ |
164 | drop_target->m_firstMotion = TRUE; | |
f03fc89f | 165 | |
829e3e8d RR |
166 | /* after this, invalidate the drop_target's GdkDragContext */ |
167 | drop_target->SetDragContext( (GdkDragContext*) NULL ); | |
33a5bc52 RR |
168 | } |
169 | ||
170 | // ---------------------------------------------------------------------------- | |
171 | // "drag_motion" | |
172 | // ---------------------------------------------------------------------------- | |
173 | ||
174 | static gboolean target_drag_motion( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
175 | GdkDragContext *context, |
176 | gint x, | |
177 | gint y, | |
178 | guint time, | |
179 | wxDropTarget *drop_target ) | |
33a5bc52 | 180 | { |
5549fa65 RR |
181 | if (g_isIdle) wxapp_install_idle_handler(); |
182 | ||
829e3e8d RR |
183 | /* Owen Taylor: "if the coordinates not in a drop zone, |
184 | return FALSE, otherwise call gtk_drag_status() and | |
185 | return TRUE" */ | |
f03fc89f | 186 | |
829e3e8d RR |
187 | /* inform the wxDropTarget about the current GdkDragContext. |
188 | this is only valid for the duration of this call */ | |
189 | drop_target->SetDragContext( context ); | |
f03fc89f | 190 | |
8e00741d RR |
191 | bool ret = FALSE; |
192 | ||
829e3e8d | 193 | if (drop_target->m_firstMotion) |
d6086ea6 | 194 | { |
829e3e8d | 195 | /* the first "drag_motion" event substitutes a "drag_enter" event */ |
8e00741d RR |
196 | ret = drop_target->OnEnter( x, y ); |
197 | } | |
198 | else | |
199 | { | |
200 | /* give program a chance to react (i.e. to say no by returning FALSE) */ | |
201 | ret = drop_target->OnMove( x, y ); | |
d6086ea6 | 202 | } |
f03fc89f | 203 | |
829e3e8d RR |
204 | /* we don't yet handle which "actions" (i.e. copy or move) |
205 | the target accepts. so far we simply accept the | |
206 | suggested action. TODO. */ | |
207 | if (ret) | |
208 | gdk_drag_status( context, context->suggested_action, time ); | |
f03fc89f | 209 | |
829e3e8d RR |
210 | /* after this, invalidate the drop_target's GdkDragContext */ |
211 | drop_target->SetDragContext( (GdkDragContext*) NULL ); | |
f03fc89f | 212 | |
829e3e8d RR |
213 | /* this has to be done because GDK has no "drag_enter" event */ |
214 | drop_target->m_firstMotion = FALSE; | |
f03fc89f | 215 | |
829e3e8d | 216 | return ret; |
33a5bc52 RR |
217 | } |
218 | ||
219 | // ---------------------------------------------------------------------------- | |
220 | // "drag_drop" | |
221 | // ---------------------------------------------------------------------------- | |
222 | ||
223 | static gboolean target_drag_drop( GtkWidget *widget, | |
f03fc89f VZ |
224 | GdkDragContext *context, |
225 | gint x, | |
226 | gint y, | |
227 | guint time, | |
228 | wxDropTarget *drop_target ) | |
33a5bc52 | 229 | { |
5549fa65 RR |
230 | if (g_isIdle) wxapp_install_idle_handler(); |
231 | ||
829e3e8d RR |
232 | /* Owen Taylor: "if the drop is not in a drop zone, |
233 | return FALSE, otherwise, if you aren't accepting | |
234 | the drop, call gtk_drag_finish() with success == FALSE | |
235 | otherwise call gtk_drag_data_get()" */ | |
4ba47b40 RR |
236 | |
237 | // printf( "drop.\n" ); | |
f03fc89f | 238 | |
829e3e8d RR |
239 | /* this seems to make a difference between not accepting |
240 | due to wrong target area and due to wrong format. let | |
241 | us hope that this is not required.. */ | |
f03fc89f | 242 | |
829e3e8d RR |
243 | /* inform the wxDropTarget about the current GdkDragContext. |
244 | this is only valid for the duration of this call */ | |
245 | drop_target->SetDragContext( context ); | |
f03fc89f | 246 | |
829e3e8d RR |
247 | /* inform the wxDropTarget about the current drag widget. |
248 | this is only valid for the duration of this call */ | |
249 | drop_target->SetDragWidget( widget ); | |
f03fc89f | 250 | |
829e3e8d RR |
251 | /* inform the wxDropTarget about the current drag time. |
252 | this is only valid for the duration of this call */ | |
253 | drop_target->SetDragTime( time ); | |
f03fc89f | 254 | |
829e3e8d | 255 | bool ret = drop_target->OnDrop( x, y ); |
f03fc89f | 256 | |
5af019af | 257 | if (!ret) |
829e3e8d RR |
258 | { |
259 | /* cancel the whole thing */ | |
260 | gtk_drag_finish( context, | |
f03fc89f VZ |
261 | FALSE, /* no success */ |
262 | FALSE, /* don't delete data on dropping side */ | |
263 | time ); | |
829e3e8d | 264 | } |
8e00741d RR |
265 | else |
266 | { | |
267 | #if wxUSE_THREADS | |
268 | /* disable GUI threads */ | |
269 | wxapp_uninstall_thread_wakeup(); | |
270 | #endif | |
271 | ||
272 | GdkAtom format = drop_target->GetMatchingPair(); | |
273 | wxASSERT( format ); | |
274 | ||
275 | /* this should trigger an "drag_data_received" event */ | |
276 | gtk_drag_get_data( widget, | |
277 | context, | |
278 | format, | |
279 | time ); | |
280 | ||
281 | #if wxUSE_THREADS | |
282 | /* re-enable GUI threads */ | |
283 | wxapp_install_thread_wakeup(); | |
284 | #endif | |
285 | } | |
f03fc89f | 286 | |
829e3e8d RR |
287 | /* after this, invalidate the drop_target's GdkDragContext */ |
288 | drop_target->SetDragContext( (GdkDragContext*) NULL ); | |
f03fc89f | 289 | |
829e3e8d RR |
290 | /* after this, invalidate the drop_target's drag widget */ |
291 | drop_target->SetDragWidget( (GtkWidget*) NULL ); | |
f03fc89f | 292 | |
829e3e8d RR |
293 | /* this has to be done because GDK has no "drag_enter" event */ |
294 | drop_target->m_firstMotion = TRUE; | |
f03fc89f | 295 | |
829e3e8d | 296 | return ret; |
33a5bc52 RR |
297 | } |
298 | ||
299 | // ---------------------------------------------------------------------------- | |
300 | // "drag_data_received" | |
301 | // ---------------------------------------------------------------------------- | |
302 | ||
303 | static void target_drag_data_received( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
304 | GdkDragContext *context, |
305 | gint x, | |
306 | gint y, | |
307 | GtkSelectionData *data, | |
308 | guint WXUNUSED(info), | |
309 | guint time, | |
310 | wxDropTarget *drop_target ) | |
33a5bc52 | 311 | { |
5549fa65 RR |
312 | if (g_isIdle) wxapp_install_idle_handler(); |
313 | ||
829e3e8d RR |
314 | /* Owen Taylor: "call gtk_drag_finish() with |
315 | success == TRUE" */ | |
316 | ||
f03fc89f | 317 | |
829e3e8d | 318 | if ((data->length <= 0) || (data->format != 8)) |
33a5bc52 | 319 | { |
829e3e8d RR |
320 | /* negative data length and non 8-bit data format |
321 | qualifies for junk */ | |
322 | gtk_drag_finish (context, FALSE, FALSE, time); | |
f03fc89f | 323 | |
f03fc89f | 324 | return; |
829e3e8d | 325 | } |
f03fc89f | 326 | |
a3e7d24d RR |
327 | wxLogDebug( wxT( "Drop target: data received") ); |
328 | ||
5af019af RR |
329 | /* inform the wxDropTarget about the current GtkSelectionData. |
330 | this is only valid for the duration of this call */ | |
331 | drop_target->SetDragData( data ); | |
f03fc89f | 332 | |
5af019af | 333 | if (drop_target->OnData( x, y )) |
829e3e8d | 334 | { |
f03fc89f | 335 | /* tell GTK that data transfer was successfull */ |
ab8884ac | 336 | gtk_drag_finish( context, TRUE, FALSE, time ); |
33a5bc52 | 337 | } |
5af019af RR |
338 | else |
339 | { | |
f03fc89f | 340 | /* tell GTK that data transfer was not successfull */ |
5af019af RR |
341 | gtk_drag_finish( context, FALSE, FALSE, time ); |
342 | } | |
f03fc89f | 343 | |
5af019af RR |
344 | /* after this, invalidate the drop_target's drag data */ |
345 | drop_target->SetDragData( (GtkSelectionData*) NULL ); | |
33a5bc52 RR |
346 | } |
347 | ||
4ba47b40 | 348 | //---------------------------------------------------------------------------- |
33a5bc52 | 349 | // wxDropTarget |
4ba47b40 | 350 | //---------------------------------------------------------------------------- |
33a5bc52 | 351 | |
8e00741d | 352 | wxDropTarget::wxDropTarget( wxDataObject *data ) |
f5368809 | 353 | { |
829e3e8d RR |
354 | m_firstMotion = TRUE; |
355 | m_dragContext = (GdkDragContext*) NULL; | |
356 | m_dragWidget = (GtkWidget*) NULL; | |
5af019af | 357 | m_dragData = (GtkSelectionData*) NULL; |
829e3e8d | 358 | m_dragTime = 0; |
8e00741d | 359 | m_data = data; |
f5368809 RR |
360 | } |
361 | ||
362 | wxDropTarget::~wxDropTarget() | |
363 | { | |
364 | } | |
365 | ||
8e00741d | 366 | bool wxDropTarget::OnEnter( int WXUNUSED(x), int WXUNUSED(y) ) |
d6086ea6 | 367 | { |
8e00741d RR |
368 | if (!m_data) |
369 | return FALSE; | |
370 | ||
371 | return (GetMatchingPair() != (GdkAtom) 0); | |
d6086ea6 RR |
372 | } |
373 | ||
374 | void wxDropTarget::OnLeave() | |
375 | { | |
376 | } | |
377 | ||
8e00741d | 378 | bool wxDropTarget::OnMove( int WXUNUSED(x), int WXUNUSED(y) ) |
d6086ea6 | 379 | { |
8e00741d | 380 | if (!m_data) |
a3e7d24d RR |
381 | return FALSE; |
382 | ||
8e00741d | 383 | return (GetMatchingPair() != (GdkAtom) 0); |
d6086ea6 RR |
384 | } |
385 | ||
8e00741d | 386 | bool wxDropTarget::OnDrop( int WXUNUSED(x), int WXUNUSED(y) ) |
d6086ea6 | 387 | { |
8e00741d | 388 | if (!m_data) |
a3e7d24d RR |
389 | return FALSE; |
390 | ||
8e00741d | 391 | return (GetMatchingPair() != (GdkAtom) 0); |
d6086ea6 RR |
392 | } |
393 | ||
8e00741d | 394 | bool wxDropTarget::OnData( int WXUNUSED(x), int WXUNUSED(y) ) |
5af019af | 395 | { |
8e00741d RR |
396 | if (!m_data) |
397 | return FALSE; | |
398 | ||
399 | if (GetMatchingPair() == (GdkAtom) 0) | |
400 | return FALSE; | |
401 | ||
402 | return GetData(); | |
5af019af RR |
403 | } |
404 | ||
8e00741d | 405 | GdkAtom wxDropTarget::GetMatchingPair() |
5af019af | 406 | { |
8e00741d RR |
407 | if (!m_data) |
408 | return (GdkAtom) 0; | |
5af019af | 409 | |
8e00741d RR |
410 | if (!m_dragContext) |
411 | return (GdkAtom) 0; | |
f03fc89f | 412 | |
22d5903e RR |
413 | GList *child = m_dragContext->targets; |
414 | while (child) | |
415 | { | |
416 | GdkAtom formatAtom = (GdkAtom) GPOINTER_TO_INT(child->data); | |
8e00741d | 417 | wxDataFormat format( formatAtom ); |
f03fc89f | 418 | |
a3e7d24d RR |
419 | #ifdef __WXDEBUG__ |
420 | char *name = gdk_atom_name( formatAtom ); | |
421 | if (name) wxLogDebug( "Drop target: drag has format: %s", name ); | |
422 | #endif | |
8e00741d RR |
423 | if (m_data->IsSupportedFormat( format )) |
424 | return formatAtom; | |
f03fc89f | 425 | |
22d5903e RR |
426 | child = child->next; |
427 | } | |
829e3e8d | 428 | |
8e00741d | 429 | return (GdkAtom) 0; |
d6086ea6 | 430 | } |
f03fc89f | 431 | |
8e00741d | 432 | bool wxDropTarget::GetData() |
d6086ea6 | 433 | { |
8e00741d RR |
434 | if (!m_dragData) |
435 | return FALSE; | |
f03fc89f | 436 | |
8e00741d RR |
437 | if (!m_data) |
438 | return FALSE; | |
f03fc89f | 439 | |
8e00741d RR |
440 | wxDataFormat dragFormat( m_dragData->target ); |
441 | ||
442 | if (!m_data->IsSupportedFormat( dragFormat )) | |
443 | return FALSE; | |
f03fc89f | 444 | |
8e00741d | 445 | if (dragFormat.GetType() == wxDF_TEXT) |
5af019af | 446 | { |
8e00741d RR |
447 | wxTextDataObject *text_object = (wxTextDataObject*)m_data; |
448 | text_object->SetText( (const char*)m_dragData->data ); | |
449 | return TRUE; | |
450 | } | |
f03fc89f | 451 | |
8e00741d | 452 | if (dragFormat.GetType() == wxDF_FILENAME) |
5af019af | 453 | { |
8e00741d RR |
454 | wxFileDataObject *file_object = (wxFileDataObject*)m_data; |
455 | file_object->SetFiles( (const char*)m_dragData->data ); | |
456 | return TRUE; | |
5af019af | 457 | } |
f03fc89f | 458 | |
8e00741d | 459 | return FALSE; |
d6086ea6 | 460 | } |
f03fc89f | 461 | |
f5368809 RR |
462 | void wxDropTarget::UnregisterWidget( GtkWidget *widget ) |
463 | { | |
223d09f6 | 464 | wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") ); |
f03fc89f | 465 | |
829e3e8d | 466 | gtk_drag_dest_unset( widget ); |
f03fc89f | 467 | |
d6086ea6 | 468 | gtk_signal_disconnect_by_func( GTK_OBJECT(widget), |
f03fc89f | 469 | GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this ); |
33a5bc52 | 470 | |
d6086ea6 | 471 | gtk_signal_disconnect_by_func( GTK_OBJECT(widget), |
f03fc89f | 472 | GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this ); |
33a5bc52 | 473 | |
d6086ea6 | 474 | gtk_signal_disconnect_by_func( GTK_OBJECT(widget), |
f03fc89f | 475 | GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this ); |
33a5bc52 | 476 | |
d6086ea6 | 477 | gtk_signal_disconnect_by_func( GTK_OBJECT(widget), |
f03fc89f | 478 | GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this ); |
f5368809 RR |
479 | } |
480 | ||
481 | void wxDropTarget::RegisterWidget( GtkWidget *widget ) | |
482 | { | |
223d09f6 | 483 | wxCHECK_RET( widget != NULL, wxT("register widget is NULL") ); |
f03fc89f | 484 | |
829e3e8d RR |
485 | /* gtk_drag_dest_set() determines what default behaviour we'd like |
486 | GTK to supply. we don't want to specify out targets (=formats) | |
487 | or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and | |
488 | not GTK_DEST_DEFAULT_DROP). instead we react individually to | |
489 | "drag_motion" and "drag_drop" events. this makes it possible | |
f03fc89f | 490 | to allow dropping on only a small area. we should set |
829e3e8d RR |
491 | GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice |
492 | highlighting if dragging over standard controls, but this | |
493 | seems to be broken without the other two. */ | |
f03fc89f | 494 | |
d6086ea6 | 495 | gtk_drag_dest_set( widget, |
f03fc89f VZ |
496 | (GtkDestDefaults) 0, /* no default behaviour */ |
497 | (GtkTargetEntry*) NULL, /* we don't supply any formats here */ | |
498 | 0, /* number of targets = 0 */ | |
499 | (GdkDragAction) 0 ); /* we don't supply any actions here */ | |
500 | ||
d6086ea6 | 501 | gtk_signal_connect( GTK_OBJECT(widget), "drag_leave", |
f03fc89f | 502 | GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this ); |
33a5bc52 | 503 | |
d6086ea6 | 504 | gtk_signal_connect( GTK_OBJECT(widget), "drag_motion", |
f03fc89f | 505 | GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this ); |
33a5bc52 | 506 | |
d6086ea6 | 507 | gtk_signal_connect( GTK_OBJECT(widget), "drag_drop", |
f03fc89f | 508 | GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this ); |
33a5bc52 | 509 | |
d6086ea6 | 510 | gtk_signal_connect( GTK_OBJECT(widget), "drag_data_received", |
f03fc89f | 511 | GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this ); |
f5368809 RR |
512 | } |
513 | ||
4ba47b40 RR |
514 | //---------------------------------------------------------------------------- |
515 | // "drag_data_get" | |
516 | //---------------------------------------------------------------------------- | |
517 | ||
f03fc89f | 518 | static void |
4ba47b40 | 519 | source_drag_data_get (GtkWidget *WXUNUSED(widget), |
f03fc89f VZ |
520 | GdkDragContext *context, |
521 | GtkSelectionData *selection_data, | |
522 | guint WXUNUSED(info), | |
523 | guint WXUNUSED(time), | |
524 | wxDropSource *drop_source ) | |
4ba47b40 | 525 | { |
5549fa65 RR |
526 | if (g_isIdle) wxapp_install_idle_handler(); |
527 | ||
a3e7d24d RR |
528 | #ifdef __WXDEBUG__ |
529 | char *name = gdk_atom_name( selection_data->target ); | |
530 | if (name) wxLogDebug( wxT("Drop source: format requested: %s"), name ); | |
531 | #endif | |
f03fc89f | 532 | |
1dd989e1 RR |
533 | drop_source->m_retValue = wxDragCancel; |
534 | ||
535 | wxDataObject *data = drop_source->m_data; | |
536 | ||
537 | if (!data) | |
538 | return; | |
539 | ||
540 | if (!data->IsSupportedFormat(selection_data->target)) | |
541 | return; | |
f03fc89f | 542 | |
1dd989e1 RR |
543 | if (data->GetDataSize(selection_data->target) == 0) |
544 | return; | |
545 | ||
546 | size_t size = data->GetDataSize(selection_data->target); | |
f03fc89f | 547 | |
1dd989e1 | 548 | // printf( "data size: %d.\n", (int)data_size ); |
f03fc89f | 549 | |
1dd989e1 RR |
550 | guchar *d = new guchar[size]; |
551 | ||
552 | if (!data->GetDataHere( selection_data->target, (void*)d )) | |
553 | { | |
554 | free( d ); | |
555 | return; | |
556 | } | |
f03fc89f | 557 | |
b453e1b2 | 558 | #if wxUSE_THREADS |
1dd989e1 RR |
559 | /* disable GUI threads */ |
560 | wxapp_uninstall_thread_wakeup(); | |
b453e1b2 | 561 | #endif |
8e193f38 | 562 | |
4ba47b40 | 563 | gtk_selection_data_set( selection_data, |
f03fc89f VZ |
564 | selection_data->target, |
565 | 8, // 8-bit | |
1dd989e1 RR |
566 | d, |
567 | size ); | |
f03fc89f | 568 | |
b453e1b2 | 569 | #if wxUSE_THREADS |
1dd989e1 RR |
570 | /* enable GUI threads */ |
571 | wxapp_install_thread_wakeup(); | |
b453e1b2 | 572 | #endif |
f03fc89f | 573 | |
1dd989e1 RR |
574 | free( d ); |
575 | ||
576 | /* so far only copy, no moves. TODO. */ | |
577 | drop_source->m_retValue = wxDragCopy; | |
4ba47b40 | 578 | } |
f03fc89f | 579 | |
4ba47b40 RR |
580 | //---------------------------------------------------------------------------- |
581 | // "drag_data_delete" | |
582 | //---------------------------------------------------------------------------- | |
583 | ||
584 | static void source_drag_data_delete( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
585 | GdkDragContext *WXUNUSED(context), |
586 | wxDropSource *drop_source ) | |
4ba47b40 | 587 | { |
5549fa65 RR |
588 | if (g_isIdle) wxapp_install_idle_handler(); |
589 | ||
b453e1b2 | 590 | // printf( "Delete the data!\n" ); |
33a5bc52 | 591 | |
4ba47b40 RR |
592 | drop_source->m_retValue = wxDragMove; |
593 | } | |
f03fc89f | 594 | |
4ba47b40 RR |
595 | //---------------------------------------------------------------------------- |
596 | // "drag_begin" | |
597 | //---------------------------------------------------------------------------- | |
598 | ||
599 | static void source_drag_begin( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
600 | GdkDragContext *WXUNUSED(context), |
601 | wxDropSource *WXUNUSED(drop_source) ) | |
4ba47b40 | 602 | { |
5549fa65 RR |
603 | if (g_isIdle) wxapp_install_idle_handler(); |
604 | ||
b453e1b2 | 605 | // printf( "drag_begin.\n" ); |
4ba47b40 | 606 | } |
f03fc89f | 607 | |
4ba47b40 RR |
608 | //---------------------------------------------------------------------------- |
609 | // "drag_end" | |
610 | //---------------------------------------------------------------------------- | |
611 | ||
612 | static void source_drag_end( GtkWidget *WXUNUSED(widget), | |
f03fc89f VZ |
613 | GdkDragContext *WXUNUSED(context), |
614 | wxDropSource *drop_source ) | |
4ba47b40 | 615 | { |
5549fa65 RR |
616 | if (g_isIdle) wxapp_install_idle_handler(); |
617 | ||
b453e1b2 | 618 | // printf( "drag_end.\n" ); |
4ba47b40 RR |
619 | |
620 | drop_source->m_waiting = FALSE; | |
621 | } | |
f03fc89f | 622 | |
4ba47b40 RR |
623 | //--------------------------------------------------------------------------- |
624 | // wxDropSource | |
625 | //--------------------------------------------------------------------------- | |
22d5903e RR |
626 | |
627 | wxDropSource::wxDropSource( wxWindow *win, const wxIcon &go, const wxIcon &stop ) | |
628 | { | |
629 | g_blockEventsOnDrag = TRUE; | |
4ba47b40 | 630 | m_waiting = TRUE; |
f03fc89f | 631 | |
22d5903e | 632 | m_window = win; |
a2053b27 RR |
633 | m_widget = win->m_widget; |
634 | if (win->m_wxwindow) m_widget = win->m_wxwindow; | |
f03fc89f | 635 | |
1dd989e1 | 636 | m_data = (wxDataObject*) NULL; |
22d5903e RR |
637 | m_retValue = wxDragCancel; |
638 | ||
639 | m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY ); | |
640 | m_goaheadCursor = wxCursor( wxCURSOR_HAND ); | |
f03fc89f | 641 | |
22d5903e RR |
642 | m_goIcon = go; |
643 | if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm ); | |
644 | m_stopIcon = stop; | |
645 | if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm ); | |
646 | } | |
647 | ||
8e193f38 VZ |
648 | wxDropSource::wxDropSource( wxDataObject& data, wxWindow *win, |
649 | const wxIcon &go, const wxIcon &stop ) | |
22d5903e | 650 | { |
4ba47b40 | 651 | m_waiting = TRUE; |
f03fc89f | 652 | |
22d5903e | 653 | m_window = win; |
a2053b27 RR |
654 | m_widget = win->m_widget; |
655 | if (win->m_wxwindow) m_widget = win->m_wxwindow; | |
22d5903e | 656 | m_retValue = wxDragCancel; |
f03fc89f | 657 | |
1dd989e1 | 658 | m_data = &data; |
22d5903e RR |
659 | |
660 | m_defaultCursor = wxCursor( wxCURSOR_NO_ENTRY ); | |
661 | m_goaheadCursor = wxCursor( wxCURSOR_HAND ); | |
f03fc89f | 662 | |
22d5903e RR |
663 | m_goIcon = go; |
664 | if (wxNullIcon == go) m_goIcon = wxIcon( page_xpm ); | |
665 | m_stopIcon = stop; | |
666 | if (wxNullIcon == stop) m_stopIcon = wxIcon( gv_xpm ); | |
667 | } | |
668 | ||
8e193f38 VZ |
669 | void wxDropSource::SetData( wxDataObject& data ) |
670 | { | |
1dd989e1 | 671 | if (m_data) |
8e193f38 VZ |
672 | delete m_data; |
673 | ||
1dd989e1 | 674 | m_data = &data; |
22d5903e RR |
675 | } |
676 | ||
8e193f38 | 677 | wxDropSource::~wxDropSource() |
22d5903e | 678 | { |
1dd989e1 RR |
679 | if (m_data) |
680 | // delete m_data; | |
22d5903e RR |
681 | |
682 | g_blockEventsOnDrag = FALSE; | |
683 | } | |
f03fc89f | 684 | |
22d5903e RR |
685 | wxDragResult wxDropSource::DoDragDrop( bool WXUNUSED(bAllowMove) ) |
686 | { | |
223d09f6 | 687 | wxASSERT_MSG( m_data, wxT("wxDragSource: no data") ); |
f03fc89f | 688 | |
a3e7d24d RR |
689 | if (!m_data) |
690 | return (wxDragResult) wxDragNone; | |
f03fc89f | 691 | |
a3e7d24d RR |
692 | if (m_data->GetFormatCount() == 0) |
693 | return (wxDragResult) wxDragNone; | |
694 | ||
4ba47b40 | 695 | g_blockEventsOnDrag = TRUE; |
f03fc89f | 696 | |
4ba47b40 | 697 | RegisterWindow(); |
f03fc89f | 698 | |
4ba47b40 | 699 | m_waiting = TRUE; |
22d5903e | 700 | |
4ba47b40 | 701 | GtkTargetList *target_list = gtk_target_list_new( (GtkTargetEntry*) NULL, 0 ); |
a3e7d24d RR |
702 | |
703 | wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ]; | |
704 | m_data->GetAllFormats( array, TRUE ); | |
705 | for (size_t i = 0; i < m_data->GetFormatCount(); i++) | |
706 | { | |
707 | GdkAtom atom = array[i]; | |
708 | wxLogDebug( wxT("Supported atom %s"), gdk_atom_name( atom ) ); | |
709 | gtk_target_list_add( target_list, atom, 0, 0 ); | |
710 | } | |
711 | delete[] array; | |
f03fc89f | 712 | |
4ba47b40 RR |
713 | GdkEventMotion event; |
714 | event.window = m_widget->window; | |
715 | int x = 0; | |
716 | int y = 0; | |
717 | GdkModifierType state; | |
718 | gdk_window_get_pointer( event.window, &x, &y, &state ); | |
719 | event.x = x; | |
720 | event.y = y; | |
721 | event.state = state; | |
19da4326 | 722 | event.time = GDK_CURRENT_TIME; |
f03fc89f | 723 | |
4ba47b40 RR |
724 | /* GTK wants to know which button was pressed which caused the dragging */ |
725 | int button_number = 0; | |
726 | if (event.state & GDK_BUTTON1_MASK) button_number = 1; | |
727 | else if (event.state & GDK_BUTTON2_MASK) button_number = 2; | |
728 | else if (event.state & GDK_BUTTON3_MASK) button_number = 3; | |
729 | ||
b453e1b2 RR |
730 | #if wxUSE_THREADS |
731 | /* disable GUI threads */ | |
732 | wxapp_uninstall_thread_wakeup(); | |
733 | #endif | |
8e193f38 | 734 | |
4ba47b40 RR |
735 | /* don't start dragging if no button is down */ |
736 | if (button_number) | |
22d5903e | 737 | { |
4ba47b40 RR |
738 | GdkDragContext *context = gtk_drag_begin( m_widget, |
739 | target_list, | |
f03fc89f VZ |
740 | GDK_ACTION_COPY, |
741 | button_number, /* number of mouse button which started drag */ | |
742 | (GdkEvent*) &event ); | |
743 | ||
4ba47b40 RR |
744 | wxMask *mask = m_goIcon.GetMask(); |
745 | GdkBitmap *bm = (GdkBitmap *) NULL; | |
746 | if (mask) bm = mask->GetBitmap(); | |
f03fc89f VZ |
747 | GdkPixmap *pm = m_goIcon.GetPixmap(); |
748 | ||
4ba47b40 | 749 | gtk_drag_set_icon_pixmap( context, |
f03fc89f VZ |
750 | gtk_widget_get_colormap( m_widget ), |
751 | pm, | |
752 | bm, | |
753 | 0, | |
754 | 0 ); | |
755 | ||
19da4326 | 756 | while (m_waiting) gtk_main_iteration();; |
4ba47b40 | 757 | } |
22d5903e | 758 | |
b453e1b2 RR |
759 | #if wxUSE_THREADS |
760 | /* re-enable GUI threads */ | |
761 | wxapp_install_thread_wakeup(); | |
762 | #endif | |
8e193f38 | 763 | |
4ba47b40 | 764 | g_blockEventsOnDrag = FALSE; |
f03fc89f | 765 | |
4ba47b40 RR |
766 | UnregisterWindow(); |
767 | ||
768 | return m_retValue; | |
22d5903e RR |
769 | } |
770 | ||
4ba47b40 | 771 | void wxDropSource::RegisterWindow() |
22d5903e | 772 | { |
4ba47b40 | 773 | if (!m_widget) return; |
f03fc89f | 774 | |
4ba47b40 | 775 | gtk_signal_connect( GTK_OBJECT(m_widget), "drag_data_get", |
f03fc89f | 776 | GTK_SIGNAL_FUNC (source_drag_data_get), (gpointer) this); |
4ba47b40 | 777 | gtk_signal_connect (GTK_OBJECT(m_widget), "drag_data_delete", |
f03fc89f | 778 | GTK_SIGNAL_FUNC (source_drag_data_delete), (gpointer) this ); |
4ba47b40 | 779 | gtk_signal_connect (GTK_OBJECT(m_widget), "drag_begin", |
f03fc89f | 780 | GTK_SIGNAL_FUNC (source_drag_begin), (gpointer) this ); |
4ba47b40 | 781 | gtk_signal_connect (GTK_OBJECT(m_widget), "drag_end", |
f03fc89f | 782 | GTK_SIGNAL_FUNC (source_drag_end), (gpointer) this ); |
4ba47b40 | 783 | |
22d5903e RR |
784 | } |
785 | ||
4ba47b40 | 786 | void wxDropSource::UnregisterWindow() |
22d5903e RR |
787 | { |
788 | if (!m_widget) return; | |
f03fc89f | 789 | |
4ba47b40 | 790 | gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget), |
f03fc89f | 791 | GTK_SIGNAL_FUNC(source_drag_data_get), (gpointer) this ); |
4ba47b40 | 792 | gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget), |
f03fc89f | 793 | GTK_SIGNAL_FUNC(source_drag_data_delete), (gpointer) this ); |
4ba47b40 | 794 | gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget), |
f03fc89f | 795 | GTK_SIGNAL_FUNC(source_drag_begin), (gpointer) this ); |
4ba47b40 | 796 | gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget), |
f03fc89f | 797 | GTK_SIGNAL_FUNC(source_drag_end), (gpointer) this ); |
22d5903e RR |
798 | } |
799 | ||
ac57418f RR |
800 | #endif |
801 | ||
93c5dd39 | 802 | // wxUSE_DRAG_AND_DROP |