]>
Commit | Line | Data |
---|---|---|
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 | ||
24 | wxClipboard *wxTheClipboard = (wxClipboard*) NULL; | |
25 | ||
fd0eed64 RR |
26 | GdkAtom g_textAtom = 0; |
27 | GdkAtom g_clipboardAtom = 0; | |
b527aac5 | 28 | GdkAtom 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 | ||
43 | struct _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 | ||
59 | static void | |
60 | targets_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 | 90 | static void |
b527aac5 RR |
91 | selection_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 | ||
158 | static gint | |
aeeb6a44 | 159 | selection_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 | ||
193 | static void | |
194 | selection_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 | ||
266 | IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject) | |
267 | ||
268 | wxClipboard::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 | ||
295 | wxClipboard::~wxClipboard() | |
b527aac5 | 296 | { |
8b53e5a2 | 297 | Clear(); |
b527aac5 | 298 | |
8b53e5a2 | 299 | if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget ); |
b527aac5 RR |
300 | } |
301 | ||
302 | void 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 | ||
327 | bool 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 | 336 | bool 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 |
413 | void wxClipboard::Close() |
414 | { | |
415 | wxCHECK_RET( m_open, "clipboard not open" ); | |
416 | ||
417 | m_open = FALSE; | |
dc86cb34 RR |
418 | } |
419 | ||
8b53e5a2 | 420 | bool 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 | 452 | bool 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 | 498 | GdkAtom 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 | ||
536 | IMPLEMENT_DYNAMIC_CLASS(wxClipboardModule,wxModule) | |
537 | ||
538 | bool wxClipboardModule::OnInit() | |
539 | { | |
8b53e5a2 | 540 | wxTheClipboard = new wxClipboard(); |
b527aac5 | 541 | |
8b53e5a2 | 542 | return TRUE; |
b527aac5 RR |
543 | } |
544 | ||
545 | void wxClipboardModule::OnExit() | |
dc86cb34 | 546 | { |
8b53e5a2 RR |
547 | if (wxTheClipboard) delete wxTheClipboard; |
548 | wxTheClipboard = (wxClipboard*) NULL; | |
dc86cb34 | 549 | } |