]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/dnd.cpp
wxSortedArray::Add must return the index of the newly
[wxWidgets.git] / src / gtk / dnd.cpp
CommitLineData
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__
510fc784 11 #pragma implementation "dnd.h"
c801d85f
KB
12#endif
13
14#include "wx/dnd.h"
c0369005
CE
15#include "wx/log.h"
16
17
06cfab17 18#if wxUSE_DRAG_AND_DROP
ac57418f 19
c801d85f
KB
20#include "wx/window.h"
21#include "wx/app.h"
22#include "wx/gdicmn.h"
b527aac5
RR
23#include "wx/intl.h"
24#include "wx/utils.h"
c801d85f 25
9e691f46
VZ
26#include "wx/gtk/private.h"
27
071a2d78 28#include <gdk/gdkprivate.h>
c801d85f 29
071a2d78
RR
30#include <gtk/gtkdnd.h>
31#include <gtk/gtkselection.h>
c801d85f 32
5549fa65
RR
33//-----------------------------------------------------------------------------
34// idle system
35//-----------------------------------------------------------------------------
36
37extern void wxapp_install_idle_handler();
38extern bool g_isIdle;
39
b453e1b2
RR
40//-----------------------------------------------------------------------------
41// thread system
42//-----------------------------------------------------------------------------
43
44#if wxUSE_THREADS
b453e1b2
RR
45#endif
46
22d5903e
RR
47//----------------------------------------------------------------------------
48// global data
49//----------------------------------------------------------------------------
c801d85f
KB
50
51extern bool g_blockEventsOnDrag;
52
2245b2b2
VZ
53// the flags used for the last DoDragDrop()
54static long gs_flagsForDrag = 0;
55
b2dea2ab
VZ
56// the trace mask we use with wxLogTrace() - call
57// wxLog::AddTraceMask(TRACE_DND) to enable the trace messages from here
58// (there are quite a few of them, so don't enable this by default)
59static const wxChar *TRACE_DND = _T("dnd");
60
22d5903e
RR
61//----------------------------------------------------------------------------
62// standard icons
63//----------------------------------------------------------------------------
64
22d5903e 65/* XPM */
90350682 66static const char * page_xpm[] = {
22d5903e
RR
67/* width height ncolors chars_per_pixel */
68"32 32 5 1",
69/* colors */
f03fc89f
VZ
70" s None c None",
71". c black",
72"X c wheat",
73"o c tan",
74"O c #6699FF",
22d5903e
RR
75/* pixels */
76" ................... ",
77" .XXXXXXXXXXXXXXXXX.. ",
78" .XXXXXXXXXXXXXXXXX.o. ",
79" .XXXXXXXXXXXXXXXXX.oo. ",
80" .XXXXXXXXXXXXXXXXX.ooo. ",
81" .XXXXXXXXXXXXXXXXX.oooo. ",
82" .XXXXXXXXXXXXXXXXX....... ",
83" .XXXXXOOOOOOOOOOXXXooooo. ",
84" .XXXXXXXXXXXXXXXXXXooooo. ",
85" .XXXXXOOOOOOOOOOXXXXXXXX. ",
86" .XXXXXXXXXXXXXXXXXXXXXXX. ",
87" .XXXXXXXOOOOOOOOOXXXXXXX. ",
88" .XXXXXXXXXXXXXXXXXXXXXXX. ",
89" .XXXXXXOOOOOOOOOOXXXXXXX. ",
90" .XXXXXXXXXXXXXXXXXXXXXXX. ",
91" .XXXXXOOOOOOOOOOXXXXXXXX. ",
92" .XXXXXXXXXXXXXXXXXXXXXXX. ",
93" .XXXXXXXOOOOOOOOOXXXXXXX. ",
94" .XXXXXXXXXXXXXXXXXXXXXXX. ",
95" .XXXXXXOOOOOOOOOOXXXXXXX. ",
96" .XXXXXXXXXXXXXXXXXXXXXXX. ",
97" .XXXXXOOOOOOOOOOXXXXXXXX. ",
98" .XXXXXXXXXXXXXXXXXXXXXXX. ",
99" .XXXXXXOOOOOOOOOOXXXXXXX. ",
100" .XXXXXXXXXXXXXXXXXXXXXXX. ",
101" .XXXXXOOOOOOOXXXXXXXXXXX. ",
102" .XXXXXXXXXXXXXXXXXXXXXXX. ",
103" .XXXXXXXXXXXXXXXXXXXXXXX. ",
104" .XXXXXXXXXXXXXXXXXXXXXXX. ",
105" .XXXXXXXXXXXXXXXXXXXXXXX. ",
106" .XXXXXXXXXXXXXXXXXXXXXXX. ",
107" ......................... "};
f03fc89f
VZ
108
109
2245b2b2
VZ
110// ============================================================================
111// private functions
112// ============================================================================
113
114// ----------------------------------------------------------------------------
115// convert between GTK+ and wxWindows DND constants
116// ----------------------------------------------------------------------------
117
118static wxDragResult ConvertFromGTK(long action)
119{
120 switch ( action )
121 {
122 case GDK_ACTION_COPY:
123 return wxDragCopy;
124
125 case GDK_ACTION_LINK:
126 return wxDragLink;
127
128 case GDK_ACTION_MOVE:
129 return wxDragMove;
130 }
131
132 return wxDragNone;
133}
4ba47b40 134
33a5bc52
RR
135// ----------------------------------------------------------------------------
136// "drag_leave"
137// ----------------------------------------------------------------------------
138
139static void target_drag_leave( GtkWidget *WXUNUSED(widget),
f03fc89f
VZ
140 GdkDragContext *context,
141 guint WXUNUSED(time),
142 wxDropTarget *drop_target )
33a5bc52 143{
5549fa65
RR
144 if (g_isIdle) wxapp_install_idle_handler();
145
829e3e8d
RR
146 /* inform the wxDropTarget about the current GdkDragContext.
147 this is only valid for the duration of this call */
148 drop_target->SetDragContext( context );
f03fc89f 149
829e3e8d
RR
150 /* we don't need return values. this event is just for
151 information */
152 drop_target->OnLeave();
f03fc89f 153
829e3e8d
RR
154 /* this has to be done because GDK has no "drag_enter" event */
155 drop_target->m_firstMotion = TRUE;
f03fc89f 156
829e3e8d
RR
157 /* after this, invalidate the drop_target's GdkDragContext */
158 drop_target->SetDragContext( (GdkDragContext*) NULL );
33a5bc52
RR
159}
160
161// ----------------------------------------------------------------------------
162// "drag_motion"
163// ----------------------------------------------------------------------------
164
165static gboolean target_drag_motion( GtkWidget *WXUNUSED(widget),
f03fc89f
VZ
166 GdkDragContext *context,
167 gint x,
168 gint y,
169 guint time,
170 wxDropTarget *drop_target )
33a5bc52 171{
5549fa65
RR
172 if (g_isIdle) wxapp_install_idle_handler();
173
829e3e8d
RR
174 /* Owen Taylor: "if the coordinates not in a drop zone,
175 return FALSE, otherwise call gtk_drag_status() and
176 return TRUE" */
f03fc89f 177
829e3e8d
RR
178 /* inform the wxDropTarget about the current GdkDragContext.
179 this is only valid for the duration of this call */
180 drop_target->SetDragContext( context );
f03fc89f 181
2245b2b2
VZ
182 // GTK+ always supposes that we want to copy the data by default while we
183 // might want to move it, so examine not only suggested_action - which is
184 // only good if we don't have our own preferences - but also the actions
185 // field
8ee9d618 186 wxDragResult result;
2245b2b2
VZ
187 if ( (gs_flagsForDrag & wxDrag_DefaultMove) == wxDrag_DefaultMove &&
188 (context->actions & GDK_ACTION_MOVE ) )
189 {
190 // move is requested by the program and allowed by GTK+ - do it, even
191 // though suggested_action may be currently wxDragCopy
8ee9d618 192 result = wxDragMove;
2245b2b2
VZ
193 }
194 else // use whatever GTK+ says we should
195 {
196 result = ConvertFromGTK(context->suggested_action);
197
198 if ( (result == wxDragMove) && !(gs_flagsForDrag & wxDrag_AllowMove) )
199 {
200 // we're requested to move but we can't
201 result = wxDragCopy;
202 }
203 }
8e00741d 204
829e3e8d 205 if (drop_target->m_firstMotion)
d6086ea6 206 {
829e3e8d 207 /* the first "drag_motion" event substitutes a "drag_enter" event */
c9057ae1 208 result = drop_target->OnEnter( x, y, result );
8e00741d
RR
209 }
210 else
211 {
212 /* give program a chance to react (i.e. to say no by returning FALSE) */
c9057ae1 213 result = drop_target->OnDragOver( x, y, result );
d6086ea6 214 }
f03fc89f 215
8ee9d618 216 bool ret = wxIsDragResultOk( result );
829e3e8d 217 if (ret)
bd77da97 218 {
8ee9d618
VZ
219 GdkDragAction action;
220 if (result == wxDragCopy)
221 action = GDK_ACTION_COPY;
b184227d
RD
222 else if (result == wxDragLink)
223 action = GDK_ACTION_LINK;
8ee9d618
VZ
224 else
225 action = GDK_ACTION_MOVE;
226
7b5d5699 227 gdk_drag_status( context, action, time );
bd77da97 228 }
f03fc89f 229
829e3e8d
RR
230 /* after this, invalidate the drop_target's GdkDragContext */
231 drop_target->SetDragContext( (GdkDragContext*) NULL );
f03fc89f 232
829e3e8d
RR
233 /* this has to be done because GDK has no "drag_enter" event */
234 drop_target->m_firstMotion = FALSE;
f03fc89f 235
829e3e8d 236 return ret;
33a5bc52
RR
237}
238
239// ----------------------------------------------------------------------------
240// "drag_drop"
241// ----------------------------------------------------------------------------
242
243static gboolean target_drag_drop( GtkWidget *widget,
f03fc89f
VZ
244 GdkDragContext *context,
245 gint x,
246 gint y,
247 guint time,
248 wxDropTarget *drop_target )
33a5bc52 249{
5549fa65
RR
250 if (g_isIdle) wxapp_install_idle_handler();
251
829e3e8d
RR
252 /* Owen Taylor: "if the drop is not in a drop zone,
253 return FALSE, otherwise, if you aren't accepting
254 the drop, call gtk_drag_finish() with success == FALSE
255 otherwise call gtk_drag_data_get()" */
4ba47b40
RR
256
257// printf( "drop.\n" );
f03fc89f 258
829e3e8d
RR
259 /* this seems to make a difference between not accepting
260 due to wrong target area and due to wrong format. let
261 us hope that this is not required.. */
f03fc89f 262
829e3e8d
RR
263 /* inform the wxDropTarget about the current GdkDragContext.
264 this is only valid for the duration of this call */
265 drop_target->SetDragContext( context );
f03fc89f 266
829e3e8d
RR
267 /* inform the wxDropTarget about the current drag widget.
268 this is only valid for the duration of this call */
269 drop_target->SetDragWidget( widget );
f03fc89f 270
829e3e8d
RR
271 /* inform the wxDropTarget about the current drag time.
272 this is only valid for the duration of this call */
273 drop_target->SetDragTime( time );
f03fc89f 274
bd77da97
RR
275/*
276 wxDragResult result = wxDragMove;
277 if (context->suggested_action == GDK_ACTION_COPY) result = wxDragCopy;
278*/
279
513abb88
RR
280 /* reset the block here as someone might very well
281 show a dialog as a reaction to a drop and this
282 wouldn't work without events */
283 g_blockEventsOnDrag = FALSE;
b2dea2ab 284
829e3e8d 285 bool ret = drop_target->OnDrop( x, y );
f03fc89f 286
5af019af 287 if (!ret)
829e3e8d 288 {
b2dea2ab 289 wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned FALSE") );
8ee9d618 290
829e3e8d
RR
291 /* cancel the whole thing */
292 gtk_drag_finish( context,
f03fc89f
VZ
293 FALSE, /* no success */
294 FALSE, /* don't delete data on dropping side */
295 time );
829e3e8d 296 }
8e00741d
RR
297 else
298 {
b2dea2ab 299 wxLogTrace(TRACE_DND, wxT( "Drop target: OnDrop returned TRUE") );
8ee9d618 300
8e00741d
RR
301#if wxUSE_THREADS
302 /* disable GUI threads */
8e00741d
RR
303#endif
304
305 GdkAtom format = drop_target->GetMatchingPair();
b72aa48c
VZ
306
307 // this does happen somehow, see bug 555111
308 wxCHECK_MSG( format, FALSE, _T("no matching GdkAtom for format?") )
8ee9d618 309
bd77da97
RR
310/*
311 GdkDragAction action = GDK_ACTION_MOVE;
8ee9d618
VZ
312 if (result == wxDragCopy) action == GDK_ACTION_COPY;
313 context->action = action;
bd77da97 314*/
8e00741d
RR
315 /* this should trigger an "drag_data_received" event */
316 gtk_drag_get_data( widget,
317 context,
318 format,
319 time );
320
321#if wxUSE_THREADS
322 /* re-enable GUI threads */
8e00741d
RR
323#endif
324 }
f03fc89f 325
829e3e8d
RR
326 /* after this, invalidate the drop_target's GdkDragContext */
327 drop_target->SetDragContext( (GdkDragContext*) NULL );
f03fc89f 328
829e3e8d
RR
329 /* after this, invalidate the drop_target's drag widget */
330 drop_target->SetDragWidget( (GtkWidget*) NULL );
f03fc89f 331
829e3e8d
RR
332 /* this has to be done because GDK has no "drag_enter" event */
333 drop_target->m_firstMotion = TRUE;
f03fc89f 334
829e3e8d 335 return ret;
33a5bc52
RR
336}
337
338// ----------------------------------------------------------------------------
339// "drag_data_received"
340// ----------------------------------------------------------------------------
341
342static void target_drag_data_received( GtkWidget *WXUNUSED(widget),
f03fc89f
VZ
343 GdkDragContext *context,
344 gint x,
345 gint y,
346 GtkSelectionData *data,
347 guint WXUNUSED(info),
348 guint time,
349 wxDropTarget *drop_target )
33a5bc52 350{
5549fa65
RR
351 if (g_isIdle) wxapp_install_idle_handler();
352
829e3e8d
RR
353 /* Owen Taylor: "call gtk_drag_finish() with
354 success == TRUE" */
355
829e3e8d 356 if ((data->length <= 0) || (data->format != 8))
33a5bc52 357 {
829e3e8d
RR
358 /* negative data length and non 8-bit data format
359 qualifies for junk */
360 gtk_drag_finish (context, FALSE, FALSE, time);
f03fc89f 361
f03fc89f 362 return;
829e3e8d 363 }
f03fc89f 364
b2dea2ab 365 wxLogTrace(TRACE_DND, wxT( "Drop target: data received event") );
8ee9d618 366
5af019af
RR
367 /* inform the wxDropTarget about the current GtkSelectionData.
368 this is only valid for the duration of this call */
369 drop_target->SetDragData( data );
f03fc89f 370
2245b2b2 371 wxDragResult result = ConvertFromGTK(context->suggested_action);
8ee9d618
VZ
372
373 if ( wxIsDragResultOk( drop_target->OnData( x, y, result ) ) )
829e3e8d 374 {
b2dea2ab 375 wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned TRUE") );
8ee9d618 376
f03fc89f 377 /* tell GTK that data transfer was successfull */
ab8884ac 378 gtk_drag_finish( context, TRUE, FALSE, time );
33a5bc52 379 }
5af019af
RR
380 else
381 {
b2dea2ab 382 wxLogTrace(TRACE_DND, wxT( "Drop target: OnData returned FALSE") );
8ee9d618 383
f03fc89f 384 /* tell GTK that data transfer was not successfull */
5af019af
RR
385 gtk_drag_finish( context, FALSE, FALSE, time );
386 }
f03fc89f 387
5af019af
RR
388 /* after this, invalidate the drop_target's drag data */
389 drop_target->SetDragData( (GtkSelectionData*) NULL );
33a5bc52
RR
390}
391
4ba47b40 392//----------------------------------------------------------------------------
33a5bc52 393// wxDropTarget
4ba47b40 394//----------------------------------------------------------------------------
33a5bc52 395
8ee9d618
VZ
396wxDropTarget::wxDropTarget( wxDataObject *data )
397 : wxDropTargetBase( data )
f5368809 398{
829e3e8d
RR
399 m_firstMotion = TRUE;
400 m_dragContext = (GdkDragContext*) NULL;
401 m_dragWidget = (GtkWidget*) NULL;
5af019af 402 m_dragData = (GtkSelectionData*) NULL;
829e3e8d 403 m_dragTime = 0;
f5368809
RR
404}
405
c9057ae1
VZ
406wxDragResult wxDropTarget::OnDragOver( wxCoord WXUNUSED(x),
407 wxCoord WXUNUSED(y),
408 wxDragResult def )
d6086ea6 409{
8ee9d618 410 // GetMatchingPair() checks for m_dataObject too, no need to do it here
2edc8f5b
VZ
411
412 // disable the debug message from GetMatchingPair() - there are too many
413 // of them otherwise
c4fda16b 414#ifdef __WXDEBUG__
2edc8f5b
VZ
415 wxLogNull noLog;
416#endif // Debug
417
c9057ae1 418 return (GetMatchingPair() != (GdkAtom) 0) ? def : wxDragNone;
d6086ea6
RR
419}
420
c9057ae1 421bool wxDropTarget::OnDrop( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y) )
d6086ea6 422{
b068c4e8 423 if (!m_dataObject)
a3e7d24d 424 return FALSE;
8ee9d618 425
8e00741d 426 return (GetMatchingPair() != (GdkAtom) 0);
d6086ea6
RR
427}
428
8ee9d618
VZ
429wxDragResult wxDropTarget::OnData( wxCoord WXUNUSED(x), wxCoord WXUNUSED(y),
430 wxDragResult def )
5af019af 431{
b068c4e8 432 if (!m_dataObject)
11e1c70d 433 return wxDragNone;
8ee9d618 434
8e00741d 435 if (GetMatchingPair() == (GdkAtom) 0)
11e1c70d 436 return wxDragNone;
8ee9d618
VZ
437
438 return GetData() ? def : wxDragNone;
5af019af
RR
439}
440
8e00741d 441GdkAtom wxDropTarget::GetMatchingPair()
5af019af 442{
8ee9d618 443 if (!m_dataObject)
8e00741d 444 return (GdkAtom) 0;
5af019af 445
8ee9d618 446 if (!m_dragContext)
8e00741d 447 return (GdkAtom) 0;
f03fc89f 448
22d5903e
RR
449 GList *child = m_dragContext->targets;
450 while (child)
451 {
452 GdkAtom formatAtom = (GdkAtom) GPOINTER_TO_INT(child->data);
2edc8f5b 453 wxDataFormat format( formatAtom );
f03fc89f 454
a3e7d24d 455#ifdef __WXDEBUG__
b2dea2ab
VZ
456 wxLogTrace(TRACE_DND, wxT("Drop target: drag has format: %s"),
457 format.GetId().c_str());
2edc8f5b
VZ
458#endif // Debug
459
b068c4e8 460 if (m_dataObject->IsSupportedFormat( format ))
2edc8f5b 461 return formatAtom;
f03fc89f 462
22d5903e
RR
463 child = child->next;
464 }
829e3e8d 465
8e00741d 466 return (GdkAtom) 0;
d6086ea6 467}
f03fc89f 468
8e00741d 469bool wxDropTarget::GetData()
d6086ea6 470{
8ee9d618 471 if (!m_dragData)
8e00741d 472 return FALSE;
f03fc89f 473
8ee9d618 474 if (!m_dataObject)
8e00741d 475 return FALSE;
f03fc89f 476
8e00741d 477 wxDataFormat dragFormat( m_dragData->target );
8ee9d618 478
b068c4e8 479 if (!m_dataObject->IsSupportedFormat( dragFormat ))
8e00741d 480 return FALSE;
f03fc89f 481
97c79de2
RR
482 m_dataObject->SetData( dragFormat, (size_t)m_dragData->length, (const void*)m_dragData->data );
483
484 return TRUE;
d6086ea6 485}
f03fc89f 486
f5368809
RR
487void wxDropTarget::UnregisterWidget( GtkWidget *widget )
488{
223d09f6 489 wxCHECK_RET( widget != NULL, wxT("unregister widget is NULL") );
f03fc89f 490
829e3e8d 491 gtk_drag_dest_unset( widget );
f03fc89f 492
d6086ea6 493 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
f03fc89f 494 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
33a5bc52 495
d6086ea6 496 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
f03fc89f 497 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
33a5bc52 498
d6086ea6 499 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
f03fc89f 500 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
33a5bc52 501
d6086ea6 502 gtk_signal_disconnect_by_func( GTK_OBJECT(widget),
f03fc89f 503 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
f5368809
RR
504}
505
506void wxDropTarget::RegisterWidget( GtkWidget *widget )
507{
223d09f6 508 wxCHECK_RET( widget != NULL, wxT("register widget is NULL") );
f03fc89f 509
829e3e8d
RR
510 /* gtk_drag_dest_set() determines what default behaviour we'd like
511 GTK to supply. we don't want to specify out targets (=formats)
512 or actions in advance (i.e. not GTK_DEST_DEFAULT_MOTION and
513 not GTK_DEST_DEFAULT_DROP). instead we react individually to
514 "drag_motion" and "drag_drop" events. this makes it possible
f03fc89f 515 to allow dropping on only a small area. we should set
829e3e8d
RR
516 GTK_DEST_DEFAULT_HIGHLIGHT as this will switch on the nice
517 highlighting if dragging over standard controls, but this
518 seems to be broken without the other two. */
f03fc89f 519
d6086ea6 520 gtk_drag_dest_set( widget,
f03fc89f
VZ
521 (GtkDestDefaults) 0, /* no default behaviour */
522 (GtkTargetEntry*) NULL, /* we don't supply any formats here */
523 0, /* number of targets = 0 */
524 (GdkDragAction) 0 ); /* we don't supply any actions here */
525
d6086ea6 526 gtk_signal_connect( GTK_OBJECT(widget), "drag_leave",
f03fc89f 527 GTK_SIGNAL_FUNC(target_drag_leave), (gpointer) this );
33a5bc52 528
d6086ea6 529 gtk_signal_connect( GTK_OBJECT(widget), "drag_motion",
f03fc89f 530 GTK_SIGNAL_FUNC(target_drag_motion), (gpointer) this );
33a5bc52 531
d6086ea6 532 gtk_signal_connect( GTK_OBJECT(widget), "drag_drop",
f03fc89f 533 GTK_SIGNAL_FUNC(target_drag_drop), (gpointer) this );
33a5bc52 534
d6086ea6 535 gtk_signal_connect( GTK_OBJECT(widget), "drag_data_received",
f03fc89f 536 GTK_SIGNAL_FUNC(target_drag_data_received), (gpointer) this );
f5368809
RR
537}
538
4ba47b40
RR
539//----------------------------------------------------------------------------
540// "drag_data_get"
541//----------------------------------------------------------------------------
542
f03fc89f 543static void
4ba47b40 544source_drag_data_get (GtkWidget *WXUNUSED(widget),
b02da6b1 545 GdkDragContext *WXUNUSED(context),
f03fc89f
VZ
546 GtkSelectionData *selection_data,
547 guint WXUNUSED(info),
548 guint WXUNUSED(time),
549 wxDropSource *drop_source )
4ba47b40 550{
5549fa65
RR
551 if (g_isIdle) wxapp_install_idle_handler();
552
97c79de2 553 wxDataFormat format( selection_data->target );
8ee9d618 554
b2dea2ab
VZ
555 wxLogTrace(TRACE_DND, wxT("Drop source: format requested: %s"),
556 format.GetId().c_str());
f6bcfd97 557
1dd989e1 558 drop_source->m_retValue = wxDragCancel;
8ee9d618 559
97c79de2 560 wxDataObject *data = drop_source->GetDataObject();
f6bcfd97 561
1dd989e1 562 if (!data)
97c79de2 563 {
b2dea2ab 564 wxLogTrace(TRACE_DND, wxT("Drop source: no data object") );
2edc8f5b 565 return;
97c79de2 566 }
1dd989e1 567
97c79de2
RR
568 if (!data->IsSupportedFormat(format))
569 {
b2dea2ab 570 wxLogTrace(TRACE_DND, wxT("Drop source: unsupported format") );
2edc8f5b 571 return;
97c79de2 572 }
f03fc89f 573
97c79de2
RR
574 if (data->GetDataSize(format) == 0)
575 {
b2dea2ab 576 wxLogTrace(TRACE_DND, wxT("Drop source: empty data") );
2edc8f5b 577 return;
97c79de2 578 }
8ee9d618 579
97c79de2 580 size_t size = data->GetDataSize(format);
f03fc89f 581
1dd989e1 582// printf( "data size: %d.\n", (int)data_size );
f03fc89f 583
1dd989e1 584 guchar *d = new guchar[size];
8ee9d618 585
97c79de2 586 if (!data->GetDataHere( format, (void*)d ))
1dd989e1 587 {
97c79de2 588 delete[] d;
2edc8f5b 589 return;
1dd989e1 590 }
f03fc89f 591
b453e1b2 592#if wxUSE_THREADS
1dd989e1 593 /* disable GUI threads */
b453e1b2 594#endif
8e193f38 595
4ba47b40 596 gtk_selection_data_set( selection_data,
f03fc89f
VZ
597 selection_data->target,
598 8, // 8-bit
1dd989e1
RR
599 d,
600 size );
f03fc89f 601
b453e1b2 602#if wxUSE_THREADS
1dd989e1 603 /* enable GUI threads */
b453e1b2 604#endif
f03fc89f 605
97c79de2 606 delete[] d;
4ba47b40 607}
f03fc89f 608
4ba47b40
RR
609//----------------------------------------------------------------------------
610// "drag_data_delete"
611//----------------------------------------------------------------------------
612
9f39393d
VZ
613static void source_drag_data_delete( GtkWidget *WXUNUSED(widget),
614 GdkDragContext *context,
615 wxDropSource *WXUNUSED(drop_source) )
4ba47b40 616{
f6bcfd97 617 if (g_isIdle)
13fb7b7a 618 wxapp_install_idle_handler();
33a5bc52 619
13fb7b7a 620 // printf( "Drag source: drag_data_delete\n" );
4ba47b40 621}
f03fc89f 622
4ba47b40
RR
623//----------------------------------------------------------------------------
624// "drag_begin"
625//----------------------------------------------------------------------------
626
627static void source_drag_begin( GtkWidget *WXUNUSED(widget),
f03fc89f
VZ
628 GdkDragContext *WXUNUSED(context),
629 wxDropSource *WXUNUSED(drop_source) )
4ba47b40 630{
f6bcfd97 631 if (g_isIdle)
13fb7b7a 632 wxapp_install_idle_handler();
5549fa65 633
13fb7b7a 634 // printf( "Drag source: drag_begin.\n" );
4ba47b40 635}
f03fc89f 636
4ba47b40
RR
637//----------------------------------------------------------------------------
638// "drag_end"
639//----------------------------------------------------------------------------
640
641static void source_drag_end( GtkWidget *WXUNUSED(widget),
f03fc89f
VZ
642 GdkDragContext *WXUNUSED(context),
643 wxDropSource *drop_source )
4ba47b40 644{
5549fa65
RR
645 if (g_isIdle) wxapp_install_idle_handler();
646
13fb7b7a 647 // printf( "Drag source: drag_end.\n" );
4ba47b40
RR
648
649 drop_source->m_waiting = FALSE;
650}
f03fc89f 651
7b5d5699
RR
652//-----------------------------------------------------------------------------
653// "configure_event" from m_iconWindow
654//-----------------------------------------------------------------------------
655
8ee9d618 656static gint
7b5d5699
RR
657gtk_dnd_window_configure_callback( GtkWidget *WXUNUSED(widget), GdkEventConfigure *WXUNUSED(event), wxDropSource *source )
658{
8ee9d618 659 if (g_isIdle)
7b5d5699
RR
660 wxapp_install_idle_handler();
661
2245b2b2 662 source->GiveFeedback( ConvertFromGTK(source->m_dragContext->action) );
8ee9d618 663
7b5d5699
RR
664 return 0;
665}
666
4ba47b40
RR
667//---------------------------------------------------------------------------
668// wxDropSource
669//---------------------------------------------------------------------------
22d5903e 670
f6bcfd97
BP
671wxDropSource::wxDropSource(wxWindow *win,
672 const wxIcon &iconCopy,
673 const wxIcon &iconMove,
674 const wxIcon &iconNone)
22d5903e 675{
4ba47b40 676 m_waiting = TRUE;
f03fc89f 677
7b5d5699 678 m_iconWindow = (GtkWidget*) NULL;
8ee9d618 679
22d5903e 680 m_window = win;
a2053b27
RR
681 m_widget = win->m_widget;
682 if (win->m_wxwindow) m_widget = win->m_wxwindow;
f03fc89f 683
22d5903e
RR
684 m_retValue = wxDragCancel;
685
f6bcfd97 686 SetIcons(iconCopy, iconMove, iconNone);
22d5903e
RR
687}
688
f6bcfd97
BP
689wxDropSource::wxDropSource(wxDataObject& data,
690 wxWindow *win,
691 const wxIcon &iconCopy,
692 const wxIcon &iconMove,
693 const wxIcon &iconNone)
22d5903e 694{
4ba47b40 695 m_waiting = TRUE;
8ee9d618 696
b068c4e8 697 SetData( data );
f03fc89f 698
7b5d5699 699 m_iconWindow = (GtkWidget*) NULL;
8ee9d618 700
22d5903e 701 m_window = win;
a2053b27
RR
702 m_widget = win->m_widget;
703 if (win->m_wxwindow) m_widget = win->m_wxwindow;
8ee9d618 704
22d5903e 705 m_retValue = wxDragCancel;
f03fc89f 706
f6bcfd97
BP
707 SetIcons(iconCopy, iconMove, iconNone);
708}
709
710void wxDropSource::SetIcons(const wxIcon &iconCopy,
711 const wxIcon &iconMove,
712 const wxIcon &iconNone)
713{
714 m_iconCopy = iconCopy;
715 m_iconMove = iconMove;
716 m_iconNone = iconNone;
717
718 if ( !m_iconCopy.Ok() )
719 m_iconCopy = wxIcon(page_xpm);
720 if ( !m_iconMove.Ok() )
721 m_iconMove = m_iconCopy;
722 if ( !m_iconNone.Ok() )
723 m_iconNone = m_iconCopy;
22d5903e
RR
724}
725
8e193f38 726wxDropSource::~wxDropSource()
22d5903e 727{
22d5903e 728}
f03fc89f 729
f6bcfd97 730void wxDropSource::PrepareIcon( int action, GdkDragContext *context )
7b5d5699 731{
f6bcfd97
BP
732 // get the right icon to display
733 wxIcon *icon = NULL;
734 if ( action & GDK_ACTION_MOVE )
735 icon = &m_iconMove;
736 else if ( action & GDK_ACTION_COPY )
737 icon = &m_iconCopy;
738 else
739 icon = &m_iconNone;
740
741 GdkBitmap *mask;
742 if ( icon->GetMask() )
743 mask = icon->GetMask()->GetBitmap();
744 else
745 mask = (GdkBitmap *)NULL;
746
747 GdkPixmap *pixmap = icon->GetPixmap();
7b5d5699
RR
748
749 gint width,height;
750 gdk_window_get_size (pixmap, &width, &height);
751
752 GdkColormap *colormap = gtk_widget_get_colormap( m_widget );
9d084560 753#ifndef __WXGTK20__
7b5d5699 754 gtk_widget_push_visual (gdk_colormap_get_visual (colormap));
9d084560 755#endif
7b5d5699
RR
756 gtk_widget_push_colormap (colormap);
757
758 m_iconWindow = gtk_window_new (GTK_WINDOW_POPUP);
759 gtk_widget_set_events (m_iconWindow, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
760 gtk_widget_set_app_paintable (GTK_WIDGET (m_iconWindow), TRUE);
761
9d084560 762#ifndef __WXGTK20__
7b5d5699 763 gtk_widget_pop_visual ();
9d084560 764#endif
7b5d5699
RR
765 gtk_widget_pop_colormap ();
766
767 gtk_widget_set_usize (m_iconWindow, width, height);
768 gtk_widget_realize (m_iconWindow);
769
770 gtk_signal_connect( GTK_OBJECT(m_iconWindow), "configure_event",
771 GTK_SIGNAL_FUNC(gtk_dnd_window_configure_callback), (gpointer)this );
8ee9d618 772
7b5d5699 773 gdk_window_set_back_pixmap (m_iconWindow->window, pixmap, FALSE);
8ee9d618 774
7b5d5699
RR
775 if (mask)
776 gtk_widget_shape_combine_mask (m_iconWindow, mask, 0, 0);
777
f6bcfd97 778 gtk_drag_set_icon_widget( context, m_iconWindow, 0, 0 );
7b5d5699
RR
779}
780
2245b2b2 781wxDragResult wxDropSource::DoDragDrop(int flags)
22d5903e 782{
2245b2b2
VZ
783 wxCHECK_MSG( m_data && m_data->GetFormatCount(), wxDragNone,
784 wxT("Drop source: no data") );
8ee9d618 785
513abb88
RR
786 // still in drag
787 if (g_blockEventsOnDrag)
2245b2b2 788 return wxDragNone;
b2dea2ab 789
f6bcfd97 790 // disabled for now
513abb88 791 g_blockEventsOnDrag = TRUE;
f03fc89f 792
4ba47b40 793 RegisterWindow();
f03fc89f 794
4ba47b40 795 m_waiting = TRUE;
22d5903e 796
4ba47b40 797 GtkTargetList *target_list = gtk_target_list_new( (GtkTargetEntry*) NULL, 0 );
8ee9d618 798
a3e7d24d 799 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
b068c4e8 800 m_data->GetAllFormats( array );
2245b2b2
VZ
801 size_t count = m_data->GetFormatCount();
802 for (size_t i = 0; i < count; i++)
a3e7d24d
RR
803 {
804 GdkAtom atom = array[i];
b2dea2ab 805 wxLogTrace(TRACE_DND, wxT("Drop source: Supported atom %s"), gdk_atom_name( atom ));
a3e7d24d
RR
806 gtk_target_list_add( target_list, atom, 0, 0 );
807 }
808 delete[] array;
f03fc89f 809
4ba47b40
RR
810 GdkEventMotion event;
811 event.window = m_widget->window;
812 int x = 0;
813 int y = 0;
814 GdkModifierType state;
815 gdk_window_get_pointer( event.window, &x, &y, &state );
816 event.x = x;
817 event.y = y;
818 event.state = state;
b02da6b1 819 event.time = (guint32)GDK_CURRENT_TIME;
f03fc89f 820
4ba47b40
RR
821 /* GTK wants to know which button was pressed which caused the dragging */
822 int button_number = 0;
823 if (event.state & GDK_BUTTON1_MASK) button_number = 1;
824 else if (event.state & GDK_BUTTON2_MASK) button_number = 2;
825 else if (event.state & GDK_BUTTON3_MASK) button_number = 3;
826
b453e1b2
RR
827#if wxUSE_THREADS
828 /* disable GUI threads */
b453e1b2 829#endif
8e193f38 830
4ba47b40
RR
831 /* don't start dragging if no button is down */
832 if (button_number)
22d5903e 833 {
9f39393d 834 int action = GDK_ACTION_COPY;
2245b2b2 835 if ( flags & wxDrag_AllowMove )
9f39393d 836 action |= GDK_ACTION_MOVE;
2245b2b2
VZ
837
838 // VZ: as we already use g_blockEventsOnDrag it shouldn't be that bad
839 // to use a global to pass the flags to the drop target but I'd
840 // surely prefer a better way to do it
841 gs_flagsForDrag = flags;
842
4ba47b40 843 GdkDragContext *context = gtk_drag_begin( m_widget,
f6bcfd97 844 target_list,
46c87a9a 845 (GdkDragAction)action,
f6bcfd97
BP
846 button_number, /* number of mouse button which started drag */
847 (GdkEvent*) &event );
8ee9d618
VZ
848
849 m_dragContext = context;
850
f6bcfd97 851 PrepareIcon( action, context );
f03fc89f 852
2245b2b2
VZ
853 while (m_waiting)
854 gtk_main_iteration();
f6bcfd97 855
2245b2b2
VZ
856 m_retValue = ConvertFromGTK(context->action);
857 if ( m_retValue == wxDragNone )
858 m_retValue = wxDragCancel;
4ba47b40 859 }
22d5903e 860
b453e1b2
RR
861#if wxUSE_THREADS
862 /* re-enable GUI threads */
b453e1b2 863#endif
8e193f38 864
4ba47b40 865 g_blockEventsOnDrag = FALSE;
f03fc89f 866
4ba47b40
RR
867 UnregisterWindow();
868
869 return m_retValue;
22d5903e
RR
870}
871
4ba47b40 872void wxDropSource::RegisterWindow()
22d5903e 873{
4ba47b40 874 if (!m_widget) return;
f6bcfd97 875
4ba47b40 876 gtk_signal_connect( GTK_OBJECT(m_widget), "drag_data_get",
f03fc89f 877 GTK_SIGNAL_FUNC (source_drag_data_get), (gpointer) this);
4ba47b40 878 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_data_delete",
f03fc89f 879 GTK_SIGNAL_FUNC (source_drag_data_delete), (gpointer) this );
4ba47b40 880 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_begin",
f03fc89f 881 GTK_SIGNAL_FUNC (source_drag_begin), (gpointer) this );
4ba47b40 882 gtk_signal_connect (GTK_OBJECT(m_widget), "drag_end",
f03fc89f 883 GTK_SIGNAL_FUNC (source_drag_end), (gpointer) this );
4ba47b40 884
22d5903e
RR
885}
886
4ba47b40 887void wxDropSource::UnregisterWindow()
22d5903e
RR
888{
889 if (!m_widget) return;
f03fc89f 890
4ba47b40 891 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
f03fc89f 892 GTK_SIGNAL_FUNC(source_drag_data_get), (gpointer) this );
4ba47b40 893 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
f03fc89f 894 GTK_SIGNAL_FUNC(source_drag_data_delete), (gpointer) this );
4ba47b40 895 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
f03fc89f 896 GTK_SIGNAL_FUNC(source_drag_begin), (gpointer) this );
4ba47b40 897 gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
f03fc89f 898 GTK_SIGNAL_FUNC(source_drag_end), (gpointer) this );
22d5903e
RR
899}
900
ac57418f 901#endif
93c5dd39 902 // wxUSE_DRAG_AND_DROP