]> git.saurik.com Git - wxWidgets.git/blob - src/gtk1/clipbrd.cpp
Fix crash in wxDC::GetMultiLineTextExtent() after last commit.
[wxWidgets.git] / src / gtk1 / clipbrd.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/clipbrd.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #if wxUSE_CLIPBOARD
14
15 #include "wx/clipbrd.h"
16
17 #ifndef WX_PRECOMP
18 #include "wx/log.h"
19 #include "wx/utils.h"
20 #include "wx/dataobj.h"
21 #endif
22
23 #include <glib.h>
24 #include <gdk/gdk.h>
25 #include <gtk/gtk.h>
26
27 //-----------------------------------------------------------------------------
28 // data
29 //-----------------------------------------------------------------------------
30
31 GdkAtom g_clipboardAtom = 0;
32 GdkAtom g_targetsAtom = 0;
33 GdkAtom g_timestampAtom = 0;
34
35 // the trace mask we use with wxLogTrace() - call
36 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
37 // (there will be a *lot* of them!)
38 #define TRACE_CLIPBOARD "clipboard"
39
40 //-----------------------------------------------------------------------------
41 // reminder
42 //-----------------------------------------------------------------------------
43
44 /* The contents of a selection are returned in a GtkSelectionData
45 structure. selection/target identify the request.
46 type specifies the type of the return; if length < 0, and
47 the data should be ignored. This structure has object semantics -
48 no fields should be modified directly, they should not be created
49 directly, and pointers to them should not be stored beyond the duration of
50 a callback. (If the last is changed, we'll need to add reference
51 counting)
52
53 struct _GtkSelectionData
54 {
55 GdkAtom selection;
56 GdkAtom target;
57 GdkAtom type;
58 gint format;
59 guchar *data;
60 gint length;
61 };
62
63 */
64
65 //-----------------------------------------------------------------------------
66 // "selection_received" for targets
67 //-----------------------------------------------------------------------------
68
69 extern "C" {
70 static void
71 targets_selection_received( GtkWidget *WXUNUSED(widget),
72 GtkSelectionData *selection_data,
73 guint32 WXUNUSED(time),
74 wxClipboard *clipboard )
75 {
76 if ( wxTheClipboard && selection_data->length > 0 )
77 {
78 // make sure we got the data in the correct form
79 GdkAtom type = selection_data->type;
80 if ( type != GDK_SELECTION_TYPE_ATOM )
81 {
82 gchar* atom_name = gdk_atom_name(type);
83 if ( strcmp(atom_name, "TARGETS") )
84 {
85 wxLogTrace( TRACE_CLIPBOARD,
86 wxT("got unsupported clipboard target") );
87
88 clipboard->m_waiting = false;
89 g_free(atom_name);
90 return;
91 }
92 g_free(atom_name);
93 }
94
95 wxDataFormat clip( selection_data->selection );
96 wxLogTrace( TRACE_CLIPBOARD,
97 wxT("selection received for targets, clipboard %s"),
98 clip.GetId().c_str() );
99
100 // the atoms we received, holding a list of targets (= formats)
101 GdkAtom *atoms = (GdkAtom *)selection_data->data;
102
103 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
104 {
105 wxDataFormat format( atoms[i] );
106
107 wxLogTrace( TRACE_CLIPBOARD,
108 wxT("selection received for targets, format %s"),
109 format.GetId().c_str() );
110
111 // printf( "format %s requested %s\n",
112 // gdk_atom_name( atoms[i] ),
113 // gdk_atom_name( clipboard->m_targetRequested ) );
114
115 if (format == clipboard->m_targetRequested)
116 {
117 clipboard->m_waiting = false;
118 clipboard->m_formatSupported = true;
119 return;
120 }
121 }
122 }
123
124 clipboard->m_waiting = false;
125 }
126 }
127
128 //-----------------------------------------------------------------------------
129 // "selection_received" for the actual data
130 //-----------------------------------------------------------------------------
131
132 extern "C" {
133 static void
134 selection_received( GtkWidget *WXUNUSED(widget),
135 GtkSelectionData *selection_data,
136 guint32 WXUNUSED(time),
137 wxClipboard *clipboard )
138 {
139 if (!wxTheClipboard)
140 {
141 clipboard->m_waiting = false;
142 return;
143 }
144
145 wxDataObject *data_object = clipboard->m_receivedData;
146
147 if (!data_object)
148 {
149 clipboard->m_waiting = false;
150 return;
151 }
152
153 if (selection_data->length <= 0)
154 {
155 clipboard->m_waiting = false;
156 return;
157 }
158
159 wxDataFormat format( selection_data->target );
160
161 // make sure we got the data in the correct format
162 if (!data_object->IsSupportedFormat( format ) )
163 {
164 clipboard->m_waiting = false;
165 return;
166 }
167
168 #if 0
169 This seems to cause problems somehow
170 // make sure we got the data in the correct form (selection type).
171 // if so, copy data to target object
172 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
173 {
174 clipboard->m_waiting = false;
175 return;
176 }
177 #endif
178
179 data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
180
181 wxTheClipboard->m_formatSupported = true;
182 clipboard->m_waiting = false;
183 }
184 }
185
186 //-----------------------------------------------------------------------------
187 // "selection_clear"
188 //-----------------------------------------------------------------------------
189
190 extern "C" {
191 static gint
192 selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
193 {
194 if (!wxTheClipboard) return TRUE;
195
196 if (event->selection == GDK_SELECTION_PRIMARY)
197 {
198 wxTheClipboard->m_ownsPrimarySelection = false;
199 }
200 else
201 if (event->selection == g_clipboardAtom)
202 {
203 wxTheClipboard->m_ownsClipboard = false;
204 }
205 else
206 {
207 wxTheClipboard->m_waiting = false;
208 return FALSE;
209 }
210
211 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
212 (!wxTheClipboard->m_ownsClipboard))
213 {
214 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
215 if (wxTheClipboard->m_data)
216 {
217 wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
218
219 wxDELETE(wxTheClipboard->m_data);
220 }
221 }
222
223 wxTheClipboard->m_waiting = false;
224 return TRUE;
225 }
226 }
227
228 //-----------------------------------------------------------------------------
229 // selection handler for supplying data
230 //-----------------------------------------------------------------------------
231
232 extern "C" {
233 static void
234 selection_handler( GtkWidget *WXUNUSED(widget),
235 GtkSelectionData *selection_data,
236 guint WXUNUSED(info),
237 guint WXUNUSED(time),
238 gpointer signal_data )
239 {
240 if (!wxTheClipboard) return;
241
242 if (!wxTheClipboard->m_data) return;
243
244 wxDataObject *data = wxTheClipboard->m_data;
245
246 // ICCCM says that TIMESTAMP is a required atom.
247 // In particular, it satisfies Klipper, which polls
248 // TIMESTAMP to see if the clipboards content has changed.
249 // It shall return the time which was used to set the data.
250 if (selection_data->target == g_timestampAtom)
251 {
252 guint timestamp = GPOINTER_TO_UINT (signal_data);
253 gtk_selection_data_set(selection_data,
254 GDK_SELECTION_TYPE_INTEGER,
255 32,
256 (guchar*)&(timestamp),
257 sizeof(timestamp));
258 wxLogTrace(TRACE_CLIPBOARD,
259 wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
260 timestamp);
261 return;
262 }
263
264 wxDataFormat format( selection_data->target );
265
266 wxLogTrace(TRACE_CLIPBOARD,
267 wxT("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
268 format.GetId().c_str(),
269 wxString::FromAscii(gdk_atom_name(selection_data->target)).c_str(),
270 wxString::FromAscii(gdk_atom_name(selection_data->type)).c_str(),
271 wxString::FromAscii(gdk_atom_name(selection_data->selection)).c_str(),
272 GPOINTER_TO_UINT( signal_data )
273 );
274
275 if (!data->IsSupportedFormat( format )) return;
276
277 int size = data->GetDataSize( format );
278
279 if (size == 0) return;
280
281 void *d = malloc(size);
282
283 // Text data will be in UTF8 in Unicode mode.
284 data->GetDataHere( selection_data->target, d );
285
286 gtk_selection_data_set(
287 selection_data,
288 GDK_SELECTION_TYPE_STRING,
289 8 * sizeof(gchar),
290 (unsigned char*) d,
291 size );
292
293 free(d);
294 }
295 }
296
297 //-----------------------------------------------------------------------------
298 // wxClipboard
299 //-----------------------------------------------------------------------------
300
301 IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
302
303 wxClipboard::wxClipboard()
304 {
305 m_open = false;
306 m_waiting = false;
307
308 m_ownsClipboard = false;
309 m_ownsPrimarySelection = false;
310
311 m_data = NULL;
312 m_receivedData = NULL;
313
314 /* we use m_targetsWidget to query what formats are available */
315
316 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
317 gtk_widget_realize( m_targetsWidget );
318
319 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
320 "selection_received",
321 GTK_SIGNAL_FUNC( targets_selection_received ),
322 (gpointer) this );
323
324 /* we use m_clipboardWidget to get and to offer data */
325
326 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
327 gtk_widget_realize( m_clipboardWidget );
328
329 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
330 "selection_received",
331 GTK_SIGNAL_FUNC( selection_received ),
332 (gpointer) this );
333
334 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
335 "selection_clear_event",
336 GTK_SIGNAL_FUNC( selection_clear_clip ),
337 (gpointer) NULL );
338
339 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
340 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
341 if (!g_timestampAtom) g_timestampAtom = gdk_atom_intern ("TIMESTAMP", FALSE);
342
343 m_formatSupported = false;
344 m_targetRequested = 0;
345 }
346
347 wxClipboard::~wxClipboard()
348 {
349 Clear();
350
351 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
352 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
353 }
354
355 void wxClipboard::Clear()
356 {
357 if (m_data)
358 {
359 #if wxUSE_THREADS
360 /* disable GUI threads */
361 #endif
362
363 // As we have data we also own the clipboard. Once we no longer own
364 // it, clear_selection is called which will set m_data to zero
365 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
366 {
367 m_waiting = true;
368
369 gtk_selection_owner_set( NULL, g_clipboardAtom,
370 (guint32) GDK_CURRENT_TIME );
371
372 while (m_waiting) gtk_main_iteration();
373 }
374
375 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
376 {
377 m_waiting = true;
378
379 gtk_selection_owner_set( NULL, GDK_SELECTION_PRIMARY,
380 (guint32) GDK_CURRENT_TIME );
381
382 while (m_waiting) gtk_main_iteration();
383 }
384
385 wxDELETE(m_data);
386
387 #if wxUSE_THREADS
388 /* re-enable GUI threads */
389 #endif
390 }
391
392 m_targetRequested = 0;
393 m_formatSupported = false;
394 }
395
396 bool wxClipboard::Open()
397 {
398 wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
399
400 m_open = true;
401
402 return true;
403 }
404
405 bool wxClipboard::SetData( wxDataObject *data )
406 {
407 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
408
409 wxCHECK_MSG( data, false, wxT("data is invalid") );
410
411 Clear();
412
413 return AddData( data );
414 }
415
416 bool wxClipboard::AddData( wxDataObject *data )
417 {
418 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
419
420 wxCHECK_MSG( data, false, wxT("data is invalid") );
421
422 // we can only store one wxDataObject
423 Clear();
424
425 m_data = data;
426
427 // get formats from wxDataObjects
428 wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
429 m_data->GetAllFormats( array );
430
431 // primary selection or clipboard
432 GdkAtom clipboard = m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
433 : g_clipboardAtom;
434
435 // by default provide TIMESTAMP as a target
436 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
437 clipboard,
438 g_timestampAtom,
439 0 );
440
441 for (size_t i = 0; i < m_data->GetFormatCount(); i++)
442 {
443 wxLogTrace( TRACE_CLIPBOARD,
444 wxT("wxClipboard now supports atom %s"),
445 array[i].GetId().c_str() );
446
447 // printf( "added %s\n",
448 // gdk_atom_name( array[i].GetFormatId() ) );
449
450 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
451 clipboard,
452 array[i],
453 0 ); /* what is info ? */
454 }
455
456 delete[] array;
457
458 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
459 "selection_get",
460 GTK_SIGNAL_FUNC(selection_handler),
461 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
462
463 #if wxUSE_THREADS
464 /* disable GUI threads */
465 #endif
466
467 /* Tell the world we offer clipboard data */
468 bool res = (gtk_selection_owner_set( m_clipboardWidget,
469 clipboard,
470 (guint32) GDK_CURRENT_TIME ));
471
472 if (m_usePrimary)
473 m_ownsPrimarySelection = res;
474 else
475 m_ownsClipboard = res;
476
477 #if wxUSE_THREADS
478 /* re-enable GUI threads */
479 #endif
480
481 return res;
482 }
483
484 void wxClipboard::Close()
485 {
486 wxCHECK_RET( m_open, wxT("clipboard not open") );
487
488 m_open = false;
489 }
490
491 bool wxClipboard::IsOpened() const
492 {
493 return m_open;
494 }
495
496 bool wxClipboard::IsSupported( const wxDataFormat& format )
497 {
498 /* reentrance problems */
499 if (m_waiting) return false;
500
501 /* store requested format to be asked for by callbacks */
502 m_targetRequested = format;
503
504 wxLogTrace( TRACE_CLIPBOARD,
505 wxT("wxClipboard:IsSupported: requested format: %s"),
506 format.GetId().c_str() );
507
508 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
509
510 m_formatSupported = false;
511
512 /* perform query. this will set m_formatSupported to
513 true if m_targetRequested is supported.
514 also, we have to wait for the "answer" from the
515 clipboard owner which is an asynchronous process.
516 therefore we set m_waiting = true here and wait
517 until the callback "targets_selection_received"
518 sets it to false */
519
520 m_waiting = true;
521
522 gtk_selection_convert( m_targetsWidget,
523 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
524 : g_clipboardAtom,
525 g_targetsAtom,
526 (guint32) GDK_CURRENT_TIME );
527
528 while (m_waiting) gtk_main_iteration();
529
530 return m_formatSupported;
531 }
532
533 bool wxClipboard::GetData( wxDataObject& data )
534 {
535 wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
536
537 /* get formats from wxDataObjects */
538 wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
539 data.GetAllFormats( array );
540
541 for (size_t i = 0; i < data.GetFormatCount(); i++)
542 {
543 wxDataFormat format( array[i] );
544
545 wxLogTrace( TRACE_CLIPBOARD,
546 wxT("wxClipboard::GetData: requested format: %s"),
547 format.GetId().c_str() );
548
549 /* is data supported by clipboard ? */
550
551 /* store requested format to be asked for by callbacks */
552 m_targetRequested = format;
553
554 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
555
556 m_formatSupported = false;
557
558 /* perform query. this will set m_formatSupported to
559 true if m_targetRequested is supported.
560 also, we have to wait for the "answer" from the
561 clipboard owner which is an asynchronous process.
562 therefore we set m_waiting = true here and wait
563 until the callback "targets_selection_received"
564 sets it to false */
565
566 m_waiting = true;
567
568 gtk_selection_convert( m_targetsWidget,
569 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
570 : g_clipboardAtom,
571 g_targetsAtom,
572 (guint32) GDK_CURRENT_TIME );
573
574 while (m_waiting) gtk_main_iteration();
575
576 if (!m_formatSupported) continue;
577
578 /* store pointer to data object to be filled up by callbacks */
579 m_receivedData = &data;
580
581 /* store requested format to be asked for by callbacks */
582 m_targetRequested = format;
583
584 wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
585
586 /* start query */
587 m_formatSupported = false;
588
589 /* ask for clipboard contents. this will set
590 m_formatSupported to true if m_targetRequested
591 is supported.
592 also, we have to wait for the "answer" from the
593 clipboard owner which is an asynchronous process.
594 therefore we set m_waiting = true here and wait
595 until the callback "targets_selection_received"
596 sets it to false */
597
598 m_waiting = true;
599
600 wxLogTrace( TRACE_CLIPBOARD,
601 wxT("wxClipboard::GetData: format found, start convert") );
602
603 gtk_selection_convert( m_clipboardWidget,
604 m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
605 : g_clipboardAtom,
606 m_targetRequested,
607 (guint32) GDK_CURRENT_TIME );
608
609 while (m_waiting) gtk_main_iteration();
610
611 /*
612 Normally this is a true error as we checked for the presence of such
613 data before, but there are applications that may return an empty
614 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
615 which would produce a false error message here, so we check for the
616 size of the string first. In ansi, GetDataSize returns an extra
617 value (for the closing null?), with unicode, the exact number of
618 tokens is given (that is more than 1 for special characters)
619 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
620 */
621 #if wxUSE_UNICODE
622 if ( format != wxDF_UNICODETEXT || data.GetDataSize(format) > 0 )
623 #else // !UNICODE
624 if ( format != wxDF_TEXT || data.GetDataSize(format) > 1 )
625 #endif // UNICODE / !UNICODE
626 {
627 wxCHECK_MSG( m_formatSupported, false,
628 wxT("error retrieving data from clipboard") );
629 }
630
631 /* return success */
632 delete[] array;
633 return true;
634 }
635
636 wxLogTrace( TRACE_CLIPBOARD,
637 wxT("wxClipboard::GetData: format not found") );
638
639 /* return failure */
640 delete[] array;
641 return false;
642 }
643
644 #endif
645 // wxUSE_CLIPBOARD