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