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