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