445 lines
12 KiB
C++
445 lines
12 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*/
|
|
|
|
#include "nsClipboard.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsIClipboardOwner.h"
|
|
#include "nsITransferable.h" // kTextMime
|
|
|
|
#include "nsIWidget.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsWidgetsCID.h"
|
|
|
|
|
|
// The class statics:
|
|
//GtkWidget* nsClipboard::sWidget = nsnull;
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsClipboard, nsBaseClipboard)
|
|
NS_IMPL_RELEASE_INHERITED(nsClipboard, nsBaseClipboard)
|
|
|
|
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
|
|
|
|
#if defined(DEBUG_akkana) || defined(DEBUG_mcafee) || defined(DEBUG_pavlov) || defined(DEBUG_blizzard)
|
|
#define DEBUG_CLIPBOARD
|
|
#endif
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// nsClipboard constructor
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
nsClipboard::nsClipboard() : nsBaseClipboard()
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::nsClipboard()\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
//NS_INIT_REFCNT();
|
|
mIgnoreEmptyNotification = PR_FALSE;
|
|
mClipboardOwner = nsnull;
|
|
mTransferable = nsnull;
|
|
mSelectionData.data = nsnull;
|
|
mSelectionData.length = 0;
|
|
CreateInvisibleWindow();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
//
|
|
// nsClipboard destructor
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
nsClipboard::~nsClipboard()
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::~nsClipboard()\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// Remove all our event handlers:
|
|
if (sWindow &&
|
|
XGetSelectionOwner(sDisplay, PRIMARY) == sWindow)
|
|
;
|
|
//gtk_selection_remove_all(sWidget);
|
|
|
|
// free the selection data, if any
|
|
}
|
|
|
|
/**
|
|
* @param aIID The name of the class implementing the method
|
|
* @param _classiiddef The name of the #define symbol that defines the IID
|
|
* for the class (e.g. NS_ISUPPORTS_IID)
|
|
*
|
|
*/
|
|
nsresult nsClipboard::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
nsresult rv = NS_NOINTERFACE;
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIClipboard))) {
|
|
*aInstancePtr = (void*) ((nsIClipboard*)this);
|
|
NS_ADDREF_THIS();
|
|
return NS_OK;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
//
|
|
// Create an invisible window to own the selection
|
|
//
|
|
void nsClipboard::CreateInvisibleWindow(void)
|
|
{
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
NS_IMETHODIMP nsClipboard::SetNativeClipboardData()
|
|
{
|
|
mIgnoreEmptyNotification = PR_TRUE;
|
|
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::SetNativeClipboardData()\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// make sure we have a good transferable
|
|
if (nsnull == mTransferable) {
|
|
printf(" SetNativeClipboardData: no transferable!\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// If we're already the selection owner, don't need to do anything,
|
|
// we'll already get the events:
|
|
if (sWindow && XGetSelectionOwner(sDisplay, PRIMARY) == sWindow)
|
|
return NS_OK;
|
|
|
|
// register as the selection owner:
|
|
if (XSetSelectionOwner(sDisplay, PRIMARY, sWindow, CurrentTime) != 0)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
mIgnoreEmptyNotification = PR_FALSE;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
//
|
|
// The blocking Paste routine
|
|
//
|
|
NS_IMETHODIMP
|
|
nsClipboard::GetNativeClipboardData(nsITransferable * aTransferable)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::GetNativeClipboardData()\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// make sure we have a good transferable
|
|
if (nsnull == aTransferable) {
|
|
printf(" GetNativeClipboardData: Transferable is null!\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Dunno why we need to do this, copying the win32 code ...
|
|
nsCOMPtr<nsITransferable> trans = do_QueryInterface(aTransferable);
|
|
if (!trans)
|
|
return rv;
|
|
|
|
//
|
|
// We can't call the copy callback when we're blocking on the paste callback;
|
|
// so if this app is already the selection owner, we need to copy our own
|
|
// data without going through the X server.
|
|
//
|
|
if (XGetSelectionOwner(sDisplay, PRIMARY) == sWindow)
|
|
{
|
|
// XXX only support text/plain for now
|
|
nsAutoString dataFlavor(kTextMime);
|
|
|
|
// Get data out of our existing transferable.
|
|
void *clipboardData;
|
|
PRUint32 dataLength;
|
|
rv = mTransferable->GetTransferData(&dataFlavor,
|
|
&clipboardData,
|
|
&dataLength);
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = trans->SetTransferData(&dataFlavor,
|
|
clipboardData, dataLength);
|
|
return rv;
|
|
}
|
|
|
|
// Only support plaintext for now:
|
|
|
|
// What is "property" (set to None here)?
|
|
// Answer (from ORA Xlib book, p. 548):
|
|
// If the property field is not None, the owner should place the
|
|
// data resulting from converting the selection into the specified
|
|
// property on the requestor window, setting the type as before.
|
|
//
|
|
// This should queue a SelectionNotify event (see that callback)
|
|
XConvertSelection(sDisplay,
|
|
XA_PRIMARY, XA_STRING, None,
|
|
sWindow, CurrentTime);
|
|
// XXX we're not supposed to use CurrentTime, we're supposed to use
|
|
// the timestamp of the event causing us to request the selection.
|
|
// Alas, we don't have that information any more by the time we get here.
|
|
|
|
//
|
|
// We've told X what type to send, and we just have to wait
|
|
// for the callback saying that the data have been transferred.
|
|
//
|
|
|
|
// Set a flag saying that we're blocking waiting for the callback:
|
|
mBlocking = PR_TRUE;
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf("Waiting for the callback\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// Now we need to wait until the callback comes in ...
|
|
// i is in case we get a runaway (yuck).
|
|
for (int i=0; mBlocking == PR_TRUE && i < 10000; ++i)
|
|
{
|
|
gtk_main_iteration_do(PR_TRUE);
|
|
}
|
|
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf("Got the callback: '%s', %d\n",
|
|
mSelectionData.data, mSelectionData.length);
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// We're back from the callback, no longer blocking:
|
|
mBlocking = PR_FALSE;
|
|
|
|
//
|
|
// Now we have data in mSelectionData.data.
|
|
// We just have to copy it to the transferable.
|
|
//
|
|
nsAutoString dataFlavor(kTextMime);
|
|
trans->SetTransferData(&dataFlavor,
|
|
mSelectionData.data, mSelectionData.length);
|
|
|
|
// Can't free the selection data -- the transferable just saves a pointer.
|
|
// But the transferable is responsible for freeing it, so we have to
|
|
// consider it freed now:
|
|
//g_free(mSelectionData.data);
|
|
mSelectionData.data = nsnull;
|
|
mSelectionData.length = 0;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// Called when the data from a paste comes in:
|
|
//
|
|
void
|
|
nsClipboard::SelectionReceivedCB (GtkWidget *aWidget,
|
|
GtkSelectionData *aSelectionData,
|
|
gpointer aData)
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::SelectionReceivedCB\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// ARGHH! GTK doesn't pass the arg to the callback, so we can't
|
|
// get "this" back! Until we solve this, get it from the service mgr:
|
|
nsresult rv;
|
|
NS_WITH_SERVICE(nsIClipboard, iclipboard, kCClipboardCID, &rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
printf("Couldn't get clipboard service!\n");
|
|
return;
|
|
}
|
|
nsClipboard* clipboard = (nsClipboard*)iclipboard;
|
|
if (!clipboard) {
|
|
printf("couldn't convert nsIClipboard to nsClipboard\n");
|
|
return;
|
|
}
|
|
|
|
clipboard->SelectionReceiver(aWidget, aSelectionData);
|
|
}
|
|
|
|
void
|
|
nsClipboard::SelectionReceiver (Window aWindow,
|
|
GtkSelectionData *aSelectionData)
|
|
{
|
|
mBlocking = PR_FALSE;
|
|
|
|
if (aSelectionData->length < 0)
|
|
{
|
|
printf("Error retrieving selection: length was %d\n",
|
|
aSelectionData->length);
|
|
return;
|
|
}
|
|
|
|
switch (aSelectionData->type)
|
|
{
|
|
case XA_STRING:
|
|
mSelectionData = *aSelectionData;
|
|
mSelectionData.data = g_new(guchar, aSelectionData->length + 1);
|
|
memcpy(mSelectionData.data,
|
|
aSelectionData->data, aSelectionData->length);
|
|
// Null terminate in case anyone cares,
|
|
// and so we can print the string for debugging:
|
|
mSelectionData.data[aSelectionData->length] = '\0';
|
|
mSelectionData.length = aSelectionData->length;
|
|
return;
|
|
|
|
default:
|
|
printf("Can't convert type %s (%ld) to string\n",
|
|
gdk_atom_name (aSelectionData->type), aSelectionData->type);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* No-op.
|
|
*
|
|
*/
|
|
NS_IMETHODIMP nsClipboard::ForceDataToClipboard()
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::ForceDataToClipboard()\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// make sure we have a good transferable
|
|
if (nsnull == mTransferable) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// This is the callback which is called when another app
|
|
// requests the selection.
|
|
//
|
|
void nsClipboard::SelectionRequestCB(Window aWindow,
|
|
Atom selection,
|
|
Atom target,
|
|
Atom property,
|
|
Window requestor,
|
|
Time time)
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::SelectionRequestCB\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
nsClipboard *clipboard = (nsClipboard *)aData;
|
|
|
|
void *clipboardData;
|
|
PRUint32 dataLength;
|
|
nsresult rv;
|
|
|
|
// Make sure we have a transferable:
|
|
if (!clipboard->mTransferable) {
|
|
printf("Clipboard has no transferable!\n");
|
|
return;
|
|
}
|
|
|
|
// XXX hack, string-only for now.
|
|
// Create string data-flavor.
|
|
nsString dataFlavor (kTextMime);
|
|
|
|
// Get data out of transferable.
|
|
rv = clipboard->mTransferable->GetTransferData(&dataFlavor,
|
|
&clipboardData,
|
|
&dataLength);
|
|
|
|
// Currently we only offer the data in GDK_SELECTION_TYPE_STRING format.
|
|
if (NS_SUCCEEDED(rv) && clipboardData && dataLength > 0) {
|
|
gtk_selection_data_set(aSelectionData,
|
|
GDK_SELECTION_TYPE_STRING, 8,
|
|
(unsigned char *)clipboardData,
|
|
dataLength);
|
|
}
|
|
else
|
|
printf("Transferable didn't support the data flavor\n");
|
|
}
|
|
|
|
|
|
|
|
// Called when another app requests selection ownership:
|
|
void nsClipboard::SelectionClearCB(GtkWidget *widget,
|
|
GdkEventSelection *event,
|
|
gpointer data)
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::SelectionClearCB\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
}
|
|
|
|
//
|
|
// The callback called after we've sent a convert selection request
|
|
// when the server calls us back with the data.
|
|
//
|
|
void
|
|
nsClipboard::SelectionNotifyCB(Window window,
|
|
Atom selection,
|
|
Atom target,
|
|
Atom property,
|
|
Time time)
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf(" nsClipboard::SelectionNotifyCB\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
|
|
// Un-block us, whether this works or not:
|
|
mBlocking = PR_FALSE;
|
|
|
|
if (property == None)
|
|
{
|
|
#ifdef DEBUG_CLIPBOARD
|
|
printf("Selection was refused!\n");
|
|
#endif /* DEBUG_CLIPBOARD */
|
|
return;
|
|
}
|
|
|
|
// I don't know what all these parameters are!
|
|
// So we're just throwing away the data.
|
|
do
|
|
{
|
|
long offset;
|
|
long length;
|
|
Atom actualType;
|
|
unsigned long nitems;
|
|
unsigned long bytesAfter;
|
|
unsigned char* propStr;
|
|
XGetWindowProperty(sDisplay, sWindow, property,
|
|
offset, length, FALSE, AnyPropertyType,
|
|
&actualType, &nitems, &bytesAfter, propStr);
|
|
} while (bytesAfter > 0);
|
|
|
|
// Now delete that window property to show we've gotten all the data:
|
|
XGetWindowProperty(sDisplay, sWindow, property,
|
|
offset, length, TRUE, AnyPropertyType,
|
|
&actualType, &nitems, &bytesAfter, propStr);
|
|
}
|