Include wx/utils.h according to precompiled headers of wx/wx.h (with other minor...
[wxWidgets.git] / src / gtk1 / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/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 #if wxUSE_CLIPBOARD
14
15 #include "wx/clipbrd.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/log.h"
19 #include "wx/utils.h"
20 #endif
21
22 #include "wx/dataobj.h"
23
24 #include <glib.h>
25 #include <gdk/gdk.h>
26 #include <gtk/gtk.h>
27
28 //-----------------------------------------------------------------------------
29 // data
30 //-----------------------------------------------------------------------------
31
32 GdkAtom g_clipboardAtom = 0;
33 GdkAtom g_targetsAtom = 0;
34 GdkAtom g_timestampAtom = 0;
35
36 // the trace mask we use with wxLogTrace() - call
37 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
38 // (there will be a *lot* of them!)
39 static const wxChar *TRACE_CLIPBOARD = _T("clipboard");
40
41 //-----------------------------------------------------------------------------
42 // reminder
43 //-----------------------------------------------------------------------------
44
45 /* The contents of a selection are returned in a GtkSelectionData
46 structure. selection/target identify the request.
47 type specifies the type of the return; if length < 0, and
48 the data should be ignored. This structure has object semantics -
49 no fields should be modified directly, they should not be created
50 directly, and pointers to them should not be stored beyond the duration of
51 a callback. (If the last is changed, we'll need to add reference
52 counting)
53
54 struct _GtkSelectionData
55 {
56 GdkAtom selection;
57 GdkAtom target;
58 GdkAtom type;
59 gint format;
60 guchar *data;
61 gint length;
62 };
63
64 */
65
66 //-----------------------------------------------------------------------------
67 // "selection_received" for targets
68 //-----------------------------------------------------------------------------
69
70 extern "C" {
71 static void
72 targets_selection_received( GtkWidget *WXUNUSED(widget),
73 GtkSelectionData *selection_data,
74 guint32 WXUNUSED(time),
75 wxClipboard *clipboard )
76 {
77 if ( wxTheClipboard && selection_data->length > 0 )
78 {
79 // make sure we got the data in the correct form
80 GdkAtom type = selection_data->type;
81 if ( type != GDK_SELECTION_TYPE_ATOM )
82 {
83 gchar* atom_name = gdk_atom_name(type);
84 if ( strcmp(atom_name, "TARGETS") )
85 {
86 wxLogTrace( TRACE_CLIPBOARD,
87 _T("got unsupported clipboard target") );
88
89 clipboard->m_waiting = false;
90 g_free(atom_name);
91 return;
92 }
93 g_free(atom_name);
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 signal_data )
243 {
244 if (!wxTheClipboard) return;
245
246 if (!wxTheClipboard->m_data) return;
247
248 wxDataObject *data = wxTheClipboard->m_data;
249
250 // ICCCM says that TIMESTAMP is a required atom.
251 // In particular, it satisfies Klipper, which polls
252 // TIMESTAMP to see if the clipboards content has changed.
253 // It shall return the time which was used to set the data.
254 if (selection_data->target == g_timestampAtom)
255 {
256 guint timestamp = GPOINTER_TO_UINT (signal_data);
257 gtk_selection_data_set(selection_data,
258 GDK_SELECTION_TYPE_INTEGER,
259 32,
260 (guchar*)&(timestamp),
261 sizeof(timestamp));
262 wxLogTrace(TRACE_CLIPBOARD,
263 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
264 timestamp);
265 return;
266 }
267
268 wxDataFormat format( selection_data->target );
269
270 #ifdef __WXDEBUG__
271 wxLogTrace(TRACE_CLIPBOARD,
272 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
273 format.GetId().c_str(),
274 wxString::FromAscii(gdk_atom_name(selection_data->target)).c_str(),
275 wxString::FromAscii(gdk_atom_name(selection_data->type)).c_str(),
276 wxString::FromAscii(gdk_atom_name(selection_data->selection)).c_str(),
277 GPOINTER_TO_UINT( signal_data )
278 );
279 #endif
280
281 if (!data->IsSupportedFormat( format )) return;
282
283 int size = data->GetDataSize( format );
284
285 if (size == 0) return;
286
287 void *d = malloc(size);
288
289 // Text data will be in UTF8 in Unicode mode.
290 data->GetDataHere( selection_data->target, d );
291
292 gtk_selection_data_set(
293 selection_data,
294 GDK_SELECTION_TYPE_STRING,
295 8 * sizeof(gchar),
296 (unsigned char*) d,
297 size );
298
299 free(d);
300 }
301 }
302
303 //-----------------------------------------------------------------------------
304 // wxClipboard
305 //-----------------------------------------------------------------------------
306
307 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
308
309 wxClipboard::wxClipboard()
310 {
311 m_open = false;
312 m_waiting = false;
313
314 m_ownsClipboard = false;
315 m_ownsPrimarySelection = false;
316
317 m_data = (wxDataObject*) NULL;
318 m_receivedData = (wxDataObject*) NULL;
319
320 /* we use m_targetsWidget to query what formats are available */
321
322 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
323 gtk_widget_realize( m_targetsWidget );
324
325 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
326 "selection_received",
327 GTK_SIGNAL_FUNC( targets_selection_received ),
328 (gpointer) this );
329
330 /* we use m_clipboardWidget to get and to offer data */
331
332 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
333 gtk_widget_realize( m_clipboardWidget );
334
335 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
336 "selection_received",
337 GTK_SIGNAL_FUNC( selection_received ),
338 (gpointer) this );
339
340 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
341 "selection_clear_event",
342 GTK_SIGNAL_FUNC( selection_clear_clip ),
343 (gpointer) NULL );
344
345 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
346 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
347 if (!g_timestampAtom) g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
348
349 m_formatSupported = false;
350 m_targetRequested = 0;
351
352 m_usePrimary = false;
353 }
354
355 wxClipboard::~wxClipboard()
356 {
357 Clear();
358
359 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
360 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
361 }
362
363 void wxClipboard::Clear()
364 {
365 if (m_data)
366 {
367 #if wxUSE_THREADS
368 /* disable GUI threads */
369 #endif
370
371 // As we have data we also own the clipboard. Once we no longer own
372 // it, clear_selection is called which will set m_data to zero
373 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
374 {
375 m_waiting = true;
376
377 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
378 (guint32) GDK_CURRENT_TIME );
379
380 while (m_waiting) gtk_main_iteration();
381 }
382
383 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
384 {
385 m_waiting = true;
386
387 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
388 (guint32) GDK_CURRENT_TIME );
389
390 while (m_waiting) gtk_main_iteration();
391 }
392
393 if (m_data)
394 {
395 delete m_data;
396 m_data = (wxDataObject*) NULL;
397 }
398
399 #if wxUSE_THREADS
400 /* re-enable GUI threads */
401 #endif
402 }
403
404 m_targetRequested = 0;
405 m_formatSupported = false;
406 }
407
408 bool wxClipboard::Open()
409 {
410 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
411
412 m_open = true;
413
414 return true;
415 }
416
417 bool wxClipboard::SetData( wxDataObject *data )
418 {
419 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
420
421 wxCHECK_MSG( data, false, wxT("data is invalid") );
422
423 Clear();
424
425 return AddData( data );
426 }
427
428 bool wxClipboard::AddData( wxDataObject *data )
429 {
430 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
431
432 wxCHECK_MSG( data, false, wxT("data is invalid") );
433
434 // we can only store one wxDataObject
435 Clear();
436
437 m_data = data;
438
439 // get formats from wxDataObjects
440 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
441 m_data->GetAllFormats( array );
442
443 // primary selection or clipboard
444 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
445 : g_clipboardAtom;
446
447 // by default provide TIMESTAMP as a target
448 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
449 clipboard,
450 g_timestampAtom,
451 0 );
452
453 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
454 {
455 wxLogTrace( TRACE_CLIPBOARD,
456 wxT("wxClipboard now supports atom %s"),
457 array[i].GetId().c_str() );
458
459 // printf( "added %s\n",
460 // gdk_atom_name( array[i].GetFormatId() ) );
461
462 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
463 clipboard,
464 array[i],
465 0 ); /* what is info ? */
466 }
467
468 delete[] array;
469
470 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
471 "selection_get",
472 GTK_SIGNAL_FUNC(selection_handler),
473 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
474
475 #if wxUSE_THREADS
476 /* disable GUI threads */
477 #endif
478
479 /* Tell the world we offer clipboard data */
480 bool res = (gtk_selection_owner_set( m_clipboardWidget,
481 clipboard,
482 (guint32) GDK_CURRENT_TIME ));
483
484 if (m_usePrimary)
485 m_ownsPrimarySelection = res;
486 else
487 m_ownsClipboard = res;
488
489 #if wxUSE_THREADS
490 /* re-enable GUI threads */
491 #endif
492
493 return res;
494 }
495
496 void wxClipboard::Close()
497 {
498 wxCHECK_RET( m_open, wxT("clipboard not open") );
499
500 m_open = false;
501 }
502
503 bool wxClipboard::IsOpened() const
504 {
505 return m_open;
506 }
507
508 bool wxClipboard::IsSupported( const wxDataFormat& format )
509 {
510 /* reentrance problems */
511 if (m_waiting) return false;
512
513 /* store requested format to be asked for by callbacks */
514 m_targetRequested = format;
515
516 wxLogTrace( TRACE_CLIPBOARD,
517 wxT("wxClipboard:IsSupported: requested format: %s"),
518 format.GetId().c_str() );
519
520 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
521
522 m_formatSupported = false;
523
524 /* perform query. this will set m_formatSupported to
525 true if m_targetRequested is supported.
526 also, we have to wait for the "answer" from the
527 clipboard owner which is an asynchronous process.
528 therefore we set m_waiting = true here and wait
529 until the callback "targets_selection_received"
530 sets it to false */
531
532 m_waiting = true;
533
534 gtk_selection_convert( m_targetsWidget,
535 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
536 : g_clipboardAtom,
537 g_targetsAtom,
538 (guint32) GDK_CURRENT_TIME );
539
540 while (m_waiting) gtk_main_iteration();
541
542 return m_formatSupported;
543 }
544
545 bool wxClipboard::GetData( wxDataObject& data )
546 {
547 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
548
549 /* get formats from wxDataObjects */
550 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
551 data.GetAllFormats( array );
552
553 for (size_t i = 0; i < data.GetFormatCount(); i++)
554 {
555 wxDataFormat format( array[i] );
556
557 wxLogTrace( TRACE_CLIPBOARD,
558 wxT("wxClipboard::GetData: requested format: %s"),
559 format.GetId().c_str() );
560
561 /* is data supported by clipboard ? */
562
563 /* store requested format to be asked for by callbacks */
564 m_targetRequested = format;
565
566 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
567
568 m_formatSupported = false;
569
570 /* perform query. this will set m_formatSupported to
571 true if m_targetRequested is supported.
572 also, we have to wait for the "answer" from the
573 clipboard owner which is an asynchronous process.
574 therefore we set m_waiting = true here and wait
575 until the callback "targets_selection_received"
576 sets it to false */
577
578 m_waiting = true;
579
580 gtk_selection_convert( m_targetsWidget,
581 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
582 : g_clipboardAtom,
583 g_targetsAtom,
584 (guint32) GDK_CURRENT_TIME );
585
586 while (m_waiting) gtk_main_iteration();
587
588 if (!m_formatSupported) continue;
589
590 /* store pointer to data object to be filled up by callbacks */
591 m_receivedData = &data;
592
593 /* store requested format to be asked for by callbacks */
594 m_targetRequested = format;
595
596 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
597
598 /* start query */
599 m_formatSupported = false;
600
601 /* ask for clipboard contents. this will set
602 m_formatSupported to true if m_targetRequested
603 is supported.
604 also, we have to wait for the "answer" from the
605 clipboard owner which is an asynchronous process.
606 therefore we set m_waiting = true here and wait
607 until the callback "targets_selection_received"
608 sets it to false */
609
610 m_waiting = true;
611
612 wxLogTrace( TRACE_CLIPBOARD,
613 wxT("wxClipboard::GetData: format found, start convert") );
614
615 gtk_selection_convert( m_clipboardWidget,
616 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
617 : g_clipboardAtom,
618 m_targetRequested,
619 (guint32) GDK_CURRENT_TIME );
620
621 while (m_waiting) gtk_main_iteration();
622
623 /* this is a true error as we checked for the presence of such data before */
624 wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") );
625
626 /* return success */
627 delete[] array;
628 return true;
629 }
630
631 wxLogTrace( TRACE_CLIPBOARD,
632 wxT("wxClipboard::GetData: format not found") );
633
634 /* return failure */
635 delete[] array;
636 return false;
637 }
638
639 #endif
640 // wxUSE_CLIPBOARD