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