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