fixed pasting of Unicode strings
[wxWidgets.git] / src / gtk / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
11 #pragma implementation "clipbrd.h"
12 #endif
13
14 // For compilers that support precompilation, includes "wx.h".
15 #include "wx/wxprec.h"
16
17 #include "wx/clipbrd.h"
18
19 #if wxUSE_CLIPBOARD
20
21 #include "wx/dataobj.h"
22 #include "wx/utils.h"
23 #include "wx/log.h"
24
25 #include <glib.h>
26 #include <gdk/gdk.h>
27 #include <gtk/gtk.h>
28
29 //-----------------------------------------------------------------------------
30 // thread system
31 //-----------------------------------------------------------------------------
32
33 #if wxUSE_THREADS
34 #endif
35
36 //-----------------------------------------------------------------------------
37 // data
38 //-----------------------------------------------------------------------------
39
40 GdkAtom g_clipboardAtom = 0;
41 GdkAtom g_targetsAtom = 0;
42
43 #if defined(__WXGTK20__) && wxUSE_UNICODE
44 extern GdkAtom g_altTextAtom;
45 #endif
46
47 // the trace mask we use with wxLogTrace() - call
48 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
49 // (there will be a *lot* of them!)
50 static const wxChar *TRACE_CLIPBOARD = _T("clipboard");
51
52 //-----------------------------------------------------------------------------
53 // reminder
54 //-----------------------------------------------------------------------------
55
56 /* The contents of a selection are returned in a GtkSelectionData
57 structure. selection/target identify the request.
58 type specifies the type of the return; if length < 0, and
59 the data should be ignored. This structure has object semantics -
60 no fields should be modified directly, they should not be created
61 directly, and pointers to them should not be stored beyond the duration of
62 a callback. (If the last is changed, we'll need to add reference
63 counting)
64
65 struct _GtkSelectionData
66 {
67 GdkAtom selection;
68 GdkAtom target;
69 GdkAtom type;
70 gint format;
71 guchar *data;
72 gint length;
73 };
74
75 */
76
77 //-----------------------------------------------------------------------------
78 // "selection_received" for targets
79 //-----------------------------------------------------------------------------
80
81 static void
82 targets_selection_received( GtkWidget *WXUNUSED(widget),
83 GtkSelectionData *selection_data,
84 guint32 WXUNUSED(time),
85 wxClipboard *clipboard )
86 {
87 if ( wxTheClipboard && selection_data->length > 0 )
88 {
89 // make sure we got the data in the correct form
90 GdkAtom type = selection_data->type;
91 if ( type != GDK_SELECTION_TYPE_ATOM )
92 {
93 if ( strcmp(gdk_atom_name(type), "TARGETS") )
94 {
95 wxLogTrace( TRACE_CLIPBOARD,
96 _T("got unsupported clipboard target") );
97
98 clipboard->m_waiting = FALSE;
99 return;
100 }
101 }
102
103 #ifdef __WXDEBUG__
104 wxDataFormat clip( selection_data->selection );
105 wxLogTrace( TRACE_CLIPBOARD,
106 wxT("selection received for targets, clipboard %s"),
107 clip.GetId().c_str() );
108 #endif // __WXDEBUG__
109
110 // the atoms we received, holding a list of targets (= formats)
111 GdkAtom *atoms = (GdkAtom *)selection_data->data;
112
113 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
114 {
115 wxDataFormat format( atoms[i] );
116
117 wxLogTrace( TRACE_CLIPBOARD,
118 wxT("selection received for targets, format %s"),
119 format.GetId().c_str() );
120
121 // printf( "format %s requested %s\n",
122 // gdk_atom_name( atoms[i] ),
123 // gdk_atom_name( clipboard->m_targetRequested ) );
124
125 if (format == clipboard->m_targetRequested)
126 {
127 clipboard->m_waiting = FALSE;
128 clipboard->m_formatSupported = TRUE;
129 return;
130 }
131 }
132 }
133
134 clipboard->m_waiting = FALSE;
135 }
136
137 //-----------------------------------------------------------------------------
138 // "selection_received" for the actual data
139 //-----------------------------------------------------------------------------
140
141 static void
142 selection_received( GtkWidget *WXUNUSED(widget),
143 GtkSelectionData *selection_data,
144 guint32 WXUNUSED(time),
145 wxClipboard *clipboard )
146 {
147 if (!wxTheClipboard)
148 {
149 clipboard->m_waiting = FALSE;
150 return;
151 }
152
153 wxDataObject *data_object = clipboard->m_receivedData;
154
155 if (!data_object)
156 {
157 clipboard->m_waiting = FALSE;
158 return;
159 }
160
161 if (selection_data->length <= 0)
162 {
163 clipboard->m_waiting = FALSE;
164 return;
165 }
166
167 wxDataFormat format( selection_data->target );
168
169 // make sure we got the data in the correct format
170 if (!data_object->IsSupportedFormat( format ) )
171 {
172 clipboard->m_waiting = FALSE;
173 return;
174 }
175
176 #if 0
177 This seems to cause problems somehow
178 // make sure we got the data in the correct form (selection type).
179 // if so, copy data to target object
180 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
181 {
182 clipboard->m_waiting = FALSE;
183 return;
184 }
185 #endif
186
187 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
188
189 wxTheClipboard->m_formatSupported = TRUE;
190 clipboard->m_waiting = FALSE;
191 }
192
193 //-----------------------------------------------------------------------------
194 // "selection_clear"
195 //-----------------------------------------------------------------------------
196
197 static gint
198 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
199 {
200 if (!wxTheClipboard) return TRUE;
201
202 if (event->selection == GDK_SELECTION_PRIMARY)
203 {
204 wxTheClipboard->m_ownsPrimarySelection = FALSE;
205 }
206 else
207 if (event->selection == g_clipboardAtom)
208 {
209 wxTheClipboard->m_ownsClipboard = FALSE;
210 }
211 else
212 {
213 wxTheClipboard->m_waiting = FALSE;
214 return FALSE;
215 }
216
217 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
218 (!wxTheClipboard->m_ownsClipboard))
219 {
220 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
221 if (wxTheClipboard->m_data)
222 {
223 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
224
225 delete wxTheClipboard->m_data;
226 wxTheClipboard->m_data = (wxDataObject*) NULL;
227 }
228 }
229
230 wxTheClipboard->m_waiting = FALSE;
231 return TRUE;
232 }
233
234 //-----------------------------------------------------------------------------
235 // selection handler for supplying data
236 //-----------------------------------------------------------------------------
237
238 static void
239 selection_handler( GtkWidget *WXUNUSED(widget),
240 GtkSelectionData *selection_data,
241 guint WXUNUSED(info),
242 guint WXUNUSED(time),
243 gpointer WXUNUSED(data) )
244 {
245 if (!wxTheClipboard) return;
246
247 if (!wxTheClipboard->m_data) return;
248
249 wxDataObject *data = wxTheClipboard->m_data;
250
251 wxDataFormat format( selection_data->target );
252
253 #ifdef __WXDEBUG__
254 wxLogTrace(TRACE_CLIPBOARD,
255 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s"),
256 format.GetId().c_str(),
257 wxString::FromAscii(gdk_atom_name(selection_data->target)).c_str(),
258 wxString::FromAscii(gdk_atom_name(selection_data->type)).c_str(),
259 wxString::FromAscii(gdk_atom_name(selection_data->selection)).c_str()
260 );
261 #endif
262
263 if (!data->IsSupportedFormat( format )) return;
264
265 int size = data->GetDataSize( format );
266
267 if (size == 0) return;
268
269 void *d = malloc(size);
270
271 // Text data will be in UTF8 in Unicode mode.
272 data->GetDataHere( selection_data->target, d );
273
274 #ifdef __WXGTK20__
275 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
276 // would show as UTF-8 data interpreted as latin1 (?) in other
277 // GTK+ apps if we used gtk_selection_data_set()
278 if (format == wxDataFormat(wxDF_UNICODETEXT))
279 {
280 gtk_selection_data_set_text(
281 selection_data,
282 (const gchar*)d,
283 size);
284 }
285 else
286 #endif
287 {
288 gtk_selection_data_set(
289 selection_data,
290 GDK_SELECTION_TYPE_STRING,
291 8*sizeof(gchar),
292 (unsigned char*) d,
293 size );
294 }
295
296 free(d);
297 }
298
299 //-----------------------------------------------------------------------------
300 // wxClipboard
301 //-----------------------------------------------------------------------------
302
303 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
304
305 wxClipboard::wxClipboard()
306 {
307 m_open = FALSE;
308 m_waiting = FALSE;
309
310 m_ownsClipboard = FALSE;
311 m_ownsPrimarySelection = FALSE;
312
313 m_data = (wxDataObject*) NULL;
314 m_receivedData = (wxDataObject*) NULL;
315
316 /* we use m_targetsWidget to query what formats are available */
317
318 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
319 gtk_widget_realize( m_targetsWidget );
320
321 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
322 "selection_received",
323 GTK_SIGNAL_FUNC( targets_selection_received ),
324 (gpointer) this );
325
326 /* we use m_clipboardWidget to get and to offer data */
327
328 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
329 gtk_widget_realize( m_clipboardWidget );
330
331 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
332 "selection_received",
333 GTK_SIGNAL_FUNC( selection_received ),
334 (gpointer) this );
335
336 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
337 "selection_clear_event",
338 GTK_SIGNAL_FUNC( selection_clear_clip ),
339 (gpointer) NULL );
340
341 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
342 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
343
344 m_formatSupported = FALSE;
345 m_targetRequested = 0;
346
347 m_usePrimary = FALSE;
348 }
349
350 wxClipboard::~wxClipboard()
351 {
352 Clear();
353
354 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
355 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
356 }
357
358 void wxClipboard::Clear()
359 {
360 if (m_data)
361 {
362 #if wxUSE_THREADS
363 /* disable GUI threads */
364 #endif
365
366 // As we have data we also own the clipboard. Once we no longer own
367 // it, clear_selection is called which will set m_data to zero
368 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
369 {
370 m_waiting = TRUE;
371
372 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
373 (guint32) GDK_CURRENT_TIME );
374
375 while (m_waiting) gtk_main_iteration();
376 }
377
378 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
379 {
380 m_waiting = TRUE;
381
382 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
383 (guint32) GDK_CURRENT_TIME );
384
385 while (m_waiting) gtk_main_iteration();
386 }
387
388 if (m_data)
389 {
390 delete m_data;
391 m_data = (wxDataObject*) NULL;
392 }
393
394 #if wxUSE_THREADS
395 /* re-enable GUI threads */
396 #endif
397 }
398
399 m_targetRequested = 0;
400 m_formatSupported = FALSE;
401 }
402
403 bool wxClipboard::Open()
404 {
405 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
406
407 m_open = TRUE;
408
409 return TRUE;
410 }
411
412 bool wxClipboard::SetData( wxDataObject *data )
413 {
414 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
415
416 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
417
418 Clear();
419
420 return AddData( data );
421 }
422
423 bool wxClipboard::AddData( wxDataObject *data )
424 {
425 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
426
427 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
428
429 // we can only store one wxDataObject
430 Clear();
431
432 m_data = data;
433
434 // get formats from wxDataObjects
435 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
436 m_data->GetAllFormats( array );
437
438 // primary selection or clipboard
439 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
440 : g_clipboardAtom;
441
442
443 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
444 {
445 wxLogTrace( TRACE_CLIPBOARD,
446 wxT("wxClipboard now supports atom %s"),
447 array[i].GetId().c_str() );
448
449 // printf( "added %s\n",
450 // gdk_atom_name( array[i].GetFormatId() ) );
451
452 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
453 clipboard,
454 array[i],
455 0 ); /* what is info ? */
456 }
457
458 delete[] array;
459
460 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
461 "selection_get",
462 GTK_SIGNAL_FUNC(selection_handler),
463 (gpointer) NULL );
464
465 #if wxUSE_THREADS
466 /* disable GUI threads */
467 #endif
468
469 /* Tell the world we offer clipboard data */
470 bool res = (gtk_selection_owner_set( m_clipboardWidget,
471 clipboard,
472 (guint32) GDK_CURRENT_TIME ));
473
474 if (m_usePrimary)
475 m_ownsPrimarySelection = res;
476 else
477 m_ownsClipboard = res;
478
479 #if wxUSE_THREADS
480 /* re-enable GUI threads */
481 #endif
482
483 return res;
484 }
485
486 void wxClipboard::Close()
487 {
488 wxCHECK_RET( m_open, wxT("clipboard not open") );
489
490 m_open = FALSE;
491 }
492
493 bool wxClipboard::IsOpened() const
494 {
495 return m_open;
496 }
497
498 bool wxClipboard::IsSupported( const wxDataFormat& format )
499 {
500 /* reentrance problems */
501 if (m_waiting) return FALSE;
502
503 /* store requested format to be asked for by callbacks */
504 m_targetRequested = format;
505
506 wxLogTrace( TRACE_CLIPBOARD,
507 wxT("wxClipboard:IsSupported: requested format: %s"),
508 format.GetId().c_str() );
509
510 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
511
512 m_formatSupported = FALSE;
513
514 /* perform query. this will set m_formatSupported to
515 TRUE if m_targetRequested is supported.
516 also, we have to wait for the "answer" from the
517 clipboard owner which is an asynchronous process.
518 therefore we set m_waiting = TRUE here and wait
519 until the callback "targets_selection_received"
520 sets it to FALSE */
521
522 m_waiting = TRUE;
523
524 gtk_selection_convert( m_targetsWidget,
525 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
526 : g_clipboardAtom,
527 g_targetsAtom,
528 (guint32) GDK_CURRENT_TIME );
529
530 while (m_waiting) gtk_main_iteration();
531
532 #if defined(__WXGTK20__) && wxUSE_UNICODE
533 if (!m_formatSupported && format == wxDataFormat(wxDF_UNICODETEXT))
534 {
535 // Another try with plain STRING format
536 extern GdkAtom g_altTextAtom;
537 return IsSupported(g_altTextAtom);
538 }
539 #endif
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 /* this is a true error as we checked for the presence of such data before */
623 wxCHECK_MSG( m_formatSupported, FALSE, wxT("error retrieving data from clipboard") );
624
625 /* return success */
626 delete[] array;
627 return TRUE;
628 }
629
630 wxLogTrace( TRACE_CLIPBOARD,
631 wxT("wxClipboard::GetData: format not found") );
632
633 /* return failure */
634 delete[] array;
635 return FALSE;
636 }
637
638 #endif
639 // wxUSE_CLIPBOARD
640