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