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