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