]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/clipbrd.cpp
tooltip bug (which I introduced recently) corrected
[wxWidgets.git] / src / gtk / clipbrd.cpp
CommitLineData
dc86cb34
RR
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
06cfab17 16#if wxUSE_CLIPBOARD
ac57418f 17
034be888
RR
18#include "wx/utils.h"
19
83624f79
RR
20#include "glib.h"
21#include "gdk/gdk.h"
22#include "gtk/gtk.h"
23
dc86cb34
RR
24//-----------------------------------------------------------------------------
25// data
26//-----------------------------------------------------------------------------
27
28wxClipboard *wxTheClipboard = (wxClipboard*) NULL;
29
fd0eed64 30GdkAtom g_clipboardAtom = 0;
b527aac5 31GdkAtom g_targetsAtom = 0;
fd0eed64 32
dc86cb34 33//-----------------------------------------------------------------------------
b527aac5 34// reminder
dc86cb34
RR
35//-----------------------------------------------------------------------------
36
b527aac5
RR
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
dc86cb34 47{
b527aac5
RR
48 GdkAtom selection;
49 GdkAtom target;
50 GdkAtom type;
51 gint format;
52 guchar *data;
53 gint length;
54};
55
56*/
dc86cb34 57
b527aac5
RR
58//-----------------------------------------------------------------------------
59// "selection_received" for targets
60//-----------------------------------------------------------------------------
61
62static void
63targets_selection_received( GtkWidget *WXUNUSED(widget),
64 GtkSelectionData *selection_data,
034be888
RR
65#if (GTK_MINOR_VERSION > 0)
66 guint32 WXUNUSED(time),
67#endif
b527aac5 68 wxClipboard *clipboard )
dc86cb34 69{
034be888
RR
70 if (!wxTheClipboard)
71 {
72 clipboard->m_waiting = FALSE;
73 return;
74 }
b527aac5 75
034be888
RR
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 }
b527aac5 88
8b53e5a2
RR
89 // the atoms we received, holding a list of targets (= formats)
90 GdkAtom *atoms = (GdkAtom *)selection_data->data;
b527aac5 91
8b53e5a2
RR
92 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
93 {
034be888
RR
94/*
95 char *name = gdk_atom_name (atoms[i]);
96 if (name) printf( "Format available: %s.\n", name );
97*/
3c1b65a4 98
8b53e5a2
RR
99 if (atoms[i] == clipboard->m_targetRequested)
100 {
034be888 101 clipboard->m_waiting = FALSE;
8b53e5a2
RR
102 clipboard->m_formatSupported = TRUE;
103 return;
104 }
105 }
b527aac5 106
034be888 107 clipboard->m_waiting = FALSE;
8b53e5a2 108 return;
dc86cb34
RR
109}
110
111//-----------------------------------------------------------------------------
b527aac5 112// "selection_received" for the actual data
dc86cb34
RR
113//-----------------------------------------------------------------------------
114
fd0eed64 115static void
b527aac5
RR
116selection_received( GtkWidget *WXUNUSED(widget),
117 GtkSelectionData *selection_data,
034be888
RR
118#if (GTK_MINOR_VERSION > 0)
119 guint32 WXUNUSED(time),
120#endif
b527aac5 121 wxClipboard *clipboard )
dc86cb34 122{
034be888
RR
123 if (!wxTheClipboard)
124 {
125 clipboard->m_waiting = FALSE;
126 return;
127 }
b527aac5 128
8b53e5a2
RR
129 wxDataObject *data_object = clipboard->m_receivedData;
130
034be888
RR
131 if (!data_object)
132 {
133 clipboard->m_waiting = FALSE;
134 return;
135 }
8b53e5a2 136
034be888
RR
137 if (selection_data->length <= 0)
138 {
139 clipboard->m_waiting = FALSE;
140 return;
141 }
b527aac5 142
034be888
RR
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 }
8b53e5a2 149
034be888
RR
150 /* make sure we got the data in the correct form (selection type).
151 if so, copy data to target object */
8b53e5a2 152
0d2a2b60 153 switch (data_object->GetFormat().GetType())
8b53e5a2
RR
154 {
155 case wxDF_TEXT:
156 {
034be888
RR
157 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
158 {
159 clipboard->m_waiting = FALSE;
160 return;
161 }
8b53e5a2
RR
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 {
034be888
RR
174 if (selection_data->type != GDK_SELECTION_TYPE_BITMAP)
175 {
176 clipboard->m_waiting = FALSE;
177 return;
178 }
8b53e5a2
RR
179
180 break;
181 }
182
183 case wxDF_PRIVATE:
184 {
034be888
RR
185 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
186 {
187 clipboard->m_waiting = FALSE;
188 return;
189 }
8b53e5a2
RR
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 {
034be888 200 clipboard->m_waiting = FALSE;
8b53e5a2
RR
201 return;
202 }
203 }
204
205 wxTheClipboard->m_formatSupported = TRUE;
034be888 206 clipboard->m_waiting = FALSE;
dc86cb34 207}
fd0eed64
RR
208
209//-----------------------------------------------------------------------------
210// "selection_clear"
211//-----------------------------------------------------------------------------
212
213static gint
aeeb6a44 214selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 215{
8b53e5a2 216 if (!wxTheClipboard) return TRUE;
2830bf19 217
aeeb6a44
RR
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 {
0d2a2b60
RR
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 }
aeeb6a44 242 }
fd0eed64 243
8b53e5a2 244 return TRUE;
fd0eed64
RR
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{
8b53e5a2 254 if (!wxTheClipboard) return;
fd0eed64 255
0d2a2b60
RR
256 if (!wxTheClipboard->m_dataBroker) return;
257
258 wxNode *node = wxTheClipboard->m_dataBroker->m_dataObjects.First();
8b53e5a2
RR
259
260 while (node)
261 {
262 wxDataObject *data_object = (wxDataObject *)node->Data();
263
0d2a2b60 264 if (data_object->GetFormat().GetAtom() != selection_data->target)
8b53e5a2
RR
265 {
266 node = node->Next();
267 break;
268 }
269
0d2a2b60 270 switch (data_object->GetFormat().GetType())
8b53e5a2
RR
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
0d2a2b60 304 if (private_object->GetSize() == 0) return;
8b53e5a2
RR
305
306 gtk_selection_data_set(
307 selection_data,
308 GDK_SELECTION_TYPE_STRING,
309 8*sizeof(gchar),
310 (unsigned char*) private_object->GetData(),
0d2a2b60 311 (int) private_object->GetSize() );
8b53e5a2
RR
312 }
313
314 default:
315 break;
316 }
317
318 node = node->Next();
319 }
fd0eed64 320}
dc86cb34
RR
321
322//-----------------------------------------------------------------------------
323// wxClipboard
324//-----------------------------------------------------------------------------
325
326IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
327
328wxClipboard::wxClipboard()
329{
8b53e5a2
RR
330 m_open = FALSE;
331
aeeb6a44
RR
332 m_ownsClipboard = FALSE;
333 m_ownsPrimarySelection = FALSE;
334
0d2a2b60 335 m_dataBroker = (wxDataBroker*) NULL;
fd0eed64 336
8b53e5a2 337 m_receivedData = (wxDataObject*) NULL;
99c67c77 338
034be888
RR
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
8b53e5a2
RR
351 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
352 gtk_widget_realize( m_clipboardWidget );
353
034be888
RR
354 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
355 "selection_received",
356 GTK_SIGNAL_FUNC( selection_received ),
357 (gpointer) this );
358
8b53e5a2
RR
359 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
360 "selection_clear_event",
aeeb6a44 361 GTK_SIGNAL_FUNC( selection_clear_clip ),
8b53e5a2 362 (gpointer) NULL );
fd0eed64 363
8b53e5a2 364 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
8b53e5a2
RR
365 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
366
367 m_formatSupported = FALSE;
368 m_targetRequested = 0;
dc86cb34
RR
369}
370
371wxClipboard::~wxClipboard()
b527aac5 372{
8b53e5a2 373 Clear();
b527aac5 374
8b53e5a2 375 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
034be888 376 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
b527aac5
RR
377}
378
379void wxClipboard::Clear()
dc86cb34 380{
0d2a2b60 381 if (m_dataBroker)
8b53e5a2
RR
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 */
fd0eed64 385
aeeb6a44 386 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
8b53e5a2
RR
387 {
388 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, GDK_CURRENT_TIME );
389 }
db1b4961 390
aeeb6a44
RR
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
0d2a2b60
RR
396 if (m_dataBroker)
397 {
398 delete m_dataBroker;
399 m_dataBroker = (wxDataBroker*) NULL;
400 }
8b53e5a2 401 }
b527aac5 402
8b53e5a2 403 m_targetRequested = 0;
b527aac5 404
8b53e5a2
RR
405 m_formatSupported = FALSE;
406}
407
408bool wxClipboard::Open()
409{
410 wxCHECK_MSG( !m_open, FALSE, "clipboard already open" );
b527aac5 411
8b53e5a2 412 m_open = TRUE;
b527aac5 413
8b53e5a2 414 return TRUE;
dc86cb34
RR
415}
416
75ce0581 417bool wxClipboard::SetData( wxDataObject *data )
dc86cb34 418{
75ce0581
RR
419 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
420
8b53e5a2 421 wxCHECK_MSG( data, FALSE, "data is invalid" );
db1b4961 422
0d2a2b60 423 Clear();
75ce0581
RR
424
425 return AddData( data );
426}
427
428bool wxClipboard::AddData( wxDataObject *data )
429{
430 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
0d2a2b60 431
75ce0581 432 wxCHECK_MSG( data, FALSE, "data is invalid" );
2830bf19 433
75ce0581 434 /* if clipboard has been cleared before, create new data broker */
8b53e5a2 435
75ce0581 436 if (!m_dataBroker) m_dataBroker = new wxDataBroker();
8b53e5a2 437
75ce0581
RR
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();
8b53e5a2 445
75ce0581
RR
446 wxCHECK_MSG( format, FALSE, "data has invalid format" );
447
448 /* This should happen automatically, but to be on the safe side */
8b53e5a2 449
75ce0581
RR
450 m_ownsClipboard = FALSE;
451 m_ownsPrimarySelection = FALSE;
aeeb6a44 452
75ce0581 453 /* Add handlers if someone requests data */
8b53e5a2 454
d345e841
RR
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
75ce0581 475 gtk_selection_add_handler( m_clipboardWidget,
8b53e5a2 476 g_clipboardAtom,
0d2a2b60 477 format,
8b53e5a2 478 selection_handler,
0d2a2b60 479 (gpointer) NULL );
8b53e5a2 480
75ce0581 481 gtk_selection_add_handler( m_clipboardWidget,
aeeb6a44 482 GDK_SELECTION_PRIMARY,
0d2a2b60 483 format,
aeeb6a44 484 selection_handler,
0d2a2b60 485 (gpointer) NULL );
d345e841 486#endif
aeeb6a44 487
75ce0581 488 /* Tell the world we offer clipboard data */
8b53e5a2 489
75ce0581 490 if (!gtk_selection_owner_set( m_clipboardWidget,
8b53e5a2
RR
491 g_clipboardAtom,
492 GDK_CURRENT_TIME ))
75ce0581
RR
493 {
494 return FALSE;
495 }
496 m_ownsClipboard = TRUE;
aeeb6a44 497
75ce0581 498 if (!gtk_selection_owner_set( m_clipboardWidget,
aeeb6a44
RR
499 GDK_SELECTION_PRIMARY,
500 GDK_CURRENT_TIME ))
75ce0581
RR
501 {
502 return FALSE;
aeeb6a44 503 }
75ce0581
RR
504 m_ownsPrimarySelection = TRUE;
505
8b53e5a2
RR
506 return TRUE;
507}
db1b4961 508
8b53e5a2
RR
509void wxClipboard::Close()
510{
511 wxCHECK_RET( m_open, "clipboard not open" );
512
513 m_open = FALSE;
dc86cb34
RR
514}
515
5f699c22 516bool wxClipboard::IsSupported( wxDataFormat format )
b527aac5 517{
0d2a2b60
RR
518 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
519
75ce0581 520 /* store requested format to be asked for by callbacks */
0d2a2b60 521
5f699c22 522 m_targetRequested = format.GetAtom();
b527aac5 523
0d2a2b60
RR
524 wxCHECK_MSG( m_targetRequested, FALSE, "invalid clipboard format" );
525
8b53e5a2 526 m_formatSupported = FALSE;
b527aac5 527
0d2a2b60 528 /* perform query. this will set m_formatSupported to
034be888
RR
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,
8b53e5a2
RR
539 g_clipboardAtom,
540 g_targetsAtom,
541 GDK_CURRENT_TIME );
034be888
RR
542
543 while (m_waiting) gtk_main_iteration();
544
8b53e5a2 545 if (!m_formatSupported) return FALSE;
75ce0581
RR
546
547 return TRUE;
548}
549
5f699c22 550bool wxClipboard::GetData( wxDataObject *data )
75ce0581
RR
551{
552 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
553
554 /* is data supported by clipboard ? */
555
5f699c22 556 if (!IsSupported( data->GetFormat() )) return FALSE;
75ce0581
RR
557
558 /* store pointer to data object to be filled up by callbacks */
559
5f699c22 560 m_receivedData = data;
75ce0581
RR
561
562 /* store requested format to be asked for by callbacks */
563
5f699c22 564 m_targetRequested = data->GetFormat().GetAtom();
b527aac5 565
75ce0581
RR
566 wxCHECK_MSG( m_targetRequested, FALSE, "invalid clipboard format" );
567
568 /* start query */
8b53e5a2 569
8b53e5a2 570 m_formatSupported = FALSE;
b527aac5 571
034be888
RR
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 */
b527aac5 580
034be888 581 m_waiting = TRUE;
75ce0581 582
8b53e5a2
RR
583 gtk_selection_convert( m_clipboardWidget,
584 g_clipboardAtom,
585 m_targetRequested,
586 GDK_CURRENT_TIME );
b527aac5 587
034be888 588 while (m_waiting) gtk_main_iteration();
b527aac5 589
0d2a2b60
RR
590 /* this is a true error as we checked for the presence of such data before */
591
8b53e5a2
RR
592 wxCHECK_MSG( m_formatSupported, FALSE, "error retrieving data from clipboard" );
593
594 return TRUE;
b527aac5
RR
595}
596
b527aac5
RR
597//-----------------------------------------------------------------------------
598// wxClipboardModule
599//-----------------------------------------------------------------------------
600
601IMPLEMENT_DYNAMIC_CLASS(wxClipboardModule,wxModule)
602
603bool wxClipboardModule::OnInit()
604{
8b53e5a2 605 wxTheClipboard = new wxClipboard();
b527aac5 606
8b53e5a2 607 return TRUE;
b527aac5
RR
608}
609
610void wxClipboardModule::OnExit()
dc86cb34 611{
8b53e5a2
RR
612 if (wxTheClipboard) delete wxTheClipboard;
613 wxTheClipboard = (wxClipboard*) NULL;
dc86cb34 614}
ac57418f
RR
615
616#endif
617
618 // wxUSE_CLIPBOARD
619