]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/clipbrd.cpp
Also update focus rect when changing selection in single selection mode, fixes #11332
[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 {
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,
9a83f860 87 wxT("got notification for alien clipboard") );
06f5d975
VZ
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 140 wxLogTrace( TRACE_CLIPBOARD,
9a83f860 141 wxT("got unsupported clipboard target") );
61b04ac6 142
eddb9644 143 return;
270c23f7 144 }
eddb9644 145 }
b527aac5 146
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() );
270c23f7 153
eddb9644
VZ
154 // the atoms we received, holding a list of targets (= formats)
155 const GdkAtom * const atoms = (GdkAtom *)selection_data->data;
156 for ( size_t i = 0; i < selection_data->length/sizeof(GdkAtom); i++ )
157 {
158 const wxDataFormat format(atoms[i]);
11e1c70d 159
eddb9644 160 wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
270c23f7 161
eddb9644
VZ
162 if ( clipboard->GTKOnTargetReceived(format) )
163 return;
8b53e5a2 164 }
dc86cb34 165}
865bb325 166}
dc86cb34 167
eddb9644
VZ
168bool wxClipboard::GTKOnTargetReceived(const wxDataFormat& format)
169{
170 if ( format != m_targetRequested )
171 return false;
172
173 m_formatSupported = true;
174 return true;
175}
176
dc86cb34 177//-----------------------------------------------------------------------------
b527aac5 178// "selection_received" for the actual data
dc86cb34
RR
179//-----------------------------------------------------------------------------
180
865bb325 181extern "C" {
270c23f7
VZ
182static void
183selection_received( GtkWidget *WXUNUSED(widget),
184 GtkSelectionData *selection_data,
034be888 185 guint32 WXUNUSED(time),
66633398 186 wxClipboard *clipboard )
dc86cb34 187{
eddb9644 188 if ( !clipboard )
034be888 189 return;
270c23f7 190
eddb9644 191 wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard);
270c23f7 192
eddb9644 193 if ( !selection_data || selection_data->length <= 0 )
e5d6aa22 194 return;
270c23f7 195
eddb9644 196 clipboard->GTKOnSelectionReceived(*selection_data);
dc86cb34 197}
865bb325 198}
fd0eed64
RR
199
200//-----------------------------------------------------------------------------
201// "selection_clear"
202//-----------------------------------------------------------------------------
203
865bb325 204extern "C" {
fd0eed64 205static gint
aeeb6a44 206selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 207{
eddb9644
VZ
208 wxClipboard * const clipboard = wxTheClipboard;
209 if ( !clipboard )
210 return TRUE;
06f5d975 211
b2b51072
VZ
212 // notice the use of OnDoneIfInProgress() here instead of just OnDone():
213 // it's perfectly possible that we're receiving this notification from GTK+
214 // even though we hadn't cleared the clipboard ourselves but because
215 // another application (or even another window in the same program)
216 // acquired it
217 wxON_BLOCK_EXIT1(wxClipboardSync::OnDoneIfInProgress, clipboard);
270c23f7 218
eddb9644 219 wxClipboard::Kind kind;
aeeb6a44
RR
220 if (event->selection == GDK_SELECTION_PRIMARY)
221 {
eddb9644
VZ
222 wxLogTrace(TRACE_CLIPBOARD, wxT("Lost primary selection" ));
223
224 kind = wxClipboard::Primary;
aeeb6a44 225 }
eddb9644 226 else if (event->selection == g_clipboardAtom)
aeeb6a44 227 {
eddb9644
VZ
228 wxLogTrace(TRACE_CLIPBOARD, wxT("Lost clipboard" ));
229
230 kind = wxClipboard::Clipboard;
aeeb6a44 231 }
eddb9644 232 else // some other selection, we're not concerned
aeeb6a44
RR
233 {
234 return FALSE;
235 }
270c23f7 236
eddb9644
VZ
237 // the clipboard is no longer in our hands, we don't need data any more
238 clipboard->GTKClearData(kind);
270c23f7 239
8b53e5a2 240 return TRUE;
fd0eed64 241}
865bb325 242}
fd0eed64
RR
243
244//-----------------------------------------------------------------------------
245// selection handler for supplying data
246//-----------------------------------------------------------------------------
247
865bb325 248extern "C" {
fd0eed64 249static void
19d89516
VZ
250selection_handler( GtkWidget *WXUNUSED(widget),
251 GtkSelectionData *selection_data,
252 guint WXUNUSED(info),
253 guint WXUNUSED(time),
d394f0c9 254 gpointer signal_data )
fd0eed64 255{
eddb9644
VZ
256 wxClipboard * const clipboard = wxTheClipboard;
257 if ( !clipboard )
258 return;
270c23f7 259
eddb9644
VZ
260 wxDataObject * const data = clipboard->GTKGetDataObject();
261 if ( !data )
262 return;
270c23f7 263
d394f0c9
MR
264 // ICCCM says that TIMESTAMP is a required atom.
265 // In particular, it satisfies Klipper, which polls
266 // TIMESTAMP to see if the clipboards content has changed.
267 // It shall return the time which was used to set the data.
268 if (selection_data->target == g_timestampAtom)
269 {
d704d2f5 270 guint timestamp = GPOINTER_TO_UINT (signal_data);
d394f0c9
MR
271 gtk_selection_data_set(selection_data,
272 GDK_SELECTION_TYPE_INTEGER,
273 32,
274 (guchar*)&(timestamp),
275 sizeof(timestamp));
276 wxLogTrace(TRACE_CLIPBOARD,
9a83f860 277 wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
d394f0c9
MR
278 timestamp);
279 return;
280 }
281
b068c4e8
RR
282 wxDataFormat format( selection_data->target );
283
ebe47451 284 wxLogTrace(TRACE_CLIPBOARD,
9a83f860 285 wxT("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
ebe47451 286 format.GetId().c_str(),
67756da4
MR
287 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->target))).c_str(),
288 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->type))).c_str(),
289 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->selection))).c_str(),
d394f0c9 290 GPOINTER_TO_UINT( signal_data )
ebe47451 291 );
3d257b8d 292
157a8f70
VZ
293 if ( !data->IsSupportedFormat( format ) )
294 return;
270c23f7 295
b068c4e8 296 int size = data->GetDataSize( format );
157a8f70
VZ
297 if ( !size )
298 return;
270c23f7 299
157a8f70 300 wxCharBuffer buf(size - 1); // it adds 1 internally (for NUL)
33754c4d 301
b82c3e60 302 // text data must be returned in UTF8 if format is wxDF_UNICODETEXT
157a8f70
VZ
303 if ( !data->GetDataHere(format, buf.data()) )
304 return;
270c23f7 305
b82c3e60
VZ
306 // use UTF8_STRING format if requested in Unicode build but just plain
307 // STRING one in ANSI or if explicitly asked in Unicode
308#if wxUSE_UNICODE
ebe47451
VS
309 if (format == wxDataFormat(wxDF_UNICODETEXT))
310 {
311 gtk_selection_data_set_text(
312 selection_data,
b82c3e60 313 (const gchar*)buf.data(),
2c906a49 314 size );
ebe47451
VS
315 }
316 else
157a8f70 317#endif // wxUSE_UNICODE
ebe47451
VS
318 {
319 gtk_selection_data_set(
320 selection_data,
321 GDK_SELECTION_TYPE_STRING,
322 8*sizeof(gchar),
b82c3e60 323 (const guchar*)buf.data(),
2c906a49 324 size );
ebe47451 325 }
fd0eed64 326}
865bb325 327}
dc86cb34 328
eddb9644
VZ
329void wxClipboard::GTKOnSelectionReceived(const GtkSelectionData& sel)
330{
9a83f860 331 wxCHECK_RET( m_receivedData, wxT("should be inside GetData()") );
eddb9644
VZ
332
333 const wxDataFormat format(sel.target);
9a83f860 334 wxLogTrace(TRACE_CLIPBOARD, wxT("Received selection %s"),
eddb9644
VZ
335 format.GetId().c_str());
336
337 if ( !m_receivedData->IsSupportedFormat(format) )
338 return;
339
340 m_receivedData->SetData(format, sel.length, sel.data);
341 m_formatSupported = true;
342}
343
3eef425f
RR
344//-----------------------------------------------------------------------------
345// asynchronous "selection_received" for targets
346//-----------------------------------------------------------------------------
347
348extern "C" {
349static void
350async_targets_selection_received( GtkWidget *WXUNUSED(widget),
351 GtkSelectionData *selection_data,
352 guint32 WXUNUSED(time),
353 wxClipboard *clipboard )
354{
355 if ( !clipboard ) // Assert?
356 return;
357
358 if (!clipboard->m_sink)
359 return;
d48b06bd 360
3eef425f 361 wxClipboardEvent *event = new wxClipboardEvent(wxEVT_CLIPBOARD_CHANGED);
311c1be9 362 event->SetEventObject( clipboard );
d48b06bd 363
3eef425f
RR
364 if ( !selection_data || selection_data->length <= 0 )
365 {
366 clipboard->m_sink->QueueEvent( event );
b4705641 367 clipboard->m_sink.Release();
3eef425f
RR
368 return;
369 }
370
371 // make sure we got the data in the correct form
372 GdkAtom type = selection_data->type;
373 if ( type != GDK_SELECTION_TYPE_ATOM )
374 {
375 if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 )
376 {
377 wxLogTrace( TRACE_CLIPBOARD,
9a83f860 378 wxT("got unsupported clipboard target") );
3eef425f
RR
379
380 clipboard->m_sink->QueueEvent( event );
b4705641 381 clipboard->m_sink.Release();
3eef425f
RR
382 return;
383 }
384 }
385
3eef425f
RR
386 // it's not really a format, of course, but we can reuse its GetId() method
387 // to format this atom as string
388 wxDataFormat clip(selection_data->selection);
389 wxLogTrace( TRACE_CLIPBOARD,
390 wxT("Received available formats for clipboard %s"),
391 clip.GetId().c_str() );
3eef425f
RR
392
393 // the atoms we received, holding a list of targets (= formats)
394 const GdkAtom * const atoms = (GdkAtom *)selection_data->data;
395 for ( size_t i = 0; i < selection_data->length/sizeof(GdkAtom); i++ )
396 {
397 const wxDataFormat format(atoms[i]);
398
399 wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str());
400
401 event->AddFormat( format );
402 }
d48b06bd 403
3eef425f 404 clipboard->m_sink->QueueEvent( event );
b4705641 405 clipboard->m_sink.Release();
3eef425f
RR
406}
407}
408
eddb9644
VZ
409// ============================================================================
410// wxClipboard implementation
411// ============================================================================
412
413// ----------------------------------------------------------------------------
414// wxClipboard ctor/dtor
415// ----------------------------------------------------------------------------
dc86cb34
RR
416
417IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
418
419wxClipboard::wxClipboard()
420{
de6185e2 421 m_open = false;
8b53e5a2 422
eddb9644
VZ
423 m_dataPrimary =
424 m_dataClipboard =
425 m_receivedData = NULL;
aeeb6a44 426
eddb9644
VZ
427 m_formatSupported = false;
428 m_targetRequested = 0;
99c67c77 429
eddb9644 430 // we use m_targetsWidget to query what formats are available
034be888
RR
431 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
432 gtk_widget_realize( m_targetsWidget );
433
9fa72bd2
MR
434 g_signal_connect (m_targetsWidget, "selection_received",
435 G_CALLBACK (targets_selection_received), this);
270c23f7 436
d48b06bd 437 // we use m_targetsWidgetAsync to query what formats are available asynchronously
3eef425f
RR
438 m_targetsWidgetAsync = gtk_window_new( GTK_WINDOW_POPUP );
439 gtk_widget_realize( m_targetsWidgetAsync );
440
441 g_signal_connect (m_targetsWidgetAsync, "selection_received",
442 G_CALLBACK (async_targets_selection_received), this);
443
eddb9644 444 // we use m_clipboardWidget to get and to offer data
8b53e5a2
RR
445 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
446 gtk_widget_realize( m_clipboardWidget );
447
9fa72bd2
MR
448 g_signal_connect (m_clipboardWidget, "selection_received",
449 G_CALLBACK (selection_received), this);
034be888 450
9fa72bd2
MR
451 g_signal_connect (m_clipboardWidget, "selection_clear_event",
452 G_CALLBACK (selection_clear_clip), NULL);
270c23f7 453
eddb9644
VZ
454 // initialize atoms we use if not done yet
455 if ( !g_clipboardAtom )
456 g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
457 if ( !g_targetsAtom )
458 g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
459 if ( !g_timestampAtom )
460 g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
dc86cb34
RR
461}
462
463wxClipboard::~wxClipboard()
b527aac5 464{
270c23f7
VZ
465 Clear();
466
05492dd1
VZ
467 gtk_widget_destroy( m_clipboardWidget );
468 gtk_widget_destroy( m_targetsWidget );
b527aac5
RR
469}
470
eddb9644
VZ
471// ----------------------------------------------------------------------------
472// wxClipboard helper functions
473// ----------------------------------------------------------------------------
474
475GdkAtom wxClipboard::GTKGetClipboardAtom() const
476{
477 return m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
478 : g_clipboardAtom;
479}
480
481void wxClipboard::GTKClearData(Kind kind)
dc86cb34 482{
511383f9 483 wxDataObject *&data = Data(kind);
eddb9644 484 if ( data )
1dd989e1 485 {
eddb9644
VZ
486 delete data;
487 data = NULL;
488 }
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
eddb9644
VZ
673 // get all supported formats from wxDataObjects
674 const size_t count = data.GetFormatCount();
675 wxDataFormatArray formats(new wxDataFormat[count]);
676 data.GetAllFormats(formats.get());
270c23f7 677
eddb9644 678 for ( size_t i = 0; i < count; i++ )
b068c4e8 679 {
eddb9644 680 const wxDataFormat format(formats[i]);
11e1c70d 681
eddb9644
VZ
682 // is this format supported by clipboard ?
683 if ( !DoIsSupported(format) )
684 continue;
270c23f7 685
eddb9644
VZ
686 wxLogTrace(TRACE_CLIPBOARD, wxT("Requesting format %s"),
687 format.GetId().c_str());
270c23f7 688
eddb9644 689 // these variables will be used by our GTKOnSelectionReceived()
b068c4e8 690 m_receivedData = &data;
de6185e2 691 m_formatSupported = false;
270c23f7 692
06f5d975
VZ
693 {
694 wxClipboardSync sync(*this);
695
eddb9644
VZ
696 gtk_selection_convert(m_clipboardWidget,
697 GTKGetClipboardAtom(),
698 format,
699 (guint32) GDK_CURRENT_TIME );
06f5d975 700 } // wait until we get the results
b527aac5 701
be809e82
VZ
702 /*
703 Normally this is a true error as we checked for the presence of such
704 data before, but there are applications that may return an empty
705 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
706 which would produce a false error message here, so we check for the
eddb9644 707 size of the string first. With ANSI, GetDataSize returns an extra
be809e82 708 value (for the closing null?), with unicode, the exact number of
eddb9644 709 tokens is given (that is more than 1 for non-ASCII characters)
be809e82
VZ
710 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
711 */
712#if wxUSE_UNICODE
713 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
714#else // !UNICODE
715 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
716#endif // UNICODE / !UNICODE
717 {
718 wxCHECK_MSG( m_formatSupported, false,
719 wxT("error retrieving data from clipboard") );
720 }
270c23f7 721
de6185e2 722 return true;
b068c4e8 723 }
270c23f7 724
eddb9644 725 wxLogTrace(TRACE_CLIPBOARD, wxT("GetData(): format not found"));
270c23f7 726
de6185e2 727 return false;
b527aac5
RR
728}
729
eddb9644 730#endif // wxUSE_CLIPBOARD