1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/clipbrd.cpp
3 // Purpose:     wxClipboard implementation for wxGTK
4 // Author:      Robert Roebling, Vadim Zeitlin
5 // Copyright:   (c) 1998 Robert Roebling
6 //              (c) 2007 Vadim Zeitlin
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // ============================================================================
11 // declarations
12 // ============================================================================
13 
14 // ----------------------------------------------------------------------------
15 // headers
16 // ----------------------------------------------------------------------------
17 
18 // For compilers that support precompilation, includes "wx.h".
19 #include "wx/wxprec.h"
20 
21 #if wxUSE_CLIPBOARD
22 
23 #include "wx/clipbrd.h"
24 
25 #ifndef WX_PRECOMP
26     #include "wx/app.h"
27     #include "wx/log.h"
28     #include "wx/utils.h"
29     #include "wx/dataobj.h"
30 #endif
31 
32 #include "wx/scopedarray.h"
33 #include "wx/scopeguard.h"
34 #include "wx/evtloop.h"
35 
36 #include "wx/gtk/private.h"
37 
38 typedef wxScopedArray<wxDataFormat> wxDataFormatArray;
39 
40 // ----------------------------------------------------------------------------
41 // data
42 // ----------------------------------------------------------------------------
43 
44 static GdkAtom  g_clipboardAtom   = 0;
45 static GdkAtom  g_targetsAtom     = 0;
46 static GdkAtom  g_timestampAtom   = 0;
47 
48 #if wxUSE_UNICODE
49 extern GdkAtom g_altTextAtom;
50 #endif
51 
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 wxT("clipboard")
56 
57 // ----------------------------------------------------------------------------
58 // wxClipboardSync: used to perform clipboard operations synchronously
59 // ----------------------------------------------------------------------------
60 
61 // constructing this object on stack will wait wait until the latest clipboard
62 // operation is finished on block exit
63 //
64 // notice that there can be no more than one such object alive at any moment,
65 // i.e. reentrancies are not allowed
66 class wxClipboardSync
67 {
68 public:
wxClipboardSync(wxClipboard & clipboard)69     wxClipboardSync(wxClipboard& clipboard)
70     {
71         wxASSERT_MSG( !ms_clipboard, wxT("reentrancy in clipboard code") );
72         ms_clipboard = &clipboard;
73     }
74 
~wxClipboardSync()75     ~wxClipboardSync()
76     {
77 #if wxUSE_CONSOLE_EVENTLOOP
78         // ensure that there is a running event loop: this might not be the
79         // case if we're called before the main event loop startup
80         wxEventLoopGuarantor ensureEventLoop;
81 #endif
82         while (ms_clipboard)
83             wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_CLIPBOARD);
84     }
85 
86     // this method must be called by GTK+ callbacks to indicate that we got the
87     // result for our clipboard operation
OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG (clipboard))88     static void OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG(clipboard))
89     {
90         wxASSERT_MSG( clipboard == ms_clipboard,
91                         wxT("got notification for alien clipboard") );
92 
93         ms_clipboard = NULL;
94     }
95 
96     // this method should be called if it's possible that no async clipboard
97     // operation is currently in progress (like it can be the case when the
98     // clipboard is cleared but not because we asked about it), it should only
99     // be called if such situation is expected -- otherwise call OnDone() which
100     // would assert in this case
OnDoneIfInProgress(wxClipboard * clipboard)101     static void OnDoneIfInProgress(wxClipboard *clipboard)
102     {
103         if ( ms_clipboard )
104             OnDone(clipboard);
105     }
106 
107 private:
108     static wxClipboard *ms_clipboard;
109 
110     wxDECLARE_NO_COPY_CLASS(wxClipboardSync);
111 };
112 
113 wxClipboard *wxClipboardSync::ms_clipboard = NULL;
114 
115 // ============================================================================
116 // clipboard callbacks implementation
117 // ============================================================================
118 
119 //-----------------------------------------------------------------------------
120 // "selection_received" for targets
121 //-----------------------------------------------------------------------------
122 
123 extern "C" {
124 static void
targets_selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)125 targets_selection_received( GtkWidget *WXUNUSED(widget),
126                             GtkSelectionData *selection_data,
127                             guint32 WXUNUSED(time),
128                             wxClipboard *clipboard )
129 {
130     if ( !clipboard )
131         return;
132 
133     wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
134 
135     if (!selection_data)
136         return;
137 
138     const int selection_data_length = gtk_selection_data_get_length(selection_data);
139     if (selection_data_length <= 0)
140         return;
141 
142     // make sure we got the data in the correct form
143     GdkAtom type = gtk_selection_data_get_data_type(selection_data);
144     if ( type != GDK_SELECTION_TYPE_ATOM )
145     {
146         if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
147         {
148             wxLogTrace( TRACE_CLIPBOARD,
149                         wxT("got unsupported clipboard target") );
150 
151             return;
152         }
153     }
154 
155     // it's not really a format, of course, but we can reuse its GetId() method
156     // to format this atom as string
157     wxDataFormat clip(gtk_selection_data_get_selection(selection_data));
158     wxLogTrace( TRACE_CLIPBOARD,
159                 wxT("Received available formats for clipboard %s"),
160                 clip.GetId().c_str() );
161 
162     // the atoms we received, holding a list of targets (= formats)
163     const GdkAtom* const atoms = (GdkAtom*)gtk_selection_data_get_data(selection_data);
164     for (size_t i = 0; i < selection_data_length / sizeof(GdkAtom); i++)
165     {
166         const wxDataFormat format(atoms[i]);
167 
168         wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
169 
170         if ( clipboard->GTKOnTargetReceived(format) )
171             return;
172     }
173 }
174 }
175 
GTKOnTargetReceived(const wxDataFormat & format)176 bool wxClipboard::GTKOnTargetReceived(const wxDataFormat& format)
177 {
178     if ( format != m_targetRequested )
179         return false;
180 
181     m_formatSupported = true;
182     return true;
183 }
184 
185 //-----------------------------------------------------------------------------
186 // "selection_received" for the actual data
187 //-----------------------------------------------------------------------------
188 
189 extern "C" {
190 static void
selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)191 selection_received( GtkWidget *WXUNUSED(widget),
192                     GtkSelectionData *selection_data,
193                     guint32 WXUNUSED(time),
194                     wxClipboard *clipboard )
195 {
196     if ( !clipboard )
197         return;
198 
199     wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
200 
201     if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0)
202         return;
203 
204     clipboard->GTKOnSelectionReceived(*selection_data);
205 }
206 }
207 
208 //-----------------------------------------------------------------------------
209 // "selection_clear"
210 //-----------------------------------------------------------------------------
211 
212 extern "C" {
213 static gint
selection_clear_clip(GtkWidget * WXUNUSED (widget),GdkEventSelection * event)214 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
215 {
216     wxClipboard * const clipboard = wxTheClipboard;
217     if ( !clipboard )
218         return TRUE;
219 
220     // notice the use of OnDoneIfInProgress() here instead of just OnDone():
221     // it's perfectly possible that we're receiving this notification from GTK+
222     // even though we hadn't cleared the clipboard ourselves but because
223     // another application (or even another window in the same program)
224     // acquired it
225     wxON_BLOCK_EXIT1(wxClipboardSync::OnDoneIfInProgress, clipboard);
226 
227     wxClipboard::Kind kind;
228     if (event->selection == GDK_SELECTION_PRIMARY)
229     {
230         wxLogTrace(TRACE_CLIPBOARD, wxT("Lost primary selection" ));
231 
232         kind = wxClipboard::Primary;
233     }
234     else if (event->selection == g_clipboardAtom)
235     {
236         wxLogTrace(TRACE_CLIPBOARD, wxT("Lost clipboard" ));
237 
238         kind = wxClipboard::Clipboard;
239     }
240     else // some other selection, we're not concerned
241     {
242         return FALSE;
243     }
244 
245     // the clipboard is no longer in our hands, we don't need data any more
246     clipboard->GTKClearData(kind);
247 
248     return TRUE;
249 }
250 }
251 
252 //-----------------------------------------------------------------------------
253 // selection handler for supplying data
254 //-----------------------------------------------------------------------------
255 
256 extern "C" {
257 static void
selection_handler(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint WXUNUSED (info),guint WXUNUSED (time),gpointer signal_data)258 selection_handler( GtkWidget *WXUNUSED(widget),
259                    GtkSelectionData *selection_data,
260                    guint WXUNUSED(info),
261                    guint WXUNUSED(time),
262                    gpointer signal_data )
263 {
264     wxClipboard * const clipboard = wxTheClipboard;
265     if ( !clipboard )
266         return;
267 
268     wxDataObject * const data = clipboard->GTKGetDataObject(
269         gtk_selection_data_get_selection(selection_data));
270     if ( !data )
271         return;
272 
273     // ICCCM says that TIMESTAMP is a required atom.
274     // In particular, it satisfies Klipper, which polls
275     // TIMESTAMP to see if the clipboards content has changed.
276     // It shall return the time which was used to set the data.
277     if (gtk_selection_data_get_target(selection_data) == g_timestampAtom)
278     {
279         guint timestamp = GPOINTER_TO_UINT (signal_data);
280         gtk_selection_data_set(selection_data,
281                                GDK_SELECTION_TYPE_INTEGER,
282                                32,
283                                (guchar*)&(timestamp),
284                                sizeof(timestamp));
285         wxLogTrace(TRACE_CLIPBOARD,
286                    wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
287                    timestamp);
288         return;
289     }
290 
291     wxDataFormat format(gtk_selection_data_get_target(selection_data));
292 
293     wxLogTrace(TRACE_CLIPBOARD,
294                wxT("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
295                format.GetId().c_str(),
296                wxString::FromAscii(wxGtkString(gdk_atom_name(gtk_selection_data_get_target(selection_data)))).c_str(),
297                wxString::FromAscii(wxGtkString(gdk_atom_name(gtk_selection_data_get_data_type(selection_data)))).c_str(),
298                wxString::FromAscii(wxGtkString(gdk_atom_name(gtk_selection_data_get_selection(selection_data)))).c_str(),
299                GPOINTER_TO_UINT( signal_data )
300                );
301 
302     if ( !data->IsSupportedFormat( format ) )
303         return;
304 
305     int size = data->GetDataSize( format );
306     if ( !size )
307         return;
308 
309     wxCharBuffer buf(size - 1); // it adds 1 internally (for NUL)
310 
311     // text data must be returned in UTF8 if format is wxDF_UNICODETEXT
312     if ( !data->GetDataHere(format, buf.data()) )
313         return;
314 
315     // use UTF8_STRING format if requested in Unicode build but just plain
316     // STRING one in ANSI or if explicitly asked in Unicode
317 #if wxUSE_UNICODE
318     if (format == wxDataFormat(wxDF_UNICODETEXT))
319     {
320         gtk_selection_data_set_text(
321             selection_data,
322             (const gchar*)buf.data(),
323             size );
324     }
325     else
326 #endif // wxUSE_UNICODE
327     {
328         gtk_selection_data_set(
329             selection_data,
330             format.GetFormatId(),
331             8*sizeof(gchar),
332             (const guchar*)buf.data(),
333             size );
334     }
335 }
336 }
337 
GTKOnSelectionReceived(const GtkSelectionData & sel)338 void wxClipboard::GTKOnSelectionReceived(const GtkSelectionData& sel)
339 {
340     wxCHECK_RET( m_receivedData, wxT("should be inside GetData()") );
341 
342     const wxDataFormat format(gtk_selection_data_get_target(const_cast<GtkSelectionData*>(&sel)));
343     wxLogTrace(TRACE_CLIPBOARD, wxT("Received selection %s"),
344                format.GetId().c_str());
345 
346     if ( !m_receivedData->IsSupportedFormat(format, wxDataObject::Set) )
347         return;
348 
349     m_receivedData->SetData(format,
350         gtk_selection_data_get_length(const_cast<GtkSelectionData*>(&sel)),
351         gtk_selection_data_get_data(const_cast<GtkSelectionData*>(&sel)));
352     m_formatSupported = true;
353 }
354 
355 //-----------------------------------------------------------------------------
356 // asynchronous "selection_received" for targets
357 //-----------------------------------------------------------------------------
358 
359 extern "C" {
360 static void
async_targets_selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)361 async_targets_selection_received( GtkWidget *WXUNUSED(widget),
362                             GtkSelectionData *selection_data,
363                             guint32 WXUNUSED(time),
364                             wxClipboard *clipboard )
365 {
366     if ( !clipboard ) // Assert?
367         return;
368 
369     if (!clipboard->m_sink)
370         return;
371 
372     wxClipboardEvent *event = new wxClipboardEvent(wxEVT_CLIPBOARD_CHANGED);
373     event->SetEventObject( clipboard );
374 
375     int selection_data_length = 0;
376     if (selection_data)
377         selection_data_length = gtk_selection_data_get_length(selection_data);
378 
379     if (selection_data_length <= 0)
380     {
381         clipboard->m_sink->QueueEvent( event );
382         clipboard->m_sink.Release();
383         return;
384     }
385 
386     // make sure we got the data in the correct form
387     GdkAtom type = gtk_selection_data_get_data_type(selection_data);
388     if ( type != GDK_SELECTION_TYPE_ATOM )
389     {
390         if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
391         {
392             wxLogTrace( TRACE_CLIPBOARD,
393                         wxT("got unsupported clipboard target") );
394 
395             clipboard->m_sink->QueueEvent( event );
396             clipboard->m_sink.Release();
397             return;
398         }
399     }
400 
401     // it's not really a format, of course, but we can reuse its GetId() method
402     // to format this atom as string
403     wxDataFormat clip(gtk_selection_data_get_selection(selection_data));
404     wxLogTrace( TRACE_CLIPBOARD,
405                 wxT("Received available formats for clipboard %s"),
406                 clip.GetId().c_str() );
407 
408     // the atoms we received, holding a list of targets (= formats)
409     const GdkAtom* const atoms = (GdkAtom*)gtk_selection_data_get_data(selection_data);
410     for (size_t i = 0; i < selection_data_length / sizeof(GdkAtom); i++)
411     {
412         const wxDataFormat format(atoms[i]);
413 
414         wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
415 
416         event->AddFormat( format );
417     }
418 
419     clipboard->m_sink->QueueEvent( event );
420     clipboard->m_sink.Release();
421 }
422 }
423 
424 // ============================================================================
425 // wxClipboard implementation
426 // ============================================================================
427 
428 // ----------------------------------------------------------------------------
429 // wxClipboard ctor/dtor
430 // ----------------------------------------------------------------------------
431 
IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)432 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
433 
434 wxClipboard::wxClipboard()
435 {
436     m_idSelectionGetHandler = 0;
437 
438     m_open = false;
439 
440     m_dataPrimary =
441     m_dataClipboard =
442     m_receivedData = NULL;
443 
444     m_formatSupported = false;
445     m_targetRequested = 0;
446 
447     // we use m_targetsWidget to query what formats are available
448     m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
449     gtk_widget_realize( m_targetsWidget );
450 
451     g_signal_connect (m_targetsWidget, "selection_received",
452                       G_CALLBACK (targets_selection_received), this);
453 
454     // we use m_targetsWidgetAsync to query what formats are available asynchronously
455     m_targetsWidgetAsync = gtk_window_new( GTK_WINDOW_POPUP );
456     gtk_widget_realize( m_targetsWidgetAsync );
457 
458     g_signal_connect (m_targetsWidgetAsync, "selection_received",
459                       G_CALLBACK (async_targets_selection_received), this);
460 
461     // we use m_clipboardWidget to get and to offer data
462     m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
463     gtk_widget_realize( m_clipboardWidget );
464 
465     g_signal_connect (m_clipboardWidget, "selection_received",
466                       G_CALLBACK (selection_received), this);
467 
468     g_signal_connect (m_clipboardWidget, "selection_clear_event",
469                       G_CALLBACK (selection_clear_clip), NULL);
470 
471     // initialize atoms we use if not done yet
472     if ( !g_clipboardAtom )
473         g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
474     if ( !g_targetsAtom )
475         g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
476     if ( !g_timestampAtom )
477         g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
478 }
479 
~wxClipboard()480 wxClipboard::~wxClipboard()
481 {
482     Clear();
483 
484     gtk_widget_destroy( m_clipboardWidget );
485     gtk_widget_destroy( m_targetsWidget );
486 }
487 
488 // ----------------------------------------------------------------------------
489 // wxClipboard helper functions
490 // ----------------------------------------------------------------------------
491 
GTKGetClipboardAtom() const492 GdkAtom wxClipboard::GTKGetClipboardAtom() const
493 {
494     return m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
495                         : g_clipboardAtom;
496 }
497 
GTKClearData(Kind kind)498 void wxClipboard::GTKClearData(Kind kind)
499 {
500     wxDataObject *&data = Data(kind);
501     wxDELETE(data);
502 }
503 
SetSelectionOwner(bool set)504 bool wxClipboard::SetSelectionOwner(bool set)
505 {
506     bool rc = gtk_selection_owner_set
507               (
508                 set ? m_clipboardWidget : NULL,
509                 GTKGetClipboardAtom(),
510                 (guint32)GDK_CURRENT_TIME
511               ) != 0;
512 
513     if ( !rc )
514     {
515         wxLogTrace(TRACE_CLIPBOARD, wxT("Failed to %sset selection owner"),
516                    set ? wxT("") : wxT("un"));
517     }
518 
519     return rc;
520 }
521 
AddSupportedTarget(GdkAtom atom)522 void wxClipboard::AddSupportedTarget(GdkAtom atom)
523 {
524     gtk_selection_add_target
525     (
526         m_clipboardWidget,
527         GTKGetClipboardAtom(),
528         atom,
529         0 // info (same as client data) unused
530     );
531 }
532 
IsSupportedAsync(wxEvtHandler * sink)533 bool wxClipboard::IsSupportedAsync(wxEvtHandler *sink)
534 {
535     if (m_sink.get())
536         return false;  // currently busy, come back later
537 
538     wxCHECK_MSG( sink, false, wxT("no sink given") );
539 
540     m_sink = sink;
541     gtk_selection_convert( m_targetsWidgetAsync,
542                            GTKGetClipboardAtom(),
543                            g_targetsAtom,
544                            (guint32) GDK_CURRENT_TIME );
545 
546     return true;
547 }
548 
DoIsSupported(const wxDataFormat & format)549 bool wxClipboard::DoIsSupported(const wxDataFormat& format)
550 {
551     wxCHECK_MSG( format, false, wxT("invalid clipboard format") );
552 
553     wxLogTrace(TRACE_CLIPBOARD, wxT("Checking if format %s is available"),
554                format.GetId().c_str());
555 
556     // these variables will be used by our GTKOnTargetReceived()
557     m_targetRequested = format;
558     m_formatSupported = false;
559 
560     // block until m_formatSupported is set from targets_selection_received
561     // callback
562     {
563         wxClipboardSync sync(*this);
564 
565         gtk_selection_convert( m_targetsWidget,
566                                GTKGetClipboardAtom(),
567                                g_targetsAtom,
568                                (guint32) GDK_CURRENT_TIME );
569     }
570 
571     return m_formatSupported;
572 }
573 
574 // ----------------------------------------------------------------------------
575 // wxClipboard public API implementation
576 // ----------------------------------------------------------------------------
577 
Clear()578 void wxClipboard::Clear()
579 {
580     gtk_selection_clear_targets( m_clipboardWidget, GTKGetClipboardAtom() );
581 
582     if ( gdk_selection_owner_get(GTKGetClipboardAtom()) ==
583             gtk_widget_get_window(m_clipboardWidget) )
584     {
585         wxClipboardSync sync(*this);
586 
587         // this will result in selection_clear_clip callback being called and
588         // it will free our data
589         SetSelectionOwner(false);
590     }
591 
592     m_targetRequested = 0;
593     m_formatSupported = false;
594 }
595 
Open()596 bool wxClipboard::Open()
597 {
598     wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
599 
600     m_open = true;
601 
602     return true;
603 }
604 
SetData(wxDataObject * data)605 bool wxClipboard::SetData( wxDataObject *data )
606 {
607     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
608 
609     wxCHECK_MSG( data, false, wxT("data is invalid") );
610 
611     Clear();
612 
613     return AddData( data );
614 }
615 
AddData(wxDataObject * data)616 bool wxClipboard::AddData( wxDataObject *data )
617 {
618     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
619 
620     wxCHECK_MSG( data, false, wxT("data is invalid") );
621 
622     // we can only store one wxDataObject so clear the old one
623     Clear();
624 
625     Data() = data;
626 
627     // get formats from wxDataObjects
628     const size_t count = data->GetFormatCount();
629     wxDataFormatArray formats(new wxDataFormat[count]);
630     data->GetAllFormats(formats.get());
631 
632     // always provide TIMESTAMP as a target, see comments in selection_handler
633     // for explanation
634     AddSupportedTarget(g_timestampAtom);
635 
636     for ( size_t i = 0; i < count; i++ )
637     {
638         const wxDataFormat format(formats[i]);
639 
640         wxLogTrace(TRACE_CLIPBOARD, wxT("Adding support for %s"),
641                    format.GetId().c_str());
642 
643         AddSupportedTarget(format);
644     }
645 
646     if ( !m_idSelectionGetHandler )
647     {
648         m_idSelectionGetHandler = g_signal_connect (
649                       m_clipboardWidget, "selection_get",
650                       G_CALLBACK (selection_handler),
651                       GUINT_TO_POINTER (gtk_get_current_event_time()) );
652     }
653 
654     // tell the world we offer clipboard data
655     return SetSelectionOwner();
656 }
657 
Close()658 void wxClipboard::Close()
659 {
660     wxCHECK_RET( m_open, wxT("clipboard not open") );
661 
662     m_open = false;
663 }
664 
IsOpened() const665 bool wxClipboard::IsOpened() const
666 {
667     return m_open;
668 }
669 
IsSupported(const wxDataFormat & format)670 bool wxClipboard::IsSupported( const wxDataFormat& format )
671 {
672     if ( DoIsSupported(format) )
673         return true;
674 
675 #if wxUSE_UNICODE
676     if ( format == wxDF_UNICODETEXT )
677     {
678         // also with plain STRING format
679         return DoIsSupported(g_altTextAtom);
680     }
681 #endif // wxUSE_UNICODE
682 
683     return false;
684 }
685 
GetData(wxDataObject & data)686 bool wxClipboard::GetData( wxDataObject& data )
687 {
688     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
689 
690     // get all supported formats from wxDataObjects: notice that we are setting
691     // the object data, so we need them in "Set" direction
692     const size_t count = data.GetFormatCount(wxDataObject::Set);
693     wxDataFormatArray formats(new wxDataFormat[count]);
694     data.GetAllFormats(formats.get(), wxDataObject::Set);
695 
696     for ( size_t i = 0; i < count; i++ )
697     {
698         const wxDataFormat format(formats[i]);
699 
700         // is this format supported by clipboard ?
701         if ( !DoIsSupported(format) )
702             continue;
703 
704         wxLogTrace(TRACE_CLIPBOARD, wxT("Requesting format %s"),
705                    format.GetId().c_str());
706 
707         // these variables will be used by our GTKOnSelectionReceived()
708         m_receivedData = &data;
709         m_formatSupported = false;
710 
711         {
712             wxClipboardSync sync(*this);
713 
714             gtk_selection_convert(m_clipboardWidget,
715                                   GTKGetClipboardAtom(),
716                                   format,
717                                   (guint32) GDK_CURRENT_TIME );
718         } // wait until we get the results
719 
720         /*
721            Normally this is a true error as we checked for the presence of such
722            data before, but there are applications that may return an empty
723            string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
724            which would produce a false error message here, so we check for the
725            size of the string first. With ANSI, GetDataSize returns an extra
726            value (for the closing null?), with unicode, the exact number of
727            tokens is given (that is more than 1 for non-ASCII characters)
728            (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
729          */
730 #if wxUSE_UNICODE
731         if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
732 #else // !UNICODE
733         if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
734 #endif // UNICODE / !UNICODE
735         {
736             wxCHECK_MSG( m_formatSupported, false,
737                          wxT("error retrieving data from clipboard") );
738         }
739 
740         return true;
741     }
742 
743     wxLogTrace(TRACE_CLIPBOARD, wxT("GetData(): format not found"));
744 
745     return false;
746 }
747 
GTKGetDataObject(GdkAtom atom)748 wxDataObject* wxClipboard::GTKGetDataObject( GdkAtom atom )
749 {
750     if ( atom == GDK_NONE )
751         return Data();
752 
753     if ( atom == GDK_SELECTION_PRIMARY )
754     {
755         wxLogTrace(TRACE_CLIPBOARD, wxT("Primary selection requested" ));
756 
757         return Data( wxClipboard::Primary );
758     }
759     else if ( atom == g_clipboardAtom )
760     {
761         wxLogTrace(TRACE_CLIPBOARD, wxT("Clipboard data requested" ));
762 
763         return Data( wxClipboard::Clipboard );
764     }
765     else // some other selection, we're not concerned
766     {
767         return (wxDataObject*)NULL;
768     }
769 }
770 
771 #endif // wxUSE_CLIPBOARD
772