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