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