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