1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/gtk/clipbrd.cpp 
   4 // Author:      Robert Roebling 
   6 // Copyright:   (c) 1998 Robert Roebling 
   7 // Licence:     wxWindows licence 
   8 ///////////////////////////////////////////////////////////////////////////// 
  10 // For compilers that support precompilation, includes "wx.h". 
  11 #include "wx/wxprec.h" 
  15 #include "wx/clipbrd.h" 
  22 #include "wx/dataobj.h" 
  24 #include "wx/gtk/private.h" 
  26 //----------------------------------------------------------------------------- 
  28 //----------------------------------------------------------------------------- 
  30 GdkAtom  g_clipboardAtom   
= 0; 
  31 GdkAtom  g_targetsAtom     
= 0; 
  32 GdkAtom  g_timestampAtom   
= 0; 
  35 extern GdkAtom g_altTextAtom
; 
  38 // the trace mask we use with wxLogTrace() - call 
  39 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here 
  40 // (there will be a *lot* of them!) 
  41 static const wxChar 
*TRACE_CLIPBOARD 
= _T("clipboard"); 
  43 //----------------------------------------------------------------------------- 
  45 //----------------------------------------------------------------------------- 
  47 /* The contents of a selection are returned in a GtkSelectionData 
  48    structure. selection/target identify the request. 
  49    type specifies the type of the return; if length < 0, and 
  50    the data should be ignored. This structure has object semantics - 
  51    no fields should be modified directly, they should not be created 
  52    directly, and pointers to them should not be stored beyond the duration of 
  53    a callback. (If the last is changed, we'll need to add reference 
  56 struct _GtkSelectionData 
  68 //----------------------------------------------------------------------------- 
  69 // "selection_received" for targets 
  70 //----------------------------------------------------------------------------- 
  74 targets_selection_received( GtkWidget 
*WXUNUSED(widget
), 
  75                             GtkSelectionData 
*selection_data
, 
  76                             guint32 
WXUNUSED(time
), 
  77                             wxClipboard 
*clipboard 
) 
  79     if ( wxTheClipboard 
&& selection_data
->length 
> 0 ) 
  81         // make sure we got the data in the correct form 
  82         GdkAtom type 
= selection_data
->type
; 
  83         if ( type 
!= GDK_SELECTION_TYPE_ATOM 
) 
  85             if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") ) 
  87                 wxLogTrace( TRACE_CLIPBOARD
, 
  88                             _T("got unsupported clipboard target") ); 
  90                 clipboard
->m_waiting 
= false; 
  96         wxDataFormat 
clip( selection_data
->selection 
); 
  97         wxLogTrace( TRACE_CLIPBOARD
, 
  98                     wxT("selection received for targets, clipboard %s"), 
  99                     clip
.GetId().c_str() ); 
 100 #endif // __WXDEBUG__ 
 102         // the atoms we received, holding a list of targets (= formats) 
 103         GdkAtom 
*atoms 
= (GdkAtom 
*)selection_data
->data
; 
 105         for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++) 
 107             wxDataFormat 
format( atoms
[i
] ); 
 109             wxLogTrace( TRACE_CLIPBOARD
, 
 110                         wxT("selection received for targets, format %s"), 
 111                         format
.GetId().c_str() ); 
 113 //            printf( "format %s requested %s\n", 
 114 //                    gdk_atom_name( atoms[i] ), 
 115 //                    gdk_atom_name( clipboard->m_targetRequested ) ); 
 117             if (format 
== clipboard
->m_targetRequested
) 
 119                 clipboard
->m_waiting 
= false; 
 120                 clipboard
->m_formatSupported 
= true; 
 126     clipboard
->m_waiting 
= false; 
 130 //----------------------------------------------------------------------------- 
 131 // "selection_received" for the actual data 
 132 //----------------------------------------------------------------------------- 
 136 selection_received( GtkWidget 
*WXUNUSED(widget
), 
 137                     GtkSelectionData 
*selection_data
, 
 138                     guint32 
WXUNUSED(time
), 
 139                     wxClipboard 
*clipboard 
) 
 143         clipboard
->m_waiting 
= false; 
 147     wxDataObject 
*data_object 
= clipboard
->m_receivedData
; 
 151         clipboard
->m_waiting 
= false; 
 155     if (selection_data
->length 
<= 0) 
 157         clipboard
->m_waiting 
= false; 
 161     wxDataFormat 
format( selection_data
->target 
); 
 163     // make sure we got the data in the correct format 
 164     if (!data_object
->IsSupportedFormat( format 
) ) 
 166         clipboard
->m_waiting 
= false; 
 171     This seems to cause problems somehow
 
 172     // make sure we got the data in the correct form (selection type). 
 173     // if so, copy data to target object 
 174     if (selection_data
->type 
!= GDK_SELECTION_TYPE_STRING
) 
 176         clipboard
->m_waiting 
= false; 
 181     data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data 
); 
 183     wxTheClipboard
->m_formatSupported 
= true; 
 184     clipboard
->m_waiting 
= false; 
 188 //----------------------------------------------------------------------------- 
 190 //----------------------------------------------------------------------------- 
 194 selection_clear_clip( GtkWidget 
*WXUNUSED(widget
), GdkEventSelection 
*event 
) 
 196     if (!wxTheClipboard
) return true; 
 198     if (event
->selection 
== GDK_SELECTION_PRIMARY
) 
 200         wxTheClipboard
->m_ownsPrimarySelection 
= false; 
 203     if (event
->selection 
== g_clipboardAtom
) 
 205         wxTheClipboard
->m_ownsClipboard 
= false; 
 209         wxTheClipboard
->m_waiting 
= false; 
 213     if ((!wxTheClipboard
->m_ownsPrimarySelection
) && 
 214         (!wxTheClipboard
->m_ownsClipboard
)) 
 216         /* the clipboard is no longer in our hands. we can the delete clipboard data. */ 
 217         if (wxTheClipboard
->m_data
) 
 219             wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" )); 
 221             delete wxTheClipboard
->m_data
; 
 222             wxTheClipboard
->m_data 
= (wxDataObject
*) NULL
; 
 226     wxTheClipboard
->m_waiting 
= false; 
 231 //----------------------------------------------------------------------------- 
 232 // selection handler for supplying data 
 233 //----------------------------------------------------------------------------- 
 237 selection_handler( GtkWidget 
*WXUNUSED(widget
), 
 238                    GtkSelectionData 
*selection_data
, 
 239                    guint 
WXUNUSED(info
), 
 240                    guint 
WXUNUSED(time
), 
 241                    gpointer signal_data 
) 
 243     if (!wxTheClipboard
) return; 
 245     if (!wxTheClipboard
->m_data
) return; 
 247     wxDataObject 
*data 
= wxTheClipboard
->m_data
; 
 249     // ICCCM says that TIMESTAMP is a required atom. 
 250     // In particular, it satisfies Klipper, which polls 
 251     // TIMESTAMP to see if the clipboards content has changed. 
 252     // It shall return the time which was used to set the data. 
 253     if (selection_data
->target 
== g_timestampAtom
) 
 255         guint timestamp 
= GPOINTER_TO_UINT (signal_data
); 
 256         gtk_selection_data_set(selection_data
, 
 257                                GDK_SELECTION_TYPE_INTEGER
, 
 259                                (guchar
*)&(timestamp
), 
 261         wxLogTrace(TRACE_CLIPBOARD
, 
 262                    _T("Clipboard TIMESTAMP requested, returning timestamp=%u"), 
 267     wxDataFormat 
format( selection_data
->target 
); 
 270     wxLogTrace(TRACE_CLIPBOARD
, 
 271                _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"), 
 272                format
.GetId().c_str(), 
 273                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->target
))).c_str(), 
 274                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->type
))).c_str(), 
 275                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->selection
))).c_str(), 
 276                GPOINTER_TO_UINT( signal_data 
) 
 280     if (!data
->IsSupportedFormat( format 
)) return; 
 282     int size 
= data
->GetDataSize( format 
); 
 284     if (size 
== 0) return; 
 286     void *d 
= malloc(size
); 
 288     // Text data will be in UTF8 in Unicode mode. 
 289     data
->GetDataHere( selection_data
->target
, d 
); 
 291     // NB: GTK+ requires special treatment of UTF8_STRING data, the text 
 292     //     would show as UTF-8 data interpreted as latin1 (?) in other 
 293     //     GTK+ apps if we used gtk_selection_data_set() 
 294     if (format 
== wxDataFormat(wxDF_UNICODETEXT
)) 
 296         gtk_selection_data_set_text( 
 303         gtk_selection_data_set( 
 305             GDK_SELECTION_TYPE_STRING
, 
 315 //----------------------------------------------------------------------------- 
 317 //----------------------------------------------------------------------------- 
 319 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
) 
 321 wxClipboard::wxClipboard() 
 326     m_ownsClipboard 
= false; 
 327     m_ownsPrimarySelection 
= false; 
 329     m_data 
= (wxDataObject
*) NULL
; 
 330     m_receivedData 
= (wxDataObject
*) NULL
; 
 332     /* we use m_targetsWidget to query what formats are available */ 
 334     m_targetsWidget 
= gtk_window_new( GTK_WINDOW_POPUP 
); 
 335     gtk_widget_realize( m_targetsWidget 
); 
 337     g_signal_connect (m_targetsWidget
, "selection_received", 
 338                       G_CALLBACK (targets_selection_received
), this); 
 340     /* we use m_clipboardWidget to get and to offer data */ 
 342     m_clipboardWidget 
= gtk_window_new( GTK_WINDOW_POPUP 
); 
 343     gtk_widget_realize( m_clipboardWidget 
); 
 345     g_signal_connect (m_clipboardWidget
, "selection_received", 
 346                       G_CALLBACK (selection_received
), this); 
 348     g_signal_connect (m_clipboardWidget
, "selection_clear_event", 
 349                       G_CALLBACK (selection_clear_clip
), NULL
); 
 351     if (!g_clipboardAtom
) g_clipboardAtom 
= gdk_atom_intern( "CLIPBOARD", FALSE 
); 
 352     if (!g_targetsAtom
) g_targetsAtom 
= gdk_atom_intern ("TARGETS", FALSE
); 
 353     if (!g_timestampAtom
) g_timestampAtom 
= gdk_atom_intern ("TIMESTAMP", FALSE
); 
 355     m_formatSupported 
= false; 
 356     m_targetRequested 
= 0; 
 358     m_usePrimary 
= false; 
 361 wxClipboard::~wxClipboard() 
 365     if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget 
); 
 366     if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget 
); 
 369 void wxClipboard::Clear() 
 374         /* disable GUI threads */ 
 377         //  As we have data we also own the clipboard. Once we no longer own 
 378         //  it, clear_selection is called which will set m_data to zero 
 379         if (gdk_selection_owner_get( g_clipboardAtom 
) == m_clipboardWidget
->window
) 
 383             gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
, 
 384                                      (guint32
) GDK_CURRENT_TIME 
); 
 386             while (m_waiting
) gtk_main_iteration(); 
 389         if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY 
) == m_clipboardWidget
->window
) 
 393             gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
, 
 394                                      (guint32
) GDK_CURRENT_TIME 
); 
 396             while (m_waiting
) gtk_main_iteration(); 
 402             m_data 
= (wxDataObject
*) NULL
; 
 406         /* re-enable GUI threads */ 
 410     m_targetRequested 
= 0; 
 411     m_formatSupported 
= false; 
 414 bool wxClipboard::Open() 
 416     wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") ); 
 423 bool wxClipboard::SetData( wxDataObject 
*data 
) 
 425     wxCHECK_MSG( m_open
, false, wxT("clipboard not open") ); 
 427     wxCHECK_MSG( data
, false, wxT("data is invalid") ); 
 431     return AddData( data 
); 
 434 bool wxClipboard::AddData( wxDataObject 
*data 
) 
 436     wxCHECK_MSG( m_open
, false, wxT("clipboard not open") ); 
 438     wxCHECK_MSG( data
, false, wxT("data is invalid") ); 
 440     // we can only store one wxDataObject 
 445     // get formats from wxDataObjects 
 446     wxDataFormat 
*array 
= new wxDataFormat
[ m_data
->GetFormatCount() ]; 
 447     m_data
->GetAllFormats( array 
); 
 449     // primary selection or clipboard 
 450     GdkAtom clipboard 
= m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 453     // by default provide TIMESTAMP as a target 
 454     gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
), 
 459     for (size_t i 
= 0; i 
< m_data
->GetFormatCount(); i
++) 
 461         wxLogTrace( TRACE_CLIPBOARD
, 
 462                     wxT("wxClipboard now supports atom %s"), 
 463                     array
[i
].GetId().c_str() ); 
 465 //        printf( "added %s\n", 
 466 //                    gdk_atom_name( array[i].GetFormatId() ) ); 
 468         gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
), 
 471                                   0 );  /* what is info ? */ 
 476     g_signal_connect (m_clipboardWidget
, "selection_get", 
 477                       G_CALLBACK (selection_handler
), 
 478                       GUINT_TO_POINTER (gtk_get_current_event_time()) ); 
 481     /* disable GUI threads */ 
 484     /* Tell the world we offer clipboard data */ 
 485     bool res 
= (gtk_selection_owner_set( m_clipboardWidget
, 
 487                                          (guint32
) GDK_CURRENT_TIME 
)); 
 490         m_ownsPrimarySelection 
= res
; 
 492         m_ownsClipboard 
= res
; 
 495     /* re-enable GUI threads */ 
 501 void wxClipboard::Close() 
 503     wxCHECK_RET( m_open
, wxT("clipboard not open") ); 
 508 bool wxClipboard::IsOpened() const 
 513 bool wxClipboard::IsSupported( const wxDataFormat
& format 
) 
 515     /* reentrance problems */ 
 516     if (m_waiting
) return false; 
 518     /* store requested format to be asked for by callbacks */ 
 519     m_targetRequested 
= format
; 
 521     wxLogTrace( TRACE_CLIPBOARD
, 
 522                 wxT("wxClipboard:IsSupported: requested format: %s"), 
 523                 format
.GetId().c_str() ); 
 525     wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") ); 
 527     m_formatSupported 
= false; 
 529     /* perform query. this will set m_formatSupported to 
 530        true if m_targetRequested is supported. 
 531        also, we have to wait for the "answer" from the 
 532        clipboard owner which is an asynchronous process. 
 533        therefore we set m_waiting = true here and wait 
 534        until the callback "targets_selection_received" 
 539     gtk_selection_convert( m_targetsWidget
, 
 540                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 543                            (guint32
) GDK_CURRENT_TIME 
); 
 545     while (m_waiting
) gtk_main_iteration(); 
 548     if (!m_formatSupported 
&& format 
== wxDataFormat(wxDF_UNICODETEXT
)) 
 550         // Another try with plain STRING format 
 551         extern GdkAtom g_altTextAtom
; 
 552         return IsSupported(g_altTextAtom
); 
 556     return m_formatSupported
; 
 559 bool wxClipboard::GetData( wxDataObject
& data 
) 
 561     wxCHECK_MSG( m_open
, false, wxT("clipboard not open") ); 
 563     /* get formats from wxDataObjects */ 
 564     wxDataFormat 
*array 
= new wxDataFormat
[ data
.GetFormatCount() ]; 
 565     data
.GetAllFormats( array 
); 
 567     for (size_t i 
= 0; i 
< data
.GetFormatCount(); i
++) 
 569         wxDataFormat 
format( array
[i
] ); 
 571         wxLogTrace( TRACE_CLIPBOARD
, 
 572                     wxT("wxClipboard::GetData: requested format: %s"), 
 573                     format
.GetId().c_str() ); 
 575         /* is data supported by clipboard ? */ 
 577         /* store requested format to be asked for by callbacks */ 
 578         m_targetRequested 
= format
; 
 580         wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") ); 
 582         m_formatSupported 
= false; 
 584        /* perform query. this will set m_formatSupported to 
 585           true if m_targetRequested is supported. 
 586           also, we have to wait for the "answer" from the 
 587           clipboard owner which is an asynchronous process. 
 588           therefore we set m_waiting = true here and wait 
 589           until the callback "targets_selection_received" 
 594         gtk_selection_convert( m_targetsWidget
, 
 595                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 598                            (guint32
) GDK_CURRENT_TIME 
); 
 600         while (m_waiting
) gtk_main_iteration(); 
 602         if (!m_formatSupported
) continue; 
 604         /* store pointer to data object to be filled up by callbacks */ 
 605         m_receivedData 
= &data
; 
 607         /* store requested format to be asked for by callbacks */ 
 608         m_targetRequested 
= format
; 
 610         wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") ); 
 613         m_formatSupported 
= false; 
 615         /* ask for clipboard contents.  this will set 
 616            m_formatSupported to true if m_targetRequested 
 618            also, we have to wait for the "answer" from the 
 619            clipboard owner which is an asynchronous process. 
 620            therefore we set m_waiting = true here and wait 
 621            until the callback "targets_selection_received" 
 626         wxLogTrace( TRACE_CLIPBOARD
, 
 627                     wxT("wxClipboard::GetData: format found, start convert") ); 
 629         gtk_selection_convert( m_clipboardWidget
, 
 630                                m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 633                                (guint32
) GDK_CURRENT_TIME 
); 
 635         while (m_waiting
) gtk_main_iteration(); 
 637         /* this is a true error as we checked for the presence of such data before */ 
 638         wxCHECK_MSG( m_formatSupported
, false, wxT("error retrieving data from clipboard") ); 
 645     wxLogTrace( TRACE_CLIPBOARD
, 
 646                 wxT("wxClipboard::GetData: format not found") );