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 static const wxChar
*TRACE_CLIPBOARD
= _T("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 _T("got unsupported clipboard target") );
88 clipboard
->m_waiting
= false;
96 wxDataFormat
clip( selection_data
->selection
);
97 wxLogTrace( TRACE_CLIPBOARD
,
98 wxT("selection received for targets, clipboard %s"),
99 clip
.GetId().c_str() );
100 #endif // __WXDEBUG__
102 // the atoms we received, holding a list of targets (= formats)
103 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
105 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
107 wxDataFormat
format( atoms
[i
] );
109 wxLogTrace( TRACE_CLIPBOARD
,
110 wxT("selection received for targets, format %s"),
111 format
.GetId().c_str() );
113 // printf( "format %s requested %s\n",
114 // gdk_atom_name( atoms[i] ),
115 // gdk_atom_name( clipboard->m_targetRequested ) );
117 if (format
== clipboard
->m_targetRequested
)
119 clipboard
->m_waiting
= false;
120 clipboard
->m_formatSupported
= true;
126 clipboard
->m_waiting
= false;
130 //-----------------------------------------------------------------------------
131 // "selection_received" for the actual data
132 //-----------------------------------------------------------------------------
136 selection_received( GtkWidget
*WXUNUSED(widget
),
137 GtkSelectionData
*selection_data
,
138 guint32
WXUNUSED(time
),
139 wxClipboard
*clipboard
)
143 clipboard
->m_waiting
= false;
147 wxDataObject
*data_object
= clipboard
->m_receivedData
;
151 clipboard
->m_waiting
= false;
155 if (selection_data
->length
<= 0)
157 clipboard
->m_waiting
= false;
161 wxDataFormat
format( selection_data
->target
);
163 // make sure we got the data in the correct format
164 if (!data_object
->IsSupportedFormat( format
) )
166 clipboard
->m_waiting
= false;
171 This seems to cause problems somehow
172 // make sure we got the data in the correct form (selection type).
173 // if so, copy data to target object
174 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
176 clipboard
->m_waiting
= false;
181 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
183 wxTheClipboard
->m_formatSupported
= true;
184 clipboard
->m_waiting
= false;
188 //-----------------------------------------------------------------------------
190 //-----------------------------------------------------------------------------
194 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
196 if (!wxTheClipboard
) return TRUE
;
198 if (event
->selection
== GDK_SELECTION_PRIMARY
)
200 wxTheClipboard
->m_ownsPrimarySelection
= false;
203 if (event
->selection
== g_clipboardAtom
)
205 wxTheClipboard
->m_ownsClipboard
= false;
209 wxTheClipboard
->m_waiting
= false;
213 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
214 (!wxTheClipboard
->m_ownsClipboard
))
216 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
217 if (wxTheClipboard
->m_data
)
219 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
221 delete wxTheClipboard
->m_data
;
222 wxTheClipboard
->m_data
= (wxDataObject
*) NULL
;
226 wxTheClipboard
->m_waiting
= false;
231 //-----------------------------------------------------------------------------
232 // selection handler for supplying data
233 //-----------------------------------------------------------------------------
237 selection_handler( GtkWidget
*WXUNUSED(widget
),
238 GtkSelectionData
*selection_data
,
239 guint
WXUNUSED(info
),
240 guint
WXUNUSED(time
),
241 gpointer signal_data
)
243 if (!wxTheClipboard
) return;
245 if (!wxTheClipboard
->m_data
) return;
247 wxDataObject
*data
= wxTheClipboard
->m_data
;
249 // ICCCM says that TIMESTAMP is a required atom.
250 // In particular, it satisfies Klipper, which polls
251 // TIMESTAMP to see if the clipboards content has changed.
252 // It shall return the time which was used to set the data.
253 if (selection_data
->target
== g_timestampAtom
)
255 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
256 gtk_selection_data_set(selection_data
,
257 GDK_SELECTION_TYPE_INTEGER
,
259 (guchar
*)&(timestamp
),
261 wxLogTrace(TRACE_CLIPBOARD
,
262 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
267 wxDataFormat
format( selection_data
->target
);
270 wxLogTrace(TRACE_CLIPBOARD
,
271 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
272 format
.GetId().c_str(),
273 wxString::FromAscii(gdk_atom_name(selection_data
->target
)).c_str(),
274 wxString::FromAscii(gdk_atom_name(selection_data
->type
)).c_str(),
275 wxString::FromAscii(gdk_atom_name(selection_data
->selection
)).c_str(),
276 GPOINTER_TO_UINT( signal_data
)
280 if (!data
->IsSupportedFormat( format
)) return;
282 int size
= data
->GetDataSize( format
);
284 if (size
== 0) return;
286 void *d
= malloc(size
);
288 // Text data will be in UTF8 in Unicode mode.
289 data
->GetDataHere( selection_data
->target
, d
);
291 gtk_selection_data_set(
293 GDK_SELECTION_TYPE_STRING
,
302 //-----------------------------------------------------------------------------
304 //-----------------------------------------------------------------------------
306 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
308 wxClipboard::wxClipboard()
313 m_ownsClipboard
= false;
314 m_ownsPrimarySelection
= false;
316 m_data
= (wxDataObject
*) NULL
;
317 m_receivedData
= (wxDataObject
*) NULL
;
319 /* we use m_targetsWidget to query what formats are available */
321 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
322 gtk_widget_realize( m_targetsWidget
);
324 gtk_signal_connect( GTK_OBJECT(m_targetsWidget
),
325 "selection_received",
326 GTK_SIGNAL_FUNC( targets_selection_received
),
329 /* we use m_clipboardWidget to get and to offer data */
331 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
332 gtk_widget_realize( m_clipboardWidget
);
334 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
335 "selection_received",
336 GTK_SIGNAL_FUNC( selection_received
),
339 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
340 "selection_clear_event",
341 GTK_SIGNAL_FUNC( selection_clear_clip
),
344 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
345 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
346 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
348 m_formatSupported
= false;
349 m_targetRequested
= 0;
352 wxClipboard::~wxClipboard()
356 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
357 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
360 void wxClipboard::Clear()
365 /* disable GUI threads */
368 // As we have data we also own the clipboard. Once we no longer own
369 // it, clear_selection is called which will set m_data to zero
370 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
374 gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
,
375 (guint32
) GDK_CURRENT_TIME
);
377 while (m_waiting
) gtk_main_iteration();
380 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
384 gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
,
385 (guint32
) GDK_CURRENT_TIME
);
387 while (m_waiting
) gtk_main_iteration();
393 m_data
= (wxDataObject
*) NULL
;
397 /* re-enable GUI threads */
401 m_targetRequested
= 0;
402 m_formatSupported
= false;
405 bool wxClipboard::Open()
407 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
414 bool wxClipboard::SetData( wxDataObject
*data
)
416 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
418 wxCHECK_MSG( data
, false, wxT("data is invalid") );
422 return AddData( data
);
425 bool wxClipboard::AddData( wxDataObject
*data
)
427 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
429 wxCHECK_MSG( data
, false, wxT("data is invalid") );
431 // we can only store one wxDataObject
436 // get formats from wxDataObjects
437 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
438 m_data
->GetAllFormats( array
);
440 // primary selection or clipboard
441 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
444 // by default provide TIMESTAMP as a target
445 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
450 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
452 wxLogTrace( TRACE_CLIPBOARD
,
453 wxT("wxClipboard now supports atom %s"),
454 array
[i
].GetId().c_str() );
456 // printf( "added %s\n",
457 // gdk_atom_name( array[i].GetFormatId() ) );
459 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
462 0 ); /* what is info ? */
467 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
469 GTK_SIGNAL_FUNC(selection_handler
),
470 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
473 /* disable GUI threads */
476 /* Tell the world we offer clipboard data */
477 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
479 (guint32
) GDK_CURRENT_TIME
));
482 m_ownsPrimarySelection
= res
;
484 m_ownsClipboard
= res
;
487 /* re-enable GUI threads */
493 void wxClipboard::Close()
495 wxCHECK_RET( m_open
, wxT("clipboard not open") );
500 bool wxClipboard::IsOpened() const
505 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
507 /* reentrance problems */
508 if (m_waiting
) return false;
510 /* store requested format to be asked for by callbacks */
511 m_targetRequested
= format
;
513 wxLogTrace( TRACE_CLIPBOARD
,
514 wxT("wxClipboard:IsSupported: requested format: %s"),
515 format
.GetId().c_str() );
517 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
519 m_formatSupported
= false;
521 /* perform query. this will set m_formatSupported to
522 true if m_targetRequested is supported.
523 also, we have to wait for the "answer" from the
524 clipboard owner which is an asynchronous process.
525 therefore we set m_waiting = true here and wait
526 until the callback "targets_selection_received"
531 gtk_selection_convert( m_targetsWidget
,
532 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
535 (guint32
) GDK_CURRENT_TIME
);
537 while (m_waiting
) gtk_main_iteration();
539 return m_formatSupported
;
542 bool wxClipboard::GetData( wxDataObject
& data
)
544 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
546 /* get formats from wxDataObjects */
547 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
548 data
.GetAllFormats( array
);
550 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
552 wxDataFormat
format( array
[i
] );
554 wxLogTrace( TRACE_CLIPBOARD
,
555 wxT("wxClipboard::GetData: requested format: %s"),
556 format
.GetId().c_str() );
558 /* is data supported by clipboard ? */
560 /* store requested format to be asked for by callbacks */
561 m_targetRequested
= format
;
563 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
565 m_formatSupported
= false;
567 /* perform query. this will set m_formatSupported to
568 true if m_targetRequested is supported.
569 also, we have to wait for the "answer" from the
570 clipboard owner which is an asynchronous process.
571 therefore we set m_waiting = true here and wait
572 until the callback "targets_selection_received"
577 gtk_selection_convert( m_targetsWidget
,
578 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
581 (guint32
) GDK_CURRENT_TIME
);
583 while (m_waiting
) gtk_main_iteration();
585 if (!m_formatSupported
) continue;
587 /* store pointer to data object to be filled up by callbacks */
588 m_receivedData
= &data
;
590 /* store requested format to be asked for by callbacks */
591 m_targetRequested
= format
;
593 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
596 m_formatSupported
= false;
598 /* ask for clipboard contents. this will set
599 m_formatSupported to true if m_targetRequested
601 also, we have to wait for the "answer" from the
602 clipboard owner which is an asynchronous process.
603 therefore we set m_waiting = true here and wait
604 until the callback "targets_selection_received"
609 wxLogTrace( TRACE_CLIPBOARD
,
610 wxT("wxClipboard::GetData: format found, start convert") );
612 gtk_selection_convert( m_clipboardWidget
,
613 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
616 (guint32
) GDK_CURRENT_TIME
);
618 while (m_waiting
) gtk_main_iteration();
621 Normally this is a true error as we checked for the presence of such
622 data before, but there are applications that may return an empty
623 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
624 which would produce a false error message here, so we check for the
625 size of the string first. In ansi, GetDataSize returns an extra
626 value (for the closing null?), with unicode, the exact number of
627 tokens is given (that is more than 1 for special characters)
628 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
631 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
633 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
634 #endif // UNICODE / !UNICODE
636 wxCHECK_MSG( m_formatSupported
, false,
637 wxT("error retrieving data from clipboard") );
645 wxLogTrace( TRACE_CLIPBOARD
,
646 wxT("wxClipboard::GetData: format not found") );