]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/clipbrd.cpp
fix from Ron for one-shot timers
[wxWidgets.git] / src / gtk / clipbrd.cpp
CommitLineData
dc86cb34
RR
1/////////////////////////////////////////////////////////////////////////////
2// Name: clipbrd.cpp
3// Purpose:
4// Author: Robert Roebling
5// Id: $Id$
6// Copyright: (c) 1998 Robert Roebling
7// Licence: wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10#ifdef __GNUG__
11#pragma implementation "clipbrd.h"
12#endif
13
14#include "wx/clipbrd.h"
15
06cfab17 16#if wxUSE_CLIPBOARD
ac57418f 17
034be888
RR
18#include "wx/utils.h"
19
83624f79
RR
20#include "glib.h"
21#include "gdk/gdk.h"
22#include "gtk/gtk.h"
23
b453e1b2
RR
24//-----------------------------------------------------------------------------
25// thread system
26//-----------------------------------------------------------------------------
27
28#if wxUSE_THREADS
29extern void wxapp_install_thread_wakeup();
30extern void wxapp_uninstall_thread_wakeup();
31#endif
32
dc86cb34
RR
33//-----------------------------------------------------------------------------
34// data
35//-----------------------------------------------------------------------------
36
37wxClipboard *wxTheClipboard = (wxClipboard*) NULL;
38
fd0eed64 39GdkAtom g_clipboardAtom = 0;
b527aac5 40GdkAtom g_targetsAtom = 0;
fd0eed64 41
dc86cb34 42//-----------------------------------------------------------------------------
b527aac5 43// reminder
dc86cb34
RR
44//-----------------------------------------------------------------------------
45
b527aac5
RR
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
53 counting)
54
55struct _GtkSelectionData
dc86cb34 56{
b527aac5
RR
57 GdkAtom selection;
58 GdkAtom target;
59 GdkAtom type;
60 gint format;
61 guchar *data;
62 gint length;
63};
64
65*/
dc86cb34 66
b527aac5
RR
67//-----------------------------------------------------------------------------
68// "selection_received" for targets
69//-----------------------------------------------------------------------------
70
71static void
72targets_selection_received( GtkWidget *WXUNUSED(widget),
73 GtkSelectionData *selection_data,
034be888
RR
74#if (GTK_MINOR_VERSION > 0)
75 guint32 WXUNUSED(time),
76#endif
b527aac5 77 wxClipboard *clipboard )
dc86cb34 78{
034be888
RR
79 if (!wxTheClipboard)
80 {
81 clipboard->m_waiting = FALSE;
82 return;
83 }
b527aac5 84
034be888
RR
85 if (selection_data->length <= 0)
86 {
87 clipboard->m_waiting = FALSE;
88 return;
89 }
90
91 /* make sure we got the data in the correct form */
92 if (selection_data->type != GDK_SELECTION_TYPE_ATOM)
93 {
94 clipboard->m_waiting = FALSE;
95 return;
96 }
b527aac5 97
8b53e5a2
RR
98 // the atoms we received, holding a list of targets (= formats)
99 GdkAtom *atoms = (GdkAtom *)selection_data->data;
b527aac5 100
8b53e5a2
RR
101 for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
102 {
7e2c43b8 103/* char *name = gdk_atom_name (atoms[i]);
ca35e608 104 if (name) printf( "Format available: %s.\n", name ); */
3c1b65a4 105
8b53e5a2
RR
106 if (atoms[i] == clipboard->m_targetRequested)
107 {
034be888 108 clipboard->m_waiting = FALSE;
8b53e5a2
RR
109 clipboard->m_formatSupported = TRUE;
110 return;
111 }
112 }
b527aac5 113
034be888 114 clipboard->m_waiting = FALSE;
8b53e5a2 115 return;
dc86cb34
RR
116}
117
118//-----------------------------------------------------------------------------
b527aac5 119// "selection_received" for the actual data
dc86cb34
RR
120//-----------------------------------------------------------------------------
121
fd0eed64 122static void
b527aac5
RR
123selection_received( GtkWidget *WXUNUSED(widget),
124 GtkSelectionData *selection_data,
034be888
RR
125#if (GTK_MINOR_VERSION > 0)
126 guint32 WXUNUSED(time),
127#endif
b527aac5 128 wxClipboard *clipboard )
dc86cb34 129{
034be888
RR
130 if (!wxTheClipboard)
131 {
132 clipboard->m_waiting = FALSE;
133 return;
134 }
b527aac5 135
8b53e5a2
RR
136 wxDataObject *data_object = clipboard->m_receivedData;
137
034be888
RR
138 if (!data_object)
139 {
140 clipboard->m_waiting = FALSE;
141 return;
142 }
8b53e5a2 143
034be888
RR
144 if (selection_data->length <= 0)
145 {
146 clipboard->m_waiting = FALSE;
147 return;
148 }
b527aac5 149
034be888
RR
150 /* make sure we got the data in the correct format */
151 if (data_object->GetFormat().GetAtom() != selection_data->target)
152 {
153 clipboard->m_waiting = FALSE;
154 return;
155 }
e2acb9ae 156
034be888
RR
157 /* make sure we got the data in the correct form (selection type).
158 if so, copy data to target object */
8b53e5a2 159
0d2a2b60 160 switch (data_object->GetFormat().GetType())
8b53e5a2
RR
161 {
162 case wxDF_TEXT:
163 {
034be888
RR
164 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
165 {
166 clipboard->m_waiting = FALSE;
167 return;
168 }
8b53e5a2
RR
169
170 wxTextDataObject *text_object = (wxTextDataObject *) data_object;
171
172 wxString text = (const char*) selection_data->data;
173
174 text_object->SetText( text );
175
176 break;
177 }
178
179 case wxDF_BITMAP:
180 {
e2acb9ae 181 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
034be888
RR
182 {
183 clipboard->m_waiting = FALSE;
184 return;
185 }
8b53e5a2 186
e2acb9ae
RR
187 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject *) data_object;
188
189 bitmap_object->SetPngData( (const char*) selection_data->data, (size_t) selection_data->length );
190
8b53e5a2
RR
191 break;
192 }
193
194 case wxDF_PRIVATE:
195 {
034be888
RR
196 if (selection_data->type != GDK_SELECTION_TYPE_STRING)
197 {
198 clipboard->m_waiting = FALSE;
199 return;
200 }
8b53e5a2
RR
201
202 wxPrivateDataObject *private_object = (wxPrivateDataObject *) data_object;
203
204 private_object->SetData( (const char*) selection_data->data, (size_t) selection_data->length );
205
206 break;
207 }
208
209 default:
210 {
034be888 211 clipboard->m_waiting = FALSE;
8b53e5a2
RR
212 return;
213 }
214 }
215
216 wxTheClipboard->m_formatSupported = TRUE;
034be888 217 clipboard->m_waiting = FALSE;
dc86cb34 218}
fd0eed64
RR
219
220//-----------------------------------------------------------------------------
221// "selection_clear"
222//-----------------------------------------------------------------------------
223
224static gint
aeeb6a44 225selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
fd0eed64 226{
8b53e5a2 227 if (!wxTheClipboard) return TRUE;
2830bf19 228
aeeb6a44
RR
229 if (event->selection == GDK_SELECTION_PRIMARY)
230 {
231 wxTheClipboard->m_ownsPrimarySelection = FALSE;
232 }
233 else
234 if (event->selection == g_clipboardAtom)
235 {
236 wxTheClipboard->m_ownsClipboard = FALSE;
237 }
238 else
239 {
e5ea3f7a 240 wxTheClipboard->m_waiting = FALSE;
aeeb6a44
RR
241 return FALSE;
242 }
243
244 if ((!wxTheClipboard->m_ownsPrimarySelection) &&
245 (!wxTheClipboard->m_ownsClipboard))
246 {
db2d879a 247 /* the clipboard is no longer in our hands. we can the delete clipboard data. */
0d2a2b60
RR
248 if (wxTheClipboard->m_dataBroker)
249 {
250 delete wxTheClipboard->m_dataBroker;
251 wxTheClipboard->m_dataBroker = (wxDataBroker*) NULL;
252 }
aeeb6a44 253 }
fd0eed64 254
e5ea3f7a 255 wxTheClipboard->m_waiting = FALSE;
8b53e5a2 256 return TRUE;
fd0eed64
RR
257}
258
259//-----------------------------------------------------------------------------
260// selection handler for supplying data
261//-----------------------------------------------------------------------------
262
263static void
264selection_handler( GtkWidget *WXUNUSED(widget), GtkSelectionData *selection_data, gpointer WXUNUSED(data) )
265{
8b53e5a2 266 if (!wxTheClipboard) return;
fd0eed64 267
0d2a2b60
RR
268 if (!wxTheClipboard->m_dataBroker) return;
269
270 wxNode *node = wxTheClipboard->m_dataBroker->m_dataObjects.First();
8b53e5a2
RR
271
272 while (node)
273 {
274 wxDataObject *data_object = (wxDataObject *)node->Data();
e2acb9ae 275
0d2a2b60 276 if (data_object->GetFormat().GetAtom() != selection_data->target)
8b53e5a2
RR
277 {
278 node = node->Next();
3874d10d 279 continue;
8b53e5a2
RR
280 }
281
0d2a2b60 282 switch (data_object->GetFormat().GetType())
8b53e5a2
RR
283 {
284 case wxDF_TEXT:
285 {
286 wxTextDataObject *text_object = (wxTextDataObject*) data_object;
287
288 wxString text = text_object->GetText();
289
93c5dd39
OK
290#if wxUSE_UNICODE
291 const wxWX2MBbuf s = text.mbc_str();
292 int len = strlen(s);
293#else // more efficient in non-Unicode
294 const char *s = text.c_str();
8b53e5a2 295 int len = (int) text.Length();
93c5dd39 296#endif
8b53e5a2
RR
297
298 gtk_selection_data_set(
299 selection_data,
300 GDK_SELECTION_TYPE_STRING,
301 8*sizeof(gchar),
93c5dd39 302 (unsigned char*) (const char*) s,
8b53e5a2
RR
303 len );
304
305 break;
306 }
307
308 case wxDF_BITMAP:
309 {
e2acb9ae 310 wxBitmapDataObject *bitmap_object = (wxBitmapDataObject*) data_object;
8b53e5a2 311
e2acb9ae
RR
312 if (bitmap_object->GetSize() == 0) return;
313
314 gtk_selection_data_set(
315 selection_data,
316 GDK_SELECTION_TYPE_STRING,
317 8*sizeof(gchar),
318 (unsigned char*) bitmap_object->GetData(),
319 (int) bitmap_object->GetSize() );
320
8b53e5a2
RR
321 break;
322 }
323
324 case wxDF_PRIVATE:
325 {
326 wxPrivateDataObject *private_object = (wxPrivateDataObject*) data_object;
327
0d2a2b60 328 if (private_object->GetSize() == 0) return;
8b53e5a2
RR
329
330 gtk_selection_data_set(
331 selection_data,
332 GDK_SELECTION_TYPE_STRING,
333 8*sizeof(gchar),
334 (unsigned char*) private_object->GetData(),
0d2a2b60 335 (int) private_object->GetSize() );
8b53e5a2
RR
336 }
337
338 default:
339 break;
340 }
341
342 node = node->Next();
343 }
fd0eed64 344}
dc86cb34
RR
345
346//-----------------------------------------------------------------------------
347// wxClipboard
348//-----------------------------------------------------------------------------
349
350IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
351
352wxClipboard::wxClipboard()
353{
8b53e5a2
RR
354 m_open = FALSE;
355
aeeb6a44
RR
356 m_ownsClipboard = FALSE;
357 m_ownsPrimarySelection = FALSE;
358
0d2a2b60 359 m_dataBroker = (wxDataBroker*) NULL;
fd0eed64 360
8b53e5a2 361 m_receivedData = (wxDataObject*) NULL;
99c67c77 362
034be888
RR
363 /* we use m_targetsWidget to query what formats are available */
364
365 m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP );
366 gtk_widget_realize( m_targetsWidget );
367
368 gtk_signal_connect( GTK_OBJECT(m_targetsWidget),
369 "selection_received",
370 GTK_SIGNAL_FUNC( targets_selection_received ),
371 (gpointer) this );
372
373 /* we use m_clipboardWidget to get and to offer data */
374
8b53e5a2
RR
375 m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP );
376 gtk_widget_realize( m_clipboardWidget );
377
034be888
RR
378 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
379 "selection_received",
380 GTK_SIGNAL_FUNC( selection_received ),
381 (gpointer) this );
382
8b53e5a2
RR
383 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
384 "selection_clear_event",
aeeb6a44 385 GTK_SIGNAL_FUNC( selection_clear_clip ),
8b53e5a2 386 (gpointer) NULL );
fd0eed64 387
8b53e5a2 388 if (!g_clipboardAtom) g_clipboardAtom = gdk_atom_intern( "CLIPBOARD", FALSE );
8b53e5a2
RR
389 if (!g_targetsAtom) g_targetsAtom = gdk_atom_intern ("TARGETS", FALSE);
390
391 m_formatSupported = FALSE;
392 m_targetRequested = 0;
7e2c43b8
RR
393
394 m_usePrimary = FALSE;
dc86cb34
RR
395}
396
397wxClipboard::~wxClipboard()
b527aac5 398{
8b53e5a2 399 Clear();
b527aac5 400
8b53e5a2 401 if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
034be888 402 if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
b527aac5
RR
403}
404
405void wxClipboard::Clear()
dc86cb34 406{
0d2a2b60 407 if (m_dataBroker)
8b53e5a2 408 {
b453e1b2
RR
409#if wxUSE_THREADS
410 /* disable GUI threads */
411 wxapp_uninstall_thread_wakeup();
412#endif
413
8b53e5a2
RR
414 /* As we have data we also own the clipboard. Once we no longer own
415 it, clear_selection is called which will set m_data to zero */
aeeb6a44 416 if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
8b53e5a2 417 {
e5ea3f7a
RR
418 m_waiting = TRUE;
419
8b53e5a2 420 gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom, GDK_CURRENT_TIME );
e5ea3f7a
RR
421
422 while (m_waiting) gtk_main_iteration();
8b53e5a2 423 }
db1b4961 424
aeeb6a44
RR
425 if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
426 {
e5ea3f7a
RR
427 m_waiting = TRUE;
428
aeeb6a44 429 gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME );
e5ea3f7a
RR
430
431 while (m_waiting) gtk_main_iteration();
aeeb6a44
RR
432 }
433
0d2a2b60
RR
434 if (m_dataBroker)
435 {
436 delete m_dataBroker;
437 m_dataBroker = (wxDataBroker*) NULL;
438 }
b453e1b2
RR
439
440#if wxUSE_THREADS
441 /* re-enable GUI threads */
442 wxapp_install_thread_wakeup();
443#endif
8b53e5a2 444 }
b527aac5 445
8b53e5a2 446 m_targetRequested = 0;
b527aac5 447
8b53e5a2
RR
448 m_formatSupported = FALSE;
449}
450
451bool wxClipboard::Open()
452{
93c5dd39 453 wxCHECK_MSG( !m_open, FALSE, _T("clipboard already open") );
b527aac5 454
8b53e5a2 455 m_open = TRUE;
cee6127e 456
8b53e5a2 457 return TRUE;
dc86cb34
RR
458}
459
75ce0581 460bool wxClipboard::SetData( wxDataObject *data )
dc86cb34 461{
93c5dd39 462 wxCHECK_MSG( m_open, FALSE, _T("clipboard not open") );
75ce0581 463
93c5dd39 464 wxCHECK_MSG( data, FALSE, _T("data is invalid") );
db1b4961 465
0d2a2b60 466 Clear();
75ce0581
RR
467
468 return AddData( data );
469}
470
471bool wxClipboard::AddData( wxDataObject *data )
472{
93c5dd39 473 wxCHECK_MSG( m_open, FALSE, _T("clipboard not open") );
0d2a2b60 474
93c5dd39 475 wxCHECK_MSG( data, FALSE, _T("data is invalid") );
2830bf19 476
75ce0581 477 /* if clipboard has been cleared before, create new data broker */
75ce0581 478 if (!m_dataBroker) m_dataBroker = new wxDataBroker();
8b53e5a2 479
75ce0581 480 /* add new data to list of offered data objects */
75ce0581
RR
481 m_dataBroker->Add( data );
482
483 /* get native format id of new data object */
75ce0581 484 GdkAtom format = data->GetFormat().GetAtom();
8b53e5a2 485
93c5dd39 486 wxCHECK_MSG( format, FALSE, _T("data has invalid format") );
75ce0581
RR
487
488 /* This should happen automatically, but to be on the safe side */
75ce0581
RR
489 m_ownsClipboard = FALSE;
490 m_ownsPrimarySelection = FALSE;
aeeb6a44 491
75ce0581 492 /* Add handlers if someone requests data */
d345e841
RR
493
494#if (GTK_MINOR_VERSION > 0)
495
496 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
497 GDK_SELECTION_PRIMARY,
498 format,
499 0 ); /* what is info ? */
500
501 gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
502 g_clipboardAtom,
503 format,
504 0 ); /* what is info ? */
505
506 gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
507 "selection_get",
508 GTK_SIGNAL_FUNC(selection_handler),
509 (gpointer) NULL );
510
511#else
512
75ce0581 513 gtk_selection_add_handler( m_clipboardWidget,
8b53e5a2 514 g_clipboardAtom,
0d2a2b60 515 format,
8b53e5a2 516 selection_handler,
0d2a2b60 517 (gpointer) NULL );
8b53e5a2 518
75ce0581 519 gtk_selection_add_handler( m_clipboardWidget,
aeeb6a44 520 GDK_SELECTION_PRIMARY,
0d2a2b60 521 format,
aeeb6a44 522 selection_handler,
0d2a2b60 523 (gpointer) NULL );
d345e841 524#endif
db2d879a 525
b453e1b2
RR
526#if wxUSE_THREADS
527 /* disable GUI threads */
528 wxapp_uninstall_thread_wakeup();
529#endif
530
75ce0581 531 /* Tell the world we offer clipboard data */
75ce0581 532 if (!gtk_selection_owner_set( m_clipboardWidget,
8b53e5a2
RR
533 g_clipboardAtom,
534 GDK_CURRENT_TIME ))
75ce0581 535 {
b453e1b2
RR
536#if wxUSE_THREADS
537 /* re-enable GUI threads */
538 wxapp_install_thread_wakeup();
539#endif
75ce0581
RR
540 return FALSE;
541 }
542 m_ownsClipboard = TRUE;
aeeb6a44 543
75ce0581 544 if (!gtk_selection_owner_set( m_clipboardWidget,
aeeb6a44
RR
545 GDK_SELECTION_PRIMARY,
546 GDK_CURRENT_TIME ))
75ce0581 547 {
b453e1b2
RR
548#if wxUSE_THREADS
549 /* re-enable GUI threads */
550 wxapp_install_thread_wakeup();
551#endif
75ce0581 552 return FALSE;
aeeb6a44 553 }
75ce0581 554 m_ownsPrimarySelection = TRUE;
b453e1b2
RR
555
556#if wxUSE_THREADS
557 /* re-enable GUI threads */
558 wxapp_install_thread_wakeup();
559#endif
75ce0581 560
8b53e5a2
RR
561 return TRUE;
562}
db1b4961 563
8b53e5a2
RR
564void wxClipboard::Close()
565{
93c5dd39 566 wxCHECK_RET( m_open, _T("clipboard not open") );
8b53e5a2
RR
567
568 m_open = FALSE;
dc86cb34
RR
569}
570
5f699c22 571bool wxClipboard::IsSupported( wxDataFormat format )
b527aac5 572{
93c5dd39 573 wxCHECK_MSG( m_open, FALSE, _T("clipboard not open") );
0d2a2b60 574
75ce0581 575 /* store requested format to be asked for by callbacks */
0d2a2b60 576
5f699c22 577 m_targetRequested = format.GetAtom();
b527aac5 578
93c5dd39 579 wxCHECK_MSG( m_targetRequested, FALSE, _T("invalid clipboard format") );
0d2a2b60 580
8b53e5a2 581 m_formatSupported = FALSE;
b527aac5 582
0d2a2b60 583 /* perform query. this will set m_formatSupported to
034be888
RR
584 TRUE if m_targetRequested is supported.
585 alsom we have to wait for the "answer" from the
586 clipboard owner which is an asynchronous process.
587 therefore we set m_waiting = TRUE here and wait
588 until the callback "targets_selection_received"
589 sets it to FALSE */
590
591 m_waiting = TRUE;
ca35e608 592
034be888 593 gtk_selection_convert( m_targetsWidget,
cee6127e 594 m_usePrimary?GDK_SELECTION_PRIMARY:g_clipboardAtom,
8b53e5a2
RR
595 g_targetsAtom,
596 GDK_CURRENT_TIME );
ca35e608 597
034be888
RR
598 while (m_waiting) gtk_main_iteration();
599
8b53e5a2 600 if (!m_formatSupported) return FALSE;
75ce0581
RR
601
602 return TRUE;
603}
604
5f699c22 605bool wxClipboard::GetData( wxDataObject *data )
75ce0581 606{
93c5dd39 607 wxCHECK_MSG( m_open, FALSE, _T("clipboard not open") );
75ce0581
RR
608
609 /* is data supported by clipboard ? */
610
5f699c22 611 if (!IsSupported( data->GetFormat() )) return FALSE;
75ce0581
RR
612
613 /* store pointer to data object to be filled up by callbacks */
614
5f699c22 615 m_receivedData = data;
75ce0581
RR
616
617 /* store requested format to be asked for by callbacks */
618
5f699c22 619 m_targetRequested = data->GetFormat().GetAtom();
b527aac5 620
93c5dd39 621 wxCHECK_MSG( m_targetRequested, FALSE, _T("invalid clipboard format") );
75ce0581
RR
622
623 /* start query */
8b53e5a2 624
8b53e5a2 625 m_formatSupported = FALSE;
b527aac5 626
034be888
RR
627 /* ask for clipboard contents. this will set
628 m_formatSupported to TRUE if m_targetRequested
629 is supported.
630 also, we have to wait for the "answer" from the
631 clipboard owner which is an asynchronous process.
632 therefore we set m_waiting = TRUE here and wait
633 until the callback "targets_selection_received"
634 sets it to FALSE */
b527aac5 635
034be888 636 m_waiting = TRUE;
75ce0581 637
8b53e5a2 638 gtk_selection_convert( m_clipboardWidget,
cee6127e 639 m_usePrimary?GDK_SELECTION_PRIMARY:g_clipboardAtom,
8b53e5a2
RR
640 m_targetRequested,
641 GDK_CURRENT_TIME );
b527aac5 642
034be888 643 while (m_waiting) gtk_main_iteration();
b527aac5 644
0d2a2b60
RR
645 /* this is a true error as we checked for the presence of such data before */
646
93c5dd39 647 wxCHECK_MSG( m_formatSupported, FALSE, _T("error retrieving data from clipboard") );
8b53e5a2
RR
648
649 return TRUE;
b527aac5
RR
650}
651
b527aac5
RR
652//-----------------------------------------------------------------------------
653// wxClipboardModule
654//-----------------------------------------------------------------------------
655
656IMPLEMENT_DYNAMIC_CLASS(wxClipboardModule,wxModule)
657
658bool wxClipboardModule::OnInit()
659{
8b53e5a2 660 wxTheClipboard = new wxClipboard();
b527aac5 661
8b53e5a2 662 return TRUE;
b527aac5
RR
663}
664
665void wxClipboardModule::OnExit()
dc86cb34 666{
8b53e5a2
RR
667 if (wxTheClipboard) delete wxTheClipboard;
668 wxTheClipboard = (wxClipboard*) NULL;
dc86cb34 669}
ac57418f
RR
670
671#endif
672
673 // wxUSE_CLIPBOARD
674