/* AbiSource Application Framework * Copyright (C) 1998 AbiSource, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "ut_debugmsg.h" #include "ut_string.h" #include "ut_assert.h" #include "xap_UnixClipboard.h" ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // This file contains all the *stuff* to deal with the // wonderful X11 clipboard and selection mechanism (two // related, but different, things). // // The clipboard is used for explicit cut/copy/paste // operations requested by the user. Application // specific code actually makes a copy of the current // selection and hands us a buffer in a named format // (such as RTF). We "ASSERT" the "CLIPBOARD" and // thereby inform the X server that we "think" we own // it. // // When we (or another application) want to paste, // we ask the X server to ask the owning application // for a list of formats that it can provide (via the // "TARGETS" verb) and if we find something that we // like, we ask (the X server to ask) the application // for to provide us the data it (in a format it supports // or converted to one that we've support (via // gtk_selection_convert())). // // On a paste, we can short-circut the server (both // calls), if we were the last one to assert the clipboard. // // After we've asserted the clipboard, the server will // send us a selection_clear when someone else asserts it. // // All clipboard operations are done on the "CLIPBOARD" atom. // ////////////////////////////////////////////////////////////////// // // The X selection mechanism is the familiar technique of // dragging a selection (usually with the left-mouse) and // then pasting with the middle-mouse. Creating a selection // in this manner is an ***implicit*** copy. We recive a // a call from the application indicating that the user has // created a selection. We "ASSERT" the "SELECTION" and // thereby inform that X server that we "think" we own it, // but *WE DO NOT MAKE A COPY OF THE SELECTION CONTENT AT // THIS TIME*. If the user clears the selection, we inform // the X server that we are "RELEASING OUR ASSERT" of the // selection. // // On a Paste, we (or another application) asks the server // (just like with the clipboard) for the selection content. // On reciving a request for the selection content, we call // back up into the application and ask for a copy of the // current selection -- that is, we delay the actual construction // of the copy buffer until it is actually needed. // On a paste, we can short-circut the server (both // calls), if we were the last one to assert the selection. // // After we've asserted the selection, the server will // send us a selection_clear when someone else asserts it. // // All selection operations are done on the "PRIMARY" atom. // ////////////////////////////////////////////////////////////////// // // Commentary: Having both a SELECTION and CLIPBOARD is a // somewhat ill-conceived idea -- in X. // // There is an explicit conflict between the clipboard and // the selection. An application cannot put something on // the clipboard without trashing the selection. The selection // is a very transient thing and should not be relied upon -- // it's just too easy for it to get clobbered. // // Currently we have the notion of a Clipboard-Paste (accessed // via the menu) and Selection-Paste (assessed via the middle // mouse button). Should we fold the selection onto the clipboard // so that the Menu-Paste pastes whichever is more recent. I've // left hooks in for this if someone wants to experiment and/or // make it a perferences option. // ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // only one instance of this class is ever created. // it is associated with the App class. // we do not know about any windows, frames, or views. // on a copy to the clipboard, a buffer to the clipped content is // provided on the downcall. // on a copy to the selection, no buffer is provided; an upcall // to the app is made upon demand; the app is responsible for // keeping track of the current window/view/frame which last // asserted the selection. ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // we use the builtin "FakeClipboard" to actually store and index // the clipboard buffers. this file is only concerned with the // interaction with the X server. ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // we create a hidden widget to and register all clipboard and // selection events on it. we are the only one that knows anything // about it. ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// // this class should be considered to consist of two parts: // // [1] top-half -- downcalls from the application in response to // user interaction // [2] bottom-half -- event callbacks from the server in response // to other events from the XServer (and indirectly // from other applications). // // top-half code will either return immediately or will make a // request to the server and spin waiting for an answer. in this // case, the bottom-half will receive the answer and release the // spin-loop -- the bottom-half ***MUST*** do this release or else // we will lock up. // // there is an inherent race condition that we must deal with // here, since the event callbacks occur in a somewhat asynchronous // fashion. for example, another application can ask us for our // selection content after we have asserted it and cleared it, // but before the server has processed our clear request. or // another application can clear its selection between the time // we ask for it to enumerate formats and the time we ask it to // send us the data. // ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// static void s_selsnd(GtkWidget * widget, GtkSelectionData * selectionData, guint info, guint32 time, gpointer data) { // callback XAP_UnixClipboard * pThis = (XAP_UnixClipboard *)gtk_object_get_data(GTK_OBJECT(widget), "clipboard"); pThis->_selsnd(selectionData,info,time,data); return; } static gint s_selclr(GtkWidget * widget, GdkEventSelection * event) { // callback XAP_UnixClipboard * pThis = (XAP_UnixClipboard *)gtk_object_get_data(GTK_OBJECT(widget), "clipboard"); return pThis->_selclr(event); } static void s_selrcv(GtkWidget * widget, GtkSelectionData *selectionData, guint32 time, gpointer data) { // callback XAP_UnixClipboard * pThis = (XAP_UnixClipboard *)gtk_object_get_data(GTK_OBJECT(widget), "clipboard"); pThis->_selrcv(selectionData,time,data); return; } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// XAP_UnixClipboard::XAP_UnixClipboard(XAP_UnixApp * pUnixApp) { // caller must call initialize() m_pUnixApp = pUnixApp; } XAP_UnixClipboard::~XAP_UnixClipboard() { UT_DEBUGMSG(("Clipboard: destroying [ownPrimary %d][ownClipboard %d]\n", m_bOwnPrimary, m_bOwnClipboard)); if (m_bOwnClipboard || m_bOwnPrimary) clearData(m_bOwnClipboard,m_bOwnPrimary); if (m_myWidget) gtk_widget_destroy(m_myWidget); } ////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////// void XAP_UnixClipboard::initialize(void) { UT_DEBUGMSG(("Clipboard: initializing\n")); m_waiting = UT_FALSE; m_bOwnClipboard = UT_FALSE; m_bOwnPrimary = UT_FALSE; m_timeClipboard = 0; m_timePrimary = 0; m_timeOnServer = 0; m_databuftype = GDK_NONE; m_atomClipboard = gdk_atom_intern("CLIPBOARD", FALSE); UT_DEBUGMSG(("Clipboard: property [%s atom %lu]\n","CLIPBOARD", m_atomClipboard)); m_atomPrimary = gdk_atom_intern("PRIMARY", FALSE); UT_DEBUGMSG(("Clipboard: property [%s atom %lu]\n","PRIMARY", m_atomPrimary)); m_atomTargets = gdk_atom_intern("TARGETS", FALSE); UT_DEBUGMSG(("Clipboard: target [%s atom %lu]\n","TARGETS",m_atomTargets)); m_atomTimestamp = gdk_atom_intern("TIMESTAMP", FALSE); UT_DEBUGMSG(("Clipboard: target [%s atom %lu]\n","TIMESTAMP",m_atomTimestamp)); #ifdef DEBUG for (int j=0, jLimit=m_vecFormat_AP_Name.getItemCount(); (j