Made wxGTK compile and link again. Broke wxMSW a little.
[wxWidgets.git] / src / gtk1 / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: clipbrd.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "clipbrd.h"
12 #endif
13
14 #include "wx/clipbrd.h"
15
16 #if wxUSE_CLIPBOARD
17
18 #include "wx/dataobj.h"
19 #include "wx/utils.h"
20 #include "wx/log.h"
21
22 #include "glib.h"
23 #include "gdk/gdk.h"
24 #include "gtk/gtk.h"
25
26 //-----------------------------------------------------------------------------
27 // thread system
28 //-----------------------------------------------------------------------------
29
30 #if wxUSE_THREADS
31 extern void wxapp_install_thread_wakeup();
32 extern void wxapp_uninstall_thread_wakeup();
33 #endif
34
35 //-----------------------------------------------------------------------------
36 // data
37 //-----------------------------------------------------------------------------
38
39 wxClipboard *wxTheClipboard = (wxClipboard*) NULL;
40
41 GdkAtom g_clipboardAtom = 0;
42 GdkAtom g_targetsAtom = 0;
43
44 //-----------------------------------------------------------------------------
45 // reminder
46 //-----------------------------------------------------------------------------
47
48 /* The contents of a selection are returned in a GtkSelectionData
49 structure. selection/target identify the request.
50 type specifies the type of the return; if length < 0, and
51 the data should be ignored. This structure has object semantics -
52 no fields should be modified directly, they should not be created
53 directly, and pointers to them should not be stored beyond the duration of
54 a callback. (If the last is changed, we'll need to add reference
55 counting)
56
57 struct _GtkSelectionData
58 {
59 GdkAtom selection;
60 GdkAtom target;
61 GdkAtom type;
62 gint format;
63 guchar *data;
64 gint length;
65 };
66
67 */
68
69 //-----------------------------------------------------------------------------
70 // "selection_received" for targets
71 //-----------------------------------------------------------------------------
72
73 static void
74 targets_selection_received( GtkWidget *WXUNUSED(widget),
75 GtkSelectionData *selection_data,
76 #if (GTK_MINOR_VERSION > 0)
77 guint32 WXUNUSED(time),
78 #endif
79 wxClipboard *clipboard )
80 {
81 if (!wxTheClipboard)
82 {
83 clipboard->m_waiting = FALSE;
84 return;
85 }
86
87 if (selection_data->length <= 0)
88 {
89 clipboard->m_waiting = FALSE;
90 return;
91 }
92
93 /* make sure we got the data in the correct form */
94 if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
95 {
96 clipboard->m_waiting = FALSE;
97 return;
98 }
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 /*
106 char *name = gdk_atom_name (atoms[i]);
107 if (name) printf( "Format available: %s.\n", name ); */
108
109 if (atoms[i] == clipboard->m_targetRequested)
110 {
111 clipboard->m_waiting = FALSE;
112 clipboard->m_formatSupported = TRUE;
113 return;
114 }
115 }
116
117 clipboard->m_waiting = FALSE;
118 return;
119 }
120
121 //-----------------------------------------------------------------------------
122 // "selection_received" for the actual data
123 //-----------------------------------------------------------------------------
124
125 static void
126 selection_received( GtkWidget *WXUNUSED(widget),
127 GtkSelectionData *selection_data,
128 #if (GTK_MINOR_VERSION > 0)
129 guint32 WXUNUSED(time),
130 #endif
131 wxClipboard *clipboard )
132 {
133 if (!wxTheClipboard)
134 {
135 clipboard->m_waiting = FALSE;
136 return;
137 }
138
139 wxDataObject *data_object = clipboard->m_receivedData;
140
141 if (!data_object)
142 {
143 clipboard->m_waiting = FALSE;
144 return;
145 }
146
147 if (selection_data->length <= 0)
148 {
149 clipboard->m_waiting = FALSE;
150 return;
151 }
152
153 wxDataFormat format( selection_data->target );
154
155 /* make sure we got the data in the correct format */
156 if (!data_object->IsSupportedFormat( format ) )
157 {
158 clipboard->m_waiting = FALSE;
159 return;
160 }
161
162 /* make sure we got the data in the correct form (selection type).
163 if so, copy data to target object */
164
165 switch (format.GetType())
166 {
167 case wxDF_TEXT:
168 {
169 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
170 {
171 clipboard->m_waiting = FALSE;
172 return;
173 }
174
175 wxTextDataObject *text_object = (wxTextDataObject *) data_object;
176
177 wxString text = (const char*) selection_data->data;
178
179 text_object->SetText( text );
180
181 break;
182 }
183
184 case wxDF_BITMAP:
185 {
186 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
187 {
188 clipboard->m_waiting = FALSE;
189 return;
190 }
191
192 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject *) data_object;
193
194 bitmap_object->SetData( (size_t) selection_data->length, (const void*) selection_data->data );
195
196 break;
197 }
198
199 case wxDF_PRIVATE:
200 {
201 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
202 {
203 clipboard->m_waiting = FALSE;
204 return;
205 }
206
207 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
208
209 break;
210 }
211
212 default:
213 {
214 clipboard->m_waiting = FALSE;
215 return;
216 }
217 }
218
219 wxTheClipboard->m_formatSupported = TRUE;
220 clipboard->m_waiting = FALSE;
221 }
222
223 //-----------------------------------------------------------------------------
224 // "selection_clear"
225 //-----------------------------------------------------------------------------
226
227 static gint
228 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
229 {
230 if (!wxTheClipboard) return TRUE;
231
232 if (event->selection == GDK_SELECTION_PRIMARY)
233 {
234 wxTheClipboard->m_ownsPrimarySelection = FALSE;
235 }
236 else
237 if (event->selection == g_clipboardAtom)
238 {
239 wxTheClipboard->m_ownsClipboard = FALSE;
240 }
241 else
242 {
243 wxTheClipboard->m_waiting = FALSE;
244 return FALSE;
245 }
246
247 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
248 (!wxTheClipboard->m_ownsClipboard))
249 {
250 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
251 if (wxTheClipboard->m_data)
252 {
253 delete wxTheClipboard->m_data;
254 wxTheClipboard->m_data = (wxDataObject*) NULL;
255 }
256 }
257
258 wxTheClipboard->m_waiting = FALSE;
259 return TRUE;
260 }
261
262 //-----------------------------------------------------------------------------
263 // selection handler for supplying data
264 //-----------------------------------------------------------------------------
265
266 static void
267 selection_handler( GtkWidget *WXUNUSED(widget), GtkSelectionData *selection_data, gpointer WXUNUSED(data) )
268 {
269 if (!wxTheClipboard) return;
270
271 if (!wxTheClipboard->m_data) return;
272
273 wxDataObject *data = wxTheClipboard->m_data;
274
275 wxDataFormat format( selection_data->target );
276
277 if (!data->IsSupportedFormat( format )) return;
278
279 if (format.GetType() == wxDF_TEXT)
280 {
281 wxTextDataObject *text_object = (wxTextDataObject*) data;
282 wxString text( text_object->GetText() );
283
284 #if wxUSE_UNICODE
285 const wxWX2MBbuf s = text.mbc_str();
286 int len = strlen(s);
287 #else // more efficient in non-Unicode
288 const char *s = text.c_str();
289 int len = (int) text.Length();
290 #endif
291 gtk_selection_data_set(
292 selection_data,
293 GDK_SELECTION_TYPE_STRING,
294 8*sizeof(gchar),
295 (unsigned char*) (const char*) s,
296 len );
297
298 return;
299 }
300
301 if (format.GetType() == wxDF_BITMAP)
302 {
303 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject*) data;
304
305 if (bitmap_object->GetDataSize() == 0) return;
306
307 gtk_selection_data_set(
308 selection_data,
309 GDK_SELECTION_TYPE_STRING,
310 8*sizeof(gchar),
311 (unsigned char*) bitmap_object->GetPngData(),
312 (int) bitmap_object->GetDataSize() );
313
314 return;
315 }
316
317 int size = data->GetDataSize( format );
318
319 if (size == 0) return;
320
321 char *d = new char[size];
322
323 data->GetDataHere( selection_data->target, (void*) d );
324
325 gtk_selection_data_set(
326 selection_data,
327 GDK_SELECTION_TYPE_STRING,
328 8*sizeof(gchar),
329 (unsigned char*) d,
330 size );
331 }
332
333 //-----------------------------------------------------------------------------
334 // wxClipboard
335 //-----------------------------------------------------------------------------
336
337 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
338
339 wxClipboard::wxClipboard()
340 {
341 m_open = FALSE;
342
343 m_ownsClipboard = FALSE;
344 m_ownsPrimarySelection = FALSE;
345
346 m_data = (wxDataObject*) NULL;
347 m_receivedData = (wxDataObject*) NULL;
348
349 /* we use m_targetsWidget to query what formats are available */
350
351 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
352 gtk_widget_realize( m_targetsWidget );
353
354 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
355 "selection_received",
356 GTK_SIGNAL_FUNC( targets_selection_received ),
357 (gpointer) this );
358
359 /* we use m_clipboardWidget to get and to offer data */
360
361 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
362 gtk_widget_realize( m_clipboardWidget );
363
364 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
365 "selection_received",
366 GTK_SIGNAL_FUNC( selection_received ),
367 (gpointer) this );
368
369 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
370 "selection_clear_event",
371 GTK_SIGNAL_FUNC( selection_clear_clip ),
372 (gpointer) NULL );
373
374 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
375 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
376
377 m_formatSupported = FALSE;
378 m_targetRequested = 0;
379
380 m_usePrimary = FALSE;
381 }
382
383 wxClipboard::~wxClipboard()
384 {
385 Clear();
386
387 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
388 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
389 }
390
391 void wxClipboard::Clear()
392 {
393 if (m_data)
394 {
395 #if wxUSE_THREADS
396 /* disable GUI threads */
397 wxapp_uninstall_thread_wakeup();
398 #endif
399
400 /* As we have data we also own the clipboard. Once we no longer own
401 it, clear_selection is called which will set m_data to zero */
402 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
403 {
404 m_waiting = TRUE;
405
406 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, GDK_CURRENT_TIME );
407
408 while (m_waiting) gtk_main_iteration();
409 }
410
411 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
412 {
413 m_waiting = TRUE;
414
415 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME );
416
417 while (m_waiting) gtk_main_iteration();
418 }
419
420 if (m_data)
421 {
422 delete m_data;
423 m_data = (wxDataObject*) NULL;
424 }
425
426 #if wxUSE_THREADS
427 /* re-enable GUI threads */
428 wxapp_install_thread_wakeup();
429 #endif
430 }
431
432 m_targetRequested = 0;
433 m_formatSupported = FALSE;
434 }
435
436 bool wxClipboard::Open()
437 {
438 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
439
440 m_open = TRUE;
441
442 return TRUE;
443 }
444
445 bool wxClipboard::SetData( wxDataObject *data )
446 {
447 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
448
449 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
450
451 Clear();
452
453 return AddData( data );
454 }
455
456 bool wxClipboard::AddData( wxDataObject *data )
457 {
458 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
459
460 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
461
462 /* we can only store one wxDataObject */
463 Clear();
464
465 m_data = data;
466
467 /* This should happen automatically, but to be on the safe side */
468 m_ownsClipboard = FALSE;
469 m_ownsPrimarySelection = FALSE;
470
471 /* get formats from wxDataObjects */
472 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
473 m_data->GetAllFormats( array );
474
475 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
476 {
477 GdkAtom atom = array[i];
478 wxLogDebug( wxT("Clipboard Supported atom %s"), gdk_atom_name( atom ) );
479
480 /* Add handlers if someone requests data. We currently always
481 offer data to the clipboard and the primary selection. Maybe
482 we should make that depend on the usePrimary flag */
483
484 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
485 GDK_SELECTION_PRIMARY,
486 atom,
487 0 ); /* what is info ? */
488
489 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
490 g_clipboardAtom,
491 atom,
492 0 ); /* what is info ? */
493 }
494
495 delete[] array;
496
497 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
498 "selection_get",
499 GTK_SIGNAL_FUNC(selection_handler),
500 (gpointer) NULL );
501
502 #if wxUSE_THREADS
503 /* disable GUI threads */
504 wxapp_uninstall_thread_wakeup();
505 #endif
506
507 /* Tell the world we offer clipboard data */
508 if (!gtk_selection_owner_set( m_clipboardWidget,
509 g_clipboardAtom,
510 GDK_CURRENT_TIME ))
511 {
512 #if wxUSE_THREADS
513 /* re-enable GUI threads */
514 wxapp_install_thread_wakeup();
515 #endif
516 return FALSE;
517 }
518 m_ownsClipboard = TRUE;
519
520 if (!gtk_selection_owner_set( m_clipboardWidget,
521 GDK_SELECTION_PRIMARY,
522 GDK_CURRENT_TIME ))
523 {
524 #if wxUSE_THREADS
525 /* re-enable GUI threads */
526 wxapp_install_thread_wakeup();
527 #endif
528 return FALSE;
529 }
530 m_ownsPrimarySelection = TRUE;
531
532 #if wxUSE_THREADS
533 /* re-enable GUI threads */
534 wxapp_install_thread_wakeup();
535 #endif
536
537 return TRUE;
538 }
539
540 void wxClipboard::Close()
541 {
542 wxCHECK_RET( m_open, wxT("clipboard not open") );
543
544 m_open = FALSE;
545 }
546
547 bool wxClipboard::IsSupported( const wxDataFormat& format )
548 {
549 /* store requested format to be asked for by callbacks */
550
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 alsom 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 ? GDK_SELECTION_PRIMARY : g_clipboardAtom,
569 g_targetsAtom,
570 GDK_CURRENT_TIME );
571
572 while (m_waiting) gtk_main_iteration();
573
574 if (!m_formatSupported) return FALSE;
575
576 return TRUE;
577 }
578
579 bool wxClipboard::GetData( wxDataObject& data )
580 {
581 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
582
583 /* get formats from wxDataObjects */
584 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
585 data.GetAllFormats( array );
586
587 for (size_t i = 0; i < data.GetFormatCount(); i++)
588 {
589 /* is data supported by clipboard ? */
590 if (!IsSupported( array[i] ))
591 continue;
592
593 /* store pointer to data object to be filled up by callbacks */
594 m_receivedData = &data;
595
596 /* store requested format to be asked for by callbacks */
597 m_targetRequested = array[i];
598
599 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
600
601 /* start query */
602 m_formatSupported = FALSE;
603
604 /* ask for clipboard contents. this will set
605 m_formatSupported to TRUE if m_targetRequested
606 is supported.
607 also, we have to wait for the "answer" from the
608 clipboard owner which is an asynchronous process.
609 therefore we set m_waiting = TRUE here and wait
610 until the callback "targets_selection_received"
611 sets it to FALSE */
612
613 m_waiting = TRUE;
614
615 gtk_selection_convert( m_clipboardWidget,
616 m_usePrimary ? GDK_SELECTION_PRIMARY : g_clipboardAtom,
617 m_targetRequested,
618 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 /* return failure */
631 delete[] array;
632 return FALSE;
633 }
634
635 #endif
636 // wxUSE_CLIPBOARD
637