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 _T("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 delete wxTheClipboard
->m_data
;
220 wxTheClipboard
->m_data
= NULL
;
224 wxTheClipboard
->m_waiting
= false;
229 //-----------------------------------------------------------------------------
230 // selection handler for supplying data
231 //-----------------------------------------------------------------------------
235 selection_handler( GtkWidget
*WXUNUSED(widget
),
236 GtkSelectionData
*selection_data
,
237 guint
WXUNUSED(info
),
238 guint
WXUNUSED(time
),
239 gpointer signal_data
)
241 if (!wxTheClipboard
) return;
243 if (!wxTheClipboard
->m_data
) return;
245 wxDataObject
*data
= wxTheClipboard
->m_data
;
247 // ICCCM says that TIMESTAMP is a required atom.
248 // In particular, it satisfies Klipper, which polls
249 // TIMESTAMP to see if the clipboards content has changed.
250 // It shall return the time which was used to set the data.
251 if (selection_data
->target
== g_timestampAtom
)
253 guint timestamp
= GPOINTER_TO_UINT (signal_data
);
254 gtk_selection_data_set(selection_data
,
255 GDK_SELECTION_TYPE_INTEGER
,
257 (guchar
*)&(timestamp
),
259 wxLogTrace(TRACE_CLIPBOARD
,
260 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
265 wxDataFormat
format( selection_data
->target
);
267 wxLogTrace(TRACE_CLIPBOARD
,
268 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
269 format
.GetId().c_str(),
270 wxString::FromAscii(gdk_atom_name(selection_data
->target
)).c_str(),
271 wxString::FromAscii(gdk_atom_name(selection_data
->type
)).c_str(),
272 wxString::FromAscii(gdk_atom_name(selection_data
->selection
)).c_str(),
273 GPOINTER_TO_UINT( signal_data
)
276 if (!data
->IsSupportedFormat( format
)) return;
278 int size
= data
->GetDataSize( format
);
280 if (size
== 0) return;
282 void *d
= malloc(size
);
284 // Text data will be in UTF8 in Unicode mode.
285 data
->GetDataHere( selection_data
->target
, d
);
287 gtk_selection_data_set(
289 GDK_SELECTION_TYPE_STRING
,
298 //-----------------------------------------------------------------------------
300 //-----------------------------------------------------------------------------
302 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
304 wxClipboard::wxClipboard()
309 m_ownsClipboard
= false;
310 m_ownsPrimarySelection
= false;
313 m_receivedData
= NULL
;
315 /* we use m_targetsWidget to query what formats are available */
317 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
318 gtk_widget_realize( m_targetsWidget
);
320 gtk_signal_connect( GTK_OBJECT(m_targetsWidget
),
321 "selection_received",
322 GTK_SIGNAL_FUNC( targets_selection_received
),
325 /* we use m_clipboardWidget to get and to offer data */
327 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
328 gtk_widget_realize( m_clipboardWidget
);
330 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
331 "selection_received",
332 GTK_SIGNAL_FUNC( selection_received
),
335 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
336 "selection_clear_event",
337 GTK_SIGNAL_FUNC( selection_clear_clip
),
340 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
341 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
342 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
344 m_formatSupported
= false;
345 m_targetRequested
= 0;
348 wxClipboard::~wxClipboard()
352 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
353 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
356 void wxClipboard::Clear()
361 /* disable GUI threads */
364 // As we have data we also own the clipboard. Once we no longer own
365 // it, clear_selection is called which will set m_data to zero
366 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
370 gtk_selection_owner_set( NULL
, g_clipboardAtom
,
371 (guint32
) GDK_CURRENT_TIME
);
373 while (m_waiting
) gtk_main_iteration();
376 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
380 gtk_selection_owner_set( NULL
, GDK_SELECTION_PRIMARY
,
381 (guint32
) GDK_CURRENT_TIME
);
383 while (m_waiting
) gtk_main_iteration();
393 /* re-enable GUI threads */
397 m_targetRequested
= 0;
398 m_formatSupported
= false;
401 bool wxClipboard::Open()
403 wxCHECK_MSG( !m_open
, false, wxT("clipboard already open") );
410 bool wxClipboard::SetData( wxDataObject
*data
)
412 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
414 wxCHECK_MSG( data
, false, wxT("data is invalid") );
418 return AddData( data
);
421 bool wxClipboard::AddData( wxDataObject
*data
)
423 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
425 wxCHECK_MSG( data
, false, wxT("data is invalid") );
427 // we can only store one wxDataObject
432 // get formats from wxDataObjects
433 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
434 m_data
->GetAllFormats( array
);
436 // primary selection or clipboard
437 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
440 // by default provide TIMESTAMP as a target
441 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
446 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
448 wxLogTrace( TRACE_CLIPBOARD
,
449 wxT("wxClipboard now supports atom %s"),
450 array
[i
].GetId().c_str() );
452 // printf( "added %s\n",
453 // gdk_atom_name( array[i].GetFormatId() ) );
455 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
458 0 ); /* what is info ? */
463 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
465 GTK_SIGNAL_FUNC(selection_handler
),
466 GUINT_TO_POINTER( gdk_event_get_time(gtk_get_current_event()) ) );
469 /* disable GUI threads */
472 /* Tell the world we offer clipboard data */
473 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
475 (guint32
) GDK_CURRENT_TIME
));
478 m_ownsPrimarySelection
= res
;
480 m_ownsClipboard
= res
;
483 /* re-enable GUI threads */
489 void wxClipboard::Close()
491 wxCHECK_RET( m_open
, wxT("clipboard not open") );
496 bool wxClipboard::IsOpened() const
501 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
503 /* reentrance problems */
504 if (m_waiting
) return false;
506 /* store requested format to be asked for by callbacks */
507 m_targetRequested
= format
;
509 wxLogTrace( TRACE_CLIPBOARD
,
510 wxT("wxClipboard:IsSupported: requested format: %s"),
511 format
.GetId().c_str() );
513 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
515 m_formatSupported
= false;
517 /* perform query. this will set m_formatSupported to
518 true if m_targetRequested is supported.
519 also, we have to wait for the "answer" from the
520 clipboard owner which is an asynchronous process.
521 therefore we set m_waiting = true here and wait
522 until the callback "targets_selection_received"
527 gtk_selection_convert( m_targetsWidget
,
528 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
531 (guint32
) GDK_CURRENT_TIME
);
533 while (m_waiting
) gtk_main_iteration();
535 return m_formatSupported
;
538 bool wxClipboard::GetData( wxDataObject
& data
)
540 wxCHECK_MSG( m_open
, false, wxT("clipboard not open") );
542 /* get formats from wxDataObjects */
543 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
544 data
.GetAllFormats( array
);
546 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
548 wxDataFormat
format( array
[i
] );
550 wxLogTrace( TRACE_CLIPBOARD
,
551 wxT("wxClipboard::GetData: requested format: %s"),
552 format
.GetId().c_str() );
554 /* is data supported by clipboard ? */
556 /* store requested format to be asked for by callbacks */
557 m_targetRequested
= format
;
559 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
561 m_formatSupported
= false;
563 /* perform query. this will set m_formatSupported to
564 true if m_targetRequested is supported.
565 also, we have to wait for the "answer" from the
566 clipboard owner which is an asynchronous process.
567 therefore we set m_waiting = true here and wait
568 until the callback "targets_selection_received"
573 gtk_selection_convert( m_targetsWidget
,
574 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
577 (guint32
) GDK_CURRENT_TIME
);
579 while (m_waiting
) gtk_main_iteration();
581 if (!m_formatSupported
) continue;
583 /* store pointer to data object to be filled up by callbacks */
584 m_receivedData
= &data
;
586 /* store requested format to be asked for by callbacks */
587 m_targetRequested
= format
;
589 wxCHECK_MSG( m_targetRequested
, false, wxT("invalid clipboard format") );
592 m_formatSupported
= false;
594 /* ask for clipboard contents. this will set
595 m_formatSupported to true if m_targetRequested
597 also, we have to wait for the "answer" from the
598 clipboard owner which is an asynchronous process.
599 therefore we set m_waiting = true here and wait
600 until the callback "targets_selection_received"
605 wxLogTrace( TRACE_CLIPBOARD
,
606 wxT("wxClipboard::GetData: format found, start convert") );
608 gtk_selection_convert( m_clipboardWidget
,
609 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
612 (guint32
) GDK_CURRENT_TIME
);
614 while (m_waiting
) gtk_main_iteration();
617 Normally this is a true error as we checked for the presence of such
618 data before, but there are applications that may return an empty
619 string (e.g. Gnumeric-1.6.1 on Linux if an empty cell is copied)
620 which would produce a false error message here, so we check for the
621 size of the string first. In ansi, GetDataSize returns an extra
622 value (for the closing null?), with unicode, the exact number of
623 tokens is given (that is more than 1 for special characters)
624 (tested with Gnumeric-1.6.1 and OpenOffice.org-2.0.2)
627 if ( format
!= wxDF_UNICODETEXT
|| data
.GetDataSize(format
) > 0 )
629 if ( format
!= wxDF_TEXT
|| data
.GetDataSize(format
) > 1 )
630 #endif // UNICODE / !UNICODE
632 wxCHECK_MSG( m_formatSupported
, false,
633 wxT("error retrieving data from clipboard") );
641 wxLogTrace( TRACE_CLIPBOARD
,
642 wxT("wxClipboard::GetData: format not found") );