1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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"
13 #include "wx/clipbrd.h"
17 #include "wx/dataobj.h"
21 #include "wx/gtk/private.h"
23 //-----------------------------------------------------------------------------
25 //-----------------------------------------------------------------------------
30 //-----------------------------------------------------------------------------
32 //-----------------------------------------------------------------------------
34 GdkAtom g_clipboardAtom
= 0;
35 GdkAtom g_targetsAtom
= 0;
36 GdkAtom g_timestampAtom
= 0;
39 extern GdkAtom g_altTextAtom
;
42 // the trace mask we use with wxLogTrace() - call
43 // wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
44 // (there will be a *lot* of them!)
45 static const wxChar
*TRACE_CLIPBOARD
= _T("clipboard");
47 //-----------------------------------------------------------------------------
49 //-----------------------------------------------------------------------------
51 /* The contents of a selection are returned in a GtkSelectionData
52 structure. selection/target identify the request.
53 type specifies the type of the return; if length < 0, and
54 the data should be ignored. This structure has object semantics -
55 no fields should be modified directly, they should not be created
56 directly, and pointers to them should not be stored beyond the duration of
57 a callback. (If the last is changed, we'll need to add reference
60 struct _GtkSelectionData
72 //-----------------------------------------------------------------------------
73 // "selection_received" for targets
74 //-----------------------------------------------------------------------------
78 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
79 GtkSelectionData
*selection_data
,
80 guint32
WXUNUSED(time
),
81 wxClipboard
*clipboard
)
83 if ( wxTheClipboard
&& selection_data
->length
> 0 )
85 // make sure we got the data in the correct form
86 GdkAtom type
= selection_data
->type
;
87 if ( type
!= GDK_SELECTION_TYPE_ATOM
)
89 if ( strcmp(wxGtkString(gdk_atom_name(type
)), "TARGETS") )
91 wxLogTrace( TRACE_CLIPBOARD
,
92 _T("got unsupported clipboard target") );
94 clipboard
->m_waiting
= FALSE
;
100 wxDataFormat
clip( selection_data
->selection
);
101 wxLogTrace( TRACE_CLIPBOARD
,
102 wxT("selection received for targets, clipboard %s"),
103 clip
.GetId().c_str() );
104 #endif // __WXDEBUG__
106 // the atoms we received, holding a list of targets (= formats)
107 GdkAtom
*atoms
= (GdkAtom
*)selection_data
->data
;
109 for (unsigned int i
=0; i
<selection_data
->length
/sizeof(GdkAtom
); i
++)
111 wxDataFormat
format( atoms
[i
] );
113 wxLogTrace( TRACE_CLIPBOARD
,
114 wxT("selection received for targets, format %s"),
115 format
.GetId().c_str() );
117 // printf( "format %s requested %s\n",
118 // gdk_atom_name( atoms[i] ),
119 // gdk_atom_name( clipboard->m_targetRequested ) );
121 if (format
== clipboard
->m_targetRequested
)
123 clipboard
->m_waiting
= FALSE
;
124 clipboard
->m_formatSupported
= TRUE
;
130 clipboard
->m_waiting
= FALSE
;
134 //-----------------------------------------------------------------------------
135 // "selection_received" for the actual data
136 //-----------------------------------------------------------------------------
140 selection_received( GtkWidget
*WXUNUSED(widget
),
141 GtkSelectionData
*selection_data
,
142 guint32
WXUNUSED(time
),
143 wxClipboard
*clipboard
)
147 clipboard
->m_waiting
= FALSE
;
151 wxDataObject
*data_object
= clipboard
->m_receivedData
;
155 clipboard
->m_waiting
= FALSE
;
159 if (selection_data
->length
<= 0)
161 clipboard
->m_waiting
= FALSE
;
165 wxDataFormat
format( selection_data
->target
);
167 // make sure we got the data in the correct format
168 if (!data_object
->IsSupportedFormat( format
) )
170 clipboard
->m_waiting
= FALSE
;
175 This seems to cause problems somehow
176 // make sure we got the data in the correct form (selection type).
177 // if so, copy data to target object
178 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
180 clipboard
->m_waiting
= FALSE
;
185 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
187 wxTheClipboard
->m_formatSupported
= TRUE
;
188 clipboard
->m_waiting
= FALSE
;
192 //-----------------------------------------------------------------------------
194 //-----------------------------------------------------------------------------
198 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
200 if (!wxTheClipboard
) return TRUE
;
202 if (event
->selection
== GDK_SELECTION_PRIMARY
)
204 wxTheClipboard
->m_ownsPrimarySelection
= FALSE
;
207 if (event
->selection
== g_clipboardAtom
)
209 wxTheClipboard
->m_ownsClipboard
= FALSE
;
213 wxTheClipboard
->m_waiting
= FALSE
;
217 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
218 (!wxTheClipboard
->m_ownsClipboard
))
220 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
221 if (wxTheClipboard
->m_data
)
223 wxLogTrace(TRACE_CLIPBOARD
, wxT("wxClipboard will get cleared" ));
225 delete wxTheClipboard
->m_data
;
226 wxTheClipboard
->m_data
= (wxDataObject
*) NULL
;
230 wxTheClipboard
->m_waiting
= FALSE
;
235 //-----------------------------------------------------------------------------
236 // selection handler for supplying data
237 //-----------------------------------------------------------------------------
241 selection_handler( GtkWidget
*WXUNUSED(widget
),
242 GtkSelectionData
*selection_data
,
243 guint
WXUNUSED(info
),
244 guint
WXUNUSED(time
),
245 gpointer signal_data
)
247 if (!wxTheClipboard
) return;
249 if (!wxTheClipboard
->m_data
) return;
251 wxDataObject
*data
= wxTheClipboard
->m_data
;
253 // ICCCM says that TIMESTAMP is a required atom.
254 // In particular, it satisfies Klipper, which polls
255 // TIMESTAMP to see if the clipboards content has changed.
256 // It shall return the time which was used to set the data.
257 if (selection_data
->target
== g_timestampAtom
)
259 uint timestamp
= GPOINTER_TO_UINT (signal_data
);
260 gtk_selection_data_set(selection_data
,
261 GDK_SELECTION_TYPE_INTEGER
,
263 (guchar
*)&(timestamp
),
265 wxLogTrace(TRACE_CLIPBOARD
,
266 _T("Clipboard TIMESTAMP requested, returning timestamp=%u"),
271 wxDataFormat
format( selection_data
->target
);
274 wxLogTrace(TRACE_CLIPBOARD
,
275 _T("clipboard data in format %s, GtkSelectionData is target=%s type=%s selection=%s timestamp=%u"),
276 format
.GetId().c_str(),
277 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->target
))).c_str(),
278 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->type
))).c_str(),
279 wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data
->selection
))).c_str(),
280 GPOINTER_TO_UINT( signal_data
)
284 if (!data
->IsSupportedFormat( format
)) return;
286 int size
= data
->GetDataSize( format
);
288 if (size
== 0) return;
290 void *d
= malloc(size
);
292 // Text data will be in UTF8 in Unicode mode.
293 data
->GetDataHere( selection_data
->target
, d
);
295 // NB: GTK+ requires special treatment of UTF8_STRING data, the text
296 // would show as UTF-8 data interpreted as latin1 (?) in other
297 // GTK+ apps if we used gtk_selection_data_set()
298 if (format
== wxDataFormat(wxDF_UNICODETEXT
))
300 gtk_selection_data_set_text(
307 gtk_selection_data_set(
309 GDK_SELECTION_TYPE_STRING
,
319 //-----------------------------------------------------------------------------
321 //-----------------------------------------------------------------------------
323 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
325 wxClipboard::wxClipboard()
330 m_ownsClipboard
= FALSE
;
331 m_ownsPrimarySelection
= FALSE
;
333 m_data
= (wxDataObject
*) NULL
;
334 m_receivedData
= (wxDataObject
*) NULL
;
336 /* we use m_targetsWidget to query what formats are available */
338 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
339 gtk_widget_realize( m_targetsWidget
);
341 g_signal_connect (m_targetsWidget
, "selection_received",
342 G_CALLBACK (targets_selection_received
), this);
344 /* we use m_clipboardWidget to get and to offer data */
346 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
347 gtk_widget_realize( m_clipboardWidget
);
349 g_signal_connect (m_clipboardWidget
, "selection_received",
350 G_CALLBACK (selection_received
), this);
352 g_signal_connect (m_clipboardWidget
, "selection_clear_event",
353 G_CALLBACK (selection_clear_clip
), NULL
);
355 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
356 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
357 if (!g_timestampAtom
) g_timestampAtom
= gdk_atom_intern ("TIMESTAMP", FALSE
);
359 m_formatSupported
= FALSE
;
360 m_targetRequested
= 0;
362 m_usePrimary
= FALSE
;
365 wxClipboard::~wxClipboard()
369 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
370 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
373 void wxClipboard::Clear()
378 /* disable GUI threads */
381 // As we have data we also own the clipboard. Once we no longer own
382 // it, clear_selection is called which will set m_data to zero
383 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
387 gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
,
388 (guint32
) GDK_CURRENT_TIME
);
390 while (m_waiting
) gtk_main_iteration();
393 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
397 gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
,
398 (guint32
) GDK_CURRENT_TIME
);
400 while (m_waiting
) gtk_main_iteration();
406 m_data
= (wxDataObject
*) NULL
;
410 /* re-enable GUI threads */
414 m_targetRequested
= 0;
415 m_formatSupported
= FALSE
;
418 bool wxClipboard::Open()
420 wxCHECK_MSG( !m_open
, FALSE
, wxT("clipboard already open") );
427 bool wxClipboard::SetData( wxDataObject
*data
)
429 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
431 wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") );
435 return AddData( data
);
438 bool wxClipboard::AddData( wxDataObject
*data
)
440 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
442 wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") );
444 // we can only store one wxDataObject
449 // get formats from wxDataObjects
450 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
451 m_data
->GetAllFormats( array
);
453 // primary selection or clipboard
454 GdkAtom clipboard
= m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
457 // by default provide TIMESTAMP as a target
458 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
463 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
465 wxLogTrace( TRACE_CLIPBOARD
,
466 wxT("wxClipboard now supports atom %s"),
467 array
[i
].GetId().c_str() );
469 // printf( "added %s\n",
470 // gdk_atom_name( array[i].GetFormatId() ) );
472 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
475 0 ); /* what is info ? */
480 g_signal_connect (m_clipboardWidget
, "selection_get",
481 G_CALLBACK (selection_handler
),
482 GUINT_TO_POINTER (gtk_get_current_event_time()) );
485 /* disable GUI threads */
488 /* Tell the world we offer clipboard data */
489 bool res
= (gtk_selection_owner_set( m_clipboardWidget
,
491 (guint32
) GDK_CURRENT_TIME
));
494 m_ownsPrimarySelection
= res
;
496 m_ownsClipboard
= res
;
499 /* re-enable GUI threads */
505 void wxClipboard::Close()
507 wxCHECK_RET( m_open
, wxT("clipboard not open") );
512 bool wxClipboard::IsOpened() const
517 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
519 /* reentrance problems */
520 if (m_waiting
) return FALSE
;
522 /* store requested format to be asked for by callbacks */
523 m_targetRequested
= format
;
525 wxLogTrace( TRACE_CLIPBOARD
,
526 wxT("wxClipboard:IsSupported: requested format: %s"),
527 format
.GetId().c_str() );
529 wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") );
531 m_formatSupported
= FALSE
;
533 /* perform query. this will set m_formatSupported to
534 TRUE if m_targetRequested is supported.
535 also, we have to wait for the "answer" from the
536 clipboard owner which is an asynchronous process.
537 therefore we set m_waiting = TRUE here and wait
538 until the callback "targets_selection_received"
543 gtk_selection_convert( m_targetsWidget
,
544 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
547 (guint32
) GDK_CURRENT_TIME
);
549 while (m_waiting
) gtk_main_iteration();
552 if (!m_formatSupported
&& format
== wxDataFormat(wxDF_UNICODETEXT
))
554 // Another try with plain STRING format
555 extern GdkAtom g_altTextAtom
;
556 return IsSupported(g_altTextAtom
);
560 return m_formatSupported
;
563 bool wxClipboard::GetData( wxDataObject
& data
)
565 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
567 /* get formats from wxDataObjects */
568 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
569 data
.GetAllFormats( array
);
571 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
573 wxDataFormat
format( array
[i
] );
575 wxLogTrace( TRACE_CLIPBOARD
,
576 wxT("wxClipboard::GetData: requested format: %s"),
577 format
.GetId().c_str() );
579 /* is data supported by clipboard ? */
581 /* store requested format to be asked for by callbacks */
582 m_targetRequested
= format
;
584 wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") );
586 m_formatSupported
= FALSE
;
588 /* perform query. this will set m_formatSupported to
589 TRUE if m_targetRequested is supported.
590 also, we have to wait for the "answer" from the
591 clipboard owner which is an asynchronous process.
592 therefore we set m_waiting = TRUE here and wait
593 until the callback "targets_selection_received"
598 gtk_selection_convert( m_targetsWidget
,
599 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
602 (guint32
) GDK_CURRENT_TIME
);
604 while (m_waiting
) gtk_main_iteration();
606 if (!m_formatSupported
) continue;
608 /* store pointer to data object to be filled up by callbacks */
609 m_receivedData
= &data
;
611 /* store requested format to be asked for by callbacks */
612 m_targetRequested
= format
;
614 wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") );
617 m_formatSupported
= FALSE
;
619 /* ask for clipboard contents. this will set
620 m_formatSupported to TRUE if m_targetRequested
622 also, we have to wait for the "answer" from the
623 clipboard owner which is an asynchronous process.
624 therefore we set m_waiting = TRUE here and wait
625 until the callback "targets_selection_received"
630 wxLogTrace( TRACE_CLIPBOARD
,
631 wxT("wxClipboard::GetData: format found, start convert") );
633 gtk_selection_convert( m_clipboardWidget
,
634 m_usePrimary
? (GdkAtom
)GDK_SELECTION_PRIMARY
637 (guint32
) GDK_CURRENT_TIME
);
639 while (m_waiting
) gtk_main_iteration();
641 /* this is a true error as we checked for the presence of such data before */
642 wxCHECK_MSG( m_formatSupported
, FALSE
, wxT("error retrieving data from clipboard") );
649 wxLogTrace( TRACE_CLIPBOARD
,
650 wxT("wxClipboard::GetData: format not found") );