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