Back to index

lightning-sunbird  0.9+nobinonly
nsClipboard.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Peter Hartshorn <peter@igelaus.com.au>
00024  *   Ken Faulkner <faulkner@igelaus.com.au>
00025  *   Dan Rosen <dr@netscape.com>
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either the GNU General Public License Version 2 or later (the "GPL"), or
00029  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* TODO:
00042  * Currently this only supports the transfer of TEXT! FIXME
00043  */
00044 
00045 #include "nsAppShell.h"
00046 #include "nsClipboard.h"
00047 
00048 #include "nsCOMPtr.h"
00049 #include "nsCRT.h"
00050 #include "nsISupportsArray.h"
00051 #include "nsXPCOM.h"
00052 #include "nsISupportsPrimitives.h"
00053 
00054 #include "nsIComponentManager.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsWidgetsCID.h"
00057 #include "nsXPIDLString.h"
00058 #include "nsPrimitiveHelpers.h"
00059 
00060 #include "nsTextFormatter.h"
00061 
00062 #include <X11/Xlib.h>
00063 #include <X11/Xatom.h>
00064 
00065 #include "xlibrgb.h"
00066 
00067 
00068 #include "nsIServiceManager.h"
00069 #include "nsICharsetConverterManager.h"
00070 // unicode conversion
00071 #  include "nsIPlatformCharset.h"
00072 
00073 
00074 // The class statics:
00075 nsITransferable            *nsClipboard::mTransferable = nsnull;
00076 Window                      nsClipboard::sWindow;
00077 Display                    *nsClipboard::sDisplay;
00078 
00079 #if defined(DEBUG_mcafee) || defined(DEBUG_pavlov)
00080 #define DEBUG_CLIPBOARD
00081 #endif
00082 
00083 NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
00084 
00085 nsClipboard::nsClipboard() {
00086   sDisplay = xxlib_rgb_get_display(nsAppShell::GetXlibRgbHandle());
00087 
00088   Init();
00089 }
00090 
00091 nsClipboard::~nsClipboard() {
00092   NS_IF_RELEASE(sWidget);
00093 }
00094 
00095 /*static*/ void nsClipboard::Shutdown() {
00096   NS_IF_RELEASE(mTransferable);
00097 }
00098 
00099 // Initialize the clipboard and create a nsWidget for communications
00100 
00101 void nsClipboard::Init() {
00102   NS_ASSERTION(!sWidget, "already initialized");
00103 
00104   sWidget = new nsWidget();
00105   if (!sWidget) return;
00106   NS_ADDREF(sWidget);
00107 
00108   const nsRect rect(0,0,100,100);
00109 
00110   sWidget->Create((nsIWidget *)nsnull, rect, Callback,
00111                   (nsIDeviceContext *)nsnull, (nsIAppShell *)nsnull,
00112                   (nsIToolkit *)nsnull, (nsWidgetInitData *)nsnull);
00113   sWindow = (Window)sWidget->GetNativeData(NS_NATIVE_WINDOW);
00114 
00115   XSelectInput(sDisplay, sWindow, 0x0fffff);
00116 }
00117 
00118 // This is the callback function for our nsWidget. It is given the
00119 // XEvent from nsAppShell.
00120 // FIXME: We _should_ assign mTransferable here depending on if its a 
00121 // selectionrequest
00122 nsEventStatus PR_CALLBACK nsClipboard::Callback(nsGUIEvent *event) {
00123   XEvent *ev = (XEvent *)event->nativeMsg;
00124   
00125   /* may be nsnull in the |event->message == NS_DESTROY| case... */
00126   if(ev == nsnull)
00127     return nsEventStatus_eIgnore;
00128 
00129   // Check the event type
00130   if (ev->type == SelectionRequest) {
00131     if (mTransferable == nsnull) {
00132       fprintf(stderr, "nsClipboard::Callback: null transferable\n");
00133       return nsEventStatus_eIgnore;
00134     }
00135 
00136     // Get the data from the Transferable
00137 
00138     const char *dataFlavor = kUnicodeMime;
00139     nsCOMPtr<nsISupports> genDataWrapper;
00140     nsresult rv;
00141     PRUint32 dataLength;
00142     void *data;
00143     data = malloc(16384);
00144     rv = mTransferable->GetTransferData(dataFlavor,
00145                                         getter_AddRefs(genDataWrapper),
00146                                         &dataLength);
00147     nsPrimitiveHelpers::CreateDataFromPrimitive(dataFlavor, genDataWrapper,
00148                                                 &data, dataLength);
00149     if (NS_SUCCEEDED(rv) && data && dataLength) {
00150       char *plainText = nsnull;
00151       PRUnichar* unicodeData = NS_REINTERPRET_CAST(PRUnichar*, data);
00152       PRInt32 plainLen = 0;
00153       nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText(unicodeData,
00154                                                             dataLength/2,
00155                                                             &plainText,
00156                                                             &plainLen);
00157       if (data) {
00158         free(data);
00159         data = plainText;
00160         dataLength = plainLen;
00161       }
00162 
00163       // Set the window property to contain the data
00164       XChangeProperty(sDisplay,
00165                       ev->xselectionrequest.requestor,
00166                       ev->xselectionrequest.property, 
00167                       ev->xselectionrequest.target,
00168                       8, PropModeReplace,
00169                       (unsigned char *)data, dataLength);
00170 
00171       // Send the requestor a SelectionNotify event
00172       XEvent aEvent;
00173       aEvent.type = SelectionNotify;
00174       aEvent.xselection.serial = ev->xselectionrequest.serial;
00175       aEvent.xselection.display = ev->xselectionrequest.display;
00176       aEvent.xselection.requestor = ev->xselectionrequest.requestor;
00177       aEvent.xselection.selection = ev->xselectionrequest.selection;
00178       aEvent.xselection.target = ev->xselectionrequest.target;
00179       aEvent.xselection.property = ev->xselectionrequest.property;
00180       aEvent.xselection.time = CurrentTime;
00181       XSendEvent(sDisplay, ev->xselectionrequest.requestor, 1, 0, &aEvent);
00182    }
00183   }
00184   return nsEventStatus_eIgnore;
00185 }
00186 
00187 nsITransferable *nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
00188 {
00189   nsITransferable *transferable = nsnull;
00190   switch (aWhichClipboard)
00191   {
00192   case kGlobalClipboard:
00193     transferable = mGlobalTransferable;
00194     break;
00195   case kSelectionClipboard:
00196     transferable = mSelectionTransferable;
00197     break;
00198   }
00199   return transferable;
00200 }
00201 
00202 // Ripped from GTK. Does the writing to the appropriate transferable.
00203 // Does *some* flavour stuff, but not all!!!
00204 // FIXME: Needs to be completed.
00205 NS_IMETHODIMP nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard)
00206 {
00207   // bomb out if we cannot get ownership.
00208   if (XSetSelectionOwner(sDisplay, XA_PRIMARY, sWindow, CurrentTime))
00209     if (XGetSelectionOwner(sDisplay, XA_PRIMARY) != sWindow) {
00210       fprintf(stderr, "nsClipboard::SetData: Cannot get ownership\n");
00211       return NS_ERROR_FAILURE;
00212     }
00213   
00214   // get flavor list that includes all flavors that can be written (including ones 
00215   // obtained through conversion)
00216   nsCOMPtr<nsISupportsArray> flavorList;
00217   nsCOMPtr<nsITransferable> transferable(GetTransferable(aWhichClipboard));
00218 
00219   // FIXME Need to make sure mTransferable has reference to selectionclipboard.
00220   // This solves the problem with copying to an external app.
00221   // but cannot be sure if its fully correct until menu copy/paste is working.
00222   if (aWhichClipboard == kSelectionClipboard) {
00223     NS_IF_RELEASE(mTransferable);
00224     mTransferable = transferable; 
00225     NS_IF_ADDREF(mTransferable);
00226   }
00227   
00228   // make sure we have a good transferable
00229   if (nsnull == transferable) {
00230 #ifdef DEBUG_faulkner
00231     fprintf(stderr, "nsClipboard::SetNativeClipboardData(): no transferable!\n");
00232 #endif /* DEBUG_faulkner */
00233     return NS_ERROR_FAILURE;
00234   }
00235 
00236   nsresult errCode = transferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) );
00237   if ( NS_FAILED(errCode) )
00238     return NS_ERROR_FAILURE;
00239 
00240   PRUint32 cnt;
00241   flavorList->Count(&cnt);
00242   for ( PRUint32 i=0; i<cnt; ++i )
00243   {
00244     nsCOMPtr<nsISupports> genericFlavor;
00245     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00246     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00247     if ( currentFlavor ) {
00248       nsXPIDLCString flavorStr;
00249       currentFlavor->ToString(getter_Copies(flavorStr));
00250 
00251       // FIXME Do we need to register this in XLIB? KenF
00252       // add these types as selection targets
00253       //      RegisterFormat(flavorStr, selectionAtom);
00254     }
00255   }
00256 
00257   mIgnoreEmptyNotification = PR_FALSE;
00258   return NS_OK;
00259 }
00260 
00261 NS_IMETHODIMP nsClipboard::SetData(nsITransferable *aTransferable,
00262                                    nsIClipboardOwner *anOwner,
00263                                    PRInt32 aWhichClipboard) 
00264 {
00265   if (XSetSelectionOwner(sDisplay, XA_PRIMARY, sWindow, CurrentTime))
00266     if (XGetSelectionOwner(sDisplay, XA_PRIMARY) != sWindow) {
00267       fprintf(stderr, "nsClipboard::SetData: Cannot get ownership\n");
00268       return NS_ERROR_FAILURE;
00269     }
00270 
00271   // Check from GTK. (must double check this!!)
00272   if ((aTransferable == mGlobalTransferable.get() &&
00273        anOwner == mGlobalOwner.get() &&
00274        aWhichClipboard == kGlobalClipboard ) ||
00275       (aTransferable == mSelectionTransferable.get() &&
00276        anOwner == mSelectionOwner.get() &&
00277        aWhichClipboard == kSelectionClipboard)
00278       )
00279     {
00280       return NS_OK;
00281     }
00282   
00283   EmptyClipboard(aWhichClipboard);
00284 
00285   switch(aWhichClipboard) {
00286   case kSelectionClipboard:
00287     mSelectionOwner = anOwner;
00288     mSelectionTransferable = aTransferable;
00289     break;
00290   case kGlobalClipboard:
00291     mGlobalOwner = anOwner;
00292     mGlobalTransferable = aTransferable;
00293     break;
00294   }
00295   
00296   return SetNativeClipboardData(aWhichClipboard);
00297 }
00298 
00299 NS_IMETHODIMP nsClipboard::GetData(nsITransferable *aTransferable,
00300                                    PRInt32 aWhichClipboard)
00301 {
00302   unsigned char *data = 0;
00303   unsigned long bytes = 0;
00304   Bool only_if_exists;
00305   Atom data_atom;
00306   int i;
00307 
00308   if (aTransferable == nsnull) {
00309     fprintf(stderr, "nsClipboard::GetData: NULL transferable\n");
00310     return NS_ERROR_FAILURE;
00311   }
00312 
00313 
00314   // Get which transferable we should use.
00315   NS_IF_RELEASE(mTransferable);
00316   mTransferable = GetTransferable(aWhichClipboard);
00317   NS_ASSERTION(!mTransferable,"mTransferable is null!! see bug 80181");
00318   if (!mTransferable) return NS_ERROR_FAILURE;
00319   NS_ADDREF(mTransferable);
00320   
00321   // If we currently own the selection, we will handle the paste 
00322   // internally, otherwise get the data from the X server
00323 
00324   if (XGetSelectionOwner(sDisplay, XA_PRIMARY) == sWindow) {
00325     const char *dataFlavor = kUnicodeMime;
00326     nsCOMPtr<nsISupports> genDataWrapper;
00327     nsresult rv;
00328     PRUint32 dataLength;
00329     nsCOMPtr<nsITransferable> trans = do_QueryInterface(aTransferable);
00330     if (!trans)
00331       return NS_ERROR_FAILURE;
00332 
00333     rv = mTransferable->GetTransferData(dataFlavor,
00334                                         getter_AddRefs(genDataWrapper),
00335                                         &dataLength);
00336     if (NS_SUCCEEDED(rv)) {
00337       rv = trans->SetTransferData(dataFlavor,
00338                                   genDataWrapper,
00339                                   dataLength);
00340     }
00341   } else {
00342     data_atom = XInternAtom(sDisplay, "DATA_ATOM", only_if_exists = False);
00343     XConvertSelection(sDisplay, XA_PRIMARY, XA_STRING, data_atom,
00344                       sWindow, CurrentTime);
00345 
00346     // Wait for the SelectNotify event
00347     mBlocking = PR_TRUE;
00348     XEvent event;
00349     for (i=0; (mBlocking == PR_TRUE) && i<10000; i++) {
00350       if (XPending(sDisplay)) {
00351         XNextEvent(sDisplay, &event);
00352         if (event.type == SelectionNotify) {
00353           mBlocking = PR_FALSE;
00354         }
00355       }
00356     }
00357     // If we got the event, mBlocking will still be true.
00358     // So, get the data.
00359     if (mBlocking == PR_FALSE) {
00360       Atom type;
00361       int format;
00362       unsigned long items;
00363       if (event.xselection.property != None) {
00364         XGetWindowProperty(sDisplay, event.xselection.requestor, 
00365                            event.xselection.property, 0, 16384/4,
00366                            0, AnyPropertyType,
00367                            &type, &format, &items, &bytes, &data);
00368         // XXX could return BadValue or BadWindow or ...
00369         bytes = strlen((char *)data);
00370       }
00371     }
00372     mBlocking = PR_FALSE;
00373 
00374     // Place the data in the transferable
00375     PRInt32 length = 0;
00376     PRUnichar *testing = nsnull;
00377     const char *constData = "";
00378     if (data)
00379        constData = (char*) data;
00380     nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(constData,
00381                                                           (PRInt32)bytes,
00382                                                           &testing,
00383                                                           &length);
00384 
00385     nsCOMPtr<nsISupports> genDataWrapper;
00386     nsPrimitiveHelpers::CreatePrimitiveForData("text/unicode",
00387                                                testing, length,
00388                                                getter_AddRefs(genDataWrapper));
00389 
00390     aTransferable->SetTransferData("text/unicode",
00391                                    genDataWrapper,
00392                                    length);
00393     if (data)
00394       XFree(data);
00395     free(testing);
00396   }
00397   return NS_OK;
00398 }
00399 
00400 NS_IMETHODIMP nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard) 
00401 {
00402   if (mIgnoreEmptyNotification) {
00403     return NS_OK;
00404   }
00405 
00406   switch(aWhichClipboard) {
00407   case kSelectionClipboard:
00408     if (mSelectionOwner) {
00409       mSelectionOwner->LosingOwnership(mSelectionTransferable);
00410       mSelectionOwner = nsnull;
00411     }
00412     mSelectionTransferable = nsnull;
00413     break;
00414   case kGlobalClipboard:
00415     if (mGlobalOwner) {
00416       mGlobalOwner->LosingOwnership(mGlobalTransferable);
00417       mGlobalOwner = nsnull;
00418     }
00419     mGlobalTransferable = nsnull;
00420     break;
00421   }
00422   return NS_OK;
00423 }
00424 
00425 NS_IMETHODIMP nsClipboard::HasDataMatchingFlavors(nsISupportsArray *aFlavorList,
00426                                                   PRInt32 aWhichClipboard,
00427                                                   PRBool *_retval) {
00428   *_retval = PR_FALSE;
00429   return NS_OK;
00430 }
00431 
00432 NS_IMETHODIMP nsClipboard::SupportsSelectionClipboard(PRBool *_retval) {
00433   NS_ENSURE_ARG_POINTER(_retval);
00434 
00435   *_retval = PR_TRUE; // we support the selection clipboard on unix.
00436   return NS_OK;
00437 }