1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk1/clipbrd.cpp
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
14 #include "wx/clipbrd.h"
19 #include "wx/dataobj.h"
26 //-----------------------------------------------------------------------------
28 //-----------------------------------------------------------------------------
30 GdkAtom g_clipboardAtom
= 0;
31 GdkAtom g_targetsAtom
= 0;
32 GdkAtom g_timestampAtom
= 0;
34 // the trace mask we use with wxLogTrace() - call
35 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
36 // (there will be a *lot* of them!)
37 #define TRACE_CLIPBOARD "clipboard"
39 //-----------------------------------------------------------------------------
41 //-----------------------------------------------------------------------------
43 /* The contents of a selection are returned in a GtkSelectionData
44 structure. selection/target identify the request.
45 type specifies the type of the return; if length < 0, and
46 the data should be ignored. This structure has object semantics -
47 no fields should be modified directly, they should not be created
48 directly, and pointers to them should not be stored beyond the duration of
49 a callback. (If the last is changed, we'll need to add reference
52 struct _GtkSelectionData
64 //-----------------------------------------------------------------------------
65 // "selection_received" for targets
66 //-----------------------------------------------------------------------------
70 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
71 GtkSelectionData
*selection_data
,
72 guint32
WXUNUSED(time
),
73 wxClipboard
*clipboard
)
75 if ( wxTheClipboard
&& selection_data
->length
> 0 )
77 // make sure we got the data in the correct form
78 GdkAtom type
= selection_data
->type
;
79 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
81 gchar
* atom_name
= gdk_atom_name(type
);
82 if ( strcmp(atom_name
, "TARGETS") )
84 wxLogTrace( TRACE_CLIPBOARD
,
85 wxT("got unsupported clipboard target") );
87 clipboard
->m_waiting
= false;
94 wxDataFormat
clip( selection_data
->selection
);
95 wxLogTrace( TRACE_CLIPBOARD
,
96 wxT("selection received for targets, clipboard %s"),
97 clip
.GetId().c_str() );
99 // the atoms we received, holding a list of targets (= formats)
100 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
102 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
104 wxDataFormat
format( atoms
[i
] );
106 wxLogTrace( TRACE_CLIPBOARD
,
107 wxT("selection received for targets, format %s"),
108 format
.GetId().c_str() );
110 // printf( "format %s requested %s\n",
111 // gdk_atom_name( atoms[i] ),
112 // gdk_atom_name( clipboard->m_targetRequested ) );
114 if (format
== clipboard
->m_targetRequested
)
116 clipboard
->m_waiting
= false;
117 clipboard
->m_formatSupported
= true;
123 clipboard
->m_waiting
= false;
127 //-----------------------------------------------------------------------------
128 // "selection_received" for the actual data
129 //-----------------------------------------------------------------------------
133 selection_received( GtkWidget
*WXUNUSED(widget
),
134 GtkSelectionData
*selection_data
,
135 guint32
WXUNUSED(time
),
136 wxClipboard
*clipboard
)
140 clipboard
->m_waiting
= false;
144 wxDataObject
*data_object
= clipboard
->m_receivedData
;
148 clipboard
->m_waiting
= false;
152 if (selection_data
->length
<= 0)
154 clipboard
->m_waiting
= false;
158 wxDataFormat
format( selection_data
->target
);
160 // make sure we got the data in the correct format
161 if (!data_object
->IsSupportedFormat( format
) )
163 clipboard
->m_waiting
= false;
168 This seems to cause problems somehow
169 // make sure we got the data in the correct form (selection type).
170 // if so, copy data to target object
171 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
173 clipboard
->m_waiting
= false;
178 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
180 wxTheClipboard
->m_formatSupported
= true;
181 clipboard
->m_waiting
= false;
185 //-----------------------------------------------------------------------------
187 //-----------------------------------------------------------------------------
191 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
193 if (!wxTheClipboard
) return TRUE
;
195 if (event
->selection
== GDK_SELECTION_PRIMARY
)
197 wxTheClipboard
->m_ownsPrimarySelection
= false;
200 if (event
->selection
== g_clipboardAtom
)
202 wxTheClipboard
->m_ownsClipboard
= false;
206 wxTheClipboard
->m_waiting
= false;
210 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
211 (!wxTheClipboard
->m_ownsClipboard
))
213 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
214 if (wxTheClipboard
->m_data
)
216 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
218 wxDELETE(wxTheClipboard
->m_data
);
222 wxTheClipboard
->m_waiting
= false;
227 //-----------------------------------------------------------------------------
228 // selection handler for supplying data
229 //-----------------------------------------------------------------------------
233 selection_handler( GtkWidget
*WXUNUSED(widget
),
234 GtkSelectionData
*selection_data
,
235 guint
WXUNUSED(info
),
236 guint
WXUNUSED(time
),
237 gpointer signal_data
)
239 if (!wxTheClipboard
) return;
241 if (!wxTheClipboard
->m_data
) return;
243 wxDataObject
*data
= wxTheClipboard
->m_data
;
245 // ICCCM says that TIMESTAMP is a required atom.
246 // In particular, it satisfies Klipper, which polls
247 // TIMESTAMP to see if the clipboards content has changed.
248 // It shall return the time which was used to set the data.
249 if (selection_data
->target
== g_timestampAtom
)
251 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
252 gtk_selection_data_set(selection_data
,
253 GDK_SELECTION_TYPE_INTEGER
,
255 (guchar
*)&(timestamp
),
257 wxLogTrace(TRACE_CLIPBOARD
,
258 wxT("Clipboard TIMESTAMP requested, returning timestamp=%u"),
263 wxDataFormat
format( selection_data
->target
);
265 wxLogTrace(TRACE_CLIPBOARD
,
266 wxT("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
267 format
.GetId().c_str(),
268 wxString::FromAscii(gdk_atom_name(selection_data
->target
)).c_str(),
269 wxString::FromAscii(gdk_atom_name(selection_data
->type
)).c_str(),
270 wxString::FromAscii(gdk_atom_name(selection_data
->selection
)).c_str(),
271 GPOINTER_TO_UINT( signal_data
)
274 if (!data
->IsSupportedFormat( format
)) return;
276 int size
= data
->GetDataSize( format
);
278 if (size
== 0) return;
280 void *d
= malloc(size
);
282 // Text data will be in UTF8 in Unicode mode.
283 data
->GetDataHere( selection_data
->target
, d
);
285 gtk_selection_data_set(
287 GDK_SELECTION_TYPE_STRING
,
296 //-----------------------------------------------------------------------------
298 //-----------------------------------------------------------------------------
300 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
302 wxClipboard::wxClipboard()
307 m_ownsClipboard
= false;
308 m_ownsPrimarySelection
= false;
311 m_receivedData
= NULL
;
313 /* we use m_targetsWidget to query what formats are available */
315 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
316 gtk_widget_realize( m_targetsWidget
);
318 gtk_signal_connect( GTK_OBJECT(m_targetsWidget
),
319 "selection_received",
320 GTK_SIGNAL_FUNC( targets_selection_received
),
323 /* we use m_clipboardWidget to get and to offer data */
325 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
326 gtk_widget_realize( m_clipboardWidget
);
328 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
329 "selection_received",
330 GTK_SIGNAL_FUNC( selection_received
),
333 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
334 "selection_clear_event",
335 GTK_SIGNAL_FUNC( selection_clear_clip
),
338 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
339 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
340 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
342 m_formatSupported
= false;
343 m_targetRequested
= 0;
346 wxClipboard::~wxClipboard()
350 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
351 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
354 void wxClipboard::Clear()
359 /* disable GUI threads */
362 // As we have data we also own the clipboard. Once we no longer own
363 // it, clear_selection is called which will set m_data to zero
364 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
368 gtk_selection_owner_set( NULL
, g_clipboardAtom
,
369 (guint32
) GDK_CURRENT_TIME
);
371 while (m_waiting
) gtk_main_iteration();
374 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
378 gtk_selection_owner_set( NULL
, GDK_SELECTION_PRIMARY
,
379 (guint32
) GDK_CURRENT_TIME
);
381 while (m_waiting
) gtk_main_iteration();
387 /* re-enable GUI threads */
391 m_targetRequested
= 0;
392 m_formatSupported
= false;
395 bool wxClipboard::Open()
397 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
404 bool wxClipboard::SetData( wxDataObject
*data
)
406 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
408 wxCHECK_MSG( data
, false, wxT("data is invalid") );
412 return AddData( data
);
415 bool wxClipboard::AddData( wxDataObject
*data
)
417 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
419 wxCHECK_MSG( data
, false, wxT("data is invalid") );
421 // we can only store one wxDataObject
426 // get formats from wxDataObjects
427 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
428 m_data
->GetAllFormats( array
);
430 // primary selection or clipboard
431 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
434 // by default provide TIMESTAMP as a target
435 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
440 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
442 wxLogTrace( TRACE_CLIPBOARD
,
443 wxT("wxClipboard now supports atom %s"),
444 array
[i
].GetId().c_str() );
446 // printf( "added %s\n",
447 // gdk_atom_name( array[i].GetFormatId() ) );
449 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
452 0 ); /* what is info ? */
457 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
459 GTK_SIGNAL_FUNC(selection_handler
),
460 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
463 /* disable GUI threads */
466 /* Tell the world we offer clipboard data */
467 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
469 (guint32
) GDK_CURRENT_TIME
));
472 m_ownsPrimarySelection
= res
;
474 m_ownsClipboard
= res
;
477 /* re-enable GUI threads */
483 void wxClipboard::Close()
485 wxCHECK_RET( m_open
, wxT("clipboard not open") );
490 bool wxClipboard::IsOpened() const
495 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
497 /* reentrance problems */
498 if (m_waiting
) return false;
500 /* store requested format to be asked for by callbacks */
501 m_targetRequested
= format
;
503 wxLogTrace( TRACE_CLIPBOARD
,
504 wxT("wxClipboard:IsSupported: requested format: %s"),
505 format
.GetId().c_str() );
507 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
509 m_formatSupported
= false;
511 /* perform query. this will set m_formatSupported to
512 true if m_targetRequested is supported.
513 also, we have to wait for the "answer" from the
514 clipboard owner which is an asynchronous process.
515 therefore we set m_waiting = true here and wait
516 until the callback "targets_selection_received"
521 gtk_selection_convert( m_targetsWidget
,
522 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
525 (guint32
) GDK_CURRENT_TIME
);
527 while (m_waiting
) gtk_main_iteration();
529 return m_formatSupported
;
532 bool wxClipboard::GetData( wxDataObject
& data
)
534 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
536 /* get formats from wxDataObjects */
537 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
538 data
.GetAllFormats( array
);
540 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
542 wxDataFormat
format( array
[i
] );
544 wxLogTrace( TRACE_CLIPBOARD
,
545 wxT("wxClipboard::GetData: requested format: %s"),
546 format
.GetId().c_str() );
548 /* is data supported by clipboard ? */
550 /* store requested format to be asked for by callbacks */
551 m_targetRequested
= format
;
553 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
555 m_formatSupported
= false;
557 /* perform query. this will set m_formatSupported to
558 true if m_targetRequested is supported.
559 also, we have to wait for the "answer" from the
560 clipboard owner which is an asynchronous process.
561 therefore we set m_waiting = true here and wait
562 until the callback "targets_selection_received"
567 gtk_selection_convert( m_targetsWidget
,
568 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
571 (guint32
) GDK_CURRENT_TIME
);
573 while (m_waiting
) gtk_main_iteration();
575 if (!m_formatSupported
) continue;
577 /* store pointer to data object to be filled up by callbacks */
578 m_receivedData
= &data
;
580 /* store requested format to be asked for by callbacks */
581 m_targetRequested
= format
;
583 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
586 m_formatSupported
= false;
588 /* ask for clipboard contents. this will set
589 m_formatSupported to true if m_targetRequested
591 also, we have to wait for the "answer" from the
592 clipboard owner which is an asynchronous process.
593 therefore we set m_waiting = true here and wait
594 until the callback "targets_selection_received"
599 wxLogTrace( TRACE_CLIPBOARD
,
600 wxT("wxClipboard::GetData: format found, start convert") );
602 gtk_selection_convert( m_clipboardWidget
,
603 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
606 (guint32
) GDK_CURRENT_TIME
);
608 while (m_waiting
) gtk_main_iteration();
611 Normally this is a true error as we checked for the presence of such
612 data before, but there are applications that may return an empty
613 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
614 which would produce a false error message here, so we check for the
615 size of the string first. In ansi, GetDataSize returns an extra
616 value (for the closing null?), with unicode, the exact number of
617 tokens is given (that is more than 1 for special characters)
618 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
621 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
623 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
624 #endif // UNICODE / !UNICODE
626 wxCHECK_MSG( m_formatSupported
, false,
627 wxT("error retrieving data from clipboard") );
635 wxLogTrace( TRACE_CLIPBOARD
,
636 wxT("wxClipboard::GetData: format not found") );