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