]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/clipbrd.cpp
wxSlider should now display int values,
[wxWidgets.git] / src / gtk1 / clipbrd.cpp
CommitLineData
dc86cb34
RR
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
06cfab17 16#if wxUSE_CLIPBOARD
ac57418f 17
e1ee679c 18#include "wx/dataobj.h"
034be888 19#include "wx/utils.h"
b068c4e8 20#include "wx/log.h"
034be888 21
83624f79
RR
22#include "glib.h"
23#include "gdk/gdk.h"
24#include "gtk/gtk.h"
25
b453e1b2
RR
26//-----------------------------------------------------------------------------
27// thread system
28//-----------------------------------------------------------------------------
29
30#if wxUSE_THREADS
31extern void wxapp_install_thread_wakeup();
32extern void wxapp_uninstall_thread_wakeup();
33#endif
34
dc86cb34
RR
35//-----------------------------------------------------------------------------
36// data
37//-----------------------------------------------------------------------------
38
39wxClipboard *wxTheClipboard = (wxClipboard*) NULL;
40
fd0eed64 41GdkAtom g_clipboardAtom = 0;
b527aac5 42GdkAtom g_targetsAtom = 0;
fd0eed64 43
dc86cb34 44//-----------------------------------------------------------------------------
b527aac5 45// reminder
dc86cb34
RR
46//-----------------------------------------------------------------------------
47
b527aac5
RR
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
57struct _GtkSelectionData
dc86cb34 58{
b527aac5
RR
59 GdkAtom selection;
60 GdkAtom target;
61 GdkAtom type;
62 gint format;
63 guchar *data;
64 gint length;
65};
66
67*/
dc86cb34 68
b527aac5
RR
69//-----------------------------------------------------------------------------
70// "selection_received" for targets
71//-----------------------------------------------------------------------------
72
73static void
74targets_selection_received( GtkWidget *WXUNUSED(widget),
75 GtkSelectionData *selection_data,
034be888
RR
76#if (GTK_MINOR_VERSION > 0)
77 guint32 WXUNUSED(time),
78#endif
b527aac5 79 wxClipboard *clipboard )
dc86cb34 80{
034be888
RR
81 if (!wxTheClipboard)
82 {
83 clipboard->m_waiting = FALSE;
84 return;
85 }
b527aac5 86
034be888
RR
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 }
b527aac5 99
8b53e5a2
RR
100 // the atoms we received, holding a list of targets (= formats)
101 GdkAtom *atoms = (GdkAtom *)selection_data->data;
b527aac5 102
8b53e5a2
RR
103 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
104 {
1dd989e1
RR
105/*
106 char *name = gdk_atom_name (atoms[i]);
ca35e608 107 if (name) printf( "Format available: %s.\n", name ); */
3c1b65a4 108
8b53e5a2
RR
109 if (atoms[i] == clipboard->m_targetRequested)
110 {
034be888 111 clipboard->m_waiting = FALSE;
8b53e5a2
RR
112 clipboard->m_formatSupported = TRUE;
113 return;
114 }
115 }
b527aac5 116
034be888 117 clipboard->m_waiting = FALSE;
8b53e5a2 118 return;
dc86cb34
RR
119}
120
121//-----------------------------------------------------------------------------
b527aac5 122// "selection_received" for the actual data
dc86cb34
RR
123//-----------------------------------------------------------------------------
124
fd0eed64 125static void
b527aac5
RR
126selection_received( GtkWidget *WXUNUSED(widget),
127 GtkSelectionData *selection_data,
034be888
RR
128#if (GTK_MINOR_VERSION > 0)
129 guint32 WXUNUSED(time),
130#endif
b527aac5 131 wxClipboard *clipboard )
dc86cb34 132{
034be888
RR
133 if (!wxTheClipboard)
134 {
135 clipboard->m_waiting = FALSE;
136 return;
137 }
b527aac5 138
8b53e5a2 139 wxDataObject *data_object = clipboard->m_receivedData;
1dd989e1 140
034be888
RR
141 if (!data_object)
142 {
143 clipboard->m_waiting = FALSE;
144 return;
145 }
8b53e5a2 146
034be888
RR
147 if (selection_data->length <= 0)
148 {
149 clipboard->m_waiting = FALSE;
150 return;
151 }
b068c4e8
RR
152
153 wxDataFormat format( selection_data->target );
b527aac5 154
034be888 155 /* make sure we got the data in the correct format */
b068c4e8 156 if (!data_object->IsSupportedFormat( format ) )
034be888
RR
157 {
158 clipboard->m_waiting = FALSE;
159 return;
160 }
e2acb9ae 161
034be888
RR
162 /* make sure we got the data in the correct form (selection type).
163 if so, copy data to target object */
8b53e5a2 164
b068c4e8 165 switch (format.GetType())
8b53e5a2
RR
166 {
167 case wxDF_TEXT:
168 {
034be888
RR
169 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
170 {
171 clipboard->m_waiting = FALSE;
172 return;
173 }
8b53e5a2
RR
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 {
e2acb9ae 186 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
034be888
RR
187 {
188 clipboard->m_waiting = FALSE;
189 return;
190 }
8b53e5a2 191
e2acb9ae
RR
192 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject *) data_object;
193
b068c4e8 194 bitmap_object->SetData( (size_t) selection_data->length, (const void*) selection_data->data );
e2acb9ae 195
8b53e5a2
RR
196 break;
197 }
198
199 case wxDF_PRIVATE:
200 {
034be888
RR
201 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
202 {
203 clipboard->m_waiting = FALSE;
204 return;
205 }
8b53e5a2 206
b068c4e8 207 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
8b53e5a2
RR
208
209 break;
210 }
211
212 default:
213 {
034be888 214 clipboard->m_waiting = FALSE;
8b53e5a2
RR
215 return;
216 }
217 }
218
219 wxTheClipboard->m_formatSupported = TRUE;
034be888 220 clipboard->m_waiting = FALSE;
dc86cb34 221}
fd0eed64
RR
222
223//-----------------------------------------------------------------------------
224// "selection_clear"
225//-----------------------------------------------------------------------------
226
227static gint
aeeb6a44 228selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 229{
8b53e5a2 230 if (!wxTheClipboard) return TRUE;
2830bf19 231
aeeb6a44
RR
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 {
e5ea3f7a 243 wxTheClipboard->m_waiting = FALSE;
aeeb6a44
RR
244 return FALSE;
245 }
246
247 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
248 (!wxTheClipboard->m_ownsClipboard))
249 {
db2d879a 250 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
1dd989e1 251 if (wxTheClipboard->m_data)
0d2a2b60 252 {
1dd989e1
RR
253 delete wxTheClipboard->m_data;
254 wxTheClipboard->m_data = (wxDataObject*) NULL;
0d2a2b60 255 }
aeeb6a44 256 }
fd0eed64 257
e5ea3f7a 258 wxTheClipboard->m_waiting = FALSE;
8b53e5a2 259 return TRUE;
fd0eed64
RR
260}
261
262//-----------------------------------------------------------------------------
263// selection handler for supplying data
264//-----------------------------------------------------------------------------
265
266static void
267selection_handler( GtkWidget *WXUNUSED(widget), GtkSelectionData *selection_data, gpointer WXUNUSED(data) )
268{
8b53e5a2 269 if (!wxTheClipboard) return;
fd0eed64 270
1dd989e1 271 if (!wxTheClipboard->m_data) return;
0d2a2b60 272
1dd989e1 273 wxDataObject *data = wxTheClipboard->m_data;
8b53e5a2 274
b068c4e8
RR
275 wxDataFormat format( selection_data->target );
276
277 if (!data->IsSupportedFormat( format )) return;
1dd989e1 278
b068c4e8 279 if (format.GetType() == wxDF_TEXT)
8b53e5a2 280 {
1dd989e1
RR
281 wxTextDataObject *text_object = (wxTextDataObject*) data;
282 wxString text( text_object->GetText() );
283
93c5dd39 284#if wxUSE_UNICODE
1dd989e1
RR
285 const wxWX2MBbuf s = text.mbc_str();
286 int len = strlen(s);
93c5dd39 287#else // more efficient in non-Unicode
1dd989e1
RR
288 const char *s = text.c_str();
289 int len = (int) text.Length();
93c5dd39 290#endif
1dd989e1
RR
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 );
8b53e5a2 297
1dd989e1
RR
298 return;
299 }
300
b068c4e8 301 if (format.GetType() == wxDF_BITMAP)
1dd989e1
RR
302 {
303 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject*) data;
8b53e5a2 304
b068c4e8 305 if (bitmap_object->GetDataSize() == 0) return;
8b53e5a2 306
1dd989e1
RR
307 gtk_selection_data_set(
308 selection_data,
309 GDK_SELECTION_TYPE_STRING,
310 8*sizeof(gchar),
b068c4e8
RR
311 (unsigned char*) bitmap_object->GetPngData(),
312 (int) bitmap_object->GetDataSize() );
8b53e5a2 313
1dd989e1 314 return;
8b53e5a2 315 }
1dd989e1 316
b068c4e8 317 int size = data->GetDataSize( format );
1dd989e1
RR
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 );
fd0eed64 331}
dc86cb34
RR
332
333//-----------------------------------------------------------------------------
334// wxClipboard
335//-----------------------------------------------------------------------------
336
337IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
338
339wxClipboard::wxClipboard()
340{
8b53e5a2
RR
341 m_open = FALSE;
342
aeeb6a44
RR
343 m_ownsClipboard = FALSE;
344 m_ownsPrimarySelection = FALSE;
345
1dd989e1 346 m_data = (wxDataObject*) NULL;
8b53e5a2 347 m_receivedData = (wxDataObject*) NULL;
99c67c77 348
034be888
RR
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
8b53e5a2
RR
361 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
362 gtk_widget_realize( m_clipboardWidget );
363
034be888
RR
364 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
365 "selection_received",
366 GTK_SIGNAL_FUNC( selection_received ),
367 (gpointer) this );
368
8b53e5a2
RR
369 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
370 "selection_clear_event",
aeeb6a44 371 GTK_SIGNAL_FUNC( selection_clear_clip ),
8b53e5a2 372 (gpointer) NULL );
fd0eed64 373
8b53e5a2 374 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
8b53e5a2
RR
375 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
376
377 m_formatSupported = FALSE;
378 m_targetRequested = 0;
7e2c43b8
RR
379
380 m_usePrimary = FALSE;
dc86cb34
RR
381}
382
383wxClipboard::~wxClipboard()
b527aac5 384{
8b53e5a2 385 Clear();
b527aac5 386
8b53e5a2 387 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
034be888 388 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
b527aac5
RR
389}
390
391void wxClipboard::Clear()
dc86cb34 392{
1dd989e1
RR
393 if (m_data)
394 {
b453e1b2
RR
395#if wxUSE_THREADS
396 /* disable GUI threads */
397 wxapp_uninstall_thread_wakeup();
398#endif
399
8b53e5a2
RR
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 */
aeeb6a44 402 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
8b53e5a2 403 {
e5ea3f7a
RR
404 m_waiting = TRUE;
405
8b53e5a2 406 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, GDK_CURRENT_TIME );
e5ea3f7a
RR
407
408 while (m_waiting) gtk_main_iteration();
8b53e5a2 409 }
db1b4961 410
aeeb6a44
RR
411 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
412 {
e5ea3f7a
RR
413 m_waiting = TRUE;
414
aeeb6a44 415 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME );
e5ea3f7a
RR
416
417 while (m_waiting) gtk_main_iteration();
aeeb6a44
RR
418 }
419
1dd989e1
RR
420 if (m_data)
421 {
422 delete m_data;
423 m_data = (wxDataObject*) NULL;
0d2a2b60 424 }
b453e1b2
RR
425
426#if wxUSE_THREADS
427 /* re-enable GUI threads */
428 wxapp_install_thread_wakeup();
429#endif
8b53e5a2 430 }
b527aac5 431
8b53e5a2 432 m_targetRequested = 0;
8b53e5a2
RR
433 m_formatSupported = FALSE;
434}
435
436bool wxClipboard::Open()
437{
223d09f6 438 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
b527aac5 439
8b53e5a2 440 m_open = TRUE;
cee6127e 441
8b53e5a2 442 return TRUE;
dc86cb34
RR
443}
444
75ce0581 445bool wxClipboard::SetData( wxDataObject *data )
dc86cb34 446{
223d09f6 447 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
75ce0581 448
223d09f6 449 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
db1b4961 450
0d2a2b60 451 Clear();
75ce0581
RR
452
453 return AddData( data );
454}
455
456bool wxClipboard::AddData( wxDataObject *data )
457{
223d09f6 458 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
0d2a2b60 459
223d09f6 460 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
2830bf19 461
b068c4e8 462 /* we can only store one wxDataObject */
1dd989e1 463 Clear();
8b53e5a2 464
1dd989e1
RR
465 m_data = data;
466
75ce0581 467 /* This should happen automatically, but to be on the safe side */
75ce0581
RR
468 m_ownsClipboard = FALSE;
469 m_ownsPrimarySelection = FALSE;
aeeb6a44 470
b068c4e8
RR
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),
d345e841 485 GDK_SELECTION_PRIMARY,
b068c4e8 486 atom,
d345e841
RR
487 0 ); /* what is info ? */
488
b068c4e8 489 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
d345e841 490 g_clipboardAtom,
b068c4e8 491 atom,
d345e841 492 0 ); /* what is info ? */
b068c4e8
RR
493 }
494
495 delete[] array;
d345e841
RR
496
497 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
498 "selection_get",
499 GTK_SIGNAL_FUNC(selection_handler),
500 (gpointer) NULL );
501
b453e1b2
RR
502#if wxUSE_THREADS
503 /* disable GUI threads */
504 wxapp_uninstall_thread_wakeup();
505#endif
506
75ce0581 507 /* Tell the world we offer clipboard data */
75ce0581 508 if (!gtk_selection_owner_set( m_clipboardWidget,
8b53e5a2
RR
509 g_clipboardAtom,
510 GDK_CURRENT_TIME ))
75ce0581 511 {
b453e1b2
RR
512#if wxUSE_THREADS
513 /* re-enable GUI threads */
514 wxapp_install_thread_wakeup();
515#endif
75ce0581
RR
516 return FALSE;
517 }
518 m_ownsClipboard = TRUE;
aeeb6a44 519
75ce0581 520 if (!gtk_selection_owner_set( m_clipboardWidget,
aeeb6a44
RR
521 GDK_SELECTION_PRIMARY,
522 GDK_CURRENT_TIME ))
75ce0581 523 {
b453e1b2
RR
524#if wxUSE_THREADS
525 /* re-enable GUI threads */
526 wxapp_install_thread_wakeup();
527#endif
75ce0581 528 return FALSE;
aeeb6a44 529 }
75ce0581 530 m_ownsPrimarySelection = TRUE;
b453e1b2
RR
531
532#if wxUSE_THREADS
533 /* re-enable GUI threads */
534 wxapp_install_thread_wakeup();
535#endif
75ce0581 536
8b53e5a2
RR
537 return TRUE;
538}
db1b4961 539
8b53e5a2
RR
540void wxClipboard::Close()
541{
223d09f6 542 wxCHECK_RET( m_open, wxT("clipboard not open") );
8b53e5a2
RR
543
544 m_open = FALSE;
dc86cb34
RR
545}
546
f536e0f2
VZ
547bool wxClipboard::IsOpened() const
548{
549 return m_open;
550}
551
e1ee679c 552bool wxClipboard::IsSupported( const wxDataFormat& format )
b527aac5 553{
75ce0581 554 /* store requested format to be asked for by callbacks */
0d2a2b60 555
1dd989e1 556 m_targetRequested = format;
b527aac5 557
223d09f6 558 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
0d2a2b60 559
8b53e5a2 560 m_formatSupported = FALSE;
b527aac5 561
0d2a2b60 562 /* perform query. this will set m_formatSupported to
034be888
RR
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;
ca35e608 571
034be888 572 gtk_selection_convert( m_targetsWidget,
1dd989e1 573 m_usePrimary ? GDK_SELECTION_PRIMARY : g_clipboardAtom,
8b53e5a2
RR
574 g_targetsAtom,
575 GDK_CURRENT_TIME );
ca35e608 576
034be888
RR
577 while (m_waiting) gtk_main_iteration();
578
8b53e5a2 579 if (!m_formatSupported) return FALSE;
75ce0581
RR
580
581 return TRUE;
582}
583
e1ee679c 584bool wxClipboard::GetData( wxDataObject& data )
75ce0581 585{
223d09f6 586 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
75ce0581 587
b068c4e8
RR
588 /* get formats from wxDataObjects */
589 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
590 data.GetAllFormats( array );
75ce0581 591
b068c4e8
RR
592 for (size_t i = 0; i < data.GetFormatCount(); i++)
593 {
594 /* is data supported by clipboard ? */
595 if (!IsSupported( array[i] ))
596 continue;
75ce0581 597
b068c4e8
RR
598 /* store pointer to data object to be filled up by callbacks */
599 m_receivedData = &data;
75ce0581 600
b068c4e8
RR
601 /* store requested format to be asked for by callbacks */
602 m_targetRequested = array[i];
b527aac5 603
b068c4e8 604 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
75ce0581 605
b068c4e8
RR
606 /* start query */
607 m_formatSupported = FALSE;
b527aac5 608
b068c4e8
RR
609 /* ask for clipboard contents. this will set
610 m_formatSupported to TRUE if m_targetRequested
611 is supported.
612 also, we have to wait for the "answer" from the
613 clipboard owner which is an asynchronous process.
614 therefore we set m_waiting = TRUE here and wait
615 until the callback "targets_selection_received"
616 sets it to FALSE */
617
618 m_waiting = TRUE;
619
620 gtk_selection_convert( m_clipboardWidget,
1dd989e1 621 m_usePrimary ? GDK_SELECTION_PRIMARY : g_clipboardAtom,
8b53e5a2
RR
622 m_targetRequested,
623 GDK_CURRENT_TIME );
b527aac5 624
b068c4e8 625 while (m_waiting) gtk_main_iteration();
b527aac5 626
b068c4e8
RR
627 /* this is a true error as we checked for the presence of such data before */
628 wxCHECK_MSG( m_formatSupported, FALSE, wxT("error retrieving data from clipboard") );
629
630 /* return success */
631 delete[] array;
632 return TRUE;
633 }
8b53e5a2 634
b068c4e8
RR
635 /* return failure */
636 delete[] array;
637 return FALSE;
b527aac5
RR
638}
639
ac57418f 640#endif
ac57418f
RR
641 // wxUSE_CLIPBOARD
642