]> git.saurik.com Git - wxWidgets.git/blame - src/gtk1/clipbrd.cpp
Proofed that iostreams break threads
[wxWidgets.git] / src / gtk1 / 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
83624f79
RR
16#include "glib.h"
17#include "gdk/gdk.h"
18#include "gtk/gtk.h"
19
dc86cb34
RR
20//-----------------------------------------------------------------------------
21// data
22//-----------------------------------------------------------------------------
23
24wxClipboard *wxTheClipboard = (wxClipboard*) NULL;
25
fd0eed64
RR
26GdkAtom g_textAtom = 0;
27GdkAtom g_clipboardAtom = 0;
b527aac5 28GdkAtom g_targetsAtom = 0;
fd0eed64 29
dc86cb34 30//-----------------------------------------------------------------------------
b527aac5 31// reminder
dc86cb34
RR
32//-----------------------------------------------------------------------------
33
b527aac5
RR
34/* The contents of a selection are returned in a GtkSelectionData
35 structure. selection/target identify the request.
36 type specifies the type of the return; if length < 0, and
37 the data should be ignored. This structure has object semantics -
38 no fields should be modified directly, they should not be created
39 directly, and pointers to them should not be stored beyond the duration of
40 a callback. (If the last is changed, we'll need to add reference
41 counting)
42
43struct _GtkSelectionData
dc86cb34 44{
b527aac5
RR
45 GdkAtom selection;
46 GdkAtom target;
47 GdkAtom type;
48 gint format;
49 guchar *data;
50 gint length;
51};
52
53*/
dc86cb34 54
b527aac5
RR
55//-----------------------------------------------------------------------------
56// "selection_received" for targets
57//-----------------------------------------------------------------------------
58
59static void
60targets_selection_received( GtkWidget *WXUNUSED(widget),
61 GtkSelectionData *selection_data,
62 wxClipboard *clipboard )
dc86cb34 63{
8b53e5a2 64 if (!wxTheClipboard) return;
b527aac5 65
8b53e5a2 66 if (selection_data->length <= 0) return;
b527aac5 67
8b53e5a2
RR
68 // make sure we got the data in the correct form
69 if (selection_data->type != GDK_SELECTION_TYPE_ATOM) return;
b527aac5 70
8b53e5a2
RR
71 // the atoms we received, holding a list of targets (= formats)
72 GdkAtom *atoms = (GdkAtom *)selection_data->data;
b527aac5 73
8b53e5a2
RR
74 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
75 {
76 if (atoms[i] == clipboard->m_targetRequested)
77 {
78 clipboard->m_formatSupported = TRUE;
79 return;
80 }
81 }
b527aac5 82
8b53e5a2 83 return;
dc86cb34
RR
84}
85
86//-----------------------------------------------------------------------------
b527aac5 87// "selection_received" for the actual data
dc86cb34
RR
88//-----------------------------------------------------------------------------
89
fd0eed64 90static void
b527aac5
RR
91selection_received( GtkWidget *WXUNUSED(widget),
92 GtkSelectionData *selection_data,
93 wxClipboard *clipboard )
dc86cb34 94{
8b53e5a2 95 if (!wxTheClipboard) return;
b527aac5 96
8b53e5a2
RR
97 wxDataObject *data_object = clipboard->m_receivedData;
98
99 if (!data_object) return;
100
101 if (selection_data->length <= 0) return;
b527aac5 102
8b53e5a2
RR
103 // make sure we got the data in the correct format
104
105 if (data_object->m_formatAtom != selection_data->target) return;
106
107 // make sure we got the data in the correct form (selection type).
108 // if so, copy data to target object
109
110 switch (data_object->GetFormat())
111 {
112 case wxDF_TEXT:
113 {
114 if (selection_data->type != GDK_SELECTION_TYPE_STRING) return;
115
116 wxTextDataObject *text_object = (wxTextDataObject *) data_object;
117
118 wxString text = (const char*) selection_data->data;
119
120 text_object->SetText( text );
121
122 break;
123 }
124
125 case wxDF_BITMAP:
126 {
127 if (selection_data->type != GDK_SELECTION_TYPE_BITMAP) return;
128
129 return;
130
131 break;
132 }
133
134 case wxDF_PRIVATE:
135 {
136 if (selection_data->type != GDK_SELECTION_TYPE_STRING) return;
137
138 wxPrivateDataObject *private_object = (wxPrivateDataObject *) data_object;
139
140 private_object->SetData( (const char*) selection_data->data, (size_t) selection_data->length );
141
142 break;
143 }
144
145 default:
146 {
147 return;
148 }
149 }
150
151 wxTheClipboard->m_formatSupported = TRUE;
dc86cb34 152}
fd0eed64
RR
153
154//-----------------------------------------------------------------------------
155// "selection_clear"
156//-----------------------------------------------------------------------------
157
158static gint
aeeb6a44 159selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 160{
8b53e5a2 161 if (!wxTheClipboard) return TRUE;
2830bf19 162
aeeb6a44
RR
163 if (event->selection == GDK_SELECTION_PRIMARY)
164 {
165 wxTheClipboard->m_ownsPrimarySelection = FALSE;
166 }
167 else
168 if (event->selection == g_clipboardAtom)
169 {
170 wxTheClipboard->m_ownsClipboard = FALSE;
171 }
172 else
173 {
174 return FALSE;
175 }
176
177 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
178 (!wxTheClipboard->m_ownsClipboard))
179 {
180 // the clipboard is no longer in our hands. we can the
181 // clipboard data.
b527aac5 182
aeeb6a44
RR
183 wxTheClipboard->m_dataObjects.Clear();
184 }
fd0eed64 185
8b53e5a2 186 return TRUE;
fd0eed64
RR
187}
188
189//-----------------------------------------------------------------------------
190// selection handler for supplying data
191//-----------------------------------------------------------------------------
192
193static void
194selection_handler( GtkWidget *WXUNUSED(widget), GtkSelectionData *selection_data, gpointer WXUNUSED(data) )
195{
8b53e5a2 196 if (!wxTheClipboard) return;
fd0eed64 197
8b53e5a2
RR
198 wxNode *node = wxTheClipboard->m_dataObjects.First();
199
200 while (node)
201 {
202 wxDataObject *data_object = (wxDataObject *)node->Data();
203
204 if (data_object->m_formatAtom != selection_data->target)
205 {
206 node = node->Next();
207 break;
208 }
209
210 switch (data_object->GetFormat())
211 {
212 case wxDF_TEXT:
213 {
214 wxTextDataObject *text_object = (wxTextDataObject*) data_object;
215
216 wxString text = text_object->GetText();
217
218 char *s = WXSTRINGCAST text;
219 int len = (int) text.Length();
220
221 gtk_selection_data_set(
222 selection_data,
223 GDK_SELECTION_TYPE_STRING,
224 8*sizeof(gchar),
225 (unsigned char*) s,
226 len );
227
228 break;
229 }
230
231 case wxDF_BITMAP:
232 {
233 // wxBitmapDataObject *private_object = (wxBitmapDataObject*) data_object;
234
235 // how do we do that ?
236
237 break;
238 }
239
240 case wxDF_PRIVATE:
241 {
242 wxPrivateDataObject *private_object = (wxPrivateDataObject*) data_object;
243
244 if (private_object->GetDataSize() == 0) return;
245
246 gtk_selection_data_set(
247 selection_data,
248 GDK_SELECTION_TYPE_STRING,
249 8*sizeof(gchar),
250 (unsigned char*) private_object->GetData(),
251 (int) private_object->GetDataSize() );
252 }
253
254 default:
255 break;
256 }
257
258 node = node->Next();
259 }
fd0eed64 260}
dc86cb34
RR
261
262//-----------------------------------------------------------------------------
263// wxClipboard
264//-----------------------------------------------------------------------------
265
266IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
267
268wxClipboard::wxClipboard()
269{
8b53e5a2
RR
270 m_open = FALSE;
271
aeeb6a44
RR
272 m_ownsClipboard = FALSE;
273 m_ownsPrimarySelection = FALSE;
274
8b53e5a2 275 m_dataObjects.DeleteContents( TRUE );
fd0eed64 276
8b53e5a2 277 m_receivedData = (wxDataObject*) NULL;
99c67c77 278
8b53e5a2
RR
279 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
280 gtk_widget_realize( m_clipboardWidget );
281
282 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
283 "selection_clear_event",
aeeb6a44 284 GTK_SIGNAL_FUNC( selection_clear_clip ),
8b53e5a2 285 (gpointer) NULL );
fd0eed64 286
8b53e5a2
RR
287 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
288 if (!g_textAtom) g_textAtom = gdk_atom_intern( "TEXT", FALSE );
289 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
290
291 m_formatSupported = FALSE;
292 m_targetRequested = 0;
dc86cb34
RR
293}
294
295wxClipboard::~wxClipboard()
b527aac5 296{
8b53e5a2 297 Clear();
b527aac5 298
8b53e5a2 299 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
b527aac5
RR
300}
301
302void wxClipboard::Clear()
dc86cb34 303{
8b53e5a2
RR
304 if (m_dataObjects.GetCount())
305 {
306 /* As we have data we also own the clipboard. Once we no longer own
307 it, clear_selection is called which will set m_data to zero */
fd0eed64 308
aeeb6a44 309 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
8b53e5a2
RR
310 {
311 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, GDK_CURRENT_TIME );
312 }
db1b4961 313
aeeb6a44
RR
314 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
315 {
316 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME );
317 }
318
8b53e5a2
RR
319 m_dataObjects.Clear();
320 }
b527aac5 321
8b53e5a2 322 m_targetRequested = 0;
b527aac5 323
8b53e5a2
RR
324 m_formatSupported = FALSE;
325}
326
327bool wxClipboard::Open()
328{
329 wxCHECK_MSG( !m_open, FALSE, "clipboard already open" );
b527aac5 330
8b53e5a2 331 m_open = TRUE;
b527aac5 332
8b53e5a2 333 return TRUE;
dc86cb34
RR
334}
335
8b53e5a2 336bool wxClipboard::SetData( wxDataObject *data )
dc86cb34 337{
8b53e5a2 338 wxCHECK_MSG( data, FALSE, "data is invalid" );
db1b4961 339
2830bf19
RR
340 wxNode *node = m_dataObjects.First();
341
342 while (node)
343 {
344 wxDataObject *d = (wxDataObject*)node->Data();
345
346 if (d->GetFormat() == data->GetFormat())
347 {
348 m_dataObjects.DeleteNode( node );
349
350 break;
351 }
352
353 node = node->Next();
354 }
355
8b53e5a2
RR
356 m_dataObjects.Append( data );
357
358 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
359
360 if (data->GetFormat() == wxDF_PRIVATE)
361 {
362 wxPrivateDataObject* pd = (wxPrivateDataObject*) data;
363
364 wxCHECK_MSG( !pd->GetId().IsEmpty(), FALSE, "private clipboard format requires ID string" );
365
366 data->m_formatAtom = GetTargetAtom( data->GetFormat(), pd->GetId() );
367 }
368 else
369 {
370 data->m_formatAtom = GetTargetAtom( data->GetFormat() );
371 }
aeeb6a44
RR
372
373 // This should happen automatically
8b53e5a2 374
aeeb6a44
RR
375 m_ownsClipboard = FALSE;
376 m_ownsPrimarySelection = FALSE;
377
8b53e5a2
RR
378 // Add handlers if someone requests data
379
380 gtk_selection_add_handler( m_clipboardWidget,
381 g_clipboardAtom,
382 data->m_formatAtom,
383 selection_handler,
384 NULL );
385
aeeb6a44
RR
386 gtk_selection_add_handler( m_clipboardWidget,
387 GDK_SELECTION_PRIMARY,
388 data->m_formatAtom,
389 selection_handler,
390 NULL );
391
8b53e5a2
RR
392 // Tell the world we offer clipboard data
393
394 if (!gtk_selection_owner_set( m_clipboardWidget,
395 g_clipboardAtom,
396 GDK_CURRENT_TIME ))
397 {
398 return FALSE;
399 }
aeeb6a44
RR
400 m_ownsClipboard = TRUE;
401
402 if (!gtk_selection_owner_set( m_clipboardWidget,
403 GDK_SELECTION_PRIMARY,
404 GDK_CURRENT_TIME ))
405 {
406 return FALSE;
407 }
408 m_ownsPrimarySelection = TRUE;
8b53e5a2
RR
409
410 return TRUE;
411}
db1b4961 412
8b53e5a2
RR
413void wxClipboard::Close()
414{
415 wxCHECK_RET( m_open, "clipboard not open" );
416
417 m_open = FALSE;
dc86cb34
RR
418}
419
8b53e5a2 420bool wxClipboard::IsSupportedFormat( wxDataFormat format, const wxString &id )
b527aac5 421{
8b53e5a2 422 m_targetRequested = GetTargetAtom( format, id );
b527aac5 423
8b53e5a2
RR
424 if (m_targetRequested == 0) return FALSE;
425
426 // add handler for target (= format) query
b527aac5 427
8b53e5a2
RR
428 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
429 "selection_received",
430 GTK_SIGNAL_FUNC( targets_selection_received ),
431 (gpointer) this );
b527aac5 432
8b53e5a2 433 m_formatSupported = FALSE;
b527aac5 434
8b53e5a2
RR
435 // perform query. this will set m_formatSupported to
436 // TRUE if m_targetRequested is supported
437
438 gtk_selection_convert( m_clipboardWidget,
439 g_clipboardAtom,
440 g_targetsAtom,
441 GDK_CURRENT_TIME );
b527aac5 442
8b53e5a2
RR
443 gtk_signal_disconnect_by_func( GTK_OBJECT(m_clipboardWidget),
444 GTK_SIGNAL_FUNC( targets_selection_received ),
445 (gpointer) this );
b527aac5 446
8b53e5a2 447 if (!m_formatSupported) return FALSE;
b527aac5 448
8b53e5a2 449 return TRUE;
b527aac5
RR
450}
451
8b53e5a2 452bool wxClipboard::GetData( wxDataObject *data )
dc86cb34 453{
8b53e5a2
RR
454 wxCHECK_MSG( m_open, FALSE, "clipboard not open" );
455
456 m_receivedData = data;
457
458 wxCHECK_MSG( m_receivedData, FALSE, "invalid data object" );
459
460 if (m_receivedData->GetFormat() == wxDF_PRIVATE)
461 {
462 wxPrivateDataObject* pd = (wxPrivateDataObject*) m_receivedData;
463
464 wxCHECK_MSG( !pd->GetId().IsEmpty(), FALSE, "private clipboard format requires ID string" );
465
466 m_targetRequested = GetTargetAtom( m_receivedData->GetFormat(), pd->GetId() );
467 }
468 else
469 {
470 m_targetRequested = GetTargetAtom( m_receivedData->GetFormat() );
471 }
472
473 data->m_formatAtom = m_targetRequested;
b527aac5 474
8b53e5a2 475 wxCHECK_MSG( m_targetRequested, FALSE, "unsupported clipboard format" );
b527aac5 476
8b53e5a2 477 m_formatSupported = FALSE;
b527aac5 478
8b53e5a2
RR
479 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
480 "selection_received",
481 GTK_SIGNAL_FUNC( selection_received ),
482 (gpointer) this );
b527aac5 483
8b53e5a2
RR
484 gtk_selection_convert( m_clipboardWidget,
485 g_clipboardAtom,
486 m_targetRequested,
487 GDK_CURRENT_TIME );
b527aac5 488
8b53e5a2
RR
489 gtk_signal_disconnect_by_func( GTK_OBJECT(m_clipboardWidget),
490 GTK_SIGNAL_FUNC( selection_received ),
491 (gpointer) this );
b527aac5 492
8b53e5a2
RR
493 wxCHECK_MSG( m_formatSupported, FALSE, "error retrieving data from clipboard" );
494
495 return TRUE;
b527aac5
RR
496}
497
8b53e5a2 498GdkAtom wxClipboard::GetTargetAtom( wxDataFormat format, const wxString &id )
b527aac5 499{
8b53e5a2
RR
500 // What is X representation of that format?
501
502 switch (format)
503 {
504 case wxDF_TEXT:
505 {
506 return GDK_TARGET_STRING;
507 // g_textAtom
508 }
509
510 case wxDF_BITMAP:
511 {
512 return GDK_TARGET_BITMAP;
513 break;
514 }
515
516 case wxDF_PRIVATE:
517 {
518 // we create our own X representation
519
520 return gdk_atom_intern( WXSTRINGCAST( id ), FALSE );
521 }
522
523 default:
524 {
525 return (GdkAtom) 0;
526 }
527 }
528
529 return (GdkAtom) 0;
dc86cb34
RR
530}
531
b527aac5
RR
532//-----------------------------------------------------------------------------
533// wxClipboardModule
534//-----------------------------------------------------------------------------
535
536IMPLEMENT_DYNAMIC_CLASS(wxClipboardModule,wxModule)
537
538bool wxClipboardModule::OnInit()
539{
8b53e5a2 540 wxTheClipboard = new wxClipboard();
b527aac5 541
8b53e5a2 542 return TRUE;
b527aac5
RR
543}
544
545void wxClipboardModule::OnExit()
dc86cb34 546{
8b53e5a2
RR
547 if (wxTheClipboard) delete wxTheClipboard;
548 wxTheClipboard = (wxClipboard*) NULL;
dc86cb34 549}