]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/clipbrd.cpp
Better fix
[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
352 wxClipboard::~wxClipboard()
353 {
354 Clear();
355
356 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
357 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
358 }
359
360 void wxClipboard::Clear()
361 {
362 if (m_data)
363 {
364 #if wxUSE_THREADS
365 /* disable GUI threads */
366 #endif
367
368 // As we have data we also own the clipboard. Once we no longer own
369 // it, clear_selection is called which will set m_data to zero
370 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
371 {
372 m_waiting = true;
373
374 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
375 (guint32) GDK_CURRENT_TIME );
376
377 while (m_waiting) gtk_main_iteration();
378 }
379
380 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
381 {
382 m_waiting = true;
383
384 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
385 (guint32) GDK_CURRENT_TIME );
386
387 while (m_waiting) gtk_main_iteration();
388 }
389
390 if (m_data)
391 {
392 delete m_data;
393 m_data = (wxDataObject*) NULL;
394 }
395
396 #if wxUSE_THREADS
397 /* re-enable GUI threads */
398 #endif
399 }
400
401 m_targetRequested = 0;
402 m_formatSupported = false;
403 }
404
405 bool wxClipboard::Open()
406 {
407 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
408
409 m_open = true;
410
411 return true;
412 }
413
414 bool wxClipboard::SetData( wxDataObject *data )
415 {
416 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
417
418 wxCHECK_MSG( data, false, wxT("data is invalid") );
419
420 Clear();
421
422 return AddData( data );
423 }
424
425 bool wxClipboard::AddData( wxDataObject *data )
426 {
427 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
428
429 wxCHECK_MSG( data, false, wxT("data is invalid") );
430
431 // we can only store one wxDataObject
432 Clear();
433
434 m_data = data;
435
436 // get formats from wxDataObjects
437 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
438 m_data->GetAllFormats( array );
439
440 // primary selection or clipboard
441 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
442 : g_clipboardAtom;
443
444 // by default provide TIMESTAMP as a target
445 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
446 clipboard,
447 g_timestampAtom,
448 0 );
449
450 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
451 {
452 wxLogTrace( TRACE_CLIPBOARD,
453 wxT("wxClipboard now supports atom %s"),
454 array[i].GetId().c_str() );
455
456 // printf( "added %s\n",
457 // gdk_atom_name( array[i].GetFormatId() ) );
458
459 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
460 clipboard,
461 array[i],
462 0 ); /* what is info ? */
463 }
464
465 delete[] array;
466
467 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
468 "selection_get",
469 GTK_SIGNAL_FUNC(selection_handler),
470 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
471
472 #if wxUSE_THREADS
473 /* disable GUI threads */
474 #endif
475
476 /* Tell the world we offer clipboard data */
477 bool res = (gtk_selection_owner_set( m_clipboardWidget,
478 clipboard,
479 (guint32) GDK_CURRENT_TIME ));
480
481 if (m_usePrimary)
482 m_ownsPrimarySelection = res;
483 else
484 m_ownsClipboard = res;
485
486 #if wxUSE_THREADS
487 /* re-enable GUI threads */
488 #endif
489
490 return res;
491 }
492
493 void wxClipboard::Close()
494 {
495 wxCHECK_RET( m_open, wxT("clipboard not open") );
496
497 m_open = false;
498 }
499
500 bool wxClipboard::IsOpened() const
501 {
502 return m_open;
503 }
504
505 bool wxClipboard::IsSupported( const wxDataFormat& format )
506 {
507 /* reentrance problems */
508 if (m_waiting) return false;
509
510 /* store requested format to be asked for by callbacks */
511 m_targetRequested = format;
512
513 wxLogTrace( TRACE_CLIPBOARD,
514 wxT("wxClipboard:IsSupported: requested format: %s"),
515 format.GetId().c_str() );
516
517 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
518
519 m_formatSupported = false;
520
521 /* perform query. this will set m_formatSupported to
522 true if m_targetRequested is supported.
523 also, we have to wait for the "answer" from the
524 clipboard owner which is an asynchronous process.
525 therefore we set m_waiting = true here and wait
526 until the callback "targets_selection_received"
527 sets it to false */
528
529 m_waiting = true;
530
531 gtk_selection_convert( m_targetsWidget,
532 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
533 : g_clipboardAtom,
534 g_targetsAtom,
535 (guint32) GDK_CURRENT_TIME );
536
537 while (m_waiting) gtk_main_iteration();
538
539 return m_formatSupported;
540 }
541
542 bool wxClipboard::GetData( wxDataObject& data )
543 {
544 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
545
546 /* get formats from wxDataObjects */
547 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
548 data.GetAllFormats( array );
549
550 for (size_t i = 0; i < data.GetFormatCount(); i++)
551 {
552 wxDataFormat format( array[i] );
553
554 wxLogTrace( TRACE_CLIPBOARD,
555 wxT("wxClipboard::GetData: requested format: %s"),
556 format.GetId().c_str() );
557
558 /* is data supported by clipboard ? */
559
560 /* store requested format to be asked for by callbacks */
561 m_targetRequested = format;
562
563 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
564
565 m_formatSupported = false;
566
567 /* perform query. this will set m_formatSupported to
568 true if m_targetRequested is supported.
569 also, we have to wait for the "answer" from the
570 clipboard owner which is an asynchronous process.
571 therefore we set m_waiting = true here and wait
572 until the callback "targets_selection_received"
573 sets it to false */
574
575 m_waiting = true;
576
577 gtk_selection_convert( m_targetsWidget,
578 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
579 : g_clipboardAtom,
580 g_targetsAtom,
581 (guint32) GDK_CURRENT_TIME );
582
583 while (m_waiting) gtk_main_iteration();
584
585 if (!m_formatSupported) continue;
586
587 /* store pointer to data object to be filled up by callbacks */
588 m_receivedData = &data;
589
590 /* store requested format to be asked for by callbacks */
591 m_targetRequested = format;
592
593 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
594
595 /* start query */
596 m_formatSupported = false;
597
598 /* ask for clipboard contents. this will set
599 m_formatSupported to true if m_targetRequested
600 is supported.
601 also, we have to wait for the "answer" from the
602 clipboard owner which is an asynchronous process.
603 therefore we set m_waiting = true here and wait
604 until the callback "targets_selection_received"
605 sets it to false */
606
607 m_waiting = true;
608
609 wxLogTrace( TRACE_CLIPBOARD,
610 wxT("wxClipboard::GetData: format found, start convert") );
611
612 gtk_selection_convert( m_clipboardWidget,
613 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
614 : g_clipboardAtom,
615 m_targetRequested,
616 (guint32) GDK_CURRENT_TIME );
617
618 while (m_waiting) gtk_main_iteration();
619
620 /*
621 Normally this is a true error as we checked for the presence of such
622 data before, but there are applications that may return an empty
623 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
624 which would produce a false error message here, so we check for the
625 size of the string first. In ansi, GetDataSize returns an extra
626 value (for the closing null?), with unicode, the exact number of
627 tokens is given (that is more than 1 for special characters)
628 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
629 */
630 #if wxUSE_UNICODE
631 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
632 #else // !UNICODE
633 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
634 #endif // UNICODE / !UNICODE
635 {
636 wxCHECK_MSG( m_formatSupported, false,
637 wxT("error retrieving data from clipboard") );
638 }
639
640 /* return success */
641 delete[] array;
642 return true;
643 }
644
645 wxLogTrace( TRACE_CLIPBOARD,
646 wxT("wxClipboard::GetData: format not found") );
647
648 /* return failure */
649 delete[] array;
650 return false;
651 }
652
653 #endif
654 // wxUSE_CLIPBOARD