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