423 lines
13 KiB
C++
423 lines
13 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>
|
|
* Peter Hartshorn <peter@igelaus.com.au>
|
|
* Ken Faulkner <faulkner@igelaus.com.au>
|
|
*/
|
|
|
|
/* TODO:
|
|
* Currently this only supports the transfer of TEXT! FIXME
|
|
*/
|
|
|
|
#include "nsClipboard.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xatom.h>
|
|
#include "string.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
#include "nsFileSpec.h"
|
|
#include "nsCRT.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsISupportsPrimitives.h"
|
|
|
|
#include "nsIComponentManager.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsWidgetsCID.h"
|
|
#include "nsXPIDLString.h"
|
|
#include "nsPrimitiveHelpers.h"
|
|
|
|
#include "nsTextFormatter.h"
|
|
#include "nsVoidArray.h"
|
|
|
|
#include "xlibrgb.h"
|
|
|
|
|
|
#include "nsIServiceManager.h"
|
|
#include "nsICharsetConverterManager.h"
|
|
// unicode conversion
|
|
#define NS_IMPL_IDS
|
|
# include "nsIPlatformCharset.h"
|
|
#undef NS_IMPL_IDS
|
|
|
|
|
|
// The class statics:
|
|
nsITransferable *nsClipboard::mTransferable = nsnull;
|
|
Window nsClipboard::sWindow;
|
|
Display *nsClipboard::sDisplay;
|
|
|
|
#if defined(DEBUG_mcafee) || defined(DEBUG_pavlov)
|
|
#define DEBUG_CLIPBOARD
|
|
#endif
|
|
|
|
NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard);
|
|
|
|
nsClipboard::nsClipboard() {
|
|
NS_INIT_REFCNT();
|
|
|
|
sDisplay = xlib_rgb_get_display();
|
|
|
|
Init();
|
|
}
|
|
|
|
nsClipboard::~nsClipboard() {
|
|
NS_IF_RELEASE(sWidget);
|
|
}
|
|
|
|
/*static*/ void nsClipboard::Shutdown() {
|
|
NS_IF_RELEASE(mTransferable);
|
|
}
|
|
|
|
// Initialize the clipboard and create a nsWidget for communications
|
|
|
|
void nsClipboard::Init() {
|
|
NS_ASSERTION(!sWidget, "already initialized");
|
|
|
|
sWidget = new nsWidget();
|
|
if (!sWidget) return;
|
|
NS_ADDREF(sWidget);
|
|
|
|
const nsRect rect(0,0,100,100);
|
|
|
|
sWidget->Create((nsIWidget *)nsnull, rect, Callback,
|
|
(nsIDeviceContext *)nsnull, (nsIAppShell *)nsnull,
|
|
(nsIToolkit *)nsnull, (nsWidgetInitData *)nsnull);
|
|
sWindow = (Window)sWidget->GetNativeData(NS_NATIVE_WINDOW);
|
|
|
|
XSelectInput(sDisplay, sWindow, 0x0fffff);
|
|
}
|
|
|
|
// This is the callback function for our nsWidget. It is given the
|
|
// XEvent from nsAppShell.
|
|
// FIXME: We _should_ assign mTransferable here depending on if its a
|
|
// selectionrequest
|
|
nsEventStatus PR_CALLBACK nsClipboard::Callback(nsGUIEvent *event) {
|
|
XEvent *ev = (XEvent *)event->nativeMsg;
|
|
|
|
// Check the event type
|
|
if (ev->type == SelectionRequest) {
|
|
if (mTransferable == nsnull) {
|
|
fprintf(stderr, "nsClipboard::Callback: null transferable\n");
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
// Get the data from the Transferable
|
|
|
|
char *dataFlavor = kUnicodeMime;
|
|
nsCOMPtr<nsISupports> genDataWrapper;
|
|
nsresult rv;
|
|
PRUint32 dataLength;
|
|
void *data;
|
|
data = malloc(16384);
|
|
rv = mTransferable->GetTransferData(dataFlavor,
|
|
getter_AddRefs(genDataWrapper),
|
|
&dataLength);
|
|
nsPrimitiveHelpers::CreateDataFromPrimitive(dataFlavor, genDataWrapper,
|
|
&data, dataLength);
|
|
if (NS_SUCCEEDED(rv) && data && dataLength) {
|
|
char *plainText = nsnull;
|
|
PRUnichar* unicodeData = NS_REINTERPRET_CAST(PRUnichar*, data);
|
|
PRInt32 plainLen = 0;
|
|
nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText(unicodeData,
|
|
dataLength/2,
|
|
&plainText,
|
|
&plainLen);
|
|
if (data) {
|
|
free(data);
|
|
data = plainText;
|
|
dataLength = plainLen;
|
|
}
|
|
|
|
// Set the window property to contain the data
|
|
XChangeProperty(sDisplay,
|
|
ev->xselectionrequest.requestor,
|
|
ev->xselectionrequest.property,
|
|
ev->xselectionrequest.target,
|
|
8, PropModeReplace,
|
|
(unsigned char *)data, dataLength);
|
|
|
|
// Send the requestor a SelectionNotify event
|
|
XEvent aEvent;
|
|
aEvent.type = SelectionNotify;
|
|
aEvent.xselection.serial = ev->xselectionrequest.serial;
|
|
aEvent.xselection.display = ev->xselectionrequest.display;
|
|
aEvent.xselection.requestor = ev->xselectionrequest.requestor;
|
|
aEvent.xselection.selection = ev->xselectionrequest.selection;
|
|
aEvent.xselection.target = ev->xselectionrequest.target;
|
|
aEvent.xselection.property = ev->xselectionrequest.property;
|
|
aEvent.xselection.time = CurrentTime;
|
|
XSendEvent(sDisplay, ev->xselectionrequest.requestor, 1, 0, &aEvent);
|
|
}
|
|
}
|
|
return nsEventStatus_eIgnore;
|
|
}
|
|
|
|
nsITransferable *nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
|
|
{
|
|
nsITransferable *transferable = nsnull;
|
|
switch (aWhichClipboard)
|
|
{
|
|
case kGlobalClipboard:
|
|
transferable = mGlobalTransferable;
|
|
break;
|
|
case kSelectionClipboard:
|
|
transferable = mSelectionTransferable;
|
|
break;
|
|
}
|
|
NS_IF_ADDREF(transferable);
|
|
return transferable;
|
|
}
|
|
|
|
// Ripped from GTK. Does the writing to the appropriate transferable.
|
|
// Does *some* flavour stuff, but not all!!!
|
|
// FIXME: Needs to be completed.
|
|
NS_IMETHODIMP nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard)
|
|
{
|
|
// bomb out if we cannot get ownership.
|
|
if (XSetSelectionOwner(sDisplay, XA_PRIMARY, sWindow, CurrentTime))
|
|
if (XGetSelectionOwner(sDisplay, XA_PRIMARY) != sWindow) {
|
|
fprintf(stderr, "nsClipboard::SetData: Cannot get ownership\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// get flavor list that includes all flavors that can be written (including ones
|
|
// obtained through conversion)
|
|
nsCOMPtr<nsISupportsArray> flavorList;
|
|
nsCOMPtr<nsITransferable> transferable(getter_AddRefs(GetTransferable(aWhichClipboard)));
|
|
|
|
// FIXME Need to make sure mTransferable has reference to selectionclipboard.
|
|
// This solves the problem with copying to an external app.
|
|
// but cannot be sure if its fully correct until menu copy/paste is working.
|
|
if (aWhichClipboard == kSelectionClipboard) {
|
|
NS_IF_RELEASE(mTransferable);
|
|
mTransferable = transferable;
|
|
NS_IF_ADDREF(mTransferable);
|
|
}
|
|
|
|
// make sure we have a good transferable
|
|
if (nsnull == transferable) {
|
|
#ifdef DEBUG_faulkner
|
|
fprintf(stderr, "nsClipboard::SetNativeClipboardData(): no transferable!\n");
|
|
#endif /* DEBUG_faulkner */
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsresult errCode = transferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) );
|
|
if ( NS_FAILED(errCode) )
|
|
return NS_ERROR_FAILURE;
|
|
|
|
PRUint32 cnt;
|
|
flavorList->Count(&cnt);
|
|
for ( PRUint32 i=0; i<cnt; ++i )
|
|
{
|
|
nsCOMPtr<nsISupports> genericFlavor;
|
|
flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
|
|
nsCOMPtr<nsISupportsString> currentFlavor ( do_QueryInterface(genericFlavor) );
|
|
if ( currentFlavor ) {
|
|
nsXPIDLCString flavorStr;
|
|
currentFlavor->ToString(getter_Copies(flavorStr));
|
|
|
|
// FIXME Do we need to register this in XLIB? KenF
|
|
// add these types as selection targets
|
|
// RegisterFormat(flavorStr, selectionAtom);
|
|
}
|
|
}
|
|
|
|
mIgnoreEmptyNotification = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::SetData(nsITransferable *aTransferable,
|
|
nsIClipboardOwner *anOwner,
|
|
PRInt32 aWhichClipboard)
|
|
{
|
|
if (XSetSelectionOwner(sDisplay, XA_PRIMARY, sWindow, CurrentTime))
|
|
if (XGetSelectionOwner(sDisplay, XA_PRIMARY) != sWindow) {
|
|
fprintf(stderr, "nsClipboard::SetData: Cannot get ownership\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
// Check from GTK. (must double check this!!)
|
|
if ((aTransferable == mGlobalTransferable.get() &&
|
|
anOwner == mGlobalOwner.get() &&
|
|
aWhichClipboard == kGlobalClipboard ) ||
|
|
(aTransferable == mSelectionTransferable.get() &&
|
|
anOwner == mSelectionOwner.get() &&
|
|
aWhichClipboard == kSelectionClipboard)
|
|
)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
EmptyClipboard(aWhichClipboard);
|
|
|
|
switch(aWhichClipboard) {
|
|
case kSelectionClipboard:
|
|
mSelectionOwner = anOwner;
|
|
mSelectionTransferable = aTransferable;
|
|
break;
|
|
case kGlobalClipboard:
|
|
mGlobalOwner = anOwner;
|
|
mGlobalTransferable = aTransferable;
|
|
break;
|
|
}
|
|
|
|
return SetNativeClipboardData(aWhichClipboard);
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::GetData(nsITransferable *aTransferable,
|
|
PRInt32 aWhichClipboard)
|
|
{
|
|
unsigned char *data = 0;
|
|
unsigned long bytes = 0;
|
|
Bool only_if_exists;
|
|
Atom data_atom;
|
|
int i;
|
|
|
|
if (aTransferable == nsnull) {
|
|
fprintf(stderr, "nsClipboard::GetData: NULL transferable\n");
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
|
|
// Get which transferable we should use.
|
|
NS_IF_RELEASE(mTransferable);
|
|
mTransferable = GetTransferable(aWhichClipboard);
|
|
NS_IF_ADDREF(mTransferable);
|
|
|
|
// If we currently own the selection, we will handle the paste
|
|
// internally, otherwise get the data from the X server
|
|
|
|
if (XGetSelectionOwner(sDisplay, XA_PRIMARY) == sWindow) {
|
|
char *dataFlavor = kUnicodeMime;
|
|
nsCOMPtr<nsISupports> genDataWrapper;
|
|
nsresult rv;
|
|
PRUint32 dataLength;
|
|
nsCOMPtr<nsITransferable> trans = do_QueryInterface(aTransferable);
|
|
if (!trans)
|
|
return NS_ERROR_FAILURE;
|
|
|
|
rv = mTransferable->GetTransferData(dataFlavor,
|
|
getter_AddRefs(genDataWrapper),
|
|
&dataLength);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
rv = trans->SetTransferData(dataFlavor,
|
|
genDataWrapper,
|
|
dataLength);
|
|
}
|
|
} else {
|
|
data_atom = XInternAtom(sDisplay, "DATA_ATOM", only_if_exists = False);
|
|
data = (unsigned char *)malloc(16384);
|
|
XConvertSelection(sDisplay, XA_PRIMARY, XA_STRING, data_atom,
|
|
sWindow, CurrentTime);
|
|
|
|
// Wait for the SelectNotify event
|
|
mBlocking = PR_TRUE;
|
|
XEvent event;
|
|
for (i=0; (mBlocking == PR_TRUE) && i<10000; i++) {
|
|
if (XPending(sDisplay)) {
|
|
XNextEvent(sDisplay, &event);
|
|
if (event.type == SelectionNotify) {
|
|
mBlocking = PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
// If we got the event, mBlocking will still be true.
|
|
// So, get the data.
|
|
if (mBlocking == PR_FALSE) {
|
|
Atom type;
|
|
int format;
|
|
unsigned long items;
|
|
if (event.xselection.property != None) {
|
|
XGetWindowProperty(sDisplay, event.xselection.requestor,
|
|
event.xselection.property, 0, 16384/4,
|
|
0, AnyPropertyType,
|
|
&type, &format, &items, &bytes, &data);
|
|
bytes = strlen((char *)data);
|
|
}
|
|
}
|
|
mBlocking = PR_FALSE;
|
|
|
|
// Place the data in the transferable
|
|
PRInt32 length = bytes;
|
|
PRUnichar *testing = (PRUnichar *)malloc(strlen((char *)data)*2+1);
|
|
nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode((const char *)data,
|
|
length,
|
|
&testing,
|
|
&length);
|
|
|
|
// FIXME Just leave as 2 for now.... but this should change... KenF
|
|
length = length * 2;
|
|
nsCOMPtr<nsISupports> genDataWrapper;
|
|
nsPrimitiveHelpers::CreatePrimitiveForData("text/unicode",
|
|
testing, length,
|
|
getter_AddRefs(genDataWrapper));
|
|
|
|
aTransferable->SetTransferData("text/unicode",
|
|
genDataWrapper,
|
|
length);
|
|
free(data);
|
|
free(testing);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
|
|
{
|
|
if (mIgnoreEmptyNotification) {
|
|
return NS_OK;
|
|
}
|
|
|
|
switch(aWhichClipboard) {
|
|
case kSelectionClipboard:
|
|
if (mSelectionOwner) {
|
|
mSelectionOwner->LosingOwnership(mSelectionTransferable);
|
|
mSelectionOwner = nsnull;
|
|
}
|
|
mSelectionTransferable = nsnull;
|
|
break;
|
|
case kGlobalClipboard:
|
|
if (mGlobalOwner) {
|
|
mGlobalOwner->LosingOwnership(mGlobalTransferable);
|
|
mGlobalOwner = nsnull;
|
|
}
|
|
mGlobalTransferable = nsnull;
|
|
break;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::ForceDataToClipboard(PRInt32 aWhichClipboard) {
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(nsISupportsArray *aFlavorList,
|
|
PRInt32 aWhichClipboard,
|
|
PRBool *_retval) {
|
|
*_retval = PR_FALSE;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsClipboard::SupportsSelectionClipboard(PRBool *_retval) {
|
|
*_retval = PR_TRUE;
|
|
return NS_OK;
|
|
}
|