]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/clipbrd.cpp
Tweaks for demos on MacOSX
[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 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #include "wx/clipbrd.h"
14
15 #if wxUSE_CLIPBOARD
16
17 #include "wx/dataobj.h"
18 #include "wx/utils.h"
19 #include "wx/log.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 #endif
31
32 //-----------------------------------------------------------------------------
33 // data
34 //-----------------------------------------------------------------------------
35
36 GdkAtom g_clipboardAtom = 0;
37 GdkAtom g_targetsAtom = 0;
38
39 // the trace mask we use with wxLogTrace() - call
40 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
41 // (there will be a *lot* of them!)
42 static const wxChar *TRACE_CLIPBOARD = _T("clipboard");
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 extern "C" {
74 static void
75 targets_selection_received( GtkWidget *WXUNUSED(widget),
76 GtkSelectionData *selection_data,
77 guint32 WXUNUSED(time),
78 wxClipboard *clipboard )
79 {
80 if ( wxTheClipboard && selection_data->length > 0 )
81 {
82 // make sure we got the data in the correct form
83 GdkAtom type = selection_data->type;
84 if ( type != GDK_SELECTION_TYPE_ATOM )
85 {
86 if ( strcmp(gdk_atom_name(type), "TARGETS") )
87 {
88 wxLogTrace( TRACE_CLIPBOARD,
89 _T("got unsupported clipboard target") );
90
91 clipboard->m_waiting = FALSE;
92 return;
93 }
94 }
95
96 #ifdef __WXDEBUG__
97 wxDataFormat clip( selection_data->selection );
98 wxLogTrace( TRACE_CLIPBOARD,
99 wxT("selection received for targets, clipboard %s"),
100 clip.GetId().c_str() );
101 #endif // __WXDEBUG__
102
103 // the atoms we received, holding a list of targets (= formats)
104 GdkAtom *atoms = (GdkAtom *)selection_data->data;
105
106 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
107 {
108 wxDataFormat format( atoms[i] );
109
110 wxLogTrace( TRACE_CLIPBOARD,
111 wxT("selection received for targets, format %s"),
112 format.GetId().c_str() );
113
114 // printf( "format %s requested %s\n",
115 // gdk_atom_name( atoms[i] ),
116 // gdk_atom_name( clipboard->m_targetRequested ) );
117
118 if (format == clipboard->m_targetRequested)
119 {
120 clipboard->m_waiting = FALSE;
121 clipboard->m_formatSupported = TRUE;
122 return;
123 }
124 }
125 }
126
127 clipboard->m_waiting = FALSE;
128 }
129 }
130
131 //-----------------------------------------------------------------------------
132 // "selection_received" for the actual data
133 //-----------------------------------------------------------------------------
134
135 extern "C" {
136 static void
137 selection_received( GtkWidget *WXUNUSED(widget),
138 GtkSelectionData *selection_data,
139 guint32 WXUNUSED(time),
140 wxClipboard *clipboard )
141 {
142 if (!wxTheClipboard)
143 {
144 clipboard->m_waiting = FALSE;
145 return;
146 }
147
148 wxDataObject *data_object = clipboard->m_receivedData;
149
150 if (!data_object)
151 {
152 clipboard->m_waiting = FALSE;
153 return;
154 }
155
156 if (selection_data->length <= 0)
157 {
158 clipboard->m_waiting = FALSE;
159 return;
160 }
161
162 wxDataFormat format( selection_data->target );
163
164 // make sure we got the data in the correct format
165 if (!data_object->IsSupportedFormat( format ) )
166 {
167 clipboard->m_waiting = FALSE;
168 return;
169 }
170
171 #if 0
172 This seems to cause problems somehow
173 // make sure we got the data in the correct form (selection type).
174 // if so, copy data to target object
175 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
176 {
177 clipboard->m_waiting = FALSE;
178 return;
179 }
180 #endif
181
182 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
183
184 wxTheClipboard->m_formatSupported = TRUE;
185 clipboard->m_waiting = FALSE;
186 }
187 }
188
189 //-----------------------------------------------------------------------------
190 // "selection_clear"
191 //-----------------------------------------------------------------------------
192
193 extern "C" {
194 static gint
195 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
196 {
197 if (!wxTheClipboard) return TRUE;
198
199 if (event->selection == GDK_SELECTION_PRIMARY)
200 {
201 wxTheClipboard->m_ownsPrimarySelection = FALSE;
202 }
203 else
204 if (event->selection == g_clipboardAtom)
205 {
206 wxTheClipboard->m_ownsClipboard = FALSE;
207 }
208 else
209 {
210 wxTheClipboard->m_waiting = FALSE;
211 return FALSE;
212 }
213
214 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
215 (!wxTheClipboard->m_ownsClipboard))
216 {
217 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
218 if (wxTheClipboard->m_data)
219 {
220 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
221
222 delete wxTheClipboard->m_data;
223 wxTheClipboard->m_data = (wxDataObject*) NULL;
224 }
225 }
226
227 wxTheClipboard->m_waiting = FALSE;
228 return TRUE;
229 }
230 }
231
232 //-----------------------------------------------------------------------------
233 // selection handler for supplying data
234 //-----------------------------------------------------------------------------
235
236 extern "C" {
237 static void
238 selection_handler( GtkWidget *WXUNUSED(widget),
239 GtkSelectionData *selection_data,
240 guint WXUNUSED(info),
241 guint WXUNUSED(time),
242 gpointer WXUNUSED(data) )
243 {
244 if (!wxTheClipboard) return;
245
246 if (!wxTheClipboard->m_data) return;
247
248 wxDataObject *data = wxTheClipboard->m_data;
249
250 wxDataFormat format( selection_data->target );
251
252 #ifdef __WXDEBUG__
253 wxLogTrace(TRACE_CLIPBOARD,
254 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s"),
255 format.GetId().c_str(),
256 wxString::FromAscii(gdk_atom_name(selection_data->target)).c_str(),
257 wxString::FromAscii(gdk_atom_name(selection_data->type)).c_str(),
258 wxString::FromAscii(gdk_atom_name(selection_data->selection)).c_str()
259 );
260 #endif
261
262 if (!data->IsSupportedFormat( format )) return;
263
264 int size = data->GetDataSize( format );
265
266 if (size == 0) return;
267
268 void *d = malloc(size);
269
270 // Text data will be in UTF8 in Unicode mode.
271 data->GetDataHere( selection_data->target, d );
272
273 gtk_selection_data_set(
274 selection_data,
275 GDK_SELECTION_TYPE_STRING,
276 8 * sizeof(gchar),
277 (unsigned char*) d,
278 size-1 );
279
280 free(d);
281 }
282 }
283
284 //-----------------------------------------------------------------------------
285 // wxClipboard
286 //-----------------------------------------------------------------------------
287
288 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
289
290 wxClipboard::wxClipboard()
291 {
292 m_open = FALSE;
293 m_waiting = FALSE;
294
295 m_ownsClipboard = FALSE;
296 m_ownsPrimarySelection = FALSE;
297
298 m_data = (wxDataObject*) NULL;
299 m_receivedData = (wxDataObject*) NULL;
300
301 /* we use m_targetsWidget to query what formats are available */
302
303 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
304 gtk_widget_realize( m_targetsWidget );
305
306 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
307 "selection_received",
308 GTK_SIGNAL_FUNC( targets_selection_received ),
309 (gpointer) this );
310
311 /* we use m_clipboardWidget to get and to offer data */
312
313 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
314 gtk_widget_realize( m_clipboardWidget );
315
316 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
317 "selection_received",
318 GTK_SIGNAL_FUNC( selection_received ),
319 (gpointer) this );
320
321 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
322 "selection_clear_event",
323 GTK_SIGNAL_FUNC( selection_clear_clip ),
324 (gpointer) NULL );
325
326 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
327 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
328
329 m_formatSupported = FALSE;
330 m_targetRequested = 0;
331
332 m_usePrimary = FALSE;
333 }
334
335 wxClipboard::~wxClipboard()
336 {
337 Clear();
338
339 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
340 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
341 }
342
343 void wxClipboard::Clear()
344 {
345 if (m_data)
346 {
347 #if wxUSE_THREADS
348 /* disable GUI threads */
349 #endif
350
351 // As we have data we also own the clipboard. Once we no longer own
352 // it, clear_selection is called which will set m_data to zero
353 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
354 {
355 m_waiting = TRUE;
356
357 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
358 (guint32) GDK_CURRENT_TIME );
359
360 while (m_waiting) gtk_main_iteration();
361 }
362
363 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
364 {
365 m_waiting = TRUE;
366
367 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
368 (guint32) GDK_CURRENT_TIME );
369
370 while (m_waiting) gtk_main_iteration();
371 }
372
373 if (m_data)
374 {
375 delete m_data;
376 m_data = (wxDataObject*) NULL;
377 }
378
379 #if wxUSE_THREADS
380 /* re-enable GUI threads */
381 #endif
382 }
383
384 m_targetRequested = 0;
385 m_formatSupported = FALSE;
386 }
387
388 bool wxClipboard::Open()
389 {
390 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
391
392 m_open = TRUE;
393
394 return TRUE;
395 }
396
397 bool wxClipboard::SetData( wxDataObject *data )
398 {
399 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
400
401 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
402
403 Clear();
404
405 return AddData( data );
406 }
407
408 bool wxClipboard::AddData( wxDataObject *data )
409 {
410 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
411
412 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
413
414 // we can only store one wxDataObject
415 Clear();
416
417 m_data = data;
418
419 // get formats from wxDataObjects
420 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
421 m_data->GetAllFormats( array );
422
423 // primary selection or clipboard
424 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
425 : g_clipboardAtom;
426
427
428 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
429 {
430 wxLogTrace( TRACE_CLIPBOARD,
431 wxT("wxClipboard now supports atom %s"),
432 array[i].GetId().c_str() );
433
434 // printf( "added %s\n",
435 // gdk_atom_name( array[i].GetFormatId() ) );
436
437 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
438 clipboard,
439 array[i],
440 0 ); /* what is info ? */
441 }
442
443 delete[] array;
444
445 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
446 "selection_get",
447 GTK_SIGNAL_FUNC(selection_handler),
448 (gpointer) NULL );
449
450 #if wxUSE_THREADS
451 /* disable GUI threads */
452 #endif
453
454 /* Tell the world we offer clipboard data */
455 bool res = (gtk_selection_owner_set( m_clipboardWidget,
456 clipboard,
457 (guint32) GDK_CURRENT_TIME ));
458
459 if (m_usePrimary)
460 m_ownsPrimarySelection = res;
461 else
462 m_ownsClipboard = res;
463
464 #if wxUSE_THREADS
465 /* re-enable GUI threads */
466 #endif
467
468 return res;
469 }
470
471 void wxClipboard::Close()
472 {
473 wxCHECK_RET( m_open, wxT("clipboard not open") );
474
475 m_open = FALSE;
476 }
477
478 bool wxClipboard::IsOpened() const
479 {
480 return m_open;
481 }
482
483 bool wxClipboard::IsSupported( const wxDataFormat& format )
484 {
485 /* reentrance problems */
486 if (m_waiting) return FALSE;
487
488 /* store requested format to be asked for by callbacks */
489 m_targetRequested = format;
490
491 wxLogTrace( TRACE_CLIPBOARD,
492 wxT("wxClipboard:IsSupported: requested format: %s"),
493 format.GetId().c_str() );
494
495 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
496
497 m_formatSupported = FALSE;
498
499 /* perform query. this will set m_formatSupported to
500 TRUE if m_targetRequested is supported.
501 also, we have to wait for the "answer" from the
502 clipboard owner which is an asynchronous process.
503 therefore we set m_waiting = TRUE here and wait
504 until the callback "targets_selection_received"
505 sets it to FALSE */
506
507 m_waiting = TRUE;
508
509 gtk_selection_convert( m_targetsWidget,
510 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
511 : g_clipboardAtom,
512 g_targetsAtom,
513 (guint32) GDK_CURRENT_TIME );
514
515 while (m_waiting) gtk_main_iteration();
516
517 return m_formatSupported;
518 }
519
520 bool wxClipboard::GetData( wxDataObject& data )
521 {
522 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
523
524 /* get formats from wxDataObjects */
525 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
526 data.GetAllFormats( array );
527
528 for (size_t i = 0; i < data.GetFormatCount(); i++)
529 {
530 wxDataFormat format( array[i] );
531
532 wxLogTrace( TRACE_CLIPBOARD,
533 wxT("wxClipboard::GetData: requested format: %s"),
534 format.GetId().c_str() );
535
536 /* is data supported by clipboard ? */
537
538 /* store requested format to be asked for by callbacks */
539 m_targetRequested = format;
540
541 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
542
543 m_formatSupported = FALSE;
544
545 /* perform query. this will set m_formatSupported to
546 TRUE if m_targetRequested is supported.
547 also, we have to wait for the "answer" from the
548 clipboard owner which is an asynchronous process.
549 therefore we set m_waiting = TRUE here and wait
550 until the callback "targets_selection_received"
551 sets it to FALSE */
552
553 m_waiting = TRUE;
554
555 gtk_selection_convert( m_targetsWidget,
556 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
557 : g_clipboardAtom,
558 g_targetsAtom,
559 (guint32) GDK_CURRENT_TIME );
560
561 while (m_waiting) gtk_main_iteration();
562
563 if (!m_formatSupported) continue;
564
565 /* store pointer to data object to be filled up by callbacks */
566 m_receivedData = &data;
567
568 /* store requested format to be asked for by callbacks */
569 m_targetRequested = format;
570
571 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
572
573 /* start query */
574 m_formatSupported = FALSE;
575
576 /* ask for clipboard contents. this will set
577 m_formatSupported to TRUE if m_targetRequested
578 is supported.
579 also, we have to wait for the "answer" from the
580 clipboard owner which is an asynchronous process.
581 therefore we set m_waiting = TRUE here and wait
582 until the callback "targets_selection_received"
583 sets it to FALSE */
584
585 m_waiting = TRUE;
586
587 wxLogTrace( TRACE_CLIPBOARD,
588 wxT("wxClipboard::GetData: format found, start convert") );
589
590 gtk_selection_convert( m_clipboardWidget,
591 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
592 : g_clipboardAtom,
593 m_targetRequested,
594 (guint32) GDK_CURRENT_TIME );
595
596 while (m_waiting) gtk_main_iteration();
597
598 /* this is a true error as we checked for the presence of such data before */
599 wxCHECK_MSG( m_formatSupported, FALSE, wxT("error retrieving data from clipboard") );
600
601 /* return success */
602 delete[] array;
603 return TRUE;
604 }
605
606 wxLogTrace( TRACE_CLIPBOARD,
607 wxT("wxClipboard::GetData: format not found") );
608
609 /* return failure */
610 delete[] array;
611 return FALSE;
612 }
613
614 #endif
615 // wxUSE_CLIPBOARD
616