Add wxEventLoop::ScheduleExit().
[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 (notice that we still use it
36 // even in release build if the compiler doesn't support variadic macros)
37 #if defined(__WXDEBUG__) || !defined(HAVE_VARIADIC_MACROS)
38
39 // the trace mask we use with wxLogTrace() - call
40 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
41 // (there will be a *lot* of them!)
42 static const wxChar *TRACE_CLIPBOARD = wxT("clipboard");
43
44 #endif // __WXDEBUG__
45
46 //-----------------------------------------------------------------------------
47 // reminder
48 //-----------------------------------------------------------------------------
49
50 /* The contents of a selection are returned in a GtkSelectionData
51 structure. selection/target identify the request.
52 type specifies the type of the return; if length < 0, and
53 the data should be ignored. This structure has object semantics -
54 no fields should be modified directly, they should not be created
55 directly, and pointers to them should not be stored beyond the duration of
56 a callback. (If the last is changed, we'll need to add reference
57 counting)
58
59 struct _GtkSelectionData
60 {
61 GdkAtom selection;
62 GdkAtom target;
63 GdkAtom type;
64 gint format;
65 guchar *data;
66 gint length;
67 };
68
69 */
70
71 //-----------------------------------------------------------------------------
72 // "selection_received" for targets
73 //-----------------------------------------------------------------------------
74
75 #if 0
76
77 static void
78 targets_selection_received( GtkWidget *WXUNUSED(widget),
79 GtkSelectionData *selection_data,
80 #if (GTK_MINOR_VERSION > 0)
81 guint32 WXUNUSED(time),
82 #endif
83 wxClipboard *clipboard )
84 {
85 if ( wxTheClipboard && selection_data->length > 0 )
86 {
87 /* make sure we got the data in the correct form */
88 GdkAtom type = selection_data->type;
89 if ( type != GDK_SELECTION_TYPE_ATOM )
90 {
91 if ( strcmp(gdk_atom_name(type), "TARGETS") )
92 {
93 wxLogTrace( TRACE_CLIPBOARD,
94 wxT("got unsupported clipboard target") );
95
96 clipboard->m_waiting = false;
97 return;
98 }
99 }
100
101 #ifdef __WXDEBUG__
102 wxDataFormat clip( selection_data->selection );
103 wxLogTrace( TRACE_CLIPBOARD,
104 wxT("selection received for targets, clipboard %s"),
105 clip.GetId().c_str() );
106 #endif // __WXDEBUG__
107
108 // the atoms we received, holding a list of targets (= formats)
109 GdkAtom *atoms = (GdkAtom *)selection_data->data;
110
111 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
112 {
113 wxDataFormat format( atoms[i] );
114
115 wxLogTrace( TRACE_CLIPBOARD,
116 wxT("selection received for targets, format %s"),
117 format.GetId().c_str() );
118
119 if (format == clipboard->m_targetRequested)
120 {
121 clipboard->m_waiting = false;
122 clipboard->m_formatSupported = true;
123 return;
124 }
125 }
126 }
127
128 clipboard->m_waiting = false;
129 }
130
131 //-----------------------------------------------------------------------------
132 // "selection_received" for the actual data
133 //-----------------------------------------------------------------------------
134
135 static void
136 selection_received( GtkWidget *WXUNUSED(widget),
137 GtkSelectionData *selection_data,
138 #if (GTK_MINOR_VERSION > 0)
139 guint32 WXUNUSED(time),
140 #endif
141 wxClipboard *clipboard )
142 {
143 if (!wxTheClipboard)
144 {
145 clipboard->m_waiting = false;
146 return;
147 }
148
149 wxDataObject *data_object = clipboard->m_receivedData;
150
151 if (!data_object)
152 {
153 clipboard->m_waiting = false;
154 return;
155 }
156
157 if (selection_data->length <= 0)
158 {
159 clipboard->m_waiting = false;
160 return;
161 }
162
163 wxDataFormat format( selection_data->target );
164
165 /* make sure we got the data in the correct format */
166 if (!data_object->IsSupportedFormat( format ) )
167 {
168 clipboard->m_waiting = false;
169 return;
170 }
171
172 /* make sure we got the data in the correct form (selection type).
173 if so, copy data to target object */
174 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
175 {
176 clipboard->m_waiting = false;
177 return;
178 }
179
180 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
181
182 wxTheClipboard->m_formatSupported = true;
183 clipboard->m_waiting = false;
184 }
185
186 //-----------------------------------------------------------------------------
187 // "selection_clear"
188 //-----------------------------------------------------------------------------
189
190 static gint
191 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
192 {
193 if (!wxTheClipboard) return TRUE;
194
195 if (event->selection == GDK_SELECTION_PRIMARY)
196 {
197 wxTheClipboard->m_ownsPrimarySelection = false;
198 }
199 else
200 if (event->selection == g_clipboardAtom)
201 {
202 wxTheClipboard->m_ownsClipboard = false;
203 }
204 else
205 {
206 wxTheClipboard->m_waiting = false;
207 return FALSE;
208 }
209
210 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
211 (!wxTheClipboard->m_ownsClipboard))
212 {
213 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
214 if (wxTheClipboard->m_data)
215 {
216 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
217
218 wxDELETE(wxTheClipboard->m_data);
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 = NULL;
296 m_receivedData = 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
310 wxClipboard::~wxClipboard()
311 {
312 Clear();
313
314 // if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
315 // if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
316 }
317
318 void wxClipboard::Clear()
319 {
320 if (m_data)
321 {
322 #if wxUSE_THREADS
323 /* disable GUI threads */
324 #endif
325
326 /* As we have data we also own the clipboard. Once we no longer own
327 it, clear_selection is called which will set m_data to zero */
328 #if 0
329 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
330 {
331 m_waiting = true;
332
333 gtk_selection_owner_set( NULL, g_clipboardAtom,
334 (guint32) GDK_CURRENT_TIME );
335
336 while (m_waiting) gtk_main_iteration();
337 }
338
339 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
340 {
341 m_waiting = true;
342
343 gtk_selection_owner_set( NULL, GDK_SELECTION_PRIMARY,
344 (guint32) GDK_CURRENT_TIME );
345
346 while (m_waiting) gtk_main_iteration();
347 }
348 #endif
349
350 wxDELETE(m_data);
351
352 #if wxUSE_THREADS
353 /* re-enable GUI threads */
354 #endif
355 }
356
357 m_targetRequested = 0;
358 m_formatSupported = false;
359 }
360
361 bool wxClipboard::Open()
362 {
363 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
364
365 m_open = true;
366
367 return true;
368 }
369
370 bool wxClipboard::SetData( wxDataObject *data )
371 {
372 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
373
374 wxCHECK_MSG( data, false, wxT("data is invalid") );
375
376 Clear();
377
378 return AddData( data );
379 }
380
381 bool wxClipboard::AddData( wxDataObject *data )
382 {
383 #if wxUSE_NANOX
384 return false;
385 #else
386 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
387
388 wxCHECK_MSG( data, false, wxT("data is invalid") );
389
390 /* we can only store one wxDataObject */
391 Clear();
392
393 m_data = data;
394
395 /* get formats from wxDataObjects */
396 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
397 m_data->GetAllFormats( array );
398
399 #if 0
400 /* primary selection or clipboard */
401 Atom clipboard = m_usePrimary ? (Atom) 1 // 1 = primary selection
402 : g_clipboardAtom;
403 #endif // 0
404
405
406 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
407 {
408 wxLogTrace( TRACE_CLIPBOARD,
409 wxT("wxClipboard now supports atom %s"),
410 array[i].GetId().c_str() );
411
412 #if 0
413 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
414 clipboard,
415 array[i],
416 0 ); /* what is info ? */
417 #endif
418 }
419
420 delete[] array;
421
422 #if 0
423 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
424 "selection_get",
425 GTK_SIGNAL_FUNC(selection_handler),
426 (gpointer) NULL );
427 #endif
428
429 #if wxUSE_THREADS
430 /* disable GUI threads */
431 #endif
432
433 bool res = false;
434 #if 0
435 /* Tell the world we offer clipboard data */
436 res = (gtk_selection_owner_set( m_clipboardWidget,
437 clipboard,
438 (guint32) GDK_CURRENT_TIME ));
439 #endif
440
441 if (m_usePrimary)
442 m_ownsPrimarySelection = res;
443 else
444 m_ownsClipboard = res;
445
446 #if wxUSE_THREADS
447 /* re-enable GUI threads */
448 #endif
449
450 return res;
451 #endif
452 }
453
454 void wxClipboard::Close()
455 {
456 wxCHECK_RET( m_open, wxT("clipboard not open") );
457
458 m_open = false;
459 }
460
461 bool wxClipboard::IsOpened() const
462 {
463 return m_open;
464 }
465
466 bool wxClipboard::IsSupported( const wxDataFormat& format )
467 {
468 /* reentrance problems */
469 if (m_waiting) return false;
470
471 /* store requested format to be asked for by callbacks */
472 m_targetRequested = format;
473
474 #if 0
475 wxLogTrace( TRACE_CLIPBOARD,
476 wxT("wxClipboard:IsSupported: requested format: %s"),
477 format.GetId().c_str() );
478 #endif
479
480 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
481
482 m_formatSupported = false;
483
484 /* perform query. this will set m_formatSupported to
485 true if m_targetRequested is supported.
486 also, we have to wait for the "answer" from the
487 clipboard owner which is an asynchronous process.
488 therefore we set m_waiting = true here and wait
489 until the callback "targets_selection_received"
490 sets it to false */
491
492 m_waiting = true;
493
494 #if 0
495 gtk_selection_convert( m_targetsWidget,
496 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
497 : g_clipboardAtom,
498 g_targetsAtom,
499 (guint32) GDK_CURRENT_TIME );
500
501 while (m_waiting) gtk_main_iteration();
502 #endif
503
504 if (!m_formatSupported) return false;
505
506 return true;
507 }
508
509 bool wxClipboard::GetData( wxDataObject& data )
510 {
511 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
512
513 /* get formats from wxDataObjects */
514 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
515 data.GetAllFormats( array );
516
517 for (size_t i = 0; i < data.GetFormatCount(); i++)
518 {
519 wxDataFormat format( array[i] );
520
521 wxLogTrace( TRACE_CLIPBOARD,
522 wxT("wxClipboard::GetData: requested format: %s"),
523 format.GetId().c_str() );
524
525 /* is data supported by clipboard ? */
526
527 /* store requested format to be asked for by callbacks */
528 m_targetRequested = format;
529
530 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
531
532 m_formatSupported = false;
533
534 /* perform query. this will set m_formatSupported to
535 true if m_targetRequested is supported.
536 also, we have to wait for the "answer" from the
537 clipboard owner which is an asynchronous process.
538 therefore we set m_waiting = true here and wait
539 until the callback "targets_selection_received"
540 sets it to false */
541
542 m_waiting = true;
543
544 #if 0
545 gtk_selection_convert( m_targetsWidget,
546 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
547 : g_clipboardAtom,
548 g_targetsAtom,
549 (guint32) GDK_CURRENT_TIME );
550
551 while (m_waiting) gtk_main_iteration();
552 #endif
553
554 if (!m_formatSupported) continue;
555
556 /* store pointer to data object to be filled up by callbacks */
557 m_receivedData = &data;
558
559 /* store requested format to be asked for by callbacks */
560 m_targetRequested = format;
561
562 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
563
564 /* start query */
565 m_formatSupported = false;
566
567 /* ask for clipboard contents. this will set
568 m_formatSupported to true if m_targetRequested
569 is supported.
570 also, we have to wait for the "answer" from the
571 clipboard owner which is an asynchronous process.
572 therefore we set m_waiting = true here and wait
573 until the callback "targets_selection_received"
574 sets it to false */
575
576 m_waiting = true;
577
578 wxLogTrace( TRACE_CLIPBOARD,
579 wxT("wxClipboard::GetData: format found, start convert") );
580
581 #if 0
582 gtk_selection_convert( m_clipboardWidget,
583 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
584 : g_clipboardAtom,
585 m_targetRequested,
586 (guint32) GDK_CURRENT_TIME );
587
588 while (m_waiting) gtk_main_iteration();
589 #endif
590
591 /* this is a true error as we checked for the presence of such data before */
592 wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") );
593
594 /* return success */
595 delete[] array;
596 return true;
597 }
598
599 wxLogTrace( TRACE_CLIPBOARD,
600 wxT("wxClipboard::GetData: format not found") );
601
602 /* return failure */
603 delete[] array;
604 return false;
605 }
606
607 #endif
608 // wxUSE_CLIPBOARD