1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/clipbrd.cpp
3 // Purpose: wxClipboard implementation for wxGTK
4 // Author: Robert Roebling, Vadim Zeitlin
6 // Copyright: (c) 1998 Robert Roebling
7 // (c) 2007 Vadim Zeitlin
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
24 #include "wx/clipbrd.h"
29 #include "wx/dataobj.h"
32 #include "wx/ptr_scpd.h"
33 #include "wx/scopeguard.h"
35 #include "wx/gtk/private.h"
37 wxDECLARE_SCOPED_ARRAY(wxDataFormat
, wxDataFormatArray
)
38 wxDEFINE_SCOPED_ARRAY(wxDataFormat
, wxDataFormatArray
)
40 // ----------------------------------------------------------------------------
42 // ----------------------------------------------------------------------------
44 static GdkAtom g_clipboardAtom
= 0;
45 static GdkAtom g_targetsAtom
= 0;
46 static GdkAtom g_timestampAtom
= 0;
49 extern GdkAtom g_altTextAtom
;
52 // the trace mask we use with wxLogTrace() - call
53 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
54 // (there will be a *lot* of them!)
55 #define TRACE_CLIPBOARD _T("clipboard")
57 // ----------------------------------------------------------------------------
58 // wxClipboardSync: used to perform clipboard operations synchronously
59 // ----------------------------------------------------------------------------
61 // constructing this object on stack will wait wait until the latest clipboard
62 // operation is finished on block exit
64 // notice that there can be no more than one such object alive at any moment,
65 // i.e. reentrancies are not allowed
69 wxClipboardSync(wxClipboard
& clipboard
)
71 wxASSERT_MSG( !ms_clipboard
, _T("reentrancy in clipboard code") );
72 ms_clipboard
= &clipboard
;
77 while ( ms_clipboard
)
81 // this method must be called by GTK+ callbacks to indicate that we got the
82 // result for our clipboard operation
83 static void OnDone(wxClipboard
*clipboard
)
85 wxASSERT_MSG( clipboard
== ms_clipboard
,
86 _T("got notification for alien clipboard") );
92 static wxClipboard
*ms_clipboard
;
94 DECLARE_NO_COPY_CLASS(wxClipboardSync
)
97 wxClipboard
*wxClipboardSync::ms_clipboard
= NULL
;
99 // ============================================================================
100 // clipboard ca;backs implementation
101 // ============================================================================
103 //-----------------------------------------------------------------------------
104 // "selection_received" for targets
105 //-----------------------------------------------------------------------------
109 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
110 GtkSelectionData
*selection_data
,
111 guint32
WXUNUSED(time
),
112 wxClipboard
*clipboard
)
117 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone
, clipboard
);
119 if ( !selection_data
|| selection_data
->length
<= 0 )
122 // make sure we got the data in the correct form
123 GdkAtom type
= selection_data
->type
;
124 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
126 if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") != 0 )
128 wxLogTrace( TRACE_CLIPBOARD
,
129 _T("got unsupported clipboard target") );
136 // it's not really a format, of course, but we can reuse its GetId() method
137 // to format this atom as string
138 wxDataFormat
clip(selection_data
->selection
);
139 wxLogTrace( TRACE_CLIPBOARD
,
140 wxT("Received available formats for clipboard %s"),
141 clip
.GetId().c_str() );
142 #endif // __WXDEBUG__
144 // the atoms we received, holding a list of targets (= formats)
145 const GdkAtom
* const atoms
= (GdkAtom
*)selection_data
->data
;
146 for ( size_t i
= 0; i
< selection_data
->length
/sizeof(GdkAtom
); i
++ )
148 const wxDataFormat
format(atoms
[i
]);
150 wxLogTrace(TRACE_CLIPBOARD
, wxT("\t%s"), format
.GetId().c_str());
152 if ( clipboard
->GTKOnTargetReceived(format
) )
158 bool wxClipboard::GTKOnTargetReceived(const wxDataFormat
& format
)
160 if ( format
!= m_targetRequested
)
163 m_formatSupported
= true;
167 //-----------------------------------------------------------------------------
168 // "selection_received" for the actual data
169 //-----------------------------------------------------------------------------
173 selection_received( GtkWidget
*WXUNUSED(widget
),
174 GtkSelectionData
*selection_data
,
175 guint32
WXUNUSED(time
),
176 wxClipboard
*clipboard
)
181 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone
, clipboard
);
183 if ( !selection_data
|| selection_data
->length
<= 0 )
186 clipboard
->GTKOnSelectionReceived(*selection_data
);
190 //-----------------------------------------------------------------------------
192 //-----------------------------------------------------------------------------
196 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
198 wxClipboard
* const clipboard
= wxTheClipboard
;
202 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone
, clipboard
);
204 wxClipboard::Kind kind
;
205 if (event
->selection
== GDK_SELECTION_PRIMARY
)
207 wxLogTrace(TRACE_CLIPBOARD
, wxT("Lost primary selection" ));
209 kind
= wxClipboard::Primary
;
211 else if (event
->selection
== g_clipboardAtom
)
213 wxLogTrace(TRACE_CLIPBOARD
, wxT("Lost clipboard" ));
215 kind
= wxClipboard::Clipboard
;
217 else // some other selection, we're not concerned
222 // the clipboard is no longer in our hands, we don't need data any more
223 clipboard
->GTKClearData(kind
);
229 //-----------------------------------------------------------------------------
230 // selection handler for supplying data
231 //-----------------------------------------------------------------------------
235 selection_handler( GtkWidget
*WXUNUSED(widget
),
236 GtkSelectionData
*selection_data
,
237 guint
WXUNUSED(info
),
238 guint
WXUNUSED(time
),
239 gpointer signal_data
)
241 wxClipboard
* const clipboard
= wxTheClipboard
;
245 wxDataObject
* const data
= clipboard
->GTKGetDataObject();
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
);
287 wxON_BLOCK_EXIT1(free
, d
);
289 // Text data will be in UTF8 in Unicode mode.
290 data
->GetDataHere( selection_data
->target
, d
);
292 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
293 // would show as UTF-8 data interpreted as latin1 (?) in other
294 // GTK+ apps if we used gtk_selection_data_set()
295 if (format
== wxDataFormat(wxDF_UNICODETEXT
))
297 gtk_selection_data_set_text(
304 gtk_selection_data_set(
306 GDK_SELECTION_TYPE_STRING
,
314 void wxClipboard::GTKOnSelectionReceived(const GtkSelectionData
& sel
)
316 wxCHECK_RET( m_receivedData
, _T("should be inside GetData()") );
318 const wxDataFormat
format(sel
.target
);
319 wxLogTrace(TRACE_CLIPBOARD
, _T("Received selection %s"),
320 format
.GetId().c_str());
322 if ( !m_receivedData
->IsSupportedFormat(format
) )
325 m_receivedData
->SetData(format
, sel
.length
, sel
.data
);
326 m_formatSupported
= true;
329 // ============================================================================
330 // wxClipboard implementation
331 // ============================================================================
333 // ----------------------------------------------------------------------------
334 // wxClipboard ctor/dtor
335 // ----------------------------------------------------------------------------
337 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
339 wxClipboard::wxClipboard()
345 m_receivedData
= NULL
;
347 m_formatSupported
= false;
348 m_targetRequested
= 0;
350 // we use m_targetsWidget to query what formats are available
351 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
352 gtk_widget_realize( m_targetsWidget
);
354 g_signal_connect (m_targetsWidget
, "selection_received",
355 G_CALLBACK (targets_selection_received
), this);
357 // we use m_clipboardWidget to get and to offer data
358 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
359 gtk_widget_realize( m_clipboardWidget
);
361 g_signal_connect (m_clipboardWidget
, "selection_received",
362 G_CALLBACK (selection_received
), this);
364 g_signal_connect (m_clipboardWidget
, "selection_clear_event",
365 G_CALLBACK (selection_clear_clip
), NULL
);
367 // initialize atoms we use if not done yet
368 if ( !g_clipboardAtom
)
369 g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
370 if ( !g_targetsAtom
)
371 g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
372 if ( !g_timestampAtom
)
373 g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
376 wxClipboard::~wxClipboard()
380 if ( m_clipboardWidget
)
381 gtk_widget_destroy( m_clipboardWidget
);
382 if ( m_targetsWidget
)
383 gtk_widget_destroy( m_targetsWidget
);
386 // ----------------------------------------------------------------------------
387 // wxClipboard helper functions
388 // ----------------------------------------------------------------------------
390 GdkAtom
wxClipboard::GTKGetClipboardAtom() const
392 return m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
396 void wxClipboard::GTKClearData(Kind kind
)
398 wxDataObject
*&data
= Data();
406 bool wxClipboard::SetSelectionOwner(bool set
)
408 bool rc
= gtk_selection_owner_set
410 set
? m_clipboardWidget
: NULL
,
411 GTKGetClipboardAtom(),
412 (guint32
)GDK_CURRENT_TIME
417 wxLogTrace(TRACE_CLIPBOARD
, _T("Failed to %sset selection owner"),
418 set
? _T("") : _T("un"));
424 void wxClipboard::AddSupportedTarget(GdkAtom atom
)
426 gtk_selection_add_target
428 GTK_WIDGET(m_clipboardWidget
),
429 GTKGetClipboardAtom(),
431 0 // info (same as client data) unused
435 bool wxClipboard::DoIsSupported(const wxDataFormat
& format
)
437 wxCHECK_MSG( format
, false, wxT("invalid clipboard format") );
439 wxLogTrace(TRACE_CLIPBOARD
, wxT("Checking if format %s is available"),
440 format
.GetId().c_str());
442 // these variables will be used by our GTKOnTargetReceived()
443 m_targetRequested
= format
;
444 m_formatSupported
= false;
446 // block until m_formatSupported is set from targets_selection_received
449 wxClipboardSync
sync(*this);
451 gtk_selection_convert( m_targetsWidget
,
452 GTKGetClipboardAtom(),
454 (guint32
) GDK_CURRENT_TIME
);
457 return m_formatSupported
;
460 // ----------------------------------------------------------------------------
461 // wxClipboard public API implementation
462 // ----------------------------------------------------------------------------
464 void wxClipboard::Clear()
466 if ( gdk_selection_owner_get(GTKGetClipboardAtom()) ==
467 m_clipboardWidget
->window
)
469 wxClipboardSync
sync(*this);
471 // this will result in selection_clear_clip callback being called and
472 // it will free our data
473 SetSelectionOwner(false);
476 m_targetRequested
= 0;
477 m_formatSupported
= false;
480 bool wxClipboard::Open()
482 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
489 bool wxClipboard::SetData( wxDataObject
*data
)
491 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
493 wxCHECK_MSG( data
, false, wxT("data is invalid") );
497 return AddData( data
);
500 bool wxClipboard::AddData( wxDataObject
*data
)
502 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
504 wxCHECK_MSG( data
, false, wxT("data is invalid") );
506 // we can only store one wxDataObject so clear the old one
511 // get formats from wxDataObjects
512 const size_t count
= data
->GetFormatCount();
513 wxDataFormatArray
formats(new wxDataFormat
[count
]);
514 data
->GetAllFormats(formats
.get());
516 // always provide TIMESTAMP as a target, see comments in selection_handler
518 AddSupportedTarget(g_timestampAtom
);
520 for ( size_t i
= 0; i
< count
; i
++ )
522 const wxDataFormat
format(formats
[i
]);
524 wxLogTrace(TRACE_CLIPBOARD
, wxT("Adding support for %s"),
525 format
.GetId().c_str());
527 AddSupportedTarget(format
);
530 g_signal_connect (m_clipboardWidget
, "selection_get",
531 G_CALLBACK (selection_handler
),
532 GUINT_TO_POINTER (gtk_get_current_event_time()) );
534 // tell the world we offer clipboard data
535 return SetSelectionOwner();
538 void wxClipboard::Close()
540 wxCHECK_RET( m_open
, wxT("clipboard not open") );
545 bool wxClipboard::IsOpened() const
550 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
552 if ( DoIsSupported(format
) )
556 if ( format
== wxDF_UNICODETEXT
)
558 // also with plain STRING format
559 return DoIsSupported(g_altTextAtom
);
561 #endif // wxUSE_UNICODE
566 bool wxClipboard::GetData( wxDataObject
& data
)
568 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
570 // get all supported formats from wxDataObjects
571 const size_t count
= data
.GetFormatCount();
572 wxDataFormatArray
formats(new wxDataFormat
[count
]);
573 data
.GetAllFormats(formats
.get());
575 for ( size_t i
= 0; i
< count
; i
++ )
577 const wxDataFormat
format(formats
[i
]);
579 // is this format supported by clipboard ?
580 if ( !DoIsSupported(format
) )
583 wxLogTrace(TRACE_CLIPBOARD
, wxT("Requesting format %s"),
584 format
.GetId().c_str());
586 // these variables will be used by our GTKOnSelectionReceived()
587 m_receivedData
= &data
;
588 m_formatSupported
= false;
591 wxClipboardSync
sync(*this);
593 gtk_selection_convert(m_clipboardWidget
,
594 GTKGetClipboardAtom(),
596 (guint32
) GDK_CURRENT_TIME
);
597 } // wait until we get the results
600 Normally this is a true error as we checked for the presence of such
601 data before, but there are applications that may return an empty
602 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
603 which would produce a false error message here, so we check for the
604 size of the string first. With ANSI, GetDataSize returns an extra
605 value (for the closing null?), with unicode, the exact number of
606 tokens is given (that is more than 1 for non-ASCII characters)
607 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
610 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
612 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
613 #endif // UNICODE / !UNICODE
615 wxCHECK_MSG( m_formatSupported
, false,
616 wxT("error retrieving data from clipboard") );
622 wxLogTrace(TRACE_CLIPBOARD
, wxT("GetData(): format not found"));
627 #endif // wxUSE_CLIPBOARD