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