]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/gtk1/clipbrd.cpp
Use "&Help" so wxMac doesn't make an extra help menu
[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 if (!data->IsSupportedFormat( format )) return;
254
255 int size = data->GetDataSize( format );
256
257 if (size == 0) return;
258
259 void *d = malloc(size);
260
261 // Text data will be in UTF8 in Unicode mode.
262 data->GetDataHere( selection_data->target, d );
263
264 gtk_selection_data_set(
265 selection_data,
266 GDK_SELECTION_TYPE_STRING,
267 8*sizeof(gchar),
268 (unsigned char*) d,
269 size );
270
271 free(d);
272}
273
274//-----------------------------------------------------------------------------
275// wxClipboard
276//-----------------------------------------------------------------------------
277
278IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
279
280wxClipboard::wxClipboard()
281{
282 m_open = FALSE;
283 m_waiting = FALSE;
284
285 m_ownsClipboard = FALSE;
286 m_ownsPrimarySelection = FALSE;
287
288 m_data = (wxDataObject*) NULL;
289 m_receivedData = (wxDataObject*) NULL;
290
291 /* we use m_targetsWidget to query what formats are available */
292
293 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
294 gtk_widget_realize( m_targetsWidget );
295
296 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
297 "selection_received",
298 GTK_SIGNAL_FUNC( targets_selection_received ),
299 (gpointer) this );
300
301 /* we use m_clipboardWidget to get and to offer data */
302
303 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
304 gtk_widget_realize( m_clipboardWidget );
305
306 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
307 "selection_received",
308 GTK_SIGNAL_FUNC( selection_received ),
309 (gpointer) this );
310
311 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
312 "selection_clear_event",
313 GTK_SIGNAL_FUNC( selection_clear_clip ),
314 (gpointer) NULL );
315
316 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
317 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
318
319 m_formatSupported = FALSE;
320 m_targetRequested = 0;
321
322 m_usePrimary = FALSE;
323}
324
325wxClipboard::~wxClipboard()
326{
327 Clear();
328
329 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
330 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
331}
332
333void wxClipboard::Clear()
334{
335 if (m_data)
336 {
337#if wxUSE_THREADS
338 /* disable GUI threads */
339#endif
340
341 // As we have data we also own the clipboard. Once we no longer own
342 // it, clear_selection is called which will set m_data to zero
343 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
344 {
345 m_waiting = TRUE;
346
347 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
348 (guint32) GDK_CURRENT_TIME );
349
350 while (m_waiting) gtk_main_iteration();
351 }
352
353 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
354 {
355 m_waiting = TRUE;
356
357 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
358 (guint32) GDK_CURRENT_TIME );
359
360 while (m_waiting) gtk_main_iteration();
361 }
362
363 if (m_data)
364 {
365 delete m_data;
366 m_data = (wxDataObject*) NULL;
367 }
368
369#if wxUSE_THREADS
370 /* re-enable GUI threads */
371#endif
372 }
373
374 m_targetRequested = 0;
375 m_formatSupported = FALSE;
376}
377
378bool wxClipboard::Open()
379{
380 wxCHECK_MSG( !m_open, FALSE, wxT("clipboard already open") );
381
382 m_open = TRUE;
383
384 return TRUE;
385}
386
387bool wxClipboard::SetData( wxDataObject *data )
388{
389 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
390
391 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
392
393 Clear();
394
395 return AddData( data );
396}
397
398bool wxClipboard::AddData( wxDataObject *data )
399{
400 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
401
402 wxCHECK_MSG( data, FALSE, wxT("data is invalid") );
403
404 // we can only store one wxDataObject
405 Clear();
406
407 m_data = data;
408
409 // get formats from wxDataObjects
410 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
411 m_data->GetAllFormats( array );
412
413 // primary selection or clipboard
414 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
415 : g_clipboardAtom;
416
417
418 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
419 {
420 wxLogTrace( TRACE_CLIPBOARD,
421 wxT("wxClipboard now supports atom %s"),
422 array[i].GetId().c_str() );
423
424// printf( "added %s\n",
425// gdk_atom_name( array[i].GetFormatId() ) );
426
427 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
428 clipboard,
429 array[i],
430 0 ); /* what is info ? */
431 }
432
433 delete[] array;
434
435 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
436 "selection_get",
437 GTK_SIGNAL_FUNC(selection_handler),
438 (gpointer) NULL );
439
440#if wxUSE_THREADS
441 /* disable GUI threads */
442#endif
443
444 /* Tell the world we offer clipboard data */
445 bool res = (gtk_selection_owner_set( m_clipboardWidget,
446 clipboard,
447 (guint32) GDK_CURRENT_TIME ));
448
449 if (m_usePrimary)
450 m_ownsPrimarySelection = res;
451 else
452 m_ownsClipboard = res;
453
454#if wxUSE_THREADS
455 /* re-enable GUI threads */
456#endif
457
458 return res;
459}
460
461void wxClipboard::Close()
462{
463 wxCHECK_RET( m_open, wxT("clipboard not open") );
464
465 m_open = FALSE;
466}
467
468bool wxClipboard::IsOpened() const
469{
470 return m_open;
471}
472
473bool wxClipboard::IsSupported( const wxDataFormat& format )
474{
475 /* reentrance problems */
476 if (m_waiting) return FALSE;
477
478 /* store requested format to be asked for by callbacks */
479 m_targetRequested = format;
480
481#if 0
482 wxLogTrace( TRACE_CLIPBOARD,
483 wxT("wxClipboard:IsSupported: requested format: %s"),
484 format.GetId().c_str() );
485#endif
486
487 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
488
489 m_formatSupported = FALSE;
490
491 /* perform query. this will set m_formatSupported to
492 TRUE if m_targetRequested is supported.
493 also, we have to wait for the "answer" from the
494 clipboard owner which is an asynchronous process.
495 therefore we set m_waiting = TRUE here and wait
496 until the callback "targets_selection_received"
497 sets it to FALSE */
498
499 m_waiting = TRUE;
500
501 gtk_selection_convert( m_targetsWidget,
502 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
503 : g_clipboardAtom,
504 g_targetsAtom,
505 (guint32) GDK_CURRENT_TIME );
506
507 while (m_waiting) gtk_main_iteration();
508
509#if defined(__WXGTK20__) && wxUSE_UNICODE
510 if (!m_formatSupported && format == wxDataFormat(wxDF_UNICODETEXT))
511 {
512 // Another try with plain STRING format
513 extern GdkAtom g_altTextAtom;
514 return IsSupported(g_altTextAtom);
515 }
516#endif
517
518 if (!m_formatSupported) return FALSE;
519
520 return TRUE;
521}
522
523bool wxClipboard::GetData( wxDataObject& data )
524{
525 wxCHECK_MSG( m_open, FALSE, wxT("clipboard not open") );
526
527 /* get formats from wxDataObjects */
528 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
529 data.GetAllFormats( array );
530
531 for (size_t i = 0; i < data.GetFormatCount(); i++)
532 {
533 wxDataFormat format( array[i] );
534
535 wxLogTrace( TRACE_CLIPBOARD,
536 wxT("wxClipboard::GetData: requested format: %s"),
537 format.GetId().c_str() );
538
539 /* is data supported by clipboard ? */
540
541 /* store requested format to be asked for by callbacks */
542 m_targetRequested = format;
543
544 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
545
546 m_formatSupported = FALSE;
547
548 /* perform query. this will set m_formatSupported to
549 TRUE if m_targetRequested is supported.
550 also, we have to wait for the "answer" from the
551 clipboard owner which is an asynchronous process.
552 therefore we set m_waiting = TRUE here and wait
553 until the callback "targets_selection_received"
554 sets it to FALSE */
555
556 m_waiting = TRUE;
557
558 gtk_selection_convert( m_targetsWidget,
559 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
560 : g_clipboardAtom,
561 g_targetsAtom,
562 (guint32) GDK_CURRENT_TIME );
563
564 while (m_waiting) gtk_main_iteration();
565
566 if (!m_formatSupported) continue;
567
568 /* store pointer to data object to be filled up by callbacks */
569 m_receivedData = &data;
570
571 /* store requested format to be asked for by callbacks */
572 m_targetRequested = format;
573
574 wxCHECK_MSG( m_targetRequested, FALSE, wxT("invalid clipboard format") );
575
576 /* start query */
577 m_formatSupported = FALSE;
578
579 /* ask for clipboard contents. this will set
580 m_formatSupported to TRUE if m_targetRequested
581 is supported.
582 also, we have to wait for the "answer" from the
583 clipboard owner which is an asynchronous process.
584 therefore we set m_waiting = TRUE here and wait
585 until the callback "targets_selection_received"
586 sets it to FALSE */
587
588 m_waiting = TRUE;
589
590 wxLogTrace( TRACE_CLIPBOARD,
591 wxT("wxClipboard::GetData: format found, start convert") );
592
593 gtk_selection_convert( m_clipboardWidget,
594 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
595 : g_clipboardAtom,
596 m_targetRequested,
597 (guint32) GDK_CURRENT_TIME );
598
599 while (m_waiting) gtk_main_iteration();
600
601 /* this is a true error as we checked for the presence of such data before */
602 wxCHECK_MSG( m_formatSupported, FALSE, wxT("error retrieving data from clipboard") );
603
604 /* return success */
605 delete[] array;
606 return TRUE;
607 }
608
609 wxLogTrace( TRACE_CLIPBOARD,
610 wxT("wxClipboard::GetData: format not found") );
611
612 /* return failure */
613 delete[] array;
614 return FALSE;
615}
616
617#endif
618 // wxUSE_CLIPBOARD
619