1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/clipbrd.cpp
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
15 #include "wx/clipbrd.h"
20 #include "wx/dataobj.h"
27 //-----------------------------------------------------------------------------
29 //-----------------------------------------------------------------------------
31 GdkAtom g_clipboardAtom
= 0;
32 GdkAtom g_targetsAtom
= 0;
33 GdkAtom g_timestampAtom
= 0;
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"
40 //-----------------------------------------------------------------------------
42 //-----------------------------------------------------------------------------
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
53 struct _GtkSelectionData
65 //-----------------------------------------------------------------------------
66 // "selection_received" for targets
67 //-----------------------------------------------------------------------------
71 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
72 GtkSelectionData
*selection_data
,
73 guint32
WXUNUSED(time
),
74 wxClipboard
*clipboard
)
76 if ( wxTheClipboard
&& selection_data
->length
> 0 )
78 // make sure we got the data in the correct form
79 GdkAtom type
= selection_data
->type
;
80 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
82 gchar
* atom_name
= gdk_atom_name(type
);
83 if ( strcmp(atom_name
, "TARGETS") )
85 wxLogTrace( TRACE_CLIPBOARD
,
86 wxT("got unsupported clipboard target") );
88 clipboard
->m_waiting
= false;
95 wxDataFormat
clip( selection_data
->selection
);
96 wxLogTrace( TRACE_CLIPBOARD
,
97 wxT("selection received for targets, clipboard %s"),
98 clip
.GetId().c_str() );
100 // the atoms we received, holding a list of targets (= formats)
101 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
103 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
105 wxDataFormat
format( atoms
[i
] );
107 wxLogTrace( TRACE_CLIPBOARD
,
108 wxT("selection received for targets, format %s"),
109 format
.GetId().c_str() );
111 // printf( "format %s requested %s\n",
112 // gdk_atom_name( atoms[i] ),
113 // gdk_atom_name( clipboard->m_targetRequested ) );
115 if (format
== clipboard
->m_targetRequested
)
117 clipboard
->m_waiting
= false;
118 clipboard
->m_formatSupported
= true;
124 clipboard
->m_waiting
= false;
128 //-----------------------------------------------------------------------------
129 // "selection_received" for the actual data
130 //-----------------------------------------------------------------------------
134 selection_received( GtkWidget
*WXUNUSED(widget
),
135 GtkSelectionData
*selection_data
,
136 guint32
WXUNUSED(time
),
137 wxClipboard
*clipboard
)
141 clipboard
->m_waiting
= false;
145 wxDataObject
*data_object
= clipboard
->m_receivedData
;
149 clipboard
->m_waiting
= false;
153 if (selection_data
->length
<= 0)
155 clipboard
->m_waiting
= false;
159 wxDataFormat
format( selection_data
->target
);
161 // make sure we got the data in the correct format
162 if (!data_object
->IsSupportedFormat( format
) )
164 clipboard
->m_waiting
= false;
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
)
174 clipboard
->m_waiting
= false;
179 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
181 wxTheClipboard
->m_formatSupported
= true;
182 clipboard
->m_waiting
= false;
186 //-----------------------------------------------------------------------------
188 //-----------------------------------------------------------------------------
192 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
194 if (!wxTheClipboard
) return TRUE
;
196 if (event
->selection
== GDK_SELECTION_PRIMARY
)
198 wxTheClipboard
->m_ownsPrimarySelection
= false;
201 if (event
->selection
== g_clipboardAtom
)
203 wxTheClipboard
->m_ownsClipboard
= false;
207 wxTheClipboard
->m_waiting
= false;
211 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
212 (!wxTheClipboard
->m_ownsClipboard
))
214 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
215 if (wxTheClipboard
->m_data
)
217 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
219 wxDELETE(wxTheClipboard
->m_data
);
223 wxTheClipboard
->m_waiting
= false;
228 //-----------------------------------------------------------------------------
229 // selection handler for supplying data
230 //-----------------------------------------------------------------------------
234 selection_handler( GtkWidget
*WXUNUSED(widget
),
235 GtkSelectionData
*selection_data
,
236 guint
WXUNUSED(info
),
237 guint
WXUNUSED(time
),
238 gpointer signal_data
)
240 if (!wxTheClipboard
) return;
242 if (!wxTheClipboard
->m_data
) return;
244 wxDataObject
*data
= wxTheClipboard
->m_data
;
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
)
252 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
253 gtk_selection_data_set(selection_data
,
254 GDK_SELECTION_TYPE_INTEGER
,
256 (guchar
*)&(timestamp
),
258 wxLogTrace(TRACE_CLIPBOARD
,
259 wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
264 wxDataFormat
format( selection_data
->target
);
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
)
275 if (!data
->IsSupportedFormat( format
)) return;
277 int size
= data
->GetDataSize( format
);
279 if (size
== 0) return;
281 void *d
= malloc(size
);
283 // Text data will be in UTF8 in Unicode mode.
284 data
->GetDataHere( selection_data
->target
, d
);
286 gtk_selection_data_set(
288 GDK_SELECTION_TYPE_STRING
,
297 //-----------------------------------------------------------------------------
299 //-----------------------------------------------------------------------------
301 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
303 wxClipboard::wxClipboard()
308 m_ownsClipboard
= false;
309 m_ownsPrimarySelection
= false;
312 m_receivedData
= NULL
;
314 /* we use m_targetsWidget to query what formats are available */
316 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
317 gtk_widget_realize( m_targetsWidget
);
319 gtk_signal_connect( GTK_OBJECT(m_targetsWidget
),
320 "selection_received",
321 GTK_SIGNAL_FUNC( targets_selection_received
),
324 /* we use m_clipboardWidget to get and to offer data */
326 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
327 gtk_widget_realize( m_clipboardWidget
);
329 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
330 "selection_received",
331 GTK_SIGNAL_FUNC( selection_received
),
334 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
335 "selection_clear_event",
336 GTK_SIGNAL_FUNC( selection_clear_clip
),
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
);
343 m_formatSupported
= false;
344 m_targetRequested
= 0;
347 wxClipboard::~wxClipboard()
351 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
352 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
355 void wxClipboard::Clear()
360 /* disable GUI threads */
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
)
369 gtk_selection_owner_set( NULL
, g_clipboardAtom
,
370 (guint32
) GDK_CURRENT_TIME
);
372 while (m_waiting
) gtk_main_iteration();
375 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
379 gtk_selection_owner_set( NULL
, GDK_SELECTION_PRIMARY
,
380 (guint32
) GDK_CURRENT_TIME
);
382 while (m_waiting
) gtk_main_iteration();
388 /* re-enable GUI threads */
392 m_targetRequested
= 0;
393 m_formatSupported
= false;
396 bool wxClipboard::Open()
398 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
405 bool wxClipboard::SetData( wxDataObject
*data
)
407 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
409 wxCHECK_MSG( data
, false, wxT("data is invalid") );
413 return AddData( data
);
416 bool wxClipboard::AddData( wxDataObject
*data
)
418 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
420 wxCHECK_MSG( data
, false, wxT("data is invalid") );
422 // we can only store one wxDataObject
427 // get formats from wxDataObjects
428 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
429 m_data
->GetAllFormats( array
);
431 // primary selection or clipboard
432 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
435 // by default provide TIMESTAMP as a target
436 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
441 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
443 wxLogTrace( TRACE_CLIPBOARD
,
444 wxT("wxClipboard now supports atom %s"),
445 array
[i
].GetId().c_str() );
447 // printf( "added %s\n",
448 // gdk_atom_name( array[i].GetFormatId() ) );
450 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
453 0 ); /* what is info ? */
458 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
460 GTK_SIGNAL_FUNC(selection_handler
),
461 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
464 /* disable GUI threads */
467 /* Tell the world we offer clipboard data */
468 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
470 (guint32
) GDK_CURRENT_TIME
));
473 m_ownsPrimarySelection
= res
;
475 m_ownsClipboard
= res
;
478 /* re-enable GUI threads */
484 void wxClipboard::Close()
486 wxCHECK_RET( m_open
, wxT("clipboard not open") );
491 bool wxClipboard::IsOpened() const
496 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
498 /* reentrance problems */
499 if (m_waiting
) return false;
501 /* store requested format to be asked for by callbacks */
502 m_targetRequested
= format
;
504 wxLogTrace( TRACE_CLIPBOARD
,
505 wxT("wxClipboard:IsSupported: requested format: %s"),
506 format
.GetId().c_str() );
508 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
510 m_formatSupported
= false;
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"
522 gtk_selection_convert( m_targetsWidget
,
523 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
526 (guint32
) GDK_CURRENT_TIME
);
528 while (m_waiting
) gtk_main_iteration();
530 return m_formatSupported
;
533 bool wxClipboard::GetData( wxDataObject
& data
)
535 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
537 /* get formats from wxDataObjects */
538 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
539 data
.GetAllFormats( array
);
541 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
543 wxDataFormat
format( array
[i
] );
545 wxLogTrace( TRACE_CLIPBOARD
,
546 wxT("wxClipboard::GetData: requested format: %s"),
547 format
.GetId().c_str() );
549 /* is data supported by clipboard ? */
551 /* store requested format to be asked for by callbacks */
552 m_targetRequested
= format
;
554 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
556 m_formatSupported
= false;
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"
568 gtk_selection_convert( m_targetsWidget
,
569 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
572 (guint32
) GDK_CURRENT_TIME
);
574 while (m_waiting
) gtk_main_iteration();
576 if (!m_formatSupported
) continue;
578 /* store pointer to data object to be filled up by callbacks */
579 m_receivedData
= &data
;
581 /* store requested format to be asked for by callbacks */
582 m_targetRequested
= format
;
584 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
587 m_formatSupported
= false;
589 /* ask for clipboard contents. this will set
590 m_formatSupported to true if m_targetRequested
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"
600 wxLogTrace( TRACE_CLIPBOARD
,
601 wxT("wxClipboard::GetData: format found, start convert") );
603 gtk_selection_convert( m_clipboardWidget
,
604 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
607 (guint32
) GDK_CURRENT_TIME
);
609 while (m_waiting
) gtk_main_iteration();
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)
622 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
624 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
625 #endif // UNICODE / !UNICODE
627 wxCHECK_MSG( m_formatSupported
, false,
628 wxT("error retrieving data from clipboard") );
636 wxLogTrace( TRACE_CLIPBOARD
,
637 wxT("wxClipboard::GetData: format not found") );