fix compilation after last change for compilers without variadic macros support
[wxWidgets.git] / src / x11 / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/x11/clipbrd.cpp
3 // Purpose: Clipboard functionality
4 // Author: Robert Roebling
5 // Created:
6 // RCS-ID: $Id$
7 // Copyright: (c) Robert Roebling
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // for compilers that support precompilation, includes "wx.h".
12 #include "wx/wxprec.h"
13
14 #if wxUSE_CLIPBOARD
15
16 #include "wx/clipbrd.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/log.h"
20 #include "wx/utils.h"
21 #include "wx/dataobj.h"
22 #endif
23
24 #include "wx/x11/private.h"
25
26 //-----------------------------------------------------------------------------
27 // data
28 //-----------------------------------------------------------------------------
29
30 #if !wxUSE_NANOX
31 Atom g_clipboardAtom = 0;
32 Atom g_targetsAtom = 0;
33 #endif
34
35 // avoid warnings about unused static variable
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 = _T("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 _T("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 delete wxTheClipboard->m_data;
218 wxTheClipboard->m_data = (wxDataObject*) NULL;
219 }
220 }
221
222 wxTheClipboard->m_waiting = false;
223 return TRUE;
224 }
225
226 //-----------------------------------------------------------------------------
227 // selection handler for supplying data
228 //-----------------------------------------------------------------------------
229
230 static void
231 selection_handler( GtkWidget *WXUNUSED(widget),
232 GtkSelectionData *selection_data,
233 guint WXUNUSED(info),
234 guint WXUNUSED(time),
235 gpointer WXUNUSED(data) )
236 {
237 if (!wxTheClipboard) return;
238
239 if (!wxTheClipboard->m_data) return;
240
241 wxDataObject *data = wxTheClipboard->m_data;
242
243 wxDataFormat format( selection_data->target );
244
245 if (!data->IsSupportedFormat( format )) return;
246
247 int size = data->GetDataSize( format );
248
249 if (size == 0) return;
250
251 void *d = malloc(size);
252
253 data->GetDataHere( selection_data->target, d );
254
255 // transform Unicode text into multibyte before putting it on clipboard
256 #if wxUSE_UNICODE
257 if ( format.GetType() == wxDF_TEXT || format.GetType() == wxDF_UNICODETEXT)
258 {
259 const wchar_t *wstr = (const wchar_t *)d;
260 size_t len = wxConvCurrent->WC2MB(NULL, wstr, 0);
261 char *str = malloc(len + 1);
262 wxConvCurrent->WC2MB(str, wstr, len);
263 str[len] = '\0';
264
265 free(d);
266 d = str;
267 }
268 #endif // wxUSE_UNICODE
269
270 gtk_selection_data_set(
271 selection_data,
272 GDK_SELECTION_TYPE_STRING,
273 8*sizeof(gchar),
274 (unsigned char*) d,
275 size );
276
277 free(d);
278 }
279
280 #endif
281
282 //-----------------------------------------------------------------------------
283 // wxClipboard
284 //-----------------------------------------------------------------------------
285
286 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
287
288 wxClipboard::wxClipboard()
289 {
290 m_open = false;
291
292 m_ownsClipboard = false;
293 m_ownsPrimarySelection = false;
294
295 m_data = (wxDataObject*) NULL;
296 m_receivedData = (wxDataObject*) NULL;
297
298 /* we use m_targetsWidget to query what formats are available */
299
300 /* we use m_clipboardWidget to get and to offer data */
301 #if !wxUSE_NANOX
302 if (!g_clipboardAtom) g_clipboardAtom = XInternAtom( (Display*) wxGetDisplay(), "CLIPBOARD", False );
303 if (!g_targetsAtom) g_targetsAtom = XInternAtom( (Display*) wxGetDisplay(), "TARGETS", False );
304 #endif
305
306 m_formatSupported = false;
307 m_targetRequested = 0;
308
309 m_usePrimary = false;
310 }
311
312 wxClipboard::~wxClipboard()
313 {
314 Clear();
315
316 // if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
317 // if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
318 }
319
320 void wxClipboard::Clear()
321 {
322 if (m_data)
323 {
324 #if wxUSE_THREADS
325 /* disable GUI threads */
326 #endif
327
328 /* As we have data we also own the clipboard. Once we no longer own
329 it, clear_selection is called which will set m_data to zero */
330 #if 0
331 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
332 {
333 m_waiting = true;
334
335 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
336 (guint32) GDK_CURRENT_TIME );
337
338 while (m_waiting) gtk_main_iteration();
339 }
340
341 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
342 {
343 m_waiting = true;
344
345 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
346 (guint32) GDK_CURRENT_TIME );
347
348 while (m_waiting) gtk_main_iteration();
349 }
350 #endif
351
352 if (m_data)
353 {
354 delete m_data;
355 m_data = (wxDataObject*) NULL;
356 }
357
358 #if wxUSE_THREADS
359 /* re-enable GUI threads */
360 #endif
361 }
362
363 m_targetRequested = 0;
364 m_formatSupported = false;
365 }
366
367 bool wxClipboard::Open()
368 {
369 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
370
371 m_open = true;
372
373 return true;
374 }
375
376 bool wxClipboard::SetData( wxDataObject *data )
377 {
378 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
379
380 wxCHECK_MSG( data, false, wxT("data is invalid") );
381
382 Clear();
383
384 return AddData( data );
385 }
386
387 bool wxClipboard::AddData( wxDataObject *data )
388 {
389 #if wxUSE_NANOX
390 return false;
391 #else
392 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
393
394 wxCHECK_MSG( data, false, wxT("data is invalid") );
395
396 /* we can only store one wxDataObject */
397 Clear();
398
399 m_data = data;
400
401 /* get formats from wxDataObjects */
402 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
403 m_data->GetAllFormats( array );
404
405 #if 0
406 /* primary selection or clipboard */
407 Atom clipboard = m_usePrimary ? (Atom) 1 // 1 = primary selection
408 : g_clipboardAtom;
409 #endif // 0
410
411
412 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
413 {
414 wxLogTrace( TRACE_CLIPBOARD,
415 wxT("wxClipboard now supports atom %s"),
416 array[i].GetId().c_str() );
417
418 #if 0
419 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
420 clipboard,
421 array[i],
422 0 ); /* what is info ? */
423 #endif
424 }
425
426 delete[] array;
427
428 #if 0
429 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
430 "selection_get",
431 GTK_SIGNAL_FUNC(selection_handler),
432 (gpointer) NULL );
433 #endif
434
435 #if wxUSE_THREADS
436 /* disable GUI threads */
437 #endif
438
439 bool res = false;
440 #if 0
441 /* Tell the world we offer clipboard data */
442 res = (gtk_selection_owner_set( m_clipboardWidget,
443 clipboard,
444 (guint32) GDK_CURRENT_TIME ));
445 #endif
446
447 if (m_usePrimary)
448 m_ownsPrimarySelection = res;
449 else
450 m_ownsClipboard = res;
451
452 #if wxUSE_THREADS
453 /* re-enable GUI threads */
454 #endif
455
456 return res;
457 #endif
458 }
459
460 void wxClipboard::Close()
461 {
462 wxCHECK_RET( m_open, wxT("clipboard not open") );
463
464 m_open = false;
465 }
466
467 bool wxClipboard::IsOpened() const
468 {
469 return m_open;
470 }
471
472 bool wxClipboard::IsSupported( const wxDataFormat& format )
473 {
474 /* reentrance problems */
475 if (m_waiting) return false;
476
477 /* store requested format to be asked for by callbacks */
478 m_targetRequested = format;
479
480 #if 0
481 wxLogTrace( TRACE_CLIPBOARD,
482 wxT("wxClipboard:IsSupported: requested format: %s"),
483 format.GetId().c_str() );
484 #endif
485
486 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
487
488 m_formatSupported = false;
489
490 /* perform query. this will set m_formatSupported to
491 true if m_targetRequested is supported.
492 also, we have to wait for the "answer" from the
493 clipboard owner which is an asynchronous process.
494 therefore we set m_waiting = true here and wait
495 until the callback "targets_selection_received"
496 sets it to false */
497
498 m_waiting = true;
499
500 #if 0
501 gtk_selection_convert( m_targetsWidget,
502 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
503 : g_clipboardAtom,
504 g_targetsAtom,
505 (guint32) GDK_CURRENT_TIME );
506
507 while (m_waiting) gtk_main_iteration();
508 #endif
509
510 if (!m_formatSupported) return false;
511
512 return true;
513 }
514
515 bool wxClipboard::GetData( wxDataObject& data )
516 {
517 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
518
519 /* get formats from wxDataObjects */
520 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
521 data.GetAllFormats( array );
522
523 for (size_t i = 0; i < data.GetFormatCount(); i++)
524 {
525 wxDataFormat format( array[i] );
526
527 wxLogTrace( TRACE_CLIPBOARD,
528 wxT("wxClipboard::GetData: requested format: %s"),
529 format.GetId().c_str() );
530
531 /* is data supported by clipboard ? */
532
533 /* store requested format to be asked for by callbacks */
534 m_targetRequested = format;
535
536 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
537
538 m_formatSupported = false;
539
540 /* perform query. this will set m_formatSupported to
541 true if m_targetRequested is supported.
542 also, we have to wait for the "answer" from the
543 clipboard owner which is an asynchronous process.
544 therefore we set m_waiting = true here and wait
545 until the callback "targets_selection_received"
546 sets it to false */
547
548 m_waiting = true;
549
550 #if 0
551 gtk_selection_convert( m_targetsWidget,
552 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
553 : g_clipboardAtom,
554 g_targetsAtom,
555 (guint32) GDK_CURRENT_TIME );
556
557 while (m_waiting) gtk_main_iteration();
558 #endif
559
560 if (!m_formatSupported) continue;
561
562 /* store pointer to data object to be filled up by callbacks */
563 m_receivedData = &data;
564
565 /* store requested format to be asked for by callbacks */
566 m_targetRequested = format;
567
568 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
569
570 /* start query */
571 m_formatSupported = false;
572
573 /* ask for clipboard contents. this will set
574 m_formatSupported to true if m_targetRequested
575 is supported.
576 also, we have to wait for the "answer" from the
577 clipboard owner which is an asynchronous process.
578 therefore we set m_waiting = true here and wait
579 until the callback "targets_selection_received"
580 sets it to false */
581
582 m_waiting = true;
583
584 wxLogTrace( TRACE_CLIPBOARD,
585 wxT("wxClipboard::GetData: format found, start convert") );
586
587 #if 0
588 gtk_selection_convert( m_clipboardWidget,
589 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
590 : g_clipboardAtom,
591 m_targetRequested,
592 (guint32) GDK_CURRENT_TIME );
593
594 while (m_waiting) gtk_main_iteration();
595 #endif
596
597 /* this is a true error as we checked for the presence of such data before */
598 wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") );
599
600 /* return success */
601 delete[] array;
602 return true;
603 }
604
605 wxLogTrace( TRACE_CLIPBOARD,
606 wxT("wxClipboard::GetData: format not found") );
607
608 /* return failure */
609 delete[] array;
610 return false;
611 }
612
613 #endif
614 // wxUSE_CLIPBOARD