1 ///////////////////////////////////////////////////////////////////////////// 
   3 // Purpose:     Clipboard functionality 
   4 // Author:      Robert Roebling 
   7 // Copyright:   (c) Robert Roebling 
   8 // Licence:     wxWindows licence 
   9 ///////////////////////////////////////////////////////////////////////////// 
  11 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  12 #pragma implementation "clipbrd.h" 
  15 #include "wx/clipbrd.h" 
  19 #include "wx/dataobj.h" 
  23 #include "wx/x11/private.h" 
  25 //----------------------------------------------------------------------------- 
  27 //----------------------------------------------------------------------------- 
  30 Atom  g_clipboardAtom   
= 0; 
  31 Atom  g_targetsAtom     
= 0; 
  34 // the trace mask we use with wxLogTrace() - call 
  35 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here 
  36 // (there will be a *lot* of them!) 
  37 static const wxChar 
*TRACE_CLIPBOARD 
= _T("clipboard"); 
  39 //----------------------------------------------------------------------------- 
  41 //----------------------------------------------------------------------------- 
  43 /* The contents of a selection are returned in a GtkSelectionData 
  44    structure. selection/target identify the request. 
  45    type specifies the type of the return; if length < 0, and 
  46    the data should be ignored. This structure has object semantics - 
  47    no fields should be modified directly, they should not be created 
  48    directly, and pointers to them should not be stored beyond the duration of 
  49    a callback. (If the last is changed, we'll need to add reference 
  52 struct _GtkSelectionData 
  64 //----------------------------------------------------------------------------- 
  65 // "selection_received" for targets 
  66 //----------------------------------------------------------------------------- 
  71 targets_selection_received( GtkWidget 
*WXUNUSED(widget
), 
  72                             GtkSelectionData 
*selection_data
, 
  73 #if (GTK_MINOR_VERSION > 0) 
  74                             guint32 
WXUNUSED(time
), 
  76                             wxClipboard 
*clipboard 
) 
  78     if ( wxTheClipboard 
&& selection_data
->length 
> 0 ) 
  80         /* make sure we got the data in the correct form */ 
  81         GdkAtom type 
= selection_data
->type
; 
  82         if ( type 
!= GDK_SELECTION_TYPE_ATOM 
) 
  84             if ( strcmp(gdk_atom_name(type
), "TARGETS") ) 
  86                 wxLogTrace( TRACE_CLIPBOARD
, 
  87                             _T("got unsupported clipboard target") ); 
  89                 clipboard
->m_waiting 
= FALSE
; 
  95         wxDataFormat 
clip( selection_data
->selection 
); 
  96         wxLogTrace( TRACE_CLIPBOARD
, 
  97                     wxT("selection received for targets, clipboard %s"), 
  98                     clip
.GetId().c_str() ); 
 101         // the atoms we received, holding a list of targets (= formats) 
 102         GdkAtom 
*atoms 
= (GdkAtom 
*)selection_data
->data
; 
 104         for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++) 
 106             wxDataFormat 
format( atoms
[i
] ); 
 108             wxLogTrace( TRACE_CLIPBOARD
, 
 109                         wxT("selection received for targets, format %s"), 
 110                         format
.GetId().c_str() ); 
 112             if (format 
== clipboard
->m_targetRequested
) 
 114                 clipboard
->m_waiting 
= FALSE
; 
 115                 clipboard
->m_formatSupported 
= TRUE
; 
 121     clipboard
->m_waiting 
= FALSE
; 
 124 //----------------------------------------------------------------------------- 
 125 // "selection_received" for the actual data 
 126 //----------------------------------------------------------------------------- 
 129 selection_received( GtkWidget 
*WXUNUSED(widget
), 
 130                     GtkSelectionData 
*selection_data
, 
 131 #if (GTK_MINOR_VERSION > 0) 
 132                     guint32 
WXUNUSED(time
), 
 134                     wxClipboard 
*clipboard 
) 
 138         clipboard
->m_waiting 
= FALSE
; 
 142     wxDataObject 
*data_object 
= clipboard
->m_receivedData
; 
 146         clipboard
->m_waiting 
= FALSE
; 
 150     if (selection_data
->length 
<= 0) 
 152         clipboard
->m_waiting 
= FALSE
; 
 156     wxDataFormat 
format( selection_data
->target 
); 
 158     /* make sure we got the data in the correct format */ 
 159     if (!data_object
->IsSupportedFormat( format 
) ) 
 161         clipboard
->m_waiting 
= FALSE
; 
 165     /* make sure we got the data in the correct form (selection type). 
 166        if so, copy data to target object */ 
 167     if (selection_data
->type 
!= GDK_SELECTION_TYPE_STRING
) 
 169         clipboard
->m_waiting 
= FALSE
; 
 173     data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data 
); 
 175     wxTheClipboard
->m_formatSupported 
= TRUE
; 
 176     clipboard
->m_waiting 
= FALSE
; 
 179 //----------------------------------------------------------------------------- 
 181 //----------------------------------------------------------------------------- 
 184 selection_clear_clip( GtkWidget 
*WXUNUSED(widget
), GdkEventSelection 
*event 
) 
 186     if (!wxTheClipboard
) return TRUE
; 
 188     if (event
->selection 
== GDK_SELECTION_PRIMARY
) 
 190         wxTheClipboard
->m_ownsPrimarySelection 
= FALSE
; 
 193     if (event
->selection 
== g_clipboardAtom
) 
 195         wxTheClipboard
->m_ownsClipboard 
= FALSE
; 
 199         wxTheClipboard
->m_waiting 
= FALSE
; 
 203     if ((!wxTheClipboard
->m_ownsPrimarySelection
) && 
 204         (!wxTheClipboard
->m_ownsClipboard
)) 
 206         /* the clipboard is no longer in our hands. we can the delete clipboard data. */ 
 207         if (wxTheClipboard
->m_data
) 
 209             wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" )); 
 211             delete wxTheClipboard
->m_data
; 
 212             wxTheClipboard
->m_data 
= (wxDataObject
*) NULL
; 
 216     wxTheClipboard
->m_waiting 
= FALSE
; 
 220 //----------------------------------------------------------------------------- 
 221 // selection handler for supplying data 
 222 //----------------------------------------------------------------------------- 
 225 selection_handler( GtkWidget 
*WXUNUSED(widget
), 
 226                    GtkSelectionData 
*selection_data
, 
 227                    guint 
WXUNUSED(info
), 
 228                    guint 
WXUNUSED(time
), 
 229                    gpointer 
WXUNUSED(data
) ) 
 231     if (!wxTheClipboard
) return; 
 233     if (!wxTheClipboard
->m_data
) return; 
 235     wxDataObject 
*data 
= wxTheClipboard
->m_data
; 
 237     wxDataFormat 
format( selection_data
->target 
); 
 239     if (!data
->IsSupportedFormat( format 
)) return; 
 241     int size 
= data
->GetDataSize( format 
); 
 243     if (size 
== 0) return; 
 245     void *d 
= malloc(size
); 
 247     data
->GetDataHere( selection_data
->target
, d 
); 
 249     // transform Unicode text into multibyte before putting it on clipboard 
 251     if ( format
.GetType() == wxDF_TEXT 
) 
 253         const wchar_t *wstr 
= (const wchar_t *)d
; 
 254         size_t len 
= wxConvCurrent
->WC2MB(NULL
, wstr
, 0); 
 255         char *str 
= malloc(len 
+ 1); 
 256         wxConvCurrent
->WC2MB(str
, wstr
, len
); 
 262 #endif // wxUSE_UNICODE 
 264     gtk_selection_data_set( 
 266         GDK_SELECTION_TYPE_STRING
, 
 276 //----------------------------------------------------------------------------- 
 278 //----------------------------------------------------------------------------- 
 280 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
) 
 282 wxClipboard::wxClipboard() 
 286     m_ownsClipboard 
= FALSE
; 
 287     m_ownsPrimarySelection 
= FALSE
; 
 289     m_data 
= (wxDataObject
*) NULL
; 
 290     m_receivedData 
= (wxDataObject
*) NULL
; 
 292     /* we use m_targetsWidget to query what formats are available */ 
 294     /* we use m_clipboardWidget to get and to offer data */ 
 296     if (!g_clipboardAtom
) g_clipboardAtom 
= XInternAtom( (Display
*) wxGetDisplay(), "CLIPBOARD", False 
); 
 297     if (!g_targetsAtom
) g_targetsAtom 
= XInternAtom( (Display
*) wxGetDisplay(), "TARGETS", False 
); 
 300     m_formatSupported 
= FALSE
; 
 301     m_targetRequested 
= 0; 
 303     m_usePrimary 
= FALSE
; 
 306 wxClipboard::~wxClipboard() 
 310 //    if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget ); 
 311 //    if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget ); 
 314 void wxClipboard::Clear() 
 319         /* disable GUI threads */ 
 322         /*  As we have data we also own the clipboard. Once we no longer own 
 323             it, clear_selection is called which will set m_data to zero */ 
 325         if (gdk_selection_owner_get( g_clipboardAtom 
) == m_clipboardWidget
->window
) 
 329             gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
, 
 330                                      (guint32
) GDK_CURRENT_TIME 
); 
 332             while (m_waiting
) gtk_main_iteration(); 
 335         if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY 
) == m_clipboardWidget
->window
) 
 339             gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
, 
 340                                      (guint32
) GDK_CURRENT_TIME 
); 
 342             while (m_waiting
) gtk_main_iteration(); 
 349             m_data 
= (wxDataObject
*) NULL
; 
 353         /* re-enable GUI threads */ 
 357     m_targetRequested 
= 0; 
 358     m_formatSupported 
= FALSE
; 
 361 bool wxClipboard::Open() 
 363     wxCHECK_MSG( !m_open
, FALSE
, wxT("clipboard already open") ); 
 370 bool wxClipboard::SetData( wxDataObject 
*data 
) 
 372     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 374     wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") ); 
 378     return AddData( data 
); 
 381 bool wxClipboard::AddData( wxDataObject 
*data 
) 
 386     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 388     wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") ); 
 390     /* we can only store one wxDataObject */ 
 395     /* get formats from wxDataObjects */ 
 396     wxDataFormat 
*array 
= new wxDataFormat
[ m_data
->GetFormatCount() ]; 
 397     m_data
->GetAllFormats( array 
); 
 400     /* primary selection or clipboard */ 
 401     Atom clipboard 
= m_usePrimary 
? (Atom
) 1  // 1 = primary selection 
 406     for (size_t i 
= 0; i 
< m_data
->GetFormatCount(); i
++) 
 408         wxLogTrace( TRACE_CLIPBOARD
, 
 409                     wxT("wxClipboard now supports atom %s"), 
 410                     array
[i
].GetId().c_str() ); 
 413         gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
), 
 416                                   0 );  /* what is info ? */ 
 423     gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
), 
 425                         GTK_SIGNAL_FUNC(selection_handler
), 
 430     /* disable GUI threads */ 
 435     /* Tell the world we offer clipboard data */ 
 436     res 
= (gtk_selection_owner_set( m_clipboardWidget
, 
 438                                          (guint32
) GDK_CURRENT_TIME 
)); 
 442         m_ownsPrimarySelection 
= res
; 
 444         m_ownsClipboard 
= res
; 
 447     /* re-enable GUI threads */ 
 454 void wxClipboard::Close() 
 456     wxCHECK_RET( m_open
, wxT("clipboard not open") ); 
 461 bool wxClipboard::IsOpened() const 
 466 bool wxClipboard::IsSupported( const wxDataFormat
& format 
) 
 468     /* reentrance problems */ 
 469     if (m_waiting
) return FALSE
; 
 471     /* store requested format to be asked for by callbacks */ 
 472     m_targetRequested 
= format
; 
 475     wxLogTrace( TRACE_CLIPBOARD
, 
 476                 wxT("wxClipboard:IsSupported: requested format: %s"), 
 477                 format
.GetId().c_str() ); 
 480     wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 482     m_formatSupported 
= FALSE
; 
 484     /* perform query. this will set m_formatSupported to 
 485        TRUE if m_targetRequested is supported. 
 486        also, we have to wait for the "answer" from the 
 487        clipboard owner which is an asynchronous process. 
 488        therefore we set m_waiting = TRUE here and wait 
 489        until the callback "targets_selection_received" 
 495     gtk_selection_convert( m_targetsWidget
, 
 496                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 499                            (guint32
) GDK_CURRENT_TIME 
); 
 501     while (m_waiting
) gtk_main_iteration(); 
 504     if (!m_formatSupported
) return FALSE
; 
 509 bool wxClipboard::GetData( wxDataObject
& data 
) 
 511     wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") ); 
 513     /* get formats from wxDataObjects */ 
 514     wxDataFormat 
*array 
= new wxDataFormat
[ data
.GetFormatCount() ]; 
 515     data
.GetAllFormats( array 
); 
 517     for (size_t i 
= 0; i 
< data
.GetFormatCount(); i
++) 
 519         wxDataFormat 
format( array
[i
] ); 
 521         wxLogTrace( TRACE_CLIPBOARD
, 
 522                     wxT("wxClipboard::GetData: requested format: %s"), 
 523                     format
.GetId().c_str() ); 
 525         /* is data supported by clipboard ? */ 
 527         /* store requested format to be asked for by callbacks */ 
 528         m_targetRequested 
= format
; 
 530         wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 532         m_formatSupported 
= FALSE
; 
 534        /* perform query. this will set m_formatSupported to 
 535           TRUE if m_targetRequested is supported. 
 536           also, we have to wait for the "answer" from the 
 537           clipboard owner which is an asynchronous process. 
 538           therefore we set m_waiting = TRUE here and wait 
 539           until the callback "targets_selection_received" 
 545         gtk_selection_convert( m_targetsWidget
, 
 546                            m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 549                            (guint32
) GDK_CURRENT_TIME 
); 
 551         while (m_waiting
) gtk_main_iteration(); 
 554         if (!m_formatSupported
) continue; 
 556         /* store pointer to data object to be filled up by callbacks */ 
 557         m_receivedData 
= &data
; 
 559         /* store requested format to be asked for by callbacks */ 
 560         m_targetRequested 
= format
; 
 562         wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") ); 
 565         m_formatSupported 
= FALSE
; 
 567         /* ask for clipboard contents.  this will set 
 568            m_formatSupported to TRUE if m_targetRequested 
 570            also, we have to wait for the "answer" from the 
 571            clipboard owner which is an asynchronous process. 
 572            therefore we set m_waiting = TRUE here and wait 
 573            until the callback "targets_selection_received" 
 578         wxLogTrace( TRACE_CLIPBOARD
, 
 579                     wxT("wxClipboard::GetData: format found, start convert") ); 
 582         gtk_selection_convert( m_clipboardWidget
, 
 583                                m_usePrimary 
? (GdkAtom
)GDK_SELECTION_PRIMARY
 
 586                                (guint32
) GDK_CURRENT_TIME 
); 
 588         while (m_waiting
) gtk_main_iteration(); 
 591         /* this is a true error as we checked for the presence of such data before */ 
 592         wxCHECK_MSG( m_formatSupported
, FALSE
, wxT("error retrieving data from clipboard") ); 
 599     wxLogTrace( TRACE_CLIPBOARD
, 
 600                 wxT("wxClipboard::GetData: format not found") );