1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/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"
23 #include "wx/gtk/private.h"
25 //-----------------------------------------------------------------------------
27 //-----------------------------------------------------------------------------
29 GdkAtom g_clipboardAtom
= 0;
30 GdkAtom g_targetsAtom
= 0;
31 GdkAtom g_timestampAtom
= 0;
34 extern GdkAtom g_altTextAtom
;
37 // the trace mask we use with wxLogTrace() - call
38 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
39 // (there will be a *lot* of them!)
40 #define TRACE_CLIPBOARD _T("clipboard")
42 //-----------------------------------------------------------------------------
44 //-----------------------------------------------------------------------------
46 /* The contents of a selection are returned in a GtkSelectionData
47 structure. selection/target identify the request.
48 type specifies the type of the return; if length < 0, and
49 the data should be ignored. This structure has object semantics -
50 no fields should be modified directly, they should not be created
51 directly, and pointers to them should not be stored beyond the duration of
52 a callback. (If the last is changed, we'll need to add reference
55 struct _GtkSelectionData
67 //-----------------------------------------------------------------------------
68 // "selection_received" for targets
69 //-----------------------------------------------------------------------------
73 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
74 GtkSelectionData
*selection_data
,
75 guint32
WXUNUSED(time
),
76 wxClipboard
*clipboard
)
78 if ( wxTheClipboard
&& selection_data
->length
> 0 )
80 // make sure we got the data in the correct form
81 GdkAtom type
= selection_data
->type
;
82 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
84 if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") )
86 wxLogTrace( TRACE_CLIPBOARD
,
87 _T("got unsupported clipboard target") );
89 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() );
101 // the atoms we received, holding a list of targets (= formats)
102 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
104 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
106 wxDataFormat
format( atoms
[i
] );
108 wxLogTrace( TRACE_CLIPBOARD
,
109 wxT("selection received for targets, format %s"),
110 format
.GetId().c_str() );
112 // printf( "format %s requested %s\n",
113 // gdk_atom_name( atoms[i] ),
114 // gdk_atom_name( clipboard->m_targetRequested ) );
116 if (format
== clipboard
->m_targetRequested
)
118 clipboard
->m_waiting
= false;
119 clipboard
->m_formatSupported
= true;
125 clipboard
->m_waiting
= false;
129 //-----------------------------------------------------------------------------
130 // "selection_received" for the actual data
131 //-----------------------------------------------------------------------------
135 selection_received( GtkWidget
*WXUNUSED(widget
),
136 GtkSelectionData
*selection_data
,
137 guint32
WXUNUSED(time
),
138 wxClipboard
*clipboard
)
142 clipboard
->m_waiting
= false;
146 wxDataObject
*data_object
= clipboard
->m_receivedData
;
150 clipboard
->m_waiting
= false;
154 if (selection_data
->length
<= 0)
156 clipboard
->m_waiting
= false;
160 wxDataFormat
format( selection_data
->target
);
162 // make sure we got the data in the correct format
163 if (!data_object
->IsSupportedFormat( format
) )
165 clipboard
->m_waiting
= false;
170 This seems to cause problems somehow
171 // make sure we got the data in the correct form (selection type).
172 // if so, copy data to target object
173 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
175 clipboard
->m_waiting
= false;
180 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
182 wxTheClipboard
->m_formatSupported
= true;
183 clipboard
->m_waiting
= false;
187 //-----------------------------------------------------------------------------
189 //-----------------------------------------------------------------------------
193 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
195 if (!wxTheClipboard
) return true;
197 if (event
->selection
== GDK_SELECTION_PRIMARY
)
199 wxTheClipboard
->m_ownsPrimarySelection
= false;
202 if (event
->selection
== g_clipboardAtom
)
204 wxTheClipboard
->m_ownsClipboard
= false;
208 wxTheClipboard
->m_waiting
= false;
212 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
213 (!wxTheClipboard
->m_ownsClipboard
))
215 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
216 if (wxTheClipboard
->m_data
)
218 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
220 delete wxTheClipboard
->m_data
;
221 wxTheClipboard
->m_data
= (wxDataObject
*) NULL
;
225 wxTheClipboard
->m_waiting
= false;
230 //-----------------------------------------------------------------------------
231 // selection handler for supplying data
232 //-----------------------------------------------------------------------------
236 selection_handler( GtkWidget
*WXUNUSED(widget
),
237 GtkSelectionData
*selection_data
,
238 guint
WXUNUSED(info
),
239 guint
WXUNUSED(time
),
240 gpointer signal_data
)
242 if (!wxTheClipboard
) return;
244 if (!wxTheClipboard
->m_data
) return;
246 wxDataObject
*data
= wxTheClipboard
->m_data
;
248 // ICCCM says that TIMESTAMP is a required atom.
249 // In particular, it satisfies Klipper, which polls
250 // TIMESTAMP to see if the clipboards content has changed.
251 // It shall return the time which was used to set the data.
252 if (selection_data
->target
== g_timestampAtom
)
254 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
255 gtk_selection_data_set(selection_data
,
256 GDK_SELECTION_TYPE_INTEGER
,
258 (guchar
*)&(timestamp
),
260 wxLogTrace(TRACE_CLIPBOARD
,
261 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
266 wxDataFormat
format( selection_data
->target
);
269 wxLogTrace(TRACE_CLIPBOARD
,
270 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
271 format
.GetId().c_str(),
272 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->target
))).c_str(),
273 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->type
))).c_str(),
274 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->selection
))).c_str(),
275 GPOINTER_TO_UINT( signal_data
)
279 if (!data
->IsSupportedFormat( format
)) return;
281 int size
= data
->GetDataSize( format
);
283 if (size
== 0) return;
285 void *d
= malloc(size
);
287 // Text data will be in UTF8 in Unicode mode.
288 data
->GetDataHere( selection_data
->target
, d
);
290 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
291 // would show as UTF-8 data interpreted as latin1 (?) in other
292 // GTK+ apps if we used gtk_selection_data_set()
293 if (format
== wxDataFormat(wxDF_UNICODETEXT
))
295 gtk_selection_data_set_text(
302 gtk_selection_data_set(
304 GDK_SELECTION_TYPE_STRING
,
314 //-----------------------------------------------------------------------------
316 //-----------------------------------------------------------------------------
318 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
320 wxClipboard::wxClipboard()
325 m_ownsClipboard
= false;
326 m_ownsPrimarySelection
= false;
328 m_data
= (wxDataObject
*) NULL
;
329 m_receivedData
= (wxDataObject
*) NULL
;
331 /* we use m_targetsWidget to query what formats are available */
333 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
334 gtk_widget_realize( m_targetsWidget
);
336 g_signal_connect (m_targetsWidget
, "selection_received",
337 G_CALLBACK (targets_selection_received
), this);
339 /* we use m_clipboardWidget to get and to offer data */
341 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
342 gtk_widget_realize( m_clipboardWidget
);
344 g_signal_connect (m_clipboardWidget
, "selection_received",
345 G_CALLBACK (selection_received
), this);
347 g_signal_connect (m_clipboardWidget
, "selection_clear_event",
348 G_CALLBACK (selection_clear_clip
), NULL
);
350 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
351 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
352 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
354 m_formatSupported
= false;
355 m_targetRequested
= 0;
357 m_usePrimary
= false;
360 wxClipboard::~wxClipboard()
364 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
365 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
368 void wxClipboard::Clear()
373 /* disable GUI threads */
376 // As we have data we also own the clipboard. Once we no longer own
377 // it, clear_selection is called which will set m_data to zero
378 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
382 gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
,
383 (guint32
) GDK_CURRENT_TIME
);
385 while (m_waiting
) gtk_main_iteration();
388 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
392 gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
,
393 (guint32
) GDK_CURRENT_TIME
);
395 while (m_waiting
) gtk_main_iteration();
402 /* re-enable GUI threads */
406 m_targetRequested
= 0;
407 m_formatSupported
= false;
410 bool wxClipboard::Open()
412 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
419 bool wxClipboard::SetData( wxDataObject
*data
)
421 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
423 wxCHECK_MSG( data
, false, wxT("data is invalid") );
427 return AddData( data
);
430 bool wxClipboard::AddData( wxDataObject
*data
)
432 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
434 wxCHECK_MSG( data
, false, wxT("data is invalid") );
436 // we can only store one wxDataObject
441 // get formats from wxDataObjects
442 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
443 m_data
->GetAllFormats( array
);
445 // primary selection or clipboard
446 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
449 // by default provide TIMESTAMP as a target
450 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
455 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
457 wxLogTrace( TRACE_CLIPBOARD
,
458 wxT("wxClipboard now supports atom %s"),
459 array
[i
].GetId().c_str() );
461 // printf( "added %s\n",
462 // gdk_atom_name( array[i].GetFormatId() ) );
464 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
467 0 ); /* what is info ? */
472 g_signal_connect (m_clipboardWidget
, "selection_get",
473 G_CALLBACK (selection_handler
),
474 GUINT_TO_POINTER (gtk_get_current_event_time()) );
477 /* disable GUI threads */
480 /* Tell the world we offer clipboard data */
481 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
483 (guint32
) GDK_CURRENT_TIME
));
486 m_ownsPrimarySelection
= res
;
488 m_ownsClipboard
= res
;
491 /* re-enable GUI threads */
497 void wxClipboard::Close()
499 wxCHECK_RET( m_open
, wxT("clipboard not open") );
504 bool wxClipboard::IsOpened() const
509 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
511 /* reentrance problems */
512 if (m_waiting
) return false;
514 /* store requested format to be asked for by callbacks */
515 m_targetRequested
= format
;
517 wxLogTrace( TRACE_CLIPBOARD
,
518 wxT("wxClipboard:IsSupported: requested format: %s"),
519 format
.GetId().c_str() );
521 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
523 m_formatSupported
= false;
525 /* perform query. this will set m_formatSupported to
526 true if m_targetRequested is supported.
527 also, we have to wait for the "answer" from the
528 clipboard owner which is an asynchronous process.
529 therefore we set m_waiting = true here and wait
530 until the callback "targets_selection_received"
535 gtk_selection_convert( m_targetsWidget
,
536 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
539 (guint32
) GDK_CURRENT_TIME
);
541 while (m_waiting
) gtk_main_iteration();
544 if (!m_formatSupported
&& format
== wxDataFormat(wxDF_UNICODETEXT
))
546 // Another try with plain STRING format
547 extern GdkAtom g_altTextAtom
;
548 return IsSupported(g_altTextAtom
);
552 return m_formatSupported
;
555 bool wxClipboard::GetData( wxDataObject
& data
)
557 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
559 /* get formats from wxDataObjects */
560 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
561 data
.GetAllFormats( array
);
563 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
565 wxDataFormat
format( array
[i
] );
567 wxLogTrace( TRACE_CLIPBOARD
,
568 wxT("wxClipboard::GetData: requested format: %s"),
569 format
.GetId().c_str() );
571 /* is data supported by clipboard ? */
573 /* store requested format to be asked for by callbacks */
574 m_targetRequested
= format
;
576 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
578 m_formatSupported
= false;
580 /* perform query. this will set m_formatSupported to
581 true if m_targetRequested is supported.
582 also, we have to wait for the "answer" from the
583 clipboard owner which is an asynchronous process.
584 therefore we set m_waiting = true here and wait
585 until the callback "targets_selection_received"
590 gtk_selection_convert( m_targetsWidget
,
591 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
594 (guint32
) GDK_CURRENT_TIME
);
596 while (m_waiting
) gtk_main_iteration();
598 if (!m_formatSupported
) continue;
600 /* store pointer to data object to be filled up by callbacks */
601 m_receivedData
= &data
;
603 /* store requested format to be asked for by callbacks */
604 m_targetRequested
= format
;
606 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
609 m_formatSupported
= false;
611 /* ask for clipboard contents. this will set
612 m_formatSupported to true if m_targetRequested
614 also, we have to wait for the "answer" from the
615 clipboard owner which is an asynchronous process.
616 therefore we set m_waiting = true here and wait
617 until the callback "targets_selection_received"
622 wxLogTrace( TRACE_CLIPBOARD
,
623 wxT("wxClipboard::GetData: format found, start convert") );
625 gtk_selection_convert( m_clipboardWidget
,
626 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
629 (guint32
) GDK_CURRENT_TIME
);
631 while (m_waiting
) gtk_main_iteration();
634 Normally this is a true error as we checked for the presence of such
635 data before, but there are applications that may return an empty
636 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
637 which would produce a false error message here, so we check for the
638 size of the string first. In ansi, GetDataSize returns an extra
639 value (for the closing null?), with unicode, the exact number of
640 tokens is given (that is more than 1 for special characters)
641 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
644 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
646 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
647 #endif // UNICODE / !UNICODE
649 wxCHECK_MSG( m_formatSupported
, false,
650 wxT("error retrieving data from clipboard") );
658 wxLogTrace( TRACE_CLIPBOARD
,
659 wxT("wxClipboard::GetData: format not found") );