]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/clipbrd.cpp
Reset wxPGEditor singleton instance pointers in dtors. This is useful if wxPropertyGr...
[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);
5276b0a5 488 wxDELETE(data);
eddb9644 489}
270c23f7 490
eddb9644
VZ
491bool wxClipboard::SetSelectionOwner(bool set)
492{
493 bool rc = gtk_selection_owner_set
494 (
495 set ? m_clipboardWidget : NULL,
496 GTKGetClipboardAtom(),
497 (guint32)GDK_CURRENT_TIME
498 );
499
500 if ( !rc )
501 {
9a83f860
VZ
502 wxLogTrace(TRACE_CLIPBOARD, wxT("Failed to %sset selection owner"),
503 set ? wxT("") : wxT("un"));
eddb9644 504 }
270c23f7 505
eddb9644
VZ
506 return rc;
507}
270c23f7 508
eddb9644
VZ
509void wxClipboard::AddSupportedTarget(GdkAtom atom)
510{
511 gtk_selection_add_target
512 (
10bd1f7d 513 m_clipboardWidget,
eddb9644
VZ
514 GTKGetClipboardAtom(),
515 atom,
516 0 // info (same as client data) unused
517 );
518}
519
3eef425f
RR
520bool wxClipboard::IsSupportedAsync(wxEvtHandler *sink)
521{
b4705641
RR
522 if (m_sink.get())
523 return false; // currently busy, come back later
d48b06bd 524
b4705641 525 wxCHECK_MSG( sink, false, wxT("no sink given") );
d48b06bd 526
3eef425f 527 m_sink = sink;
3eef425f
RR
528 gtk_selection_convert( m_targetsWidgetAsync,
529 GTKGetClipboardAtom(),
530 g_targetsAtom,
531 (guint32) GDK_CURRENT_TIME );
d48b06bd 532
3eef425f
RR
533 return true;
534}
535
eddb9644
VZ
536bool wxClipboard::DoIsSupported(const wxDataFormat& format)
537{
538 wxCHECK_MSG( format, false, wxT("invalid clipboard format") );
270c23f7 539
eddb9644
VZ
540 wxLogTrace(TRACE_CLIPBOARD, wxT("Checking if format %s is available"),
541 format.GetId().c_str());
542
543 // these variables will be used by our GTKOnTargetReceived()
544 m_targetRequested = format;
545 m_formatSupported = false;
546
547 // block until m_formatSupported is set from targets_selection_received
548 // callback
549 {
550 wxClipboardSync sync(*this);
551
552 gtk_selection_convert( m_targetsWidget,
553 GTKGetClipboardAtom(),
554 g_targetsAtom,
555 (guint32) GDK_CURRENT_TIME );
556 }
557
558 return m_formatSupported;
559}
560
561// ----------------------------------------------------------------------------
562// wxClipboard public API implementation
563// ----------------------------------------------------------------------------
564
565void wxClipboard::Clear()
566{
1c54277e
VZ
567 gtk_selection_clear_targets( m_clipboardWidget, GTKGetClipboardAtom() );
568
eddb9644
VZ
569 if ( gdk_selection_owner_get(GTKGetClipboardAtom()) ==
570 m_clipboardWidget->window )
571 {
572 wxClipboardSync sync(*this);
573
574 // this will result in selection_clear_clip callback being called and
575 // it will free our data
576 SetSelectionOwner(false);
8b53e5a2 577 }
270c23f7 578
8b53e5a2 579 m_targetRequested = 0;
de6185e2 580 m_formatSupported = false;
8b53e5a2
RR
581}
582
583bool wxClipboard::Open()
584{
de6185e2 585 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
270c23f7 586
de6185e2 587 m_open = true;
270c23f7 588
de6185e2 589 return true;
dc86cb34
RR
590}
591
75ce0581 592bool wxClipboard::SetData( wxDataObject *data )
dc86cb34 593{
de6185e2 594 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
270c23f7 595
de6185e2 596 wxCHECK_MSG( data, false, wxT("data is invalid") );
270c23f7 597
0d2a2b60 598 Clear();
75ce0581
RR
599
600 return AddData( data );
601}
602
603bool wxClipboard::AddData( wxDataObject *data )
604{
de6185e2 605 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
270c23f7 606
de6185e2 607 wxCHECK_MSG( data, false, wxT("data is invalid") );
270c23f7 608
eddb9644 609 // we can only store one wxDataObject so clear the old one
1dd989e1 610 Clear();
270c23f7 611
eddb9644 612 Data() = data;
1dd989e1 613
ca11abde 614 // get formats from wxDataObjects
eddb9644
VZ
615 const size_t count = data->GetFormatCount();
616 wxDataFormatArray formats(new wxDataFormat[count]);
617 data->GetAllFormats(formats.get());
11e1c70d 618
eddb9644
VZ
619 // always provide TIMESTAMP as a target, see comments in selection_handler
620 // for explanation
621 AddSupportedTarget(g_timestampAtom);
11e1c70d 622
eddb9644 623 for ( size_t i = 0; i < count; i++ )
b068c4e8 624 {
eddb9644 625 const wxDataFormat format(formats[i]);
11e1c70d 626
eddb9644
VZ
627 wxLogTrace(TRACE_CLIPBOARD, wxT("Adding support for %s"),
628 format.GetId().c_str());
3d257b8d 629
eddb9644 630 AddSupportedTarget(format);
b068c4e8
RR
631 }
632
9fa72bd2 633 g_signal_connect (m_clipboardWidget, "selection_get",
d394f0c9
MR
634 G_CALLBACK (selection_handler),
635 GUINT_TO_POINTER (gtk_get_current_event_time()) );
d345e841 636
eddb9644
VZ
637 // tell the world we offer clipboard data
638 return SetSelectionOwner();
8b53e5a2 639}
db1b4961 640
8b53e5a2
RR
641void wxClipboard::Close()
642{
223d09f6 643 wxCHECK_RET( m_open, wxT("clipboard not open") );
270c23f7 644
de6185e2 645 m_open = false;
dc86cb34
RR
646}
647
f536e0f2
VZ
648bool wxClipboard::IsOpened() const
649{
650 return m_open;
651}
652
e1ee679c 653bool wxClipboard::IsSupported( const wxDataFormat& format )
b527aac5 654{
eddb9644
VZ
655 if ( DoIsSupported(format) )
656 return true;
270c23f7 657
5e081315 658#if wxUSE_UNICODE
eddb9644 659 if ( format == wxDF_UNICODETEXT )
c7d6d883 660 {
eddb9644
VZ
661 // also with plain STRING format
662 return DoIsSupported(g_altTextAtom);
c7d6d883 663 }
eddb9644 664#endif // wxUSE_UNICODE
c7d6d883 665
eddb9644 666 return false;
270c23f7
VZ
667}
668
e1ee679c 669bool wxClipboard::GetData( wxDataObject& data )
75ce0581 670{
de6185e2 671 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
270c23f7 672
7eb670ac
VZ
673 // get all supported formats from wxDataObjects: notice that we are setting
674 // the object data, so we need them in "Set" direction
675 const size_t count = data.GetFormatCount(wxDataObject::Set);
eddb9644 676 wxDataFormatArray formats(new wxDataFormat[count]);
7eb670ac 677 data.GetAllFormats(formats.get(), wxDataObject::Set);
270c23f7 678
eddb9644 679 for ( size_t i = 0; i < count; i++ )
b068c4e8 680 {
eddb9644 681 const wxDataFormat format(formats[i]);
11e1c70d 682
eddb9644
VZ
683 // is this format supported by clipboard ?
684 if ( !DoIsSupported(format) )
685 continue;
270c23f7 686
eddb9644
VZ
687 wxLogTrace(TRACE_CLIPBOARD, wxT("Requesting format %s"),
688 format.GetId().c_str());
270c23f7 689
eddb9644 690 // these variables will be used by our GTKOnSelectionReceived()
b068c4e8 691 m_receivedData = &data;
de6185e2 692 m_formatSupported = false;
270c23f7 693
06f5d975
VZ
694 {
695 wxClipboardSync sync(*this);
696
eddb9644
VZ
697 gtk_selection_convert(m_clipboardWidget,
698 GTKGetClipboardAtom(),
699 format,
700 (guint32) GDK_CURRENT_TIME );
06f5d975 701 } // wait until we get the results
b527aac5 702
be809e82
VZ
703 /*
704 Normally this is a true error as we checked for the presence of such
705 data before, but there are applications that may return an empty
706 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
707 which would produce a false error message here, so we check for the
eddb9644 708 size of the string first. With ANSI, GetDataSize returns an extra
be809e82 709 value (for the closing null?), with unicode, the exact number of
eddb9644 710 tokens is given (that is more than 1 for non-ASCII characters)
be809e82
VZ
711 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
712 */
713#if wxUSE_UNICODE
714 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
715#else // !UNICODE
716 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
717#endif // UNICODE / !UNICODE
718 {
719 wxCHECK_MSG( m_formatSupported, false,
720 wxT("error retrieving data from clipboard") );
721 }
270c23f7 722
de6185e2 723 return true;
b068c4e8 724 }
270c23f7 725
eddb9644 726 wxLogTrace(TRACE_CLIPBOARD, wxT("GetData(): format not found"));
270c23f7 727
de6185e2 728 return false;
b527aac5
RR
729}
730
eddb9644 731#endif // wxUSE_CLIPBOARD