1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/x11/clipbrd.cpp
3// Purpose:     Clipboard functionality
4// Author:      Robert Roebling
5// Created:
6// RCS-ID:      $Id: clipbrd.cpp 56402 2008-10-17 13:45:34Z VZ $
7// Copyright:   (c) Robert Roebling
8// Licence:     wxWindows licence
9/////////////////////////////////////////////////////////////////////////////
10
11// for compilers that support precompilation, includes "wx.h".
12#include "wx/wxprec.h"
13
14#if wxUSE_CLIPBOARD
15
16#include "wx/clipbrd.h"
17
18#ifndef WX_PRECOMP
19    #include "wx/log.h"
20    #include "wx/utils.h"
21    #include "wx/dataobj.h"
22#endif
23
24#include "wx/x11/private.h"
25
26//-----------------------------------------------------------------------------
27// data
28//-----------------------------------------------------------------------------
29
30#if !wxUSE_NANOX
31Atom  g_clipboardAtom   = 0;
32Atom  g_targetsAtom     = 0;
33#endif
34
35// avoid warnings about unused static variable (notice that we still use it
36// even in release build if the compiler doesn't support variadic macros)
37#if defined(__WXDEBUG__) || !defined(HAVE_VARIADIC_MACROS)
38
39// the trace mask we use with wxLogTrace() - call
40// wxLog::AddTraceMask(TRACE_CLIPBOARD) to enable the trace messages from here
41// (there will be a *lot* of them!)
42static const wxChar *TRACE_CLIPBOARD = _T("clipboard");
43
44#endif // __WXDEBUG__
45
46//-----------------------------------------------------------------------------
47// reminder
48//-----------------------------------------------------------------------------
49
50/* The contents of a selection are returned in a GtkSelectionData
51   structure. selection/target identify the request.
52   type specifies the type of the return; if length < 0, and
53   the data should be ignored. This structure has object semantics -
54   no fields should be modified directly, they should not be created
55   directly, and pointers to them should not be stored beyond the duration of
56   a callback. (If the last is changed, we'll need to add reference
57   counting)
58
59struct _GtkSelectionData
60{
61  GdkAtom selection;
62  GdkAtom target;
63  GdkAtom type;
64  gint    format;
65  guchar *data;
66  gint    length;
67};
68
69*/
70
71//-----------------------------------------------------------------------------
72// "selection_received" for targets
73//-----------------------------------------------------------------------------
74
75#if 0
76
77static void
78targets_selection_received( GtkWidget *WXUNUSED(widget),
79                            GtkSelectionData *selection_data,
80#if (GTK_MINOR_VERSION > 0)
81                            guint32 WXUNUSED(time),
82#endif
83                            wxClipboard *clipboard )
84{
85    if ( wxTheClipboard && selection_data->length > 0 )
86    {
87        /* make sure we got the data in the correct form */
88        GdkAtom type = selection_data->type;
89        if ( type != GDK_SELECTION_TYPE_ATOM )
90        {
91            if ( strcmp(gdk_atom_name(type), "TARGETS") )
92            {
93                wxLogTrace( TRACE_CLIPBOARD,
94                            _T("got unsupported clipboard target") );
95
96                clipboard->m_waiting = false;
97                return;
98            }
99        }
100
101#ifdef __WXDEBUG__
102        wxDataFormat clip( selection_data->selection );
103        wxLogTrace( TRACE_CLIPBOARD,
104                    wxT("selection received for targets, clipboard %s"),
105                    clip.GetId().c_str() );
106#endif // __WXDEBUG__
107
108        // the atoms we received, holding a list of targets (= formats)
109        GdkAtom *atoms = (GdkAtom *)selection_data->data;
110
111        for (unsigned int i=0; i<selection_data->length/sizeof(GdkAtom); i++)
112        {
113            wxDataFormat format( atoms[i] );
114
115            wxLogTrace( TRACE_CLIPBOARD,
116                        wxT("selection received for targets, format %s"),
117                        format.GetId().c_str() );
118
119            if (format == clipboard->m_targetRequested)
120            {
121                clipboard->m_waiting = false;
122                clipboard->m_formatSupported = true;
123                return;
124            }
125        }
126    }
127
128    clipboard->m_waiting = false;
129}
130
131//-----------------------------------------------------------------------------
132// "selection_received" for the actual data
133//-----------------------------------------------------------------------------
134
135static void
136selection_received( GtkWidget *WXUNUSED(widget),
137                    GtkSelectionData *selection_data,
138#if (GTK_MINOR_VERSION > 0)
139                    guint32 WXUNUSED(time),
140#endif
141                    wxClipboard *clipboard )
142{
143    if (!wxTheClipboard)
144    {
145        clipboard->m_waiting = false;
146        return;
147    }
148
149    wxDataObject *data_object = clipboard->m_receivedData;
150
151    if (!data_object)
152    {
153        clipboard->m_waiting = false;
154        return;
155    }
156
157    if (selection_data->length <= 0)
158    {
159        clipboard->m_waiting = false;
160        return;
161    }
162
163    wxDataFormat format( selection_data->target );
164
165    /* make sure we got the data in the correct format */
166    if (!data_object->IsSupportedFormat( format ) )
167    {
168        clipboard->m_waiting = false;
169        return;
170    }
171
172    /* make sure we got the data in the correct form (selection type).
173       if so, copy data to target object */
174    if (selection_data->type != GDK_SELECTION_TYPE_STRING)
175    {
176        clipboard->m_waiting = false;
177        return;
178    }
179
180    data_object->SetData( format, (size_t) selection_data->length, (const char*) selection_data->data );
181
182    wxTheClipboard->m_formatSupported = true;
183    clipboard->m_waiting = false;
184}
185
186//-----------------------------------------------------------------------------
187// "selection_clear"
188//-----------------------------------------------------------------------------
189
190static gint
191selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event )
192{
193    if (!wxTheClipboard) return TRUE;
194
195    if (event->selection == GDK_SELECTION_PRIMARY)
196    {
197        wxTheClipboard->m_ownsPrimarySelection = false;
198    }
199    else
200    if (event->selection == g_clipboardAtom)
201    {
202        wxTheClipboard->m_ownsClipboard = false;
203    }
204    else
205    {
206        wxTheClipboard->m_waiting = false;
207        return FALSE;
208    }
209
210    if ((!wxTheClipboard->m_ownsPrimarySelection) &&
211        (!wxTheClipboard->m_ownsClipboard))
212    {
213        /* the clipboard is no longer in our hands. we can the delete clipboard data. */
214        if (wxTheClipboard->m_data)
215        {
216            wxLogTrace(TRACE_CLIPBOARD, wxT("wxClipboard will get cleared" ));
217
218            delete wxTheClipboard->m_data;
219            wxTheClipboard->m_data = (wxDataObject*) NULL;
220        }
221    }
222
223    wxTheClipboard->m_waiting = false;
224    return TRUE;
225}
226
227//-----------------------------------------------------------------------------
228// selection handler for supplying data
229//-----------------------------------------------------------------------------
230
231static void
232selection_handler( GtkWidget *WXUNUSED(widget),
233                   GtkSelectionData *selection_data,
234                   guint WXUNUSED(info),
235                   guint WXUNUSED(time),
236                   gpointer WXUNUSED(data) )
237{
238    if (!wxTheClipboard) return;
239
240    if (!wxTheClipboard->m_data) return;
241
242    wxDataObject *data = wxTheClipboard->m_data;
243
244    wxDataFormat format( selection_data->target );
245
246    if (!data->IsSupportedFormat( format )) return;
247
248    int size = data->GetDataSize( format );
249
250    if (size == 0) return;
251
252    void *d = malloc(size);
253
254    data->GetDataHere( selection_data->target, d );
255
256    // transform Unicode text into multibyte before putting it on clipboard
257#if wxUSE_UNICODE
258    if ( format.GetType() == wxDF_TEXT || format.GetType() == wxDF_UNICODETEXT)
259    {
260        const wchar_t *wstr = (const wchar_t *)d;
261        size_t len = wxConvCurrent->WC2MB(NULL, wstr, 0);
262        if ( len == wxCONV_FAILED )
263        {
264            free(d);
265            return;
266        }
267
268        char *str = malloc(len + 1);
269        wxConvCurrent->WC2MB(str, wstr, len + 1);
270        str[len] = '\0';
271
272        free(d);
273        d = str;
274    }
275#endif // wxUSE_UNICODE
276
277    gtk_selection_data_set(
278        selection_data,
279        GDK_SELECTION_TYPE_STRING,
280        8*sizeof(gchar),
281        (unsigned char*) d,
282        size );
283
284    free(d);
285}
286
287#endif
288
289//-----------------------------------------------------------------------------
290// wxClipboard
291//-----------------------------------------------------------------------------
292
293IMPLEMENT_DYNAMIC_CLASS(wxClipboard,wxObject)
294
295wxClipboard::wxClipboard()
296{
297    m_open = false;
298
299    m_ownsClipboard = false;
300    m_ownsPrimarySelection = false;
301
302    m_data = (wxDataObject*) NULL;
303    m_receivedData = (wxDataObject*) NULL;
304
305    /* we use m_targetsWidget to query what formats are available */
306
307    /* we use m_clipboardWidget to get and to offer data */
308#if !wxUSE_NANOX
309    if (!g_clipboardAtom) g_clipboardAtom = XInternAtom( (Display*) wxGetDisplay(), "CLIPBOARD", False );
310    if (!g_targetsAtom) g_targetsAtom = XInternAtom( (Display*) wxGetDisplay(), "TARGETS", False );
311#endif
312
313    m_formatSupported = false;
314    m_targetRequested = 0;
315
316    m_usePrimary = false;
317}
318
319wxClipboard::~wxClipboard()
320{
321    Clear();
322
323//    if (m_clipboardWidget) gtk_widget_destroy( m_clipboardWidget );
324//    if (m_targetsWidget) gtk_widget_destroy( m_targetsWidget );
325}
326
327void wxClipboard::Clear()
328{
329    if (m_data)
330    {
331#if wxUSE_THREADS
332        /* disable GUI threads */
333#endif
334
335        /*  As we have data we also own the clipboard. Once we no longer own
336            it, clear_selection is called which will set m_data to zero */
337#if 0
338        if (gdk_selection_owner_get( g_clipboardAtom ) == m_clipboardWidget->window)
339        {
340            m_waiting = true;
341
342            gtk_selection_owner_set( (GtkWidget*) NULL, g_clipboardAtom,
343                                     (guint32) GDK_CURRENT_TIME );
344
345            while (m_waiting) gtk_main_iteration();
346        }
347
348        if (gdk_selection_owner_get( GDK_SELECTION_PRIMARY ) == m_clipboardWidget->window)
349        {
350            m_waiting = true;
351
352            gtk_selection_owner_set( (GtkWidget*) NULL, GDK_SELECTION_PRIMARY,
353                                     (guint32) GDK_CURRENT_TIME );
354
355            while (m_waiting) gtk_main_iteration();
356        }
357#endif
358
359        if (m_data)
360        {
361            delete m_data;
362            m_data = (wxDataObject*) NULL;
363        }
364
365#if wxUSE_THREADS
366        /* re-enable GUI threads */
367#endif
368    }
369
370    m_targetRequested = 0;
371    m_formatSupported = false;
372}
373
374bool wxClipboard::Open()
375{
376    wxCHECK_MSG( !m_open, false, wxT("clipboard already open") );
377
378    m_open = true;
379
380    return true;
381}
382
383bool wxClipboard::SetData( wxDataObject *data )
384{
385    wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
386
387    wxCHECK_MSG( data, false, wxT("data is invalid") );
388
389    Clear();
390
391    return AddData( data );
392}
393
394bool wxClipboard::AddData( wxDataObject *data )
395{
396#if wxUSE_NANOX
397    return false;
398#else
399    wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
400
401    wxCHECK_MSG( data, false, wxT("data is invalid") );
402
403    /* we can only store one wxDataObject */
404    Clear();
405
406    m_data = data;
407
408    /* get formats from wxDataObjects */
409    wxDataFormat *array = new wxDataFormat[ m_data->GetFormatCount() ];
410    m_data->GetAllFormats( array );
411
412#if 0
413    /* primary selection or clipboard */
414    Atom clipboard = m_usePrimary ? (Atom) 1  // 1 = primary selection
415                                  : g_clipboardAtom;
416#endif // 0
417
418
419    for (size_t i = 0; i < m_data->GetFormatCount(); i++)
420    {
421        wxLogTrace( TRACE_CLIPBOARD,
422                    wxT("wxClipboard now supports atom %s"),
423                    array[i].GetId().c_str() );
424
425#if 0
426        gtk_selection_add_target( GTK_WIDGET(m_clipboardWidget),
427                                  clipboard,
428                                  array[i],
429                                  0 );  /* what is info ? */
430#endif
431    }
432
433    delete[] array;
434
435#if 0
436    gtk_signal_connect( GTK_OBJECT(m_clipboardWidget),
437                        "selection_get",
438                        GTK_SIGNAL_FUNC(selection_handler),
439                        (gpointer) NULL );
440#endif
441
442#if wxUSE_THREADS
443    /* disable GUI threads */
444#endif
445
446    bool res = false;
447#if 0
448    /* Tell the world we offer clipboard data */
449    res = (gtk_selection_owner_set( m_clipboardWidget,
450                                         clipboard,
451                                         (guint32) GDK_CURRENT_TIME ));
452#endif
453
454    if (m_usePrimary)
455        m_ownsPrimarySelection = res;
456    else
457        m_ownsClipboard = res;
458
459#if wxUSE_THREADS
460    /* re-enable GUI threads */
461#endif
462
463    return res;
464#endif
465}
466
467void wxClipboard::Close()
468{
469    wxCHECK_RET( m_open, wxT("clipboard not open") );
470
471    m_open = false;
472}
473
474bool wxClipboard::IsOpened() const
475{
476    return m_open;
477}
478
479bool wxClipboard::IsSupported( const wxDataFormat& format )
480{
481    /* reentrance problems */
482    if (m_waiting) return false;
483
484    /* store requested format to be asked for by callbacks */
485    m_targetRequested = format;
486
487#if 0
488    wxLogTrace( TRACE_CLIPBOARD,
489                wxT("wxClipboard:IsSupported: requested format: %s"),
490                format.GetId().c_str() );
491#endif
492
493    wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
494
495    m_formatSupported = false;
496
497    /* perform query. this will set m_formatSupported to
498       true if m_targetRequested is supported.
499       also, we have to wait for the "answer" from the
500       clipboard owner which is an asynchronous process.
501       therefore we set m_waiting = true here and wait
502       until the callback "targets_selection_received"
503       sets it to false */
504
505    m_waiting = true;
506
507#if 0
508    gtk_selection_convert( m_targetsWidget,
509                           m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
510                                        : g_clipboardAtom,
511                           g_targetsAtom,
512                           (guint32) GDK_CURRENT_TIME );
513
514    while (m_waiting) gtk_main_iteration();
515#endif
516
517    if (!m_formatSupported) return false;
518
519    return true;
520}
521
522bool wxClipboard::GetData( wxDataObject& data )
523{
524    wxCHECK_MSG( m_open, false, wxT("clipboard not open") );
525
526    /* get formats from wxDataObjects */
527    wxDataFormat *array = new wxDataFormat[ data.GetFormatCount() ];
528    data.GetAllFormats( array );
529
530    for (size_t i = 0; i < data.GetFormatCount(); i++)
531    {
532        wxDataFormat format( array[i] );
533
534        wxLogTrace( TRACE_CLIPBOARD,
535                    wxT("wxClipboard::GetData: requested format: %s"),
536                    format.GetId().c_str() );
537
538        /* is data supported by clipboard ? */
539
540        /* store requested format to be asked for by callbacks */
541        m_targetRequested = format;
542
543        wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
544
545        m_formatSupported = false;
546
547       /* perform query. this will set m_formatSupported to
548          true if m_targetRequested is supported.
549          also, we have to wait for the "answer" from the
550          clipboard owner which is an asynchronous process.
551          therefore we set m_waiting = true here and wait
552          until the callback "targets_selection_received"
553          sets it to false */
554
555        m_waiting = true;
556
557#if 0
558        gtk_selection_convert( m_targetsWidget,
559                           m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
560                                        : g_clipboardAtom,
561                           g_targetsAtom,
562                           (guint32) GDK_CURRENT_TIME );
563
564        while (m_waiting) gtk_main_iteration();
565#endif
566
567        if (!m_formatSupported) continue;
568
569        /* store pointer to data object to be filled up by callbacks */
570        m_receivedData = &data;
571
572        /* store requested format to be asked for by callbacks */
573        m_targetRequested = format;
574
575        wxCHECK_MSG( m_targetRequested, false, wxT("invalid clipboard format") );
576
577        /* start query */
578        m_formatSupported = false;
579
580        /* ask for clipboard contents.  this will set
581           m_formatSupported to true if m_targetRequested
582           is supported.
583           also, we have to wait for the "answer" from the
584           clipboard owner which is an asynchronous process.
585           therefore we set m_waiting = true here and wait
586           until the callback "targets_selection_received"
587           sets it to false */
588
589        m_waiting = true;
590
591        wxLogTrace( TRACE_CLIPBOARD,
592                    wxT("wxClipboard::GetData: format found, start convert") );
593
594#if 0
595        gtk_selection_convert( m_clipboardWidget,
596                               m_usePrimary ? (GdkAtom)GDK_SELECTION_PRIMARY
597                                            : g_clipboardAtom,
598                               m_targetRequested,
599                               (guint32) GDK_CURRENT_TIME );
600
601        while (m_waiting) gtk_main_iteration();
602#endif
603
604        /* this is a true error as we checked for the presence of such data before */
605        wxCHECK_MSG( m_formatSupported, false, wxT("error retrieving data from clipboard") );
606
607        /* return success */
608        delete[] array;
609        return true;
610    }
611
612    wxLogTrace( TRACE_CLIPBOARD,
613                wxT("wxClipboard::GetData: format not found") );
614
615    /* return failure */
616    delete[] array;
617    return false;
618}
619
620#endif
621  // wxUSE_CLIPBOARD
622