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