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