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