1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/clipbrd.cpp
3 // Purpose: wxClipboard implementation for wxGTK
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // ============================================================================
12 // ============================================================================
14 // ----------------------------------------------------------------------------
16 // ----------------------------------------------------------------------------
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
23 #include "wx/clipbrd.h"
28 #include "wx/dataobj.h"
31 #include "wx/scopeguard.h"
33 #include "wx/gtk/private.h"
35 //-----------------------------------------------------------------------------
37 //-----------------------------------------------------------------------------
39 static GdkAtom g_clipboardAtom
= 0;
40 static GdkAtom g_targetsAtom
= 0;
41 static GdkAtom g_timestampAtom
= 0;
44 extern GdkAtom g_altTextAtom
;
47 // the trace mask we use with wxLogTrace() - call
48 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
49 // (there will be a *lot* of them!)
50 #define TRACE_CLIPBOARD _T("clipboard")
52 // ----------------------------------------------------------------------------
53 // wxClipboardSync: used to perform clipboard operations synchronously
54 // ----------------------------------------------------------------------------
56 // constructing this object on stack will wait wait until the latest clipboard
57 // operation is finished on block exit
59 // notice that there can be no more than one such object alive at any moment,
60 // i.e. reentrancies are not allowed
64 wxClipboardSync(wxClipboard
& clipboard
)
66 wxASSERT_MSG( !ms_clipboard
, _T("reentrancy in clipboard code") );
67 ms_clipboard
= &clipboard
;
72 while ( ms_clipboard
)
76 // this method must be called by GTK+ callbacks to indicate that we got the
77 // result for our clipboard operation
78 static void OnDone(wxClipboard
*clipboard
)
80 wxASSERT_MSG( clipboard
== ms_clipboard
,
81 _T("got notification for alien clipboard") );
87 static wxClipboard
*ms_clipboard
;
89 DECLARE_NO_COPY_CLASS(wxClipboardSync
)
92 wxClipboard
*wxClipboardSync
::ms_clipboard
= NULL
;
94 //-----------------------------------------------------------------------------
95 // "selection_received" for targets
96 //-----------------------------------------------------------------------------
100 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
101 GtkSelectionData
*selection_data
,
102 guint32
WXUNUSED(time
),
103 wxClipboard
*clipboard
)
105 wxON_BLOCK_EXIT1(wxClipboardSync
::OnDone
, clipboard
);
107 if ( wxTheClipboard
&& selection_data
->length
> 0 )
109 // make sure we got the data in the correct form
110 GdkAtom type
= selection_data
->type
;
111 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
113 if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") )
115 wxLogTrace( TRACE_CLIPBOARD
,
116 _T("got unsupported clipboard target") );
123 wxDataFormat
clip( selection_data
->selection
);
124 wxLogTrace( TRACE_CLIPBOARD
,
125 wxT("selection received for targets, clipboard %s"),
126 clip
.GetId().c_str() );
127 #endif // __WXDEBUG__
129 // the atoms we received, holding a list of targets (= formats)
130 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
132 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
134 wxDataFormat
format( atoms
[i
] );
136 wxLogTrace( TRACE_CLIPBOARD
,
137 wxT("selection received for targets, format %s"),
138 format
.GetId().c_str() );
140 // printf( "format %s requested %s\n",
141 // gdk_atom_name( atoms[i] ),
142 // gdk_atom_name( clipboard->m_targetRequested ) );
144 if (format
== clipboard
->m_targetRequested
)
146 clipboard
->m_formatSupported
= true;
154 //-----------------------------------------------------------------------------
155 // "selection_received" for the actual data
156 //-----------------------------------------------------------------------------
160 selection_received( GtkWidget
*WXUNUSED(widget
),
161 GtkSelectionData
*selection_data
,
162 guint32
WXUNUSED(time
),
163 wxClipboard
*clipboard
)
165 wxON_BLOCK_EXIT1(wxClipboardSync
::OnDone
, clipboard
);
170 wxDataObject
*data_object
= clipboard
->m_receivedData
;
175 if (selection_data
->length
<= 0)
178 wxDataFormat
format( selection_data
->target
);
180 // make sure we got the data in the correct format
181 if (!data_object
->IsSupportedFormat( format
) )
184 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
186 wxTheClipboard
->m_formatSupported
= true;
190 //-----------------------------------------------------------------------------
192 //-----------------------------------------------------------------------------
196 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
198 wxON_BLOCK_EXIT1(wxClipboardSync
::OnDone
, wxTheClipboard
);
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;
216 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
217 (!wxTheClipboard
->m_ownsClipboard
))
219 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
220 if (wxTheClipboard
->m_data
)
222 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
224 delete wxTheClipboard
->m_data
;
225 wxTheClipboard
->m_data
= (wxDataObject
*) NULL
;
233 //-----------------------------------------------------------------------------
234 // selection handler for supplying data
235 //-----------------------------------------------------------------------------
239 selection_handler( GtkWidget
*WXUNUSED(widget
),
240 GtkSelectionData
*selection_data
,
241 guint
WXUNUSED(info
),
242 guint
WXUNUSED(time
),
243 gpointer signal_data
)
245 if (!wxTheClipboard
) return;
247 if (!wxTheClipboard
->m_data
) return;
249 wxDataObject
*data
= wxTheClipboard
->m_data
;
251 // ICCCM says that TIMESTAMP is a required atom.
252 // In particular, it satisfies Klipper, which polls
253 // TIMESTAMP to see if the clipboards content has changed.
254 // It shall return the time which was used to set the data.
255 if (selection_data
->target
== g_timestampAtom
)
257 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
258 gtk_selection_data_set(selection_data
,
259 GDK_SELECTION_TYPE_INTEGER
,
261 (guchar
*)&(timestamp
),
263 wxLogTrace(TRACE_CLIPBOARD
,
264 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
269 wxDataFormat
format( selection_data
->target
);
272 wxLogTrace(TRACE_CLIPBOARD
,
273 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
274 format
.GetId().c_str(),
275 wxString
::FromAscii(wxGtkString(gdk_atom_name(selection_data
->target
))).c_str(),
276 wxString
::FromAscii(wxGtkString(gdk_atom_name(selection_data
->type
))).c_str(),
277 wxString
::FromAscii(wxGtkString(gdk_atom_name(selection_data
->selection
))).c_str(),
278 GPOINTER_TO_UINT( signal_data
)
282 if (!data
->IsSupportedFormat( format
)) return;
284 int size
= data
->GetDataSize( format
);
286 if (size
== 0) return;
288 void *d
= malloc(size
);
290 // Text data will be in UTF8 in Unicode mode.
291 data
->GetDataHere( selection_data
->target
, d
);
293 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
294 // would show as UTF-8 data interpreted as latin1 (?) in other
295 // GTK+ apps if we used gtk_selection_data_set()
296 if (format
== wxDataFormat(wxDF_UNICODETEXT
))
298 gtk_selection_data_set_text(
305 gtk_selection_data_set(
307 GDK_SELECTION_TYPE_STRING
,
317 //-----------------------------------------------------------------------------
319 //-----------------------------------------------------------------------------
321 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
323 wxClipboard
::wxClipboard()
327 m_ownsClipboard
= false;
328 m_ownsPrimarySelection
= false;
330 m_data
= (wxDataObject
*) NULL
;
331 m_receivedData
= (wxDataObject
*) NULL
;
333 /* we use m_targetsWidget to query what formats are available */
335 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
336 gtk_widget_realize( m_targetsWidget
);
338 g_signal_connect (m_targetsWidget
, "selection_received",
339 G_CALLBACK (targets_selection_received
), this);
341 /* we use m_clipboardWidget to get and to offer data */
343 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
344 gtk_widget_realize( m_clipboardWidget
);
346 g_signal_connect (m_clipboardWidget
, "selection_received",
347 G_CALLBACK (selection_received
), this);
349 g_signal_connect (m_clipboardWidget
, "selection_clear_event",
350 G_CALLBACK (selection_clear_clip
), NULL
);
352 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
353 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
354 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
356 m_formatSupported
= false;
357 m_targetRequested
= 0;
359 m_usePrimary
= false;
362 wxClipboard
::~wxClipboard()
366 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
367 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
370 void wxClipboard
::Clear()
374 // As we have data we also own the clipboard. Once we no longer own
375 // it, clear_selection is called which will set m_data to zero
376 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
378 wxClipboardSync
sync(*this);
380 gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
,
381 (guint32
) GDK_CURRENT_TIME
);
384 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
386 wxClipboardSync
sync(*this);
388 gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
,
389 (guint32
) GDK_CURRENT_TIME
);
396 m_targetRequested
= 0;
397 m_formatSupported
= false;
400 bool wxClipboard
::Open()
402 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
409 bool wxClipboard
::SetData( wxDataObject
*data
)
411 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
413 wxCHECK_MSG( data
, false, wxT("data is invalid") );
417 return AddData( data
);
420 bool wxClipboard
::AddData( wxDataObject
*data
)
422 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
424 wxCHECK_MSG( data
, false, wxT("data is invalid") );
426 // we can only store one wxDataObject
431 // get formats from wxDataObjects
432 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
433 m_data
->GetAllFormats( array
);
435 // primary selection or clipboard
436 GdkAtom clipboard
= m_usePrimary ?
(GdkAtom
)GDK_SELECTION_PRIMARY
439 // by default provide TIMESTAMP as a target
440 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
445 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
447 wxLogTrace( TRACE_CLIPBOARD
,
448 wxT("wxClipboard now supports atom %s"),
449 array
[i
].GetId().c_str() );
451 // printf( "added %s\n",
452 // gdk_atom_name( array[i].GetFormatId() ) );
454 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
457 0 ); /* what is info ? */
462 g_signal_connect (m_clipboardWidget
, "selection_get",
463 G_CALLBACK (selection_handler
),
464 GUINT_TO_POINTER (gtk_get_current_event_time()) );
467 /* disable GUI threads */
470 /* Tell the world we offer clipboard data */
471 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
473 (guint32
) GDK_CURRENT_TIME
));
476 m_ownsPrimarySelection
= res
;
478 m_ownsClipboard
= res
;
481 /* re-enable GUI threads */
487 void wxClipboard
::Close()
489 wxCHECK_RET( m_open
, wxT("clipboard not open") );
494 bool wxClipboard
::IsOpened() const
499 bool wxClipboard
::IsSupported( const wxDataFormat
& format
)
501 /* store requested format to be asked for by callbacks */
502 m_targetRequested
= format
;
504 wxLogTrace( TRACE_CLIPBOARD
,
505 wxT("wxClipboard:IsSupported: requested format: %s"),
506 format
.GetId().c_str() );
508 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
510 m_formatSupported
= false;
512 // block until m_formatSupported is set from targets_selection_received
515 wxClipboardSync
sync(*this);
517 gtk_selection_convert( m_targetsWidget
,
518 m_usePrimary ?
(GdkAtom
)GDK_SELECTION_PRIMARY
521 (guint32
) GDK_CURRENT_TIME
);
522 } // wait until we get the results
525 if (!m_formatSupported
&& format
== wxDataFormat(wxDF_UNICODETEXT
))
527 // Another try with plain STRING format
528 extern GdkAtom g_altTextAtom
;
529 return IsSupported(g_altTextAtom
);
533 return m_formatSupported
;
536 bool wxClipboard
::GetData( wxDataObject
& data
)
538 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
540 /* get formats from wxDataObjects */
541 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
542 data
.GetAllFormats( array
);
544 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
546 wxDataFormat
format( array
[i
] );
548 wxLogTrace( TRACE_CLIPBOARD
,
549 wxT("wxClipboard::GetData: requested format: %s"),
550 format
.GetId().c_str() );
552 /* is data supported by clipboard ? */
554 /* store requested format to be asked for by callbacks */
555 m_targetRequested
= format
;
557 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
559 m_formatSupported
= false;
561 // block until m_formatSupported is set by targets_selection_received
563 wxClipboardSync
sync(*this);
565 gtk_selection_convert( m_targetsWidget
,
566 m_usePrimary ?
(GdkAtom
)GDK_SELECTION_PRIMARY
569 (guint32
) GDK_CURRENT_TIME
);
570 } // wait until we get the results
572 if (!m_formatSupported
) continue;
574 /* store pointer to data object to be filled up by callbacks */
575 m_receivedData
= &data
;
577 /* store requested format to be asked for by callbacks */
578 m_targetRequested
= format
;
580 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
583 m_formatSupported
= false;
585 wxLogTrace( TRACE_CLIPBOARD
,
586 wxT("wxClipboard::GetData: format found, start convert") );
589 wxClipboardSync
sync(*this);
591 gtk_selection_convert( m_clipboardWidget
,
592 m_usePrimary ?
(GdkAtom
)GDK_SELECTION_PRIMARY
595 (guint32
) GDK_CURRENT_TIME
);
596 } // wait until we get the results
599 Normally this is a true error as we checked for the presence of such
600 data before, but there are applications that may return an empty
601 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
602 which would produce a false error message here, so we check for the
603 size of the string first. In ansi, GetDataSize returns an extra
604 value (for the closing null?), with unicode, the exact number of
605 tokens is given (that is more than 1 for special characters)
606 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
609 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
611 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
612 #endif // UNICODE / !UNICODE
614 wxCHECK_MSG( m_formatSupported
, false,
615 wxT("error retrieving data from clipboard") );
623 wxLogTrace( TRACE_CLIPBOARD
,
624 wxT("wxClipboard::GetData: format not found") );