X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/eddb964405c1db1a931a347611aed0af6ee0e3b7..8caabedb27e0a1a35bbc392b84a92285e56ca6e3:/src/gtk/clipbrd.cpp diff --git a/src/gtk/clipbrd.cpp b/src/gtk/clipbrd.cpp index e6b79180a1..15ffd8be44 100644 --- a/src/gtk/clipbrd.cpp +++ b/src/gtk/clipbrd.cpp @@ -80,7 +80,7 @@ public: // this method must be called by GTK+ callbacks to indicate that we got the // result for our clipboard operation - static void OnDone(wxClipboard *clipboard) + static void OnDone(wxClipboard * WXUNUSED_UNLESS_DEBUG(clipboard)) { wxASSERT_MSG( clipboard == ms_clipboard, _T("got notification for alien clipboard") ); @@ -88,6 +88,17 @@ public: ms_clipboard = NULL; } + // this method should be called if it's possible that no async clipboard + // operation is currently in progress (like it can be the case when the + // clipboard is cleared but not because we asked about it), it should only + // be called if such situation is expected -- otherwise call OnDone() which + // would assert in this case + static void OnDoneIfInProgress(wxClipboard *clipboard) + { + if ( ms_clipboard ) + OnDone(clipboard); + } + private: static wxClipboard *ms_clipboard; @@ -97,7 +108,7 @@ private: wxClipboard *wxClipboardSync::ms_clipboard = NULL; // ============================================================================ -// clipboard ca;backs implementation +// clipboard callbacks implementation // ============================================================================ //----------------------------------------------------------------------------- @@ -199,7 +210,12 @@ selection_clear_clip( GtkWidget *WXUNUSED(widget), GdkEventSelection *event ) if ( !clipboard ) return TRUE; - wxON_BLOCK_EXIT1(wxClipboardSync::OnDone, clipboard); + // notice the use of OnDoneIfInProgress() here instead of just OnDone(): + // it's perfectly possible that we're receiving this notification from GTK+ + // even though we hadn't cleared the clipboard ourselves but because + // another application (or even another window in the same program) + // acquired it + wxON_BLOCK_EXIT1(wxClipboardSync::OnDoneIfInProgress, clipboard); wxClipboard::Kind kind; if (event->selection == GDK_SELECTION_PRIMARY) @@ -275,37 +291,39 @@ selection_handler( GtkWidget *WXUNUSED(widget), wxString::FromAscii(wxGtkString(gdk_atom_name(selection_data->selection))).c_str(), GPOINTER_TO_UINT( signal_data ) ); -#endif +#endif // __WXDEBUG__ - if (!data->IsSupportedFormat( format )) return; + if ( !data->IsSupportedFormat( format ) ) + return; int size = data->GetDataSize( format ); + if ( !size ) + return; - if (size == 0) return; - - void *d = malloc(size); - wxON_BLOCK_EXIT1(free, d); + wxCharBuffer buf(size - 1); // it adds 1 internally (for NUL) - // Text data will be in UTF8 in Unicode mode. - data->GetDataHere( selection_data->target, d ); + // text data must be returned in UTF8 if format is wxDF_UNICODETEXT + if ( !data->GetDataHere(format, buf.data()) ) + return; - // NB: GTK+ requires special treatment of UTF8_STRING data, the text - // would show as UTF-8 data interpreted as latin1 (?) in other - // GTK+ apps if we used gtk_selection_data_set() + // use UTF8_STRING format if requested in Unicode build but just plain + // STRING one in ANSI or if explicitly asked in Unicode +#if wxUSE_UNICODE if (format == wxDataFormat(wxDF_UNICODETEXT)) { gtk_selection_data_set_text( selection_data, - (const gchar*)d, + (const gchar*)buf.data(), size ); } else +#endif // wxUSE_UNICODE { gtk_selection_data_set( selection_data, GDK_SELECTION_TYPE_STRING, 8*sizeof(gchar), - (unsigned char*) d, + (const guchar*)buf.data(), size ); } } @@ -326,6 +344,73 @@ void wxClipboard::GTKOnSelectionReceived(const GtkSelectionData& sel) m_formatSupported = true; } +//----------------------------------------------------------------------------- +// asynchronous "selection_received" for targets +//----------------------------------------------------------------------------- + +extern "C" { +static void +async_targets_selection_received( GtkWidget *WXUNUSED(widget), + GtkSelectionData *selection_data, + guint32 WXUNUSED(time), + wxClipboard *clipboard ) +{ + if ( !clipboard ) // Assert? + return; + + if (!clipboard->m_sink) + return; + + wxClipboardEvent *event = new wxClipboardEvent(wxEVT_CLIPBOARD_CHANGED); + event->SetEventObject( clipboard ); + + if ( !selection_data || selection_data->length <= 0 ) + { + clipboard->m_sink->QueueEvent( event ); + clipboard->m_sink.Release(); + return; + } + + // make sure we got the data in the correct form + GdkAtom type = selection_data->type; + if ( type != GDK_SELECTION_TYPE_ATOM ) + { + if ( strcmp(wxGtkString(gdk_atom_name(type)), "TARGETS") != 0 ) + { + wxLogTrace( TRACE_CLIPBOARD, + _T("got unsupported clipboard target") ); + + clipboard->m_sink->QueueEvent( event ); + clipboard->m_sink.Release(); + return; + } + } + +#ifdef __WXDEBUG__ + // it's not really a format, of course, but we can reuse its GetId() method + // to format this atom as string + wxDataFormat clip(selection_data->selection); + wxLogTrace( TRACE_CLIPBOARD, + wxT("Received available formats for clipboard %s"), + clip.GetId().c_str() ); +#endif // __WXDEBUG__ + + // the atoms we received, holding a list of targets (= formats) + const GdkAtom * const atoms = (GdkAtom *)selection_data->data; + for ( size_t i = 0; i < selection_data->length/sizeof(GdkAtom); i++ ) + { + const wxDataFormat format(atoms[i]); + + wxLogTrace(TRACE_CLIPBOARD, wxT("\t%s"), format.GetId().c_str()); + + event->AddFormat( format ); + } + + clipboard->m_sink->QueueEvent( event ); + clipboard->m_sink.Release(); +} +} + // ============================================================================ // wxClipboard implementation // ============================================================================ @@ -347,8 +432,6 @@ wxClipboard::wxClipboard() m_formatSupported = false; m_targetRequested = 0; - m_usePrimary = false; - // we use m_targetsWidget to query what formats are available m_targetsWidget = gtk_window_new( GTK_WINDOW_POPUP ); gtk_widget_realize( m_targetsWidget ); @@ -356,6 +439,13 @@ wxClipboard::wxClipboard() g_signal_connect (m_targetsWidget, "selection_received", G_CALLBACK (targets_selection_received), this); + // we use m_targetsWidgetAsync to query what formats asynchronously + m_targetsWidgetAsync = gtk_window_new( GTK_WINDOW_POPUP ); + gtk_widget_realize( m_targetsWidgetAsync ); + + g_signal_connect (m_targetsWidgetAsync, "selection_received", + G_CALLBACK (async_targets_selection_received), this); + // we use m_clipboardWidget to get and to offer data m_clipboardWidget = gtk_window_new( GTK_WINDOW_POPUP ); gtk_widget_realize( m_clipboardWidget ); @@ -379,10 +469,8 @@ wxClipboard::~wxClipboard() { Clear(); - if ( m_clipboardWidget ) - gtk_widget_destroy( m_clipboardWidget ); - if ( m_targetsWidget ) - gtk_widget_destroy( m_targetsWidget ); + gtk_widget_destroy( m_clipboardWidget ); + gtk_widget_destroy( m_targetsWidget ); } // ---------------------------------------------------------------------------- @@ -397,7 +485,7 @@ GdkAtom wxClipboard::GTKGetClipboardAtom() const void wxClipboard::GTKClearData(Kind kind) { - wxDataObject *&data = Data(); + wxDataObject *&data = Data(kind); if ( data ) { delete data; @@ -427,13 +515,29 @@ void wxClipboard::AddSupportedTarget(GdkAtom atom) { gtk_selection_add_target ( - GTK_WIDGET(m_clipboardWidget), + m_clipboardWidget, GTKGetClipboardAtom(), atom, 0 // info (same as client data) unused ); } +bool wxClipboard::IsSupportedAsync(wxEvtHandler *sink) +{ + if (m_sink.get()) + return false; // currently busy, come back later + + wxCHECK_MSG( sink, false, wxT("no sink given") ); + + m_sink = sink; + gtk_selection_convert( m_targetsWidgetAsync, + GTKGetClipboardAtom(), + g_targetsAtom, + (guint32) GDK_CURRENT_TIME ); + + return true; +} + bool wxClipboard::DoIsSupported(const wxDataFormat& format) { wxCHECK_MSG( format, false, wxT("invalid clipboard format") );