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