]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/clipbrd.cpp
Quote file names with spaces in wxFileType::ExpandCommand().
[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!)
9a83f860 56#define TRACE_CLIPBOARD wxT("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 {
9a83f860 72 wxASSERT_MSG( !ms_clipboard, wxT("reentrancy in clipboard code") );
06f5d975
VZ
73 ms_clipboard = &clipboard;
74 }
75
76 ~wxClipboardSync()
77 {
439c7eae
VZ
78 // ensure that there is a running event loop: this might not be the
79 // case if we're called before the main event loop startup
80 wxEventLoopGuarantor ensureEventLoop;
81
d48b06bd 82 while (ms_clipboard)
dde19c21 83 wxEventLoopBase::GetActive()->YieldFor(wxEVT_CATEGORY_CLIPBOARD);
06f5d975
VZ
84 }
85
86 // this method must be called by GTK+ callbacks to indicate that we got the
87 // result for our clipboard operation
e0d1fd7f 88 static void OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG(clipboard))
06f5d975
VZ
89 {
90 wxASSERT_MSG( clipboard == ms_clipboard,
9a83f860 91 wxT("got notification for alien clipboard") );
06f5d975
VZ
92
93 ms_clipboard = NULL;
94 }
95
b2b51072
VZ
96 // this method should be called if it's possible that no async clipboard
97 // operation is currently in progress (like it can be the case when the
98 // clipboard is cleared but not because we asked about it), it should only
99 // be called if such situation is expected -- otherwise call OnDone() which
100 // would assert in this case
101 static void OnDoneIfInProgress(wxClipboard *clipboard)
102 {
103 if ( ms_clipboard )
104 OnDone(clipboard);
105 }
106
06f5d975
VZ
107private:
108 static wxClipboard *ms_clipboard;
109
c0c133e1 110 wxDECLARE_NO_COPY_CLASS(wxClipboardSync);
b527aac5
RR
111};
112
06f5d975 113wxClipboard *wxClipboardSync::ms_clipboard = NULL;
dc86cb34 114
eddb9644 115// ============================================================================
3eef425f 116// clipboard callbacks implementation
eddb9644
VZ
117// ============================================================================
118
b527aac5
RR
119//-----------------------------------------------------------------------------
120// "selection_received" for targets
121//-----------------------------------------------------------------------------
122
865bb325 123extern "C" {
b527aac5 124static void
270c23f7
VZ
125targets_selection_received( GtkWidget *WXUNUSED(widget),
126 GtkSelectionData *selection_data,
034be888 127 guint32 WXUNUSED(time),
66633398 128 wxClipboard *clipboard )
dc86cb34 129{
eddb9644
VZ
130 if ( !clipboard )
131 return;
132
06f5d975
VZ
133 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
134
eddb9644
VZ
135 if ( !selection_data || selection_data->length <= 0 )
136 return;
137
138 // make sure we got the data in the correct form
139 GdkAtom type = selection_data->type;
140 if ( type != GDK_SELECTION_TYPE_ATOM )
034be888 141 {
eddb9644 142 if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
270c23f7 143 {
eddb9644 144 wxLogTrace( TRACE_CLIPBOARD,
9a83f860 145 wxT("got unsupported clipboard target") );
61b04ac6 146
eddb9644 147 return;
270c23f7 148 }
eddb9644 149 }
b527aac5 150
eddb9644
VZ
151 // it's not really a format, of course, but we can reuse its GetId() method
152 // to format this atom as string
153 wxDataFormat clip(selection_data->selection);
154 wxLogTrace( TRACE_CLIPBOARD,
155 wxT("Received available formats for clipboard %s"),
156 clip.GetId().c_str() );
270c23f7 157
eddb9644
VZ
158 // the atoms we received, holding a list of targets (= formats)
159 const GdkAtom * const atoms = (GdkAtom *)selection_data->data;
160 for ( size_t i = 0; i < selection_data->length/sizeof(GdkAtom); i++ )
161 {
162 const wxDataFormat format(atoms[i]);
11e1c70d 163
eddb9644 164 wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
270c23f7 165
eddb9644
VZ
166 if ( clipboard->GTKOnTargetReceived(format) )
167 return;
8b53e5a2 168 }
dc86cb34 169}
865bb325 170}
dc86cb34 171
eddb9644
VZ
172bool wxClipboard::GTKOnTargetReceived(const wxDataFormat& format)
173{
174 if ( format != m_targetRequested )
175 return false;
176
177 m_formatSupported = true;
178 return true;
179}
180
dc86cb34 181//-----------------------------------------------------------------------------
b527aac5 182// "selection_received" for the actual data
dc86cb34
RR
183//-----------------------------------------------------------------------------
184
865bb325 185extern "C" {
270c23f7
VZ
186static void
187selection_received( GtkWidget *WXUNUSED(widget),
188 GtkSelectionData *selection_data,
034be888 189 guint32 WXUNUSED(time),
66633398 190 wxClipboard *clipboard )
dc86cb34 191{
eddb9644 192 if ( !clipboard )
034be888 193 return;
270c23f7 194
eddb9644 195 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
270c23f7 196
eddb9644 197 if ( !selection_data || selection_data->length <= 0 )
e5d6aa22 198 return;
270c23f7 199
eddb9644 200 clipboard->GTKOnSelectionReceived(*selection_data);
dc86cb34 201}
865bb325 202}
fd0eed64
RR
203
204//-----------------------------------------------------------------------------
205// "selection_clear"
206//-----------------------------------------------------------------------------
207
865bb325 208extern "C" {
fd0eed64 209static gint
aeeb6a44 210selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 211{
eddb9644
VZ
212 wxClipboard * const clipboard = wxTheClipboard;
213 if ( !clipboard )
214 return TRUE;
06f5d975 215
b2b51072
VZ
216 // notice the use of OnDoneIfInProgress() here instead of just OnDone():
217 // it's perfectly possible that we're receiving this notification from GTK+
218 // even though we hadn't cleared the clipboard ourselves but because
219 // another application (or even another window in the same program)
220 // acquired it
221 wxON_BLOCK_EXIT1(wxClipboardSync::OnDoneIfInProgress, clipboard);
270c23f7 222
eddb9644 223 wxClipboard::Kind kind;
aeeb6a44
RR
224 if (event->selection == GDK_SELECTION_PRIMARY)
225 {
eddb9644
VZ
226 wxLogTrace(TRACE_CLIPBOARD, wxT("Lost primary selection" ));
227
228 kind = wxClipboard::Primary;
aeeb6a44 229 }
eddb9644 230 else if (event->selection == g_clipboardAtom)
aeeb6a44 231 {
eddb9644
VZ
232 wxLogTrace(TRACE_CLIPBOARD, wxT("Lost clipboard" ));
233
234 kind = wxClipboard::Clipboard;
aeeb6a44 235 }
eddb9644 236 else // some other selection, we're not concerned
aeeb6a44
RR
237 {
238 return FALSE;
239 }
270c23f7 240
eddb9644
VZ
241 // the clipboard is no longer in our hands, we don't need data any more
242 clipboard->GTKClearData(kind);
270c23f7 243
8b53e5a2 244 return TRUE;
fd0eed64 245}
865bb325 246}
fd0eed64
RR
247
248//-----------------------------------------------------------------------------
249// selection handler for supplying data
250//-----------------------------------------------------------------------------
251
865bb325 252extern "C" {
fd0eed64 253static void
19d89516
VZ
254selection_handler( GtkWidget *WXUNUSED(widget),
255 GtkSelectionData *selection_data,
256 guint WXUNUSED(info),
257 guint WXUNUSED(time),
d394f0c9 258 gpointer signal_data )
fd0eed64 259{
eddb9644
VZ
260 wxClipboard * const clipboard = wxTheClipboard;
261 if ( !clipboard )
262 return;
270c23f7 263
eddb9644
VZ
264 wxDataObject * const data = clipboard->GTKGetDataObject();
265 if ( !data )
266 return;
270c23f7 267
d394f0c9
MR
268 // ICCCM says that TIMESTAMP is a required atom.
269 // In particular, it satisfies Klipper, which polls
270 // TIMESTAMP to see if the clipboards content has changed.
271 // It shall return the time which was used to set the data.
272 if (selection_data->target == g_timestampAtom)
273 {
d704d2f5 274 guint timestamp = GPOINTER_TO_UINT (signal_data);
d394f0c9
MR
275 gtk_selection_data_set(selection_data,
276 GDK_SELECTION_TYPE_INTEGER,
277 32,
278 (guchar*)&(timestamp),
279 sizeof(timestamp));
280 wxLogTrace(TRACE_CLIPBOARD,
9a83f860 281 wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
d394f0c9
MR
282 timestamp);
283 return;
284 }
285
b068c4e8
RR
286 wxDataFormat format( selection_data->target );
287
ebe47451 288 wxLogTrace(TRACE_CLIPBOARD,
9a83f860 289 wxT("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
ebe47451 290 format.GetId().c_str(),
67756da4
MR
291 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->target))).c_str(),
292 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->type))).c_str(),
293 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->selection))).c_str(),
d394f0c9 294 GPOINTER_TO_UINT( signal_data )
ebe47451 295 );
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{
9a83f860 335 wxCHECK_RET( m_receivedData, wxT("should be inside GetData()") );
eddb9644
VZ
336
337 const wxDataFormat format(sel.target);
9a83f860 338 wxLogTrace(TRACE_CLIPBOARD, wxT("Received selection %s"),
eddb9644
VZ
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,
9a83f860 382 wxT("got unsupported clipboard target") );
3eef425f
RR
383
384 clipboard->m_sink->QueueEvent( event );
b4705641 385 clipboard->m_sink.Release();
3eef425f
RR
386 return;
387 }
388 }
389
3eef425f
RR
390 // it's not really a format, of course, but we can reuse its GetId() method
391 // to format this atom as string
392 wxDataFormat clip(selection_data->selection);
393 wxLogTrace( TRACE_CLIPBOARD,
394 wxT("Received available formats for clipboard %s"),
395 clip.GetId().c_str() );
3eef425f
RR
396
397 // the atoms we received, holding a list of targets (= formats)
398 const GdkAtom * const atoms = (GdkAtom *)selection_data->data;
399 for ( size_t i = 0; i < selection_data->length/sizeof(GdkAtom); i++ )
400 {
401 const wxDataFormat format(atoms[i]);
402
403 wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
404
405 event->AddFormat( format );
406 }
d48b06bd 407
3eef425f 408 clipboard->m_sink->QueueEvent( event );
b4705641 409 clipboard->m_sink.Release();
3eef425f
RR
410}
411}
412
eddb9644
VZ
413// ============================================================================
414// wxClipboard implementation
415// ============================================================================
416
417// ----------------------------------------------------------------------------
418// wxClipboard ctor/dtor
419// ----------------------------------------------------------------------------
dc86cb34
RR
420
421IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
422
423wxClipboard::wxClipboard()
424{
de6185e2 425 m_open = false;
8b53e5a2 426
eddb9644
VZ
427 m_dataPrimary =
428 m_dataClipboard =
429 m_receivedData = NULL;
aeeb6a44 430
eddb9644
VZ
431 m_formatSupported = false;
432 m_targetRequested = 0;
99c67c77 433
eddb9644 434 // we use m_targetsWidget to query what formats are available
034be888
RR
435 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
436 gtk_widget_realize( m_targetsWidget );
437
9fa72bd2
MR
438 g_signal_connect (m_targetsWidget, "selection_received",
439 G_CALLBACK (targets_selection_received), this);
270c23f7 440
d48b06bd 441 // we use m_targetsWidgetAsync to query what formats are available asynchronously
3eef425f
RR
442 m_targetsWidgetAsync = gtk_window_new( GTK_WINDOW_POPUP );
443 gtk_widget_realize( m_targetsWidgetAsync );
444
445 g_signal_connect (m_targetsWidgetAsync, "selection_received",
446 G_CALLBACK (async_targets_selection_received), this);
447
eddb9644 448 // we use m_clipboardWidget to get and to offer data
8b53e5a2
RR
449 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
450 gtk_widget_realize( m_clipboardWidget );
451
9fa72bd2
MR
452 g_signal_connect (m_clipboardWidget, "selection_received",
453 G_CALLBACK (selection_received), this);
034be888 454
9fa72bd2
MR
455 g_signal_connect (m_clipboardWidget, "selection_clear_event",
456 G_CALLBACK (selection_clear_clip), NULL);
270c23f7 457
eddb9644
VZ
458 // initialize atoms we use if not done yet
459 if ( !g_clipboardAtom )
460 g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
461 if ( !g_targetsAtom )
462 g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
463 if ( !g_timestampAtom )
464 g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
dc86cb34
RR
465}
466
467wxClipboard::~wxClipboard()
b527aac5 468{
270c23f7
VZ
469 Clear();
470
05492dd1
VZ
471 gtk_widget_destroy( m_clipboardWidget );
472 gtk_widget_destroy( m_targetsWidget );
b527aac5
RR
473}
474
eddb9644
VZ
475// ----------------------------------------------------------------------------
476// wxClipboard helper functions
477// ----------------------------------------------------------------------------
478
479GdkAtom wxClipboard::GTKGetClipboardAtom() const
480{
481 return m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
482 : g_clipboardAtom;
483}
484
485void wxClipboard::GTKClearData(Kind kind)
dc86cb34 486{
511383f9 487 wxDataObject *&data = Data(kind);
eddb9644 488 if ( data )
1dd989e1 489 {
eddb9644
VZ
490 delete data;
491 data = NULL;
492 }
493}
270c23f7 494
eddb9644
VZ
495bool wxClipboard::SetSelectionOwner(bool set)
496{
497 bool rc = gtk_selection_owner_set
498 (
499 set ? m_clipboardWidget : NULL,
500 GTKGetClipboardAtom(),
501 (guint32)GDK_CURRENT_TIME
502 );
503
504 if ( !rc )
505 {
9a83f860
VZ
506 wxLogTrace(TRACE_CLIPBOARD, wxT("Failed to %sset selection owner"),
507 set ? wxT("") : wxT("un"));
eddb9644 508 }
270c23f7 509
eddb9644
VZ
510 return rc;
511}
270c23f7 512
eddb9644
VZ
513void wxClipboard::AddSupportedTarget(GdkAtom atom)
514{
515 gtk_selection_add_target
516 (
10bd1f7d 517 m_clipboardWidget,
eddb9644
VZ
518 GTKGetClipboardAtom(),
519 atom,
520 0 // info (same as client data) unused
521 );
522}
523
3eef425f
RR
524bool wxClipboard::IsSupportedAsync(wxEvtHandler *sink)
525{
b4705641
RR
526 if (m_sink.get())
527 return false; // currently busy, come back later
d48b06bd 528
b4705641 529 wxCHECK_MSG( sink, false, wxT("no sink given") );
d48b06bd 530
3eef425f 531 m_sink = sink;
3eef425f
RR
532 gtk_selection_convert( m_targetsWidgetAsync,
533 GTKGetClipboardAtom(),
534 g_targetsAtom,
535 (guint32) GDK_CURRENT_TIME );
d48b06bd 536
3eef425f
RR
537 return true;
538}
539
eddb9644
VZ
540bool wxClipboard::DoIsSupported(const wxDataFormat& format)
541{
542 wxCHECK_MSG( format, false, wxT("invalid clipboard format") );
270c23f7 543
eddb9644
VZ
544 wxLogTrace(TRACE_CLIPBOARD, wxT("Checking if format %s is available"),
545 format.GetId().c_str());
546
547 // these variables will be used by our GTKOnTargetReceived()
548 m_targetRequested = format;
549 m_formatSupported = false;
550
551 // block until m_formatSupported is set from targets_selection_received
552 // callback
553 {
554 wxClipboardSync sync(*this);
555
556 gtk_selection_convert( m_targetsWidget,
557 GTKGetClipboardAtom(),
558 g_targetsAtom,
559 (guint32) GDK_CURRENT_TIME );
560 }
561
562 return m_formatSupported;
563}
564
565// ----------------------------------------------------------------------------
566// wxClipboard public API implementation
567// ----------------------------------------------------------------------------
568
569void wxClipboard::Clear()
570{
1c54277e
VZ
571 gtk_selection_clear_targets( m_clipboardWidget, GTKGetClipboardAtom() );
572
eddb9644
VZ
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
7eb670ac
VZ
677 // get all supported formats from wxDataObjects: notice that we are setting
678 // the object data, so we need them in "Set" direction
679 const size_t count = data.GetFormatCount(wxDataObject::Set);
eddb9644 680 wxDataFormatArray formats(new wxDataFormat[count]);
7eb670ac 681 data.GetAllFormats(formats.get(), wxDataObject::Set);
270c23f7 682
eddb9644 683 for ( size_t i = 0; i < count; i++ )
b068c4e8 684 {
eddb9644 685 const wxDataFormat format(formats[i]);
11e1c70d 686
eddb9644
VZ
687 // is this format supported by clipboard ?
688 if ( !DoIsSupported(format) )
689 continue;
270c23f7 690
eddb9644
VZ
691 wxLogTrace(TRACE_CLIPBOARD, wxT("Requesting format %s"),
692 format.GetId().c_str());
270c23f7 693
eddb9644 694 // these variables will be used by our GTKOnSelectionReceived()
b068c4e8 695 m_receivedData = &data;
de6185e2 696 m_formatSupported = false;
270c23f7 697
06f5d975
VZ
698 {
699 wxClipboardSync sync(*this);
700
eddb9644
VZ
701 gtk_selection_convert(m_clipboardWidget,
702 GTKGetClipboardAtom(),
703 format,
704 (guint32) GDK_CURRENT_TIME );
06f5d975 705 } // wait until we get the results
b527aac5 706
be809e82
VZ
707 /*
708 Normally this is a true error as we checked for the presence of such
709 data before, but there are applications that may return an empty
710 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
711 which would produce a false error message here, so we check for the
eddb9644 712 size of the string first. With ANSI, GetDataSize returns an extra
be809e82 713 value (for the closing null?), with unicode, the exact number of
eddb9644 714 tokens is given (that is more than 1 for non-ASCII characters)
be809e82
VZ
715 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
716 */
717#if wxUSE_UNICODE
718 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
719#else // !UNICODE
720 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
721#endif // UNICODE / !UNICODE
722 {
723 wxCHECK_MSG( m_formatSupported, false,
724 wxT("error retrieving data from clipboard") );
725 }
270c23f7 726
de6185e2 727 return true;
b068c4e8 728 }
270c23f7 729
eddb9644 730 wxLogTrace(TRACE_CLIPBOARD, wxT("GetData(): format not found"));
270c23f7 731
de6185e2 732 return false;
b527aac5
RR
733}
734
eddb9644 735#endif // wxUSE_CLIPBOARD