suppress spurious error message when copying from clipboard brought empty string...
[wxWidgets.git] / src / gtk / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/clipbrd.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_CLIPBOARD
14
15 #include "wx/clipbrd.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/log.h"
19 #include "wx/utils.h"
20 #include "wx/dataobj.h"
21 #endif
22
23 #include "wx/gtk/private.h"
24
25 //-----------------------------------------------------------------------------
26 // data
27 //-----------------------------------------------------------------------------
28
29 GdkAtom g_clipboardAtom = 0;
30 GdkAtom g_targetsAtom = 0;
31 GdkAtom g_timestampAtom = 0;
32
33 #if wxUSE_UNICODE
34 extern GdkAtom g_altTextAtom;
35 #endif
36
37 // the trace mask we use with wxLogTrace() - call
38 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
39 // (there will be a *lot* of them!)
40 #define TRACE_CLIPBOARD _T("clipboard")
41
42 //-----------------------------------------------------------------------------
43 // reminder
44 //-----------------------------------------------------------------------------
45
46 /* The contents of a selection are returned in a GtkSelectionData
47 structure. selection/target identify the request.
48 type specifies the type of the return; if length < 0, and
49 the data should be ignored. This structure has object semantics -
50 no fields should be modified directly, they should not be created
51 directly, and pointers to them should not be stored beyond the duration of
52 a callback. (If the last is changed, we'll need to add reference
53 counting)
54
55 struct _GtkSelectionData
56 {
57 GdkAtom selection;
58 GdkAtom target;
59 GdkAtom type;
60 gint format;
61 guchar *data;
62 gint length;
63 };
64
65 */
66
67 //-----------------------------------------------------------------------------
68 // "selection_received" for targets
69 //-----------------------------------------------------------------------------
70
71 extern "C" {
72 static void
73 targets_selection_received( GtkWidget *WXUNUSED(widget),
74 GtkSelectionData *selection_data,
75 guint32 WXUNUSED(time),
76 wxClipboard *clipboard )
77 {
78 if ( wxTheClipboard && selection_data->length > 0 )
79 {
80 // make sure we got the data in the correct form
81 GdkAtom type = selection_data->type;
82 if ( type != GDK_SELECTION_TYPE_ATOM )
83 {
84 if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") )
85 {
86 wxLogTrace( TRACE_CLIPBOARD,
87 _T("got unsupported clipboard target") );
88
89 clipboard->m_waiting = false;
90 return;
91 }
92 }
93
94 #ifdef __WXDEBUG__
95 wxDataFormat clip( selection_data->selection );
96 wxLogTrace( TRACE_CLIPBOARD,
97 wxT("selection received for targets, clipboard %s"),
98 clip.GetId().c_str() );
99 #endif // __WXDEBUG__
100
101 // the atoms we received, holding a list of targets (= formats)
102 GdkAtom *atoms = (GdkAtom *)selection_data->data;
103
104 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
105 {
106 wxDataFormat format( atoms[i] );
107
108 wxLogTrace( TRACE_CLIPBOARD,
109 wxT("selection received for targets, format %s"),
110 format.GetId().c_str() );
111
112 // printf( "format %s requested %s\n",
113 // gdk_atom_name( atoms[i] ),
114 // gdk_atom_name( clipboard->m_targetRequested ) );
115
116 if (format == clipboard->m_targetRequested)
117 {
118 clipboard->m_waiting = false;
119 clipboard->m_formatSupported = true;
120 return;
121 }
122 }
123 }
124
125 clipboard->m_waiting = false;
126 }
127 }
128
129 //-----------------------------------------------------------------------------
130 // "selection_received" for the actual data
131 //-----------------------------------------------------------------------------
132
133 extern "C" {
134 static void
135 selection_received( GtkWidget *WXUNUSED(widget),
136 GtkSelectionData *selection_data,
137 guint32 WXUNUSED(time),
138 wxClipboard *clipboard )
139 {
140 if (!wxTheClipboard)
141 {
142 clipboard->m_waiting = false;
143 return;
144 }
145
146 wxDataObject *data_object = clipboard->m_receivedData;
147
148 if (!data_object)
149 {
150 clipboard->m_waiting = false;
151 return;
152 }
153
154 if (selection_data->length <= 0)
155 {
156 clipboard->m_waiting = false;
157 return;
158 }
159
160 wxDataFormat format( selection_data->target );
161
162 // make sure we got the data in the correct format
163 if (!data_object->IsSupportedFormat( format ) )
164 {
165 clipboard->m_waiting = false;
166 return;
167 }
168
169 #if 0
170 This seems to cause problems somehow
171 // make sure we got the data in the correct form (selection type).
172 // if so, copy data to target object
173 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
174 {
175 clipboard->m_waiting = false;
176 return;
177 }
178 #endif
179
180 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
181
182 wxTheClipboard->m_formatSupported = true;
183 clipboard->m_waiting = false;
184 }
185 }
186
187 //-----------------------------------------------------------------------------
188 // "selection_clear"
189 //-----------------------------------------------------------------------------
190
191 extern "C" {
192 static gint
193 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
194 {
195 if (!wxTheClipboard) return true;
196
197 if (event->selection == GDK_SELECTION_PRIMARY)
198 {
199 wxTheClipboard->m_ownsPrimarySelection = false;
200 }
201 else
202 if (event->selection == g_clipboardAtom)
203 {
204 wxTheClipboard->m_ownsClipboard = false;
205 }
206 else
207 {
208 wxTheClipboard->m_waiting = false;
209 return FALSE;
210 }
211
212 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
213 (!wxTheClipboard->m_ownsClipboard))
214 {
215 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
216 if (wxTheClipboard->m_data)
217 {
218 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
219
220 delete wxTheClipboard->m_data;
221 wxTheClipboard->m_data = (wxDataObject*) NULL;
222 }
223 }
224
225 wxTheClipboard->m_waiting = false;
226 return TRUE;
227 }
228 }
229
230 //-----------------------------------------------------------------------------
231 // selection handler for supplying data
232 //-----------------------------------------------------------------------------
233
234 extern "C" {
235 static void
236 selection_handler( GtkWidget *WXUNUSED(widget),
237 GtkSelectionData *selection_data,
238 guint WXUNUSED(info),
239 guint WXUNUSED(time),
240 gpointer signal_data )
241 {
242 if (!wxTheClipboard) return;
243
244 if (!wxTheClipboard->m_data) return;
245
246 wxDataObject *data = wxTheClipboard->m_data;
247
248 // ICCCM says that TIMESTAMP is a required atom.
249 // In particular, it satisfies Klipper, which polls
250 // TIMESTAMP to see if the clipboards content has changed.
251 // It shall return the time which was used to set the data.
252 if (selection_data->target == g_timestampAtom)
253 {
254 guint timestamp = GPOINTER_TO_UINT (signal_data);
255 gtk_selection_data_set(selection_data,
256 GDK_SELECTION_TYPE_INTEGER,
257 32,
258 (guchar*)&(timestamp),
259 sizeof(timestamp));
260 wxLogTrace(TRACE_CLIPBOARD,
261 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
262 timestamp);
263 return;
264 }
265
266 wxDataFormat format( selection_data->target );
267
268 #ifdef __WXDEBUG__
269 wxLogTrace(TRACE_CLIPBOARD,
270 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
271 format.GetId().c_str(),
272 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->target))).c_str(),
273 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->type))).c_str(),
274 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->selection))).c_str(),
275 GPOINTER_TO_UINT( signal_data )
276 );
277 #endif
278
279 if (!data->IsSupportedFormat( format )) return;
280
281 int size = data->GetDataSize( format );
282
283 if (size == 0) return;
284
285 void *d = malloc(size);
286
287 // Text data will be in UTF8 in Unicode mode.
288 data->GetDataHere( selection_data->target, d );
289
290 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
291 // would show as UTF-8 data interpreted as latin1 (?) in other
292 // GTK+ apps if we used gtk_selection_data_set()
293 if (format == wxDataFormat(wxDF_UNICODETEXT))
294 {
295 gtk_selection_data_set_text(
296 selection_data,
297 (const gchar*)d,
298 size );
299 }
300 else
301 {
302 gtk_selection_data_set(
303 selection_data,
304 GDK_SELECTION_TYPE_STRING,
305 8*sizeof(gchar),
306 (unsigned char*) d,
307 size );
308 }
309
310 free(d);
311 }
312 }
313
314 //-----------------------------------------------------------------------------
315 // wxClipboard
316 //-----------------------------------------------------------------------------
317
318 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
319
320 wxClipboard::wxClipboard()
321 {
322 m_open = false;
323 m_waiting = false;
324
325 m_ownsClipboard = false;
326 m_ownsPrimarySelection = false;
327
328 m_data = (wxDataObject*) NULL;
329 m_receivedData = (wxDataObject*) NULL;
330
331 /* we use m_targetsWidget to query what formats are available */
332
333 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
334 gtk_widget_realize( m_targetsWidget );
335
336 g_signal_connect (m_targetsWidget, "selection_received",
337 G_CALLBACK (targets_selection_received), this);
338
339 /* we use m_clipboardWidget to get and to offer data */
340
341 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
342 gtk_widget_realize( m_clipboardWidget );
343
344 g_signal_connect (m_clipboardWidget, "selection_received",
345 G_CALLBACK (selection_received), this);
346
347 g_signal_connect (m_clipboardWidget, "selection_clear_event",
348 G_CALLBACK (selection_clear_clip), NULL);
349
350 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
351 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
352 if (!g_timestampAtom) g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
353
354 m_formatSupported = false;
355 m_targetRequested = 0;
356
357 m_usePrimary = false;
358 }
359
360 wxClipboard::~wxClipboard()
361 {
362 Clear();
363
364 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
365 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
366 }
367
368 void wxClipboard::Clear()
369 {
370 if (m_data)
371 {
372 #if wxUSE_THREADS
373 /* disable GUI threads */
374 #endif
375
376 // As we have data we also own the clipboard. Once we no longer own
377 // it, clear_selection is called which will set m_data to zero
378 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
379 {
380 m_waiting = true;
381
382 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
383 (guint32) GDK_CURRENT_TIME );
384
385 while (m_waiting) gtk_main_iteration();
386 }
387
388 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
389 {
390 m_waiting = true;
391
392 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
393 (guint32) GDK_CURRENT_TIME );
394
395 while (m_waiting) gtk_main_iteration();
396 }
397
398 delete m_data;
399 m_data = NULL;
400
401 #if wxUSE_THREADS
402 /* re-enable GUI threads */
403 #endif
404 }
405
406 m_targetRequested = 0;
407 m_formatSupported = false;
408 }
409
410 bool wxClipboard::Open()
411 {
412 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
413
414 m_open = true;
415
416 return true;
417 }
418
419 bool wxClipboard::SetData( wxDataObject *data )
420 {
421 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
422
423 wxCHECK_MSG( data, false, wxT("data is invalid") );
424
425 Clear();
426
427 return AddData( data );
428 }
429
430 bool wxClipboard::AddData( wxDataObject *data )
431 {
432 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
433
434 wxCHECK_MSG( data, false, wxT("data is invalid") );
435
436 // we can only store one wxDataObject
437 Clear();
438
439 m_data = data;
440
441 // get formats from wxDataObjects
442 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
443 m_data->GetAllFormats( array );
444
445 // primary selection or clipboard
446 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
447 : g_clipboardAtom;
448
449 // by default provide TIMESTAMP as a target
450 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
451 clipboard,
452 g_timestampAtom,
453 0 );
454
455 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
456 {
457 wxLogTrace( TRACE_CLIPBOARD,
458 wxT("wxClipboard now supports atom %s"),
459 array[i].GetId().c_str() );
460
461 // printf( "added %s\n",
462 // gdk_atom_name( array[i].GetFormatId() ) );
463
464 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
465 clipboard,
466 array[i],
467 0 ); /* what is info ? */
468 }
469
470 delete[] array;
471
472 g_signal_connect (m_clipboardWidget, "selection_get",
473 G_CALLBACK (selection_handler),
474 GUINT_TO_POINTER (gtk_get_current_event_time()) );
475
476 #if wxUSE_THREADS
477 /* disable GUI threads */
478 #endif
479
480 /* Tell the world we offer clipboard data */
481 bool res = (gtk_selection_owner_set( m_clipboardWidget,
482 clipboard,
483 (guint32) GDK_CURRENT_TIME ));
484
485 if (m_usePrimary)
486 m_ownsPrimarySelection = res;
487 else
488 m_ownsClipboard = res;
489
490 #if wxUSE_THREADS
491 /* re-enable GUI threads */
492 #endif
493
494 return res;
495 }
496
497 void wxClipboard::Close()
498 {
499 wxCHECK_RET( m_open, wxT("clipboard not open") );
500
501 m_open = false;
502 }
503
504 bool wxClipboard::IsOpened() const
505 {
506 return m_open;
507 }
508
509 bool wxClipboard::IsSupported( const wxDataFormat& format )
510 {
511 /* reentrance problems */
512 if (m_waiting) return false;
513
514 /* store requested format to be asked for by callbacks */
515 m_targetRequested = format;
516
517 wxLogTrace( TRACE_CLIPBOARD,
518 wxT("wxClipboard:IsSupported: requested format: %s"),
519 format.GetId().c_str() );
520
521 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
522
523 m_formatSupported = false;
524
525 /* perform query. this will set m_formatSupported to
526 true if m_targetRequested is supported.
527 also, we have to wait for the "answer" from the
528 clipboard owner which is an asynchronous process.
529 therefore we set m_waiting = true here and wait
530 until the callback "targets_selection_received"
531 sets it to false */
532
533 m_waiting = true;
534
535 gtk_selection_convert( m_targetsWidget,
536 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
537 : g_clipboardAtom,
538 g_targetsAtom,
539 (guint32) GDK_CURRENT_TIME );
540
541 while (m_waiting) gtk_main_iteration();
542
543 #if wxUSE_UNICODE
544 if (!m_formatSupported && format == wxDataFormat(wxDF_UNICODETEXT))
545 {
546 // Another try with plain STRING format
547 extern GdkAtom g_altTextAtom;
548 return IsSupported(g_altTextAtom);
549 }
550 #endif
551
552 return m_formatSupported;
553 }
554
555 bool wxClipboard::GetData( wxDataObject& data )
556 {
557 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
558
559 /* get formats from wxDataObjects */
560 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
561 data.GetAllFormats( array );
562
563 for (size_t i = 0; i < data.GetFormatCount(); i++)
564 {
565 wxDataFormat format( array[i] );
566
567 wxLogTrace( TRACE_CLIPBOARD,
568 wxT("wxClipboard::GetData: requested format: %s"),
569 format.GetId().c_str() );
570
571 /* is data supported by clipboard ? */
572
573 /* store requested format to be asked for by callbacks */
574 m_targetRequested = format;
575
576 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
577
578 m_formatSupported = false;
579
580 /* perform query. this will set m_formatSupported to
581 true if m_targetRequested is supported.
582 also, we have to wait for the "answer" from the
583 clipboard owner which is an asynchronous process.
584 therefore we set m_waiting = true here and wait
585 until the callback "targets_selection_received"
586 sets it to false */
587
588 m_waiting = true;
589
590 gtk_selection_convert( m_targetsWidget,
591 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
592 : g_clipboardAtom,
593 g_targetsAtom,
594 (guint32) GDK_CURRENT_TIME );
595
596 while (m_waiting) gtk_main_iteration();
597
598 if (!m_formatSupported) continue;
599
600 /* store pointer to data object to be filled up by callbacks */
601 m_receivedData = &data;
602
603 /* store requested format to be asked for by callbacks */
604 m_targetRequested = format;
605
606 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
607
608 /* start query */
609 m_formatSupported = false;
610
611 /* ask for clipboard contents. this will set
612 m_formatSupported to true if m_targetRequested
613 is supported.
614 also, we have to wait for the "answer" from the
615 clipboard owner which is an asynchronous process.
616 therefore we set m_waiting = true here and wait
617 until the callback "targets_selection_received"
618 sets it to false */
619
620 m_waiting = true;
621
622 wxLogTrace( TRACE_CLIPBOARD,
623 wxT("wxClipboard::GetData: format found, start convert") );
624
625 gtk_selection_convert( m_clipboardWidget,
626 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
627 : g_clipboardAtom,
628 m_targetRequested,
629 (guint32) GDK_CURRENT_TIME );
630
631 while (m_waiting) gtk_main_iteration();
632
633 /*
634 Normally this is a true error as we checked for the presence of such
635 data before, but there are applications that may return an empty
636 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
637 which would produce a false error message here, so we check for the
638 size of the string first. In ansi, GetDataSize returns an extra
639 value (for the closing null?), with unicode, the exact number of
640 tokens is given (that is more than 1 for special characters)
641 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
642 */
643 #if wxUSE_UNICODE
644 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
645 #else // !UNICODE
646 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
647 #endif // UNICODE / !UNICODE
648 {
649 wxCHECK_MSG( m_formatSupported, false,
650 wxT("error retrieving data from clipboard") );
651 }
652
653 /* return success */
654 delete[] array;
655 return true;
656 }
657
658 wxLogTrace( TRACE_CLIPBOARD,
659 wxT("wxClipboard::GetData: format not found") );
660
661 /* return failure */
662 delete[] array;
663 return false;
664 }
665
666 #endif
667 // wxUSE_CLIPBOARD