Comitted GTK part of clipboard patch, that
[wxWidgets.git] / src / gtk1 / 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 if (!data->IsSupportedFormat( format )) return;
254
255 int size = data->GetDataSize( format );
256
257 if (size == 0) return;
258
259 void *d = malloc(size);
260
261 // Text data will be in UTF8 in Unicode mode.
262 data->GetDataHere( selection_data->target, d );
263
264 gtk_selection_data_set(
265 selection_data,
266 GDK_SELECTION_TYPE_STRING,
267 8*sizeof(gchar),
268 (unsigned char*) d,
269 size );
270
271 free(d);
272 }
273
274 //-----------------------------------------------------------------------------
275 // wxClipboard
276 //-----------------------------------------------------------------------------
277
278 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
279
280 wxClipboard::wxClipboard()
281 {
282 m_open = FALSE;
283 m_waiting = FALSE;
284
285 m_ownsClipboard = FALSE;
286 m_ownsPrimarySelection = FALSE;
287
288 m_data = (wxDataObject*) NULL;
289 m_receivedData = (wxDataObject*) NULL;
290
291 /* we use m_targetsWidget to query what formats are available */
292
293 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
294 gtk_widget_realize( m_targetsWidget );
295
296 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
297 "selection_received",
298 GTK_SIGNAL_FUNC( targets_selection_received ),
299 (gpointer) this );
300
301 /* we use m_clipboardWidget to get and to offer data */
302
303 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
304 gtk_widget_realize( m_clipboardWidget );
305
306 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
307 "selection_received",
308 GTK_SIGNAL_FUNC( selection_received ),
309 (gpointer) this );
310
311 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
312 "selection_clear_event",
313 GTK_SIGNAL_FUNC( selection_clear_clip ),
314 (gpointer) NULL );
315
316 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
317 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
318
319 m_formatSupported = FALSE;
320 m_targetRequested = 0;
321
322 m_usePrimary = FALSE;
323 }
324
325 wxClipboard::~wxClipboard()
326 {
327 Clear();
328
329 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
330 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
331 }
332
333 void wxClipboard::Clear()
334 {
335 if (m_data)
336 {
337 #if wxUSE_THREADS
338 /* disable GUI threads */
339 #endif
340
341 // As we have data we also own the clipboard. Once we no longer own
342 // it, clear_selection is called which will set m_data to zero
343 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
344 {
345 m_waiting = TRUE;
346
347 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
348 (guint32) GDK_CURRENT_TIME );
349
350 while (m_waiting) gtk_main_iteration();
351 }
352
353 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
354 {
355 m_waiting = TRUE;
356
357 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
358 (guint32) GDK_CURRENT_TIME );
359
360 while (m_waiting) gtk_main_iteration();
361 }
362
363 if (m_data)
364 {
365 delete m_data;
366 m_data = (wxDataObject*) NULL;
367 }
368
369 #if wxUSE_THREADS
370 /* re-enable GUI threads */
371 #endif
372 }
373
374 m_targetRequested = 0;
375 m_formatSupported = FALSE;
376 }
377
378 bool wxClipboard::Open()
379 {
380 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
381
382 m_open = TRUE;
383
384 return TRUE;
385 }
386
387 bool wxClipboard::SetData( wxDataObject *data )
388 {
389 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
390
391 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
392
393 Clear();
394
395 return AddData( data );
396 }
397
398 bool wxClipboard::AddData( wxDataObject *data )
399 {
400 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
401
402 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
403
404 // we can only store one wxDataObject
405 Clear();
406
407 m_data = data;
408
409 // get formats from wxDataObjects
410 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
411 m_data->GetAllFormats( array );
412
413 // primary selection or clipboard
414 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
415 : g_clipboardAtom;
416
417
418 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
419 {
420 wxLogTrace( TRACE_CLIPBOARD,
421 wxT("wxClipboard now supports atom %s"),
422 array[i].GetId().c_str() );
423
424 // printf( "added %s\n",
425 // gdk_atom_name( array[i].GetFormatId() ) );
426
427 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
428 clipboard,
429 array[i],
430 0 ); /* what is info ? */
431 }
432
433 delete[] array;
434
435 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
436 "selection_get",
437 GTK_SIGNAL_FUNC(selection_handler),
438 (gpointer) NULL );
439
440 #if wxUSE_THREADS
441 /* disable GUI threads */
442 #endif
443
444 /* Tell the world we offer clipboard data */
445 bool res = (gtk_selection_owner_set( m_clipboardWidget,
446 clipboard,
447 (guint32) GDK_CURRENT_TIME ));
448
449 if (m_usePrimary)
450 m_ownsPrimarySelection = res;
451 else
452 m_ownsClipboard = res;
453
454 #if wxUSE_THREADS
455 /* re-enable GUI threads */
456 #endif
457
458 return res;
459 }
460
461 void wxClipboard::Close()
462 {
463 wxCHECK_RET( m_open, wxT("clipboard not open") );
464
465 m_open = FALSE;
466 }
467
468 bool wxClipboard::IsOpened() const
469 {
470 return m_open;
471 }
472
473 bool wxClipboard::IsSupported( const wxDataFormat& format )
474 {
475 /* reentrance problems */
476 if (m_waiting) return FALSE;
477
478 /* store requested format to be asked for by callbacks */
479 m_targetRequested = format;
480
481 #if 0
482 wxLogTrace( TRACE_CLIPBOARD,
483 wxT("wxClipboard:IsSupported: requested format: %s"),
484 format.GetId().c_str() );
485 #endif
486
487 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
488
489 m_formatSupported = FALSE;
490
491 /* perform query. this will set m_formatSupported to
492 TRUE if m_targetRequested is supported.
493 also, we have to wait for the "answer" from the
494 clipboard owner which is an asynchronous process.
495 therefore we set m_waiting = TRUE here and wait
496 until the callback "targets_selection_received"
497 sets it to FALSE */
498
499 m_waiting = TRUE;
500
501 gtk_selection_convert( m_targetsWidget,
502 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
503 : g_clipboardAtom,
504 g_targetsAtom,
505 (guint32) GDK_CURRENT_TIME );
506
507 while (m_waiting) gtk_main_iteration();
508
509 #if defined(__WXGTK20__) && wxUSE_UNICODE
510 if (!m_formatSupported && format == wxDataFormat(wxDF_UNICODETEXT))
511 {
512 // Another try with plain STRING format
513 extern GdkAtom g_altTextAtom;
514 return IsSupported(g_altTextAtom);
515 }
516 #endif
517
518 if (!m_formatSupported) return FALSE;
519
520 return TRUE;
521 }
522
523 bool wxClipboard::GetData( wxDataObject& data )
524 {
525 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
526
527 /* get formats from wxDataObjects */
528 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
529 data.GetAllFormats( array );
530
531 for (size_t i = 0; i < data.GetFormatCount(); i++)
532 {
533 wxDataFormat format( array[i] );
534
535 wxLogTrace( TRACE_CLIPBOARD,
536 wxT("wxClipboard::GetData: requested format: %s"),
537 format.GetId().c_str() );
538
539 /* is data supported by clipboard ? */
540
541 /* store requested format to be asked for by callbacks */
542 m_targetRequested = format;
543
544 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
545
546 m_formatSupported = FALSE;
547
548 /* perform query. this will set m_formatSupported to
549 TRUE if m_targetRequested is supported.
550 also, we have to wait for the "answer" from the
551 clipboard owner which is an asynchronous process.
552 therefore we set m_waiting = TRUE here and wait
553 until the callback "targets_selection_received"
554 sets it to FALSE */
555
556 m_waiting = TRUE;
557
558 gtk_selection_convert( m_targetsWidget,
559 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
560 : g_clipboardAtom,
561 g_targetsAtom,
562 (guint32) GDK_CURRENT_TIME );
563
564 while (m_waiting) gtk_main_iteration();
565
566 if (!m_formatSupported) continue;
567
568 /* store pointer to data object to be filled up by callbacks */
569 m_receivedData = &data;
570
571 /* store requested format to be asked for by callbacks */
572 m_targetRequested = format;
573
574 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
575
576 /* start query */
577 m_formatSupported = FALSE;
578
579 /* ask for clipboard contents. this will set
580 m_formatSupported to TRUE if m_targetRequested
581 is supported.
582 also, we have to wait for the "answer" from the
583 clipboard owner which is an asynchronous process.
584 therefore we set m_waiting = TRUE here and wait
585 until the callback "targets_selection_received"
586 sets it to FALSE */
587
588 m_waiting = TRUE;
589
590 wxLogTrace( TRACE_CLIPBOARD,
591 wxT("wxClipboard::GetData: format found, start convert") );
592
593 gtk_selection_convert( m_clipboardWidget,
594 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
595 : g_clipboardAtom,
596 m_targetRequested,
597 (guint32) GDK_CURRENT_TIME );
598
599 while (m_waiting) gtk_main_iteration();
600
601 /* this is a true error as we checked for the presence of such data before */
602 wxCHECK_MSG( m_formatSupported, FALSE, wxT("error retrieving data from clipboard") );
603
604 /* return success */
605 delete[] array;
606 return TRUE;
607 }
608
609 wxLogTrace( TRACE_CLIPBOARD,
610 wxT("wxClipboard::GetData: format not found") );
611
612 /* return failure */
613 delete[] array;
614 return FALSE;
615 }
616
617 #endif
618 // wxUSE_CLIPBOARD
619