add other known types for gdk_pixbuf_save()
[wxWidgets.git] / src / x11 / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/x11/clipbrd.cpp
3 // Purpose: Clipboard functionality
4 // Author: Robert Roebling
5 // Created:
6 // Copyright: (c) Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // for compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_CLIPBOARD
14
15 #include "wx/clipbrd.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/log.h"
19 #include "wx/utils.h"
20 #include "wx/dataobj.h"
21 #endif
22
23 #include "wx/x11/private.h"
24
25 //-----------------------------------------------------------------------------
26 // data
27 //-----------------------------------------------------------------------------
28
29 #if !wxUSE_NANOX
30 Atom g_clipboardAtom = 0;
31 Atom g_targetsAtom = 0;
32 #endif
33
34 // avoid warnings about unused static variable (notice that we still use it
35 // even in release build if the compiler doesn't support variadic macros)
36 #if defined(__WXDEBUG__) || !defined(HAVE_VARIADIC_MACROS)
37
38 // the trace mask we use with wxLogTrace() - call
39 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
40 // (there will be a *lot* of them!)
41 static const wxChar *TRACE_CLIPBOARD = wxT("clipboard");
42
43 #endif // __WXDEBUG__
44
45 //-----------------------------------------------------------------------------
46 // reminder
47 //-----------------------------------------------------------------------------
48
49 /* The contents of a selection are returned in a GtkSelectionData
50 structure. selection/target identify the request.
51 type specifies the type of the return; if length < 0, and
52 the data should be ignored. This structure has object semantics -
53 no fields should be modified directly, they should not be created
54 directly, and pointers to them should not be stored beyond the duration of
55 a callback. (If the last is changed, we'll need to add reference
56 counting)
57
58 struct _GtkSelectionData
59 {
60 GdkAtom selection;
61 GdkAtom target;
62 GdkAtom type;
63 gint format;
64 guchar *data;
65 gint length;
66 };
67
68 */
69
70 //-----------------------------------------------------------------------------
71 // "selection_received" for targets
72 //-----------------------------------------------------------------------------
73
74 #if 0
75
76 static void
77 targets_selection_received( GtkWidget *WXUNUSED(widget),
78 GtkSelectionData *selection_data,
79 #if (GTK_MINOR_VERSION > 0)
80 guint32 WXUNUSED(time),
81 #endif
82 wxClipboard *clipboard )
83 {
84 if ( wxTheClipboard && selection_data->length > 0 )
85 {
86 /* make sure we got the data in the correct form */
87 GdkAtom type = selection_data->type;
88 if ( type != GDK_SELECTION_TYPE_ATOM )
89 {
90 if ( strcmp(gdk_atom_name(type), "TARGETS") )
91 {
92 wxLogTrace( TRACE_CLIPBOARD,
93 wxT("got unsupported clipboard target") );
94
95 clipboard->m_waiting = false;
96 return;
97 }
98 }
99
100 #ifdef __WXDEBUG__
101 wxDataFormat clip( selection_data->selection );
102 wxLogTrace( TRACE_CLIPBOARD,
103 wxT("selection received for targets, clipboard %s"),
104 clip.GetId().c_str() );
105 #endif // __WXDEBUG__
106
107 // the atoms we received, holding a list of targets (= formats)
108 GdkAtom *atoms = (GdkAtom *)selection_data->data;
109
110 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
111 {
112 wxDataFormat format( atoms[i] );
113
114 wxLogTrace( TRACE_CLIPBOARD,
115 wxT("selection received for targets, format %s"),
116 format.GetId().c_str() );
117
118 if (format == clipboard->m_targetRequested)
119 {
120 clipboard->m_waiting = false;
121 clipboard->m_formatSupported = true;
122 return;
123 }
124 }
125 }
126
127 clipboard->m_waiting = false;
128 }
129
130 //-----------------------------------------------------------------------------
131 // "selection_received" for the actual data
132 //-----------------------------------------------------------------------------
133
134 static void
135 selection_received( GtkWidget *WXUNUSED(widget),
136 GtkSelectionData *selection_data,
137 #if (GTK_MINOR_VERSION > 0)
138 guint32 WXUNUSED(time),
139 #endif
140 wxClipboard *clipboard )
141 {
142 if (!wxTheClipboard)
143 {
144 clipboard->m_waiting = false;
145 return;
146 }
147
148 wxDataObject *data_object = clipboard->m_receivedData;
149
150 if (!data_object)
151 {
152 clipboard->m_waiting = false;
153 return;
154 }
155
156 if (selection_data->length <= 0)
157 {
158 clipboard->m_waiting = false;
159 return;
160 }
161
162 wxDataFormat format( selection_data->target );
163
164 /* make sure we got the data in the correct format */
165 if (!data_object->IsSupportedFormat( format ) )
166 {
167 clipboard->m_waiting = false;
168 return;
169 }
170
171 /* make sure we got the data in the correct form (selection type).
172 if so, copy data to target object */
173 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
174 {
175 clipboard->m_waiting = false;
176 return;
177 }
178
179 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
180
181 wxTheClipboard->m_formatSupported = true;
182 clipboard->m_waiting = false;
183 }
184
185 //-----------------------------------------------------------------------------
186 // "selection_clear"
187 //-----------------------------------------------------------------------------
188
189 static gint
190 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
191 {
192 if (!wxTheClipboard) return TRUE;
193
194 if (event->selection == GDK_SELECTION_PRIMARY)
195 {
196 wxTheClipboard->m_ownsPrimarySelection = false;
197 }
198 else
199 if (event->selection == g_clipboardAtom)
200 {
201 wxTheClipboard->m_ownsClipboard = false;
202 }
203 else
204 {
205 wxTheClipboard->m_waiting = false;
206 return FALSE;
207 }
208
209 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
210 (!wxTheClipboard->m_ownsClipboard))
211 {
212 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
213 if (wxTheClipboard->m_data)
214 {
215 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
216
217 wxDELETE(wxTheClipboard->m_data);
218 }
219 }
220
221 wxTheClipboard->m_waiting = false;
222 return TRUE;
223 }
224
225 //-----------------------------------------------------------------------------
226 // selection handler for supplying data
227 //-----------------------------------------------------------------------------
228
229 static void
230 selection_handler( GtkWidget *WXUNUSED(widget),
231 GtkSelectionData *selection_data,
232 guint WXUNUSED(info),
233 guint WXUNUSED(time),
234 gpointer WXUNUSED(data) )
235 {
236 if (!wxTheClipboard) return;
237
238 if (!wxTheClipboard->m_data) return;
239
240 wxDataObject *data = wxTheClipboard->m_data;
241
242 wxDataFormat format( selection_data->target );
243
244 if (!data->IsSupportedFormat( format )) return;
245
246 int size = data->GetDataSize( format );
247
248 if (size == 0) return;
249
250 void *d = malloc(size);
251
252 data->GetDataHere( selection_data->target, d );
253
254 // transform Unicode text into multibyte before putting it on clipboard
255 #if wxUSE_UNICODE
256 if ( format.GetType() == wxDF_TEXT || format.GetType() == wxDF_UNICODETEXT)
257 {
258 const wchar_t *wstr = (const wchar_t *)d;
259 size_t len = wxConvCurrent->WC2MB(NULL, wstr, 0);
260 char *str = malloc(len + 1);
261 wxConvCurrent->WC2MB(str, wstr, len);
262 str[len] = '\0';
263
264 free(d);
265 d = str;
266 }
267 #endif // wxUSE_UNICODE
268
269 gtk_selection_data_set(
270 selection_data,
271 GDK_SELECTION_TYPE_STRING,
272 8*sizeof(gchar),
273 (unsigned char*) d,
274 size );
275
276 free(d);
277 }
278
279 #endif
280
281 //-----------------------------------------------------------------------------
282 // wxClipboard
283 //-----------------------------------------------------------------------------
284
285 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
286
287 wxClipboard::wxClipboard()
288 {
289 m_open = false;
290
291 m_ownsClipboard = false;
292 m_ownsPrimarySelection = false;
293
294 m_data = NULL;
295 m_receivedData = NULL;
296
297 /* we use m_targetsWidget to query what formats are available */
298
299 /* we use m_clipboardWidget to get and to offer data */
300 #if !wxUSE_NANOX
301 if (!g_clipboardAtom) g_clipboardAtom = XInternAtom( (Display*) wxGetDisplay(), "CLIPBOARD", False );
302 if (!g_targetsAtom) g_targetsAtom = XInternAtom( (Display*) wxGetDisplay(), "TARGETS", False );
303 #endif
304
305 m_formatSupported = false;
306 m_targetRequested = 0;
307 }
308
309 wxClipboard::~wxClipboard()
310 {
311 Clear();
312
313 // if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
314 // if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
315 }
316
317 void wxClipboard::Clear()
318 {
319 if (m_data)
320 {
321 #if wxUSE_THREADS
322 /* disable GUI threads */
323 #endif
324
325 /* As we have data we also own the clipboard. Once we no longer own
326 it, clear_selection is called which will set m_data to zero */
327 #if 0
328 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
329 {
330 m_waiting = true;
331
332 gtk_selection_owner_set( NULL, g_clipboardAtom,
333 (guint32) GDK_CURRENT_TIME );
334
335 while (m_waiting) gtk_main_iteration();
336 }
337
338 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
339 {
340 m_waiting = true;
341
342 gtk_selection_owner_set( NULL, GDK_SELECTION_PRIMARY,
343 (guint32) GDK_CURRENT_TIME );
344
345 while (m_waiting) gtk_main_iteration();
346 }
347 #endif
348
349 wxDELETE(m_data);
350
351 #if wxUSE_THREADS
352 /* re-enable GUI threads */
353 #endif
354 }
355
356 m_targetRequested = 0;
357 m_formatSupported = false;
358 }
359
360 bool wxClipboard::Open()
361 {
362 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
363
364 m_open = true;
365
366 return true;
367 }
368
369 bool wxClipboard::SetData( wxDataObject *data )
370 {
371 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
372
373 wxCHECK_MSG( data, false, wxT("data is invalid") );
374
375 Clear();
376
377 return AddData( data );
378 }
379
380 bool wxClipboard::AddData( wxDataObject *data )
381 {
382 #if wxUSE_NANOX
383 return false;
384 #else
385 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
386
387 wxCHECK_MSG( data, false, wxT("data is invalid") );
388
389 /* we can only store one wxDataObject */
390 Clear();
391
392 m_data = data;
393
394 /* get formats from wxDataObjects */
395 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
396 m_data->GetAllFormats( array );
397
398 #if 0
399 /* primary selection or clipboard */
400 Atom clipboard = m_usePrimary ? (Atom) 1 // 1 = primary selection
401 : g_clipboardAtom;
402 #endif // 0
403
404
405 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
406 {
407 wxLogTrace( TRACE_CLIPBOARD,
408 wxT("wxClipboard now supports atom %s"),
409 array[i].GetId().c_str() );
410
411 #if 0
412 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
413 clipboard,
414 array[i],
415 0 ); /* what is info ? */
416 #endif
417 }
418
419 delete[] array;
420
421 #if 0
422 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
423 "selection_get",
424 GTK_SIGNAL_FUNC(selection_handler),
425 (gpointer) NULL );
426 #endif
427
428 #if wxUSE_THREADS
429 /* disable GUI threads */
430 #endif
431
432 bool res = false;
433 #if 0
434 /* Tell the world we offer clipboard data */
435 res = (gtk_selection_owner_set( m_clipboardWidget,
436 clipboard,
437 (guint32) GDK_CURRENT_TIME ));
438 #endif
439
440 if (m_usePrimary)
441 m_ownsPrimarySelection = res;
442 else
443 m_ownsClipboard = res;
444
445 #if wxUSE_THREADS
446 /* re-enable GUI threads */
447 #endif
448
449 return res;
450 #endif
451 }
452
453 void wxClipboard::Close()
454 {
455 wxCHECK_RET( m_open, wxT("clipboard not open") );
456
457 m_open = false;
458 }
459
460 bool wxClipboard::IsOpened() const
461 {
462 return m_open;
463 }
464
465 bool wxClipboard::IsSupported( const wxDataFormat& format )
466 {
467 /* reentrance problems */
468 if (m_waiting) return false;
469
470 /* store requested format to be asked for by callbacks */
471 m_targetRequested = format;
472
473 #if 0
474 wxLogTrace( TRACE_CLIPBOARD,
475 wxT("wxClipboard:IsSupported: requested format: %s"),
476 format.GetId().c_str() );
477 #endif
478
479 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
480
481 m_formatSupported = false;
482
483 /* perform query. this will set m_formatSupported to
484 true if m_targetRequested is supported.
485 also, we have to wait for the "answer" from the
486 clipboard owner which is an asynchronous process.
487 therefore we set m_waiting = true here and wait
488 until the callback "targets_selection_received"
489 sets it to false */
490
491 m_waiting = true;
492
493 #if 0
494 gtk_selection_convert( m_targetsWidget,
495 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
496 : g_clipboardAtom,
497 g_targetsAtom,
498 (guint32) GDK_CURRENT_TIME );
499
500 while (m_waiting) gtk_main_iteration();
501 #endif
502
503 if (!m_formatSupported) return false;
504
505 return true;
506 }
507
508 bool wxClipboard::GetData( wxDataObject& data )
509 {
510 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
511
512 /* get formats from wxDataObjects */
513 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
514 data.GetAllFormats( array );
515
516 for (size_t i = 0; i < data.GetFormatCount(); i++)
517 {
518 wxDataFormat format( array[i] );
519
520 wxLogTrace( TRACE_CLIPBOARD,
521 wxT("wxClipboard::GetData: requested format: %s"),
522 format.GetId().c_str() );
523
524 /* is data supported by clipboard ? */
525
526 /* store requested format to be asked for by callbacks */
527 m_targetRequested = format;
528
529 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
530
531 m_formatSupported = false;
532
533 /* perform query. this will set m_formatSupported to
534 true if m_targetRequested is supported.
535 also, we have to wait for the "answer" from the
536 clipboard owner which is an asynchronous process.
537 therefore we set m_waiting = true here and wait
538 until the callback "targets_selection_received"
539 sets it to false */
540
541 m_waiting = true;
542
543 #if 0
544 gtk_selection_convert( m_targetsWidget,
545 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
546 : g_clipboardAtom,
547 g_targetsAtom,
548 (guint32) GDK_CURRENT_TIME );
549
550 while (m_waiting) gtk_main_iteration();
551 #endif
552
553 if (!m_formatSupported) continue;
554
555 /* store pointer to data object to be filled up by callbacks */
556 m_receivedData = &data;
557
558 /* store requested format to be asked for by callbacks */
559 m_targetRequested = format;
560
561 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
562
563 /* start query */
564 m_formatSupported = false;
565
566 /* ask for clipboard contents. this will set
567 m_formatSupported to true if m_targetRequested
568 is supported.
569 also, we have to wait for the "answer" from the
570 clipboard owner which is an asynchronous process.
571 therefore we set m_waiting = true here and wait
572 until the callback "targets_selection_received"
573 sets it to false */
574
575 m_waiting = true;
576
577 wxLogTrace( TRACE_CLIPBOARD,
578 wxT("wxClipboard::GetData: format found, start convert") );
579
580 #if 0
581 gtk_selection_convert( m_clipboardWidget,
582 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
583 : g_clipboardAtom,
584 m_targetRequested,
585 (guint32) GDK_CURRENT_TIME );
586
587 while (m_waiting) gtk_main_iteration();
588 #endif
589
590 /* this is a true error as we checked for the presence of such data before */
591 wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") );
592
593 /* return success */
594 delete[] array;
595 return true;
596 }
597
598 wxLogTrace( TRACE_CLIPBOARD,
599 wxT("wxClipboard::GetData: format not found") );
600
601 /* return failure */
602 delete[] array;
603 return false;
604 }
605
606 #endif
607 // wxUSE_CLIPBOARD