1 /////////////////////////////////////////////////////////////////////////////
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
11 #pragma implementation "clipbrd.h"
14 #include "wx/clipbrd.h"
18 #include "wx/dataobj.h"
26 //-----------------------------------------------------------------------------
28 //-----------------------------------------------------------------------------
31 extern void wxapp_install_thread_wakeup();
32 extern void wxapp_uninstall_thread_wakeup();
35 //-----------------------------------------------------------------------------
37 //-----------------------------------------------------------------------------
39 wxClipboard
*wxTheClipboard
= (wxClipboard
*) NULL
;
41 GdkAtom g_clipboardAtom
= 0;
42 GdkAtom g_targetsAtom
= 0;
44 //-----------------------------------------------------------------------------
46 //-----------------------------------------------------------------------------
48 /* The contents of a selection are returned in a GtkSelectionData
49 structure. selection/target identify the request.
50 type specifies the type of the return; if length < 0, and
51 the data should be ignored. This structure has object semantics -
52 no fields should be modified directly, they should not be created
53 directly, and pointers to them should not be stored beyond the duration of
54 a callback. (If the last is changed, we'll need to add reference
57 struct _GtkSelectionData
69 //-----------------------------------------------------------------------------
70 // "selection_received" for targets
71 //-----------------------------------------------------------------------------
74 targets_selection_received( GtkWidget
*WXUNUSED(widget
),
75 GtkSelectionData
*selection_data
,
76 #if (GTK_MINOR_VERSION > 0)
77 guint32
WXUNUSED(time
),
79 wxClipboard
*clipboard
)
83 clipboard
->m_waiting
= FALSE
;
87 if (selection_data
->length
<= 0)
89 clipboard
->m_waiting
= FALSE
;
93 /* make sure we got the data in the correct form */
94 if (selection_data
->type
!= GDK_SELECTION_TYPE_ATOM
)
96 clipboard
->m_waiting
= FALSE
;
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
++)
106 char *name = gdk_atom_name (atoms[i]);
107 if (name) printf( "Format available: %s.\n", name ); */
109 if (atoms
[i
] == clipboard
->m_targetRequested
)
111 clipboard
->m_waiting
= FALSE
;
112 clipboard
->m_formatSupported
= TRUE
;
117 clipboard
->m_waiting
= FALSE
;
121 //-----------------------------------------------------------------------------
122 // "selection_received" for the actual data
123 //-----------------------------------------------------------------------------
126 selection_received( GtkWidget
*WXUNUSED(widget
),
127 GtkSelectionData
*selection_data
,
128 #if (GTK_MINOR_VERSION > 0)
129 guint32
WXUNUSED(time
),
131 wxClipboard
*clipboard
)
135 clipboard
->m_waiting
= FALSE
;
139 wxDataObject
*data_object
= clipboard
->m_receivedData
;
143 clipboard
->m_waiting
= FALSE
;
147 if (selection_data
->length
<= 0)
149 clipboard
->m_waiting
= FALSE
;
153 wxDataFormat
format( selection_data
->target
);
155 /* make sure we got the data in the correct format */
156 if (!data_object
->IsSupportedFormat( format
) )
158 clipboard
->m_waiting
= FALSE
;
162 /* make sure we got the data in the correct form (selection type).
163 if so, copy data to target object */
165 switch (format
.GetType())
169 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
171 clipboard
->m_waiting
= FALSE
;
175 wxTextDataObject
*text_object
= (wxTextDataObject
*) data_object
;
177 wxString text
= (const char*) selection_data
->data
;
179 text_object
->SetText( text
);
186 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
188 clipboard
->m_waiting
= FALSE
;
192 wxBitmapDataObject
*bitmap_object
= (wxBitmapDataObject
*) data_object
;
194 bitmap_object
->SetData( (size_t) selection_data
->length
, (const void*) selection_data
->data
);
201 if (selection_data
->type
!= GDK_SELECTION_TYPE_STRING
)
203 clipboard
->m_waiting
= FALSE
;
207 data_object
->SetData( format
, (size_t) selection_data
->length
, (const char*) selection_data
->data
);
214 clipboard
->m_waiting
= FALSE
;
219 wxTheClipboard
->m_formatSupported
= TRUE
;
220 clipboard
->m_waiting
= FALSE
;
223 //-----------------------------------------------------------------------------
225 //-----------------------------------------------------------------------------
228 selection_clear_clip( GtkWidget
*WXUNUSED(widget
), GdkEventSelection
*event
)
230 if (!wxTheClipboard
) return TRUE
;
232 if (event
->selection
== GDK_SELECTION_PRIMARY
)
234 wxTheClipboard
->m_ownsPrimarySelection
= FALSE
;
237 if (event
->selection
== g_clipboardAtom
)
239 wxTheClipboard
->m_ownsClipboard
= FALSE
;
243 wxTheClipboard
->m_waiting
= FALSE
;
247 if ((!wxTheClipboard
->m_ownsPrimarySelection
) &&
248 (!wxTheClipboard
->m_ownsClipboard
))
250 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
251 if (wxTheClipboard
->m_data
)
253 delete wxTheClipboard
->m_data
;
254 wxTheClipboard
->m_data
= (wxDataObject
*) NULL
;
258 wxTheClipboard
->m_waiting
= FALSE
;
262 //-----------------------------------------------------------------------------
263 // selection handler for supplying data
264 //-----------------------------------------------------------------------------
267 selection_handler( GtkWidget
*WXUNUSED(widget
), GtkSelectionData
*selection_data
, gpointer
WXUNUSED(data
) )
269 if (!wxTheClipboard
) return;
271 if (!wxTheClipboard
->m_data
) return;
273 wxDataObject
*data
= wxTheClipboard
->m_data
;
275 wxDataFormat
format( selection_data
->target
);
277 if (!data
->IsSupportedFormat( format
)) return;
279 if (format
.GetType() == wxDF_TEXT
)
281 wxTextDataObject
*text_object
= (wxTextDataObject
*) data
;
282 wxString
text( text_object
->GetText() );
285 const wxWX2MBbuf s
= text
.mbc_str();
287 #else // more efficient in non-Unicode
288 const char *s
= text
.c_str();
289 int len
= (int) text
.Length();
291 gtk_selection_data_set(
293 GDK_SELECTION_TYPE_STRING
,
295 (unsigned char*) (const char*) s
,
301 if (format
.GetType() == wxDF_BITMAP
)
303 wxBitmapDataObject
*bitmap_object
= (wxBitmapDataObject
*) data
;
305 if (bitmap_object
->GetDataSize() == 0) return;
307 gtk_selection_data_set(
309 GDK_SELECTION_TYPE_STRING
,
311 (unsigned char*) bitmap_object
->GetPngData(),
312 (int) bitmap_object
->GetDataSize() );
317 int size
= data
->GetDataSize( format
);
319 if (size
== 0) return;
321 char *d
= new char[size
];
323 data
->GetDataHere( selection_data
->target
, (void*) d
);
325 gtk_selection_data_set(
327 GDK_SELECTION_TYPE_STRING
,
333 //-----------------------------------------------------------------------------
335 //-----------------------------------------------------------------------------
337 IMPLEMENT_DYNAMIC_CLASS(wxClipboard
,wxObject
)
339 wxClipboard::wxClipboard()
343 m_ownsClipboard
= FALSE
;
344 m_ownsPrimarySelection
= FALSE
;
346 m_data
= (wxDataObject
*) NULL
;
347 m_receivedData
= (wxDataObject
*) NULL
;
349 /* we use m_targetsWidget to query what formats are available */
351 m_targetsWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
352 gtk_widget_realize( m_targetsWidget
);
354 gtk_signal_connect( GTK_OBJECT(m_targetsWidget
),
355 "selection_received",
356 GTK_SIGNAL_FUNC( targets_selection_received
),
359 /* we use m_clipboardWidget to get and to offer data */
361 m_clipboardWidget
= gtk_window_new( GTK_WINDOW_POPUP
);
362 gtk_widget_realize( m_clipboardWidget
);
364 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
365 "selection_received",
366 GTK_SIGNAL_FUNC( selection_received
),
369 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
370 "selection_clear_event",
371 GTK_SIGNAL_FUNC( selection_clear_clip
),
374 if (!g_clipboardAtom
) g_clipboardAtom
= gdk_atom_intern( "CLIPBOARD", FALSE
);
375 if (!g_targetsAtom
) g_targetsAtom
= gdk_atom_intern ("TARGETS", FALSE
);
377 m_formatSupported
= FALSE
;
378 m_targetRequested
= 0;
380 m_usePrimary
= FALSE
;
383 wxClipboard::~wxClipboard()
387 if (m_clipboardWidget
) gtk_widget_destroy( m_clipboardWidget
);
388 if (m_targetsWidget
) gtk_widget_destroy( m_targetsWidget
);
391 void wxClipboard::Clear()
396 /* disable GUI threads */
397 wxapp_uninstall_thread_wakeup();
400 /* As we have data we also own the clipboard. Once we no longer own
401 it, clear_selection is called which will set m_data to zero */
402 if (gdk_selection_owner_get( g_clipboardAtom
) == m_clipboardWidget
->window
)
406 gtk_selection_owner_set( (GtkWidget
*) NULL
, g_clipboardAtom
, GDK_CURRENT_TIME
);
408 while (m_waiting
) gtk_main_iteration();
411 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY
) == m_clipboardWidget
->window
)
415 gtk_selection_owner_set( (GtkWidget
*) NULL
, GDK_SELECTION_PRIMARY
, GDK_CURRENT_TIME
);
417 while (m_waiting
) gtk_main_iteration();
423 m_data
= (wxDataObject
*) NULL
;
427 /* re-enable GUI threads */
428 wxapp_install_thread_wakeup();
432 m_targetRequested
= 0;
433 m_formatSupported
= FALSE
;
436 bool wxClipboard::Open()
438 wxCHECK_MSG( !m_open
, FALSE
, wxT("clipboard already open") );
445 bool wxClipboard::SetData( wxDataObject
*data
)
447 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
449 wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") );
453 return AddData( data
);
456 bool wxClipboard::AddData( wxDataObject
*data
)
458 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
460 wxCHECK_MSG( data
, FALSE
, wxT("data is invalid") );
462 /* we can only store one wxDataObject */
467 /* This should happen automatically, but to be on the safe side */
468 m_ownsClipboard
= FALSE
;
469 m_ownsPrimarySelection
= FALSE
;
471 /* get formats from wxDataObjects */
472 wxDataFormat
*array
= new wxDataFormat
[ m_data
->GetFormatCount() ];
473 m_data
->GetAllFormats( array
);
475 for (size_t i
= 0; i
< m_data
->GetFormatCount(); i
++)
477 GdkAtom atom
= array
[i
];
478 wxLogDebug( wxT("Clipboard Supported atom %s"), gdk_atom_name( atom
) );
480 /* Add handlers if someone requests data. We currently always
481 offer data to the clipboard and the primary selection. Maybe
482 we should make that depend on the usePrimary flag */
484 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
485 GDK_SELECTION_PRIMARY
,
487 0 ); /* what is info ? */
489 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget
),
492 0 ); /* what is info ? */
497 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget
),
499 GTK_SIGNAL_FUNC(selection_handler
),
503 /* disable GUI threads */
504 wxapp_uninstall_thread_wakeup();
507 /* Tell the world we offer clipboard data */
508 if (!gtk_selection_owner_set( m_clipboardWidget
,
513 /* re-enable GUI threads */
514 wxapp_install_thread_wakeup();
518 m_ownsClipboard
= TRUE
;
520 if (!gtk_selection_owner_set( m_clipboardWidget
,
521 GDK_SELECTION_PRIMARY
,
525 /* re-enable GUI threads */
526 wxapp_install_thread_wakeup();
530 m_ownsPrimarySelection
= TRUE
;
533 /* re-enable GUI threads */
534 wxapp_install_thread_wakeup();
540 void wxClipboard::Close()
542 wxCHECK_RET( m_open
, wxT("clipboard not open") );
547 bool wxClipboard::IsOpened() const
552 bool wxClipboard::IsSupported( const wxDataFormat
& format
)
554 /* store requested format to be asked for by callbacks */
556 m_targetRequested
= format
;
558 wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") );
560 m_formatSupported
= FALSE
;
562 /* perform query. this will set m_formatSupported to
563 TRUE if m_targetRequested is supported.
564 alsom we have to wait for the "answer" from the
565 clipboard owner which is an asynchronous process.
566 therefore we set m_waiting = TRUE here and wait
567 until the callback "targets_selection_received"
572 gtk_selection_convert( m_targetsWidget
,
573 m_usePrimary
? GDK_SELECTION_PRIMARY
: g_clipboardAtom
,
577 while (m_waiting
) gtk_main_iteration();
579 if (!m_formatSupported
) return FALSE
;
584 bool wxClipboard::GetData( wxDataObject
& data
)
586 wxCHECK_MSG( m_open
, FALSE
, wxT("clipboard not open") );
588 /* get formats from wxDataObjects */
589 wxDataFormat
*array
= new wxDataFormat
[ data
.GetFormatCount() ];
590 data
.GetAllFormats( array
);
592 for (size_t i
= 0; i
< data
.GetFormatCount(); i
++)
594 /* is data supported by clipboard ? */
595 if (!IsSupported( array
[i
] ))
598 /* store pointer to data object to be filled up by callbacks */
599 m_receivedData
= &data
;
601 /* store requested format to be asked for by callbacks */
602 m_targetRequested
= array
[i
];
604 wxCHECK_MSG( m_targetRequested
, FALSE
, wxT("invalid clipboard format") );
607 m_formatSupported
= FALSE
;
609 /* ask for clipboard contents. this will set
610 m_formatSupported to TRUE if m_targetRequested
612 also, we have to wait for the "answer" from the
613 clipboard owner which is an asynchronous process.
614 therefore we set m_waiting = TRUE here and wait
615 until the callback "targets_selection_received"
620 gtk_selection_convert( m_clipboardWidget
,
621 m_usePrimary
? GDK_SELECTION_PRIMARY
: g_clipboardAtom
,
625 while (m_waiting
) gtk_main_iteration();
627 /* this is a true error as we checked for the presence of such data before */
628 wxCHECK_MSG( m_formatSupported
, FALSE
, wxT("error retrieving data from clipboard") );