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_targetsAtom     = 0;
45 static GdkAtom  g_timestampAtom   = 0;
46 
47 #if wxUSE_UNICODE
48 extern GdkAtom g_altTextAtom;
49 #endif
50 
51 // the trace mask we use with wxLogTrace() - call
52 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
53 // (there will be a *lot* of them!)
54 #define TRACE_CLIPBOARD wxT("clipboard")
55 
56 // ----------------------------------------------------------------------------
57 // wxClipboardSync: used to perform clipboard operations synchronously
58 // ----------------------------------------------------------------------------
59 
60 // constructing this object on stack will wait wait until the latest clipboard
61 // operation is finished on block exit
62 //
63 // notice that there can be no more than one such object alive at any moment,
64 // i.e. reentrancies are not allowed
65 class wxClipboardSync
66 {
67 public:
wxClipboardSync(wxClipboard & clipboard)68     wxClipboardSync(wxClipboard& clipboard)
69     {
70         wxASSERT_MSG( !ms_clipboard, wxT("reentrancy in clipboard code") );
71         ms_clipboard = &clipboard;
72     }
73 
~wxClipboardSync()74     ~wxClipboardSync()
75     {
76 #if wxUSE_CONSOLE_EVENTLOOP
77         // ensure that there is a running event loop: this might not be the
78         // case if we're called before the main event loop startup
79         wxEventLoopGuarantor ensureEventLoop;
80 #endif
81         while (ms_clipboard)
82             wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_CLIPBOARD);
83     }
84 
85     // this method must be called by GTK+ callbacks to indicate that we got the
86     // result for our clipboard operation
OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG (clipboard))87     static void OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG(clipboard))
88     {
89         wxASSERT_MSG( clipboard == ms_clipboard,
90                         wxT("got notification for alien clipboard") );
91 
92         ms_clipboard = NULL;
93     }
94 
95     // this method should be called if it's possible that no async clipboard
96     // operation is currently in progress (like it can be the case when the
97     // clipboard is cleared but not because we asked about it), it should only
98     // be called if such situation is expected -- otherwise call OnDone() which
99     // would assert in this case
OnDoneIfInProgress(wxClipboard * clipboard)100     static void OnDoneIfInProgress(wxClipboard *clipboard)
101     {
102         if ( ms_clipboard )
103             OnDone(clipboard);
104     }
105 
106 private:
107     static wxClipboard *ms_clipboard;
108 
109     wxDECLARE_NO_COPY_CLASS(wxClipboardSync);
110 };
111 
112 wxClipboard *wxClipboardSync::ms_clipboard = NULL;
113 
114 // ============================================================================
115 // clipboard callbacks implementation
116 // ============================================================================
117 
118 //-----------------------------------------------------------------------------
119 // "selection_received" for targets
120 //-----------------------------------------------------------------------------
121 
122 extern "C" {
123 static void
targets_selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)124 targets_selection_received( GtkWidget *WXUNUSED(widget),
125                             GtkSelectionData *selection_data,
126                             guint32 WXUNUSED(time),
127                             wxClipboard *clipboard )
128 {
129     if ( !clipboard )
130         return;
131 
132     wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
133 
134     if (!selection_data)
135         return;
136 
137     const int selection_data_length = gtk_selection_data_get_length(selection_data);
138     if (selection_data_length <= 0)
139         return;
140 
141     // make sure we got the data in the correct form
142     GdkAtom type = gtk_selection_data_get_data_type(selection_data);
143     if ( type != GDK_SELECTION_TYPE_ATOM )
144     {
145         if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
146         {
147             wxLogTrace( TRACE_CLIPBOARD,
148                         wxT("got unsupported clipboard target") );
149 
150             return;
151         }
152     }
153 
154     // it's not really a format, of course, but we can reuse its GetId() method
155     // to format this atom as string
156     wxDataFormat clip(gtk_selection_data_get_selection(selection_data));
157     wxLogTrace( TRACE_CLIPBOARD,
158                 wxT("Received available formats for clipboard %s"),
159                 clip.GetId().c_str() );
160 
161     // the atoms we received, holding a list of targets (= formats)
162     const GdkAtom* const atoms = reinterpret_cast<const GdkAtom*>(gtk_selection_data_get_data(selection_data));
163     for (size_t i = 0; i < selection_data_length / sizeof(GdkAtom); i++)
164     {
165         const wxDataFormat format(atoms[i]);
166 
167         wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
168 
169         if ( clipboard->GTKOnTargetReceived(format) )
170             return;
171     }
172 }
173 }
174 
GTKOnTargetReceived(const wxDataFormat & format)175 bool wxClipboard::GTKOnTargetReceived(const wxDataFormat& format)
176 {
177     if ( format != m_targetRequested )
178         return false;
179 
180     m_formatSupported = true;
181     return true;
182 }
183 
184 //-----------------------------------------------------------------------------
185 // "selection_received" for the actual data
186 //-----------------------------------------------------------------------------
187 
188 extern "C" {
189 static void
selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)190 selection_received( GtkWidget *WXUNUSED(widget),
191                     GtkSelectionData *selection_data,
192                     guint32 WXUNUSED(time),
193                     wxClipboard *clipboard )
194 {
195     if ( !clipboard )
196         return;
197 
198     wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
199 
200     if (!selection_data || gtk_selection_data_get_length(selection_data) <= 0)
201         return;
202 
203     clipboard->GTKOnSelectionReceived(*selection_data);
204 }
205 }
206 
207 //-----------------------------------------------------------------------------
208 // "selection_clear"
209 //-----------------------------------------------------------------------------
210 
211 extern "C" {
212 static gint
selection_clear_clip(GtkWidget * WXUNUSED (widget),GdkEventSelection * event)213 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
214 {
215     wxClipboard * const clipboard = wxTheClipboard;
216     if ( !clipboard )
217         return TRUE;
218 
219     // notice the use of OnDoneIfInProgress() here instead of just OnDone():
220     // it's perfectly possible that we're receiving this notification from GTK+
221     // even though we hadn't cleared the clipboard ourselves but because
222     // another application (or even another window in the same program)
223     // acquired it
224     wxON_BLOCK_EXIT1(wxClipboardSync::OnDoneIfInProgress, clipboard);
225 
226     wxClipboard::Kind kind;
227     if (event->selection == GDK_SELECTION_PRIMARY)
228     {
229         wxLogTrace(TRACE_CLIPBOARD, wxT("Lost primary selection" ));
230 
231         kind = wxClipboard::Primary;
232     }
233     else if ( event->selection == GDK_SELECTION_CLIPBOARD )
234     {
235         wxLogTrace(TRACE_CLIPBOARD, wxT("Lost clipboard" ));
236 
237         kind = wxClipboard::Clipboard;
238     }
239     else // some other selection, we're not concerned
240     {
241         return FALSE;
242     }
243 
244     // the clipboard is no longer in our hands, we don't need data any more
245     clipboard->GTKClearData(kind);
246 
247     return TRUE;
248 }
249 }
250 
251 //-----------------------------------------------------------------------------
252 // selection handler for supplying data
253 //-----------------------------------------------------------------------------
254 
255 extern "C" {
256 static void
selection_handler(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint WXUNUSED (info),guint WXUNUSED (time),gpointer signal_data)257 selection_handler( GtkWidget *WXUNUSED(widget),
258                    GtkSelectionData *selection_data,
259                    guint WXUNUSED(info),
260                    guint WXUNUSED(time),
261                    gpointer signal_data )
262 {
263     wxClipboard * const clipboard = wxTheClipboard;
264     if ( !clipboard )
265         return;
266 
267     wxDataObject * const data = clipboard->GTKGetDataObject(
268         gtk_selection_data_get_selection(selection_data));
269     if ( !data )
270         return;
271 
272     unsigned timestamp = unsigned(wxUIntPtr(signal_data));
273 
274     // ICCCM says that TIMESTAMP is a required atom.
275     // In particular, it satisfies Klipper, which polls
276     // TIMESTAMP to see if the clipboards content has changed.
277     // It shall return the time which was used to set the data.
278     if (gtk_selection_data_get_target(selection_data) == g_timestampAtom)
279     {
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                timestamp
300                );
301 
302     if ( !data->IsSupportedFormat( format ) )
303         return;
304 
305     int size = data->GetDataSize( format );
306     if ( !size )
307         return;
308 
309     wxLogTrace(TRACE_CLIPBOARD, "Valid clipboard data found");
310 
311     wxCharBuffer buf(size - 1); // it adds 1 internally (for NUL)
312 
313     // text data must be returned in UTF8 if format is wxDF_UNICODETEXT
314     if ( !data->GetDataHere(format, buf.data()) )
315         return;
316 
317     // use UTF8_STRING format if requested in Unicode build but just plain
318     // STRING one in ANSI or if explicitly asked in Unicode
319 #if wxUSE_UNICODE
320     if (format == wxDataFormat(wxDF_UNICODETEXT))
321     {
322         gtk_selection_data_set_text(
323             selection_data,
324             (const gchar*)buf.data(),
325             size );
326     }
327     else
328 #endif // wxUSE_UNICODE
329     {
330         gtk_selection_data_set(
331             selection_data,
332             format.GetFormatId(),
333             8*sizeof(gchar),
334             (const guchar*)buf.data(),
335             size );
336     }
337 }
338 }
339 
GTKOnSelectionReceived(const GtkSelectionData & sel)340 void wxClipboard::GTKOnSelectionReceived(const GtkSelectionData& sel)
341 {
342     wxCHECK_RET( m_receivedData, wxT("should be inside GetData()") );
343 
344     const wxDataFormat format(gtk_selection_data_get_target(const_cast<GtkSelectionData*>(&sel)));
345     wxLogTrace(TRACE_CLIPBOARD, wxT("Received selection %s"),
346                format.GetId().c_str());
347 
348     if ( !m_receivedData->IsSupportedFormat(format, wxDataObject::Set) )
349         return;
350 
351     m_receivedData->SetData(format,
352         gtk_selection_data_get_length(const_cast<GtkSelectionData*>(&sel)),
353         gtk_selection_data_get_data(const_cast<GtkSelectionData*>(&sel)));
354     m_formatSupported = true;
355 }
356 
357 //-----------------------------------------------------------------------------
358 // asynchronous "selection_received" for targets
359 //-----------------------------------------------------------------------------
360 
361 extern "C" {
362 static void
async_targets_selection_received(GtkWidget * WXUNUSED (widget),GtkSelectionData * selection_data,guint32 WXUNUSED (time),wxClipboard * clipboard)363 async_targets_selection_received( GtkWidget *WXUNUSED(widget),
364                             GtkSelectionData *selection_data,
365                             guint32 WXUNUSED(time),
366                             wxClipboard *clipboard )
367 {
368     if ( !clipboard ) // Assert?
369         return;
370 
371     if (!clipboard->m_sink)
372         return;
373 
374     wxClipboardEvent *event = new wxClipboardEvent(wxEVT_CLIPBOARD_CHANGED);
375     event->SetEventObject( clipboard );
376 
377     int selection_data_length = 0;
378     if (selection_data)
379         selection_data_length = gtk_selection_data_get_length(selection_data);
380 
381     if (selection_data_length <= 0)
382     {
383         clipboard->m_sink->QueueEvent( event );
384         clipboard->m_sink.Release();
385         return;
386     }
387 
388     // make sure we got the data in the correct form
389     GdkAtom type = gtk_selection_data_get_data_type(selection_data);
390     if ( type != GDK_SELECTION_TYPE_ATOM )
391     {
392         if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
393         {
394             wxLogTrace( TRACE_CLIPBOARD,
395                         wxT("got unsupported clipboard target") );
396 
397             clipboard->m_sink->QueueEvent( event );
398             clipboard->m_sink.Release();
399             return;
400         }
401     }
402 
403     // it's not really a format, of course, but we can reuse its GetId() method
404     // to format this atom as string
405     wxDataFormat clip(gtk_selection_data_get_selection(selection_data));
406     wxLogTrace( TRACE_CLIPBOARD,
407                 wxT("Received available formats for clipboard %s"),
408                 clip.GetId().c_str() );
409 
410     // the atoms we received, holding a list of targets (= formats)
411     const GdkAtom* const atoms = reinterpret_cast<const GdkAtom*>(gtk_selection_data_get_data(selection_data));
412     for (size_t i = 0; i < selection_data_length / sizeof(GdkAtom); i++)
413     {
414         const wxDataFormat format(atoms[i]);
415 
416         wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
417 
418         event->AddFormat( format );
419     }
420 
421     clipboard->m_sink->QueueEvent( event );
422     clipboard->m_sink.Release();
423 }
424 }
425 
426 // ============================================================================
427 // wxClipboard implementation
428 // ============================================================================
429 
430 // ----------------------------------------------------------------------------
431 // wxClipboard ctor/dtor
432 // ----------------------------------------------------------------------------
433 
434 wxIMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject);
435 
wxClipboard()436 wxClipboard::wxClipboard()
437 {
438     m_idSelectionGetHandler = 0;
439 
440     m_open = false;
441 
442     m_dataPrimary =
443     m_dataClipboard =
444     m_receivedData = NULL;
445 
446     m_formatSupported = false;
447     m_targetRequested = 0;
448 
449     // we use m_targetsWidget to query what formats are available
450     m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
451     gtk_widget_realize( m_targetsWidget );
452 
453     g_signal_connect (m_targetsWidget, "selection_received",
454                       G_CALLBACK (targets_selection_received), this);
455 
456     // we use m_targetsWidgetAsync to query what formats are available asynchronously
457     m_targetsWidgetAsync = gtk_window_new( GTK_WINDOW_POPUP );
458     gtk_widget_realize( m_targetsWidgetAsync );
459 
460     g_signal_connect (m_targetsWidgetAsync, "selection_received",
461                       G_CALLBACK (async_targets_selection_received), this);
462 
463     // we use m_clipboardWidget to get and to offer data
464     m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
465     gtk_widget_realize( m_clipboardWidget );
466 
467     g_signal_connect (m_clipboardWidget, "selection_received",
468                       G_CALLBACK (selection_received), this);
469 
470     g_signal_connect (m_clipboardWidget, "selection_clear_event",
471                       G_CALLBACK (selection_clear_clip), NULL);
472 
473     // initialize atoms we use if not done yet
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                         : (GdkAtom)GDK_SELECTION_CLIPBOARD;
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 
Flush()596 bool wxClipboard::Flush()
597 {
598     // Only store the non-primary clipboard when flushing. The primary clipboard is a scratch-space
599     // formed using the currently selected text.
600     if ( !m_usePrimary )
601     {
602         GtkClipboard* clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
603 
604         gtk_clipboard_set_can_store(clipboard, NULL, 0);
605         gtk_clipboard_store(clipboard);
606 
607         return true;
608     }
609 
610     return false;
611 }
612 
Open()613 bool wxClipboard::Open()
614 {
615     wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
616 
617     m_open = true;
618 
619     return true;
620 }
621 
SetData(wxDataObject * data)622 bool wxClipboard::SetData( wxDataObject *data )
623 {
624     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
625 
626     wxCHECK_MSG( data, false, wxT("data is invalid") );
627 
628     Clear();
629 
630     return AddData( data );
631 }
632 
AddData(wxDataObject * data)633 bool wxClipboard::AddData( wxDataObject *data )
634 {
635     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
636 
637     wxCHECK_MSG( data, false, wxT("data is invalid") );
638 
639     // we can only store one wxDataObject so clear the old one
640     Clear();
641 
642     Data() = data;
643 
644     // get formats from wxDataObjects
645     const size_t count = data->GetFormatCount();
646     wxDataFormatArray formats(count);
647     data->GetAllFormats(formats.get());
648 
649     // always provide TIMESTAMP as a target, see comments in selection_handler
650     // for explanation
651     AddSupportedTarget(g_timestampAtom);
652 
653     for ( size_t i = 0; i < count; i++ )
654     {
655         const wxDataFormat format(formats[i]);
656 
657         wxLogTrace(TRACE_CLIPBOARD, wxT("Adding support for %s"),
658                    format.GetId().c_str());
659 
660         AddSupportedTarget(format);
661     }
662 
663     if ( !m_idSelectionGetHandler )
664     {
665         m_idSelectionGetHandler = g_signal_connect (
666                       m_clipboardWidget, "selection_get",
667                       G_CALLBACK (selection_handler),
668                       GUINT_TO_POINTER (gtk_get_current_event_time()) );
669     }
670 
671     // tell the world we offer clipboard data
672     return SetSelectionOwner();
673 }
674 
Close()675 void wxClipboard::Close()
676 {
677     wxCHECK_RET( m_open, wxT("clipboard not open") );
678 
679     m_open = false;
680 }
681 
IsOpened() const682 bool wxClipboard::IsOpened() const
683 {
684     return m_open;
685 }
686 
IsSupported(const wxDataFormat & format)687 bool wxClipboard::IsSupported( const wxDataFormat& format )
688 {
689     if ( DoIsSupported(format) )
690         return true;
691 
692 #if wxUSE_UNICODE
693     if ( format == wxDF_UNICODETEXT )
694     {
695         // also with plain STRING format
696         return DoIsSupported(g_altTextAtom);
697     }
698 #endif // wxUSE_UNICODE
699 
700     return false;
701 }
702 
GetData(wxDataObject & data)703 bool wxClipboard::GetData( wxDataObject& data )
704 {
705     wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
706 
707     // get all supported formats from wxDataObjects: notice that we are setting
708     // the object data, so we need them in "Set" direction
709     const size_t count = data.GetFormatCount(wxDataObject::Set);
710     wxDataFormatArray formats(count);
711     data.GetAllFormats(formats.get(), wxDataObject::Set);
712 
713     for ( size_t i = 0; i < count; i++ )
714     {
715         const wxDataFormat format(formats[i]);
716 
717         // is this format supported by clipboard ?
718         if ( !DoIsSupported(format) )
719             continue;
720 
721         wxLogTrace(TRACE_CLIPBOARD, wxT("Requesting format %s"),
722                    format.GetId().c_str());
723 
724         // these variables will be used by our GTKOnSelectionReceived()
725         m_receivedData = &data;
726         m_formatSupported = false;
727 
728         {
729             wxClipboardSync sync(*this);
730 
731             gtk_selection_convert(m_clipboardWidget,
732                                   GTKGetClipboardAtom(),
733                                   format,
734                                   (guint32) GDK_CURRENT_TIME );
735         } // wait until we get the results
736 
737         /*
738            Normally this is a true error as we checked for the presence of such
739            data before, but there are applications that may return an empty
740            string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
741            which would produce a false error message here, so we check for the
742            size of the string first. With ANSI, GetDataSize returns an extra
743            value (for the closing null?), with unicode, the exact number of
744            tokens is given (that is more than 1 for non-ASCII characters)
745            (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
746          */
747 #if wxUSE_UNICODE
748         if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
749 #else // !UNICODE
750         if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
751 #endif // UNICODE / !UNICODE
752         {
753             wxCHECK_MSG( m_formatSupported, false,
754                          wxT("error retrieving data from clipboard") );
755         }
756 
757         return true;
758     }
759 
760     wxLogTrace(TRACE_CLIPBOARD, wxT("GetData(): format not found"));
761 
762     return false;
763 }
764 
GTKGetDataObject(GdkAtom atom)765 wxDataObject* wxClipboard::GTKGetDataObject( GdkAtom atom )
766 {
767     if ( atom == GDK_NONE )
768         return Data();
769 
770     if ( atom == GDK_SELECTION_PRIMARY )
771     {
772         wxLogTrace(TRACE_CLIPBOARD, wxT("Primary selection requested" ));
773 
774         return Data( wxClipboard::Primary );
775     }
776     else if ( atom == GDK_SELECTION_CLIPBOARD )
777     {
778         wxLogTrace(TRACE_CLIPBOARD, wxT("Clipboard data requested" ));
779 
780         return Data( wxClipboard::Clipboard );
781     }
782     else // some other selection, we're not concerned
783     {
784         return (wxDataObject*)NULL;
785     }
786 }
787 
788 #endif // wxUSE_CLIPBOARD
789