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