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