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