Files
Mozilla/mozilla/widget/src/xlib/nsClipboard.cpp
pp%ludusdesign.com 637edb15e8 Conversion of ::GetIID() to NS_GET_IID(). Bug #20232, r=scc@netscape.com
git-svn-id: svn://10.0.0.236/trunk@59703 18797224-902f-48f8-a5cc-f745e15eee43
2000-02-04 00:39:15 +00:00

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);
}