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