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