1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        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" 
  13 #include "wx/clipbrd.h" 
  17 #include "wx/dataobj.h" 
  21 #include "wx/gtk/private.h" 
  23 //----------------------------------------------------------------------------- 
  25 //----------------------------------------------------------------------------- 
  30 //----------------------------------------------------------------------------- 
  32 //----------------------------------------------------------------------------- 
  34 GdkAtom  g_clipboardAtom   
= 0; 
  35 GdkAtom  g_targetsAtom     
= 0; 
  36 GdkAtom  g_timestampAtom   
= 0; 
  38 #if defined(__WXGTK20__) && wxUSE_UNICODE 
  39 extern GdkAtom g_altTextAtom
; 
  42 // the trace mask we use with wxLogTrace() - call 
  43 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here 
  44 // (there will be a *lot* of them!) 
  45 static const wxChar 
*TRACE_CLIPBOARD 
= _T("clipboard"); 
  47 //----------------------------------------------------------------------------- 
  49 //----------------------------------------------------------------------------- 
  51 /* The contents of a selection are returned in a GtkSelectionData 
  52    structure. selection/target identify the request. 
  53    type specifies the type of the return; if length < 0, and 
  54    the data should be ignored. This structure has object semantics - 
  55    no fields should be modified directly, they should not be created 
  56    directly, and pointers to them should not be stored beyond the duration of 
  57    a callback. (If the last is changed, we'll need to add reference 
  60 struct _GtkSelectionData 
  72 //----------------------------------------------------------------------------- 
  73 // "selection_received" for targets 
  74 //----------------------------------------------------------------------------- 
  78 targets_selection_received( GtkWidget 
*WXUNUSED(widget
), 
  79                             GtkSelectionData 
*selection_data
, 
  80                             guint32 
WXUNUSED(time
), 
  81                             wxClipboard 
*clipboard 
) 
  83     if ( wxTheClipboard 
&& selection_data
->length 
> 0 ) 
  85         // make sure we got the data in the correct form 
  86         GdkAtom type 
= selection_data
->type
; 
  87         if ( type 
!= GDK_SELECTION_TYPE_ATOM 
) 
  89             if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") ) 
  91                 wxLogTrace( TRACE_CLIPBOARD
, 
  92                             _T("got unsupported clipboard target") ); 
  94                 clipboard
->m_waiting 
= FALSE
; 
 100         wxDataFormat 
clip( selection_data
->selection 
); 
 101         wxLogTrace( TRACE_CLIPBOARD
, 
 102                     wxT("selection received for targets, clipboard %s"), 
 103                     clip
.GetId().c_str() ); 
 104 #endif // __WXDEBUG__ 
 106         // the atoms we received, holding a list of targets (= formats) 
 107         GdkAtom 
*atoms 
= (GdkAtom 
*)selection_data
->data
; 
 109         for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++) 
 111             wxDataFormat 
format( atoms
[i
] ); 
 113             wxLogTrace( TRACE_CLIPBOARD
, 
 114                         wxT("selection received for targets, format %s"), 
 115                         format
.GetId().c_str() ); 
 117 //            printf( "format %s requested %s\n", 
 118 //                    gdk_atom_name( atoms[i] ), 
 119 //                    gdk_atom_name( clipboard->m_targetRequested ) ); 
 121             if (format 
== clipboard
->m_targetRequested
) 
 123                 clipboard
->m_waiting 
= FALSE
; 
 124                 clipboard
->m_formatSupported 
= TRUE
; 
 130     clipboard
->m_waiting 
= FALSE
; 
 134 //----------------------------------------------------------------------------- 
 135 // "selection_received" for the actual data 
 136 //----------------------------------------------------------------------------- 
 140 selection_received( GtkWidget 
*WXUNUSED(widget
), 
 141                     GtkSelectionData 
*selection_data
, 
 142                     guint32 
WXUNUSED(time
), 
 143                     wxClipboard 
*clipboard 
) 
 147         clipboard
->m_waiting 
= FALSE
; 
 151     wxDataObject 
*data_object 
= clipboard
->m_receivedData
; 
 155         clipboard
->m_waiting 
= FALSE
; 
 159     if (selection_data
->length 
<= 0) 
 161         clipboard
->m_waiting 
= FALSE
; 
 165     wxDataFormat 
format( selection_data
->target 
); 
 167     // make sure we got the data in the correct format 
 168     if (!data_object
->IsSupportedFormat( format 
) ) 
 170         clipboard
->m_waiting 
= FALSE
; 
 175     This seems to cause problems somehow
 
 176     // make sure we got the data in the correct form (selection type). 
 177     // if so, copy data to target object 
 178     if (selection_data
->type 
!= GDK_SELECTION_TYPE_STRING
) 
 180         clipboard
->m_waiting 
= FALSE
; 
 185     data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data 
); 
 187     wxTheClipboard
->m_formatSupported 
= TRUE
; 
 188     clipboard
->m_waiting 
= FALSE
; 
 192 //----------------------------------------------------------------------------- 
 194 //----------------------------------------------------------------------------- 
 198 selection_clear_clip( GtkWidget 
*WXUNUSED(widget
), GdkEventSelection 
*event 
) 
 200     if (!wxTheClipboard
) return TRUE
; 
 202     if (event
->selection 
== GDK_SELECTION_PRIMARY
) 
 204         wxTheClipboard
->m_ownsPrimarySelection 
= FALSE
; 
 207     if (event
->selection 
== g_clipboardAtom
) 
 209         wxTheClipboard
->m_ownsClipboard 
= FALSE
; 
 213         wxTheClipboard
->m_waiting 
= FALSE
; 
 217     if ((!wxTheClipboard
->m_ownsPrimarySelection
) && 
 218         (!wxTheClipboard
->m_ownsClipboard
)) 
 220         /* the clipboard is no longer in our hands. we can the delete clipboard data. */ 
 221         if (wxTheClipboard
->m_data
) 
 223             wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" )); 
 225             delete wxTheClipboard
->m_data
; 
 226             wxTheClipboard
->m_data 
= (wxDataObject
*) NULL
; 
 230     wxTheClipboard
->m_waiting 
= FALSE
; 
 235 //----------------------------------------------------------------------------- 
 236 // selection handler for supplying data 
 237 //----------------------------------------------------------------------------- 
 241 selection_handler( GtkWidget 
*WXUNUSED(widget
), 
 242                    GtkSelectionData 
*selection_data
, 
 243                    guint 
WXUNUSED(info
), 
 244                    guint 
WXUNUSED(time
), 
 245                    gpointer signal_data 
) 
 247     if (!wxTheClipboard
) return; 
 249     if (!wxTheClipboard
->m_data
) return; 
 251     wxDataObject 
*data 
= wxTheClipboard
->m_data
; 
 253     // ICCCM says that TIMESTAMP is a required atom. 
 254     // In particular, it satisfies Klipper, which polls 
 255     // TIMESTAMP to see if the clipboards content has changed. 
 256     // It shall return the time which was used to set the data. 
 257     if (selection_data
->target 
== g_timestampAtom
) 
 259         uint timestamp 
= GPOINTER_TO_UINT (signal_data
); 
 260         gtk_selection_data_set(selection_data
, 
 261                                GDK_SELECTION_TYPE_INTEGER
, 
 263                                (guchar
*)&(timestamp
), 
 265         wxLogTrace(TRACE_CLIPBOARD
, 
 266                    _T("Clipboard TIMESTAMP requested, returning timestamp=%u"), 
 271     wxDataFormat 
format( selection_data
->target 
); 
 274     wxLogTrace(TRACE_CLIPBOARD
, 
 275                _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"), 
 276                format
.GetId().c_str(), 
 277                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->target
))).c_str(), 
 278                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->type
))).c_str(), 
 279                wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->selection
))).c_str(), 
 280                GPOINTER_TO_UINT( signal_data 
) 
 284     if (!data
->IsSupportedFormat( format 
)) return; 
 286     int size 
= data
->GetDataSize( format 
); 
 288     if (size 
== 0) return; 
 290     void *d 
= malloc(size
); 
 292     // Text data will be in UTF8 in Unicode mode. 
 293     data
->GetDataHere( selection_data
->target
, d 
); 
 295     // NB: GTK+ requires special treatment of UTF8_STRING data, the text 
 296     //     would show as UTF-8 data interpreted as latin1 (?) in other 
 297     //     GTK+ apps if we used gtk_selection_data_set() 
 298     if (format 
== wxDataFormat(wxDF_UNICODETEXT
)) 
 300         gtk_selection_data_set_text( 
 307         gtk_selection_data_set( 
 309             GDK_SELECTION_TYPE_STRING
, 
 319 //----------------------------------------------------------------------------- 
 321 //----------------------------------------------------------------------------- 
 323 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
) 
 325 wxClipboard::wxClipboard() 
 330     m_ownsClipboard 
= FALSE
; 
 331     m_ownsPrimarySelection 
= FALSE
; 
 333     m_data 
= (wxDataObject
*) NULL
; 
 334     m_receivedData 
= (wxDataObject
*) NULL
; 
 336     /* we use m_targetsWidget to query what formats are available */ 
 338     m_targetsWidget 
= gtk_window_new( GTK_WINDOW_POPUP 
); 
 339     gtk_widget_realize( m_targetsWidget 
); 
 341     g_signal_connect (m_targetsWidget
, "selection_received", 
 342                       G_CALLBACK (targets_selection_received
), this); 
 344     /* we use m_clipboardWidget to get and to offer data */ 
 346     m_clipboardWidget 
= gtk_window_new( GTK_WINDOW_POPUP 
); 
 347     gtk_widget_realize( m_clipboardWidget 
); 
 349     g_signal_connect (m_clipboardWidget
, "selection_received", 
 350                       G_CALLBACK (selection_received
), this); 
 352     g_signal_connect (m_clipboardWidget
, "selection_clear_event", 
 353                       G_CALLBACK (selection_clear_clip
), NULL
); 
 355     if (!g_clipboardAtom
) g_clipboardAtom 
= gdk_atom_intern( "CLIPBOARD", FALSE 
); 
 356     if (!g_targetsAtom
) g_targetsAtom 
= gdk_atom_intern ("TARGETS", FALSE
); 
 357     if (!g_timestampAtom
) g_timestampAtom 
= gdk_atom_intern ("TIMESTAMP", FALSE
); 
 359     m_formatSupported 
= FALSE
; 
 360     m_targetRequested 
= 0; 
 362     m_usePrimary 
= FALSE
; 
 365 wxClipboard::~wxClipboard() 
 369     if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget 
); 
 370     if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget 
); 
 373 void wxClipboard::Clear() 
 378         /* disable GUI threads */ 
 381         //  As we have data we also own the clipboard. Once we no longer own 
 382         //  it, clear_selection is called which will set m_data to zero 
 383         if (gdk_selection_owner_get( g_clipboardAtom 
) == m_clipboardWidget
->window
) 
 387             gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
, 
 388                                      (guint32
) GDK_CURRENT_TIME 
); 
 390             while (m_waiting
) gtk_main_iteration(); 
 393         if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY 
) == m_clipboardWidget
->window
) 
 397             gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
, 
 398                                      (guint32
) GDK_CURRENT_TIME 
); 
 400             while (m_waiting
) gtk_main_iteration(); 
 406             m_data 
= (wxDataObject
*) NULL
; 
 410         /* re-enable GUI threads */ 
 414     m_targetRequested 
= 0; 
 415     m_formatSupported 
= FALSE
; 
 418 bool wxClipboard::Open() 
 420     wxCHECK_MSG( !m_open
, FALSE
, wxT("clipboard already open") ); 
 427 bool wxClipboard::SetData( wxDataObject 
*data 
) 
 429     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 431     wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") ); 
 435     return AddData( data 
); 
 438 bool wxClipboard::AddData( wxDataObject 
*data 
) 
 440     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 442     wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") ); 
 444     // we can only store one wxDataObject 
 449     // get formats from wxDataObjects 
 450     wxDataFormat 
*array 
= new wxDataFormat
[ m_data
->GetFormatCount() ]; 
 451     m_data
->GetAllFormats( array 
); 
 453     // primary selection or clipboard 
 454     GdkAtom clipboard 
= m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 457     // by default provide TIMESTAMP as a target 
 458     gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
), 
 463     for (size_t i 
= 0; i 
< m_data
->GetFormatCount(); i
++) 
 465         wxLogTrace( TRACE_CLIPBOARD
, 
 466                     wxT("wxClipboard now supports atom %s"), 
 467                     array
[i
].GetId().c_str() ); 
 469 //        printf( "added %s\n", 
 470 //                    gdk_atom_name( array[i].GetFormatId() ) ); 
 472         gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
), 
 475                                   0 );  /* what is info ? */ 
 480     g_signal_connect (m_clipboardWidget
, "selection_get", 
 481                       G_CALLBACK (selection_handler
), 
 482                       GUINT_TO_POINTER (gtk_get_current_event_time()) ); 
 485     /* disable GUI threads */ 
 488     /* Tell the world we offer clipboard data */ 
 489     bool res 
= (gtk_selection_owner_set( m_clipboardWidget
, 
 491                                          (guint32
) GDK_CURRENT_TIME 
)); 
 494         m_ownsPrimarySelection 
= res
; 
 496         m_ownsClipboard 
= res
; 
 499     /* re-enable GUI threads */ 
 505 void wxClipboard::Close() 
 507     wxCHECK_RET( m_open
, wxT("clipboard not open") ); 
 512 bool wxClipboard::IsOpened() const 
 517 bool wxClipboard::IsSupported( const wxDataFormat
& format 
) 
 519     /* reentrance problems */ 
 520     if (m_waiting
) return FALSE
; 
 522     /* store requested format to be asked for by callbacks */ 
 523     m_targetRequested 
= format
; 
 525     wxLogTrace( TRACE_CLIPBOARD
, 
 526                 wxT("wxClipboard:IsSupported: requested format: %s"), 
 527                 format
.GetId().c_str() ); 
 529     wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 531     m_formatSupported 
= FALSE
; 
 533     /* perform query. this will set m_formatSupported to 
 534        TRUE if m_targetRequested is supported. 
 535        also, we have to wait for the "answer" from the 
 536        clipboard owner which is an asynchronous process. 
 537        therefore we set m_waiting = TRUE here and wait 
 538        until the callback "targets_selection_received" 
 543     gtk_selection_convert( m_targetsWidget
, 
 544                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 547                            (guint32
) GDK_CURRENT_TIME 
); 
 549     while (m_waiting
) gtk_main_iteration(); 
 551 #if defined(__WXGTK20__) && wxUSE_UNICODE 
 552     if (!m_formatSupported 
&& format 
== wxDataFormat(wxDF_UNICODETEXT
)) 
 554         // Another try with plain STRING format 
 555         extern GdkAtom g_altTextAtom
; 
 556         return IsSupported(g_altTextAtom
); 
 560     return m_formatSupported
; 
 563 bool wxClipboard::GetData( wxDataObject
& data 
) 
 565     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 567     /* get formats from wxDataObjects */ 
 568     wxDataFormat 
*array 
= new wxDataFormat
[ data
.GetFormatCount() ]; 
 569     data
.GetAllFormats( array 
); 
 571     for (size_t i 
= 0; i 
< data
.GetFormatCount(); i
++) 
 573         wxDataFormat 
format( array
[i
] ); 
 575         wxLogTrace( TRACE_CLIPBOARD
, 
 576                     wxT("wxClipboard::GetData: requested format: %s"), 
 577                     format
.GetId().c_str() ); 
 579         /* is data supported by clipboard ? */ 
 581         /* store requested format to be asked for by callbacks */ 
 582         m_targetRequested 
= format
; 
 584         wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 586         m_formatSupported 
= FALSE
; 
 588        /* perform query. this will set m_formatSupported to 
 589           TRUE if m_targetRequested is supported. 
 590           also, we have to wait for the "answer" from the 
 591           clipboard owner which is an asynchronous process. 
 592           therefore we set m_waiting = TRUE here and wait 
 593           until the callback "targets_selection_received" 
 598         gtk_selection_convert( m_targetsWidget
, 
 599                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 602                            (guint32
) GDK_CURRENT_TIME 
); 
 604         while (m_waiting
) gtk_main_iteration(); 
 606         if (!m_formatSupported
) continue; 
 608         /* store pointer to data object to be filled up by callbacks */ 
 609         m_receivedData 
= &data
; 
 611         /* store requested format to be asked for by callbacks */ 
 612         m_targetRequested 
= format
; 
 614         wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 617         m_formatSupported 
= FALSE
; 
 619         /* ask for clipboard contents.  this will set 
 620            m_formatSupported to TRUE if m_targetRequested 
 622            also, we have to wait for the "answer" from the 
 623            clipboard owner which is an asynchronous process. 
 624            therefore we set m_waiting = TRUE here and wait 
 625            until the callback "targets_selection_received" 
 630         wxLogTrace( TRACE_CLIPBOARD
, 
 631                     wxT("wxClipboard::GetData: format found, start convert") ); 
 633         gtk_selection_convert( m_clipboardWidget
, 
 634                                m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 637                                (guint32
) GDK_CURRENT_TIME 
); 
 639         while (m_waiting
) gtk_main_iteration(); 
 641         /* this is a true error as we checked for the presence of such data before */ 
 642         wxCHECK_MSG( m_formatSupported
, FALSE
, wxT("error retrieving data from clipboard") ); 
 649     wxLogTrace( TRACE_CLIPBOARD
, 
 650                 wxT("wxClipboard::GetData: format not found") );