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  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1999-2000
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *   Mike Pinkerton <pinkerton@netscape.com>
00026  *   Dan Rosen <dr@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either the GNU General Public License Version 2 or later (the "GPL"), or
00030  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsClipboard.h"
00043 
00044 #include <gdk/gdkx.h>
00045 #include <X11/Xlib.h>
00046 
00047 #include "nsCOMPtr.h"
00048 #include "nsCRT.h"
00049 #include "nsISupportsArray.h"
00050 #include "nsXPCOM.h"
00051 #include "nsISupportsPrimitives.h"
00052 #include "nsReadableUtils.h"
00053 
00054 #include "nsIComponentManager.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsWidgetsCID.h"
00057 #include "nsXPIDLString.h"
00058 #include "nsReadableUtils.h"
00059 #include "nsUnicharUtils.h"
00060 #include "nsPrimitiveHelpers.h"
00061 
00062 #include "nsTextFormatter.h"
00063 
00064 #include "nsIServiceManager.h"
00065 
00066 #include "prtime.h"
00067 #include "prthread.h"
00068 
00069 // For manipulation of the X event queue
00070 #include <sys/time.h>
00071 #include <sys/types.h>
00072 #include <unistd.h>
00073 #ifdef POLL_WITH_XCONNECTIONNUMBER
00074 #include <poll.h>
00075 #endif
00076 
00077 // unicode conversion
00078 #include "nsIPlatformCharset.h"
00079 #include "nsICharsetConverterManager.h"
00080 
00081 static void ConvertHTMLtoUCS2(char* data, PRInt32 dataLength,
00082                               PRUnichar** unicodeData, PRInt32& outUnicodeLen);
00083 static void GetHTMLCharset   (char* data, PRInt32 dataLength, nsACString& str);
00084 
00085 // The class statics:
00086 GtkWidget* nsClipboard::sWidget = 0;
00087 
00088 // Define this to enable the obsolete X cut buffer mechanism
00089 // In general, a bad idea (see http://www.jwz.org/doc/x-cut-and-paste.html)
00090 // but it might have its uses for backwards compatibility.
00091 #undef USE_CUTBUFFERS
00092 
00093 
00094 static GdkAtom GDK_SELECTION_CLIPBOARD;
00095 
00096 NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
00097 
00098 //-------------------------------------------------------------------------
00099 //
00100 // nsClipboard constructor
00101 //
00102 //-------------------------------------------------------------------------
00103 nsClipboard::nsClipboard()
00104 {
00105 #ifdef DEBUG_CLIPBOARD
00106   g_print("nsClipboard::nsClipboard()\n");
00107 #endif /* DEBUG_CLIPBOARD */
00108 
00109   mIgnoreEmptyNotification = PR_FALSE;
00110   mGlobalTransferable = nsnull;
00111   mSelectionTransferable = nsnull;
00112   mGlobalOwner = nsnull;
00113   mSelectionOwner = nsnull;
00114   mSelectionData.data = nsnull;
00115   mSelectionData.length = 0;
00116 
00117   // initialize the widget, etc we're binding to
00118   Init();
00119 }
00120 
00121 // XXX if GTK's internal code changes this isn't going to work
00122 // copied from gtk code because it is a static function we can't get to it.
00123 // need to bug owen taylor about getting this code public.
00124 
00125 typedef struct _GtkSelectionTargetList GtkSelectionTargetList;
00126 
00127 struct _GtkSelectionTargetList {
00128   GdkAtom selection;
00129   GtkTargetList *list;
00130 };
00131 
00132 static const char *gtk_selection_handler_key = "gtk-selection-handlers";
00133 
00134 void __gtk_selection_target_list_remove (GtkWidget *widget, GdkAtom selection)
00135 {
00136   GtkSelectionTargetList *sellist;
00137   GList *tmp_list, *tmp_list2;
00138   GList *lists;
00139   lists = (GList*)gtk_object_get_data (GTK_OBJECT (widget), gtk_selection_handler_key);
00140   tmp_list = lists;
00141   while (tmp_list) {
00142     sellist = (GtkSelectionTargetList*)tmp_list->data;
00143     if (sellist->selection == selection) {
00144       gtk_target_list_unref (sellist->list);
00145       g_free (sellist);
00146       tmp_list->data = nsnull;
00147       tmp_list2 = tmp_list->prev;
00148       lists = g_list_remove_link(lists, tmp_list);
00149       g_list_free_1(tmp_list);
00150       tmp_list = tmp_list2;
00151     }
00152     if (tmp_list)
00153       tmp_list = tmp_list->next;
00154   }
00155   gtk_object_set_data(GTK_OBJECT(widget), gtk_selection_handler_key, lists);
00156 }
00157 
00158 //-------------------------------------------------------------------------
00159 //
00160 // nsClipboard destructor
00161 //
00162 //-------------------------------------------------------------------------
00163 nsClipboard::~nsClipboard()
00164 {
00165 #ifdef DEBUG_CLIPBOARD
00166   g_print("nsClipboard::~nsClipboard()\n");  
00167 #endif /* DEBUG_CLIPBOARD */
00168 
00169   // Remove all our event handlers:
00170   if (sWidget) {
00171     if (gdk_selection_owner_get(GDK_SELECTION_PRIMARY) == sWidget->window)
00172       gtk_selection_remove_all(sWidget);
00173 
00174     if (gdk_selection_owner_get(GDK_SELECTION_CLIPBOARD) == sWidget->window)
00175       gtk_selection_remove_all(sWidget);
00176   }
00177 
00178   // free the selection data, if any
00179   if (mSelectionData.data != nsnull)
00180     nsMemory::Free(mSelectionData.data);
00181 
00182   gtk_object_remove_data(GTK_OBJECT(sWidget), "cb");
00183 
00184   if (sWidget) {
00185     gtk_widget_unref(sWidget);
00186     sWidget = nsnull;
00187   }
00188 }
00189 
00190 
00191 //-------------------------------------------------------------------------
00192 void nsClipboard::Init(void)
00193 {
00194 #ifdef DEBUG_CLIPBOARD
00195   g_print("nsClipboard::Init\n");
00196 #endif
00197 
00198   GDK_SELECTION_CLIPBOARD = gdk_atom_intern("CLIPBOARD", FALSE);
00199 
00200   // create invisible widget to use for the clipboard
00201   sWidget = gtk_invisible_new();
00202 
00203   // add the clipboard pointer to the widget so we can get it.
00204   gtk_object_set_data(GTK_OBJECT(sWidget), "cb", this);
00205 
00206   // Handle selection requests if we called gtk_selection_add_target:
00207   gtk_signal_connect(GTK_OBJECT(sWidget), "selection_get",
00208                      GTK_SIGNAL_FUNC(nsClipboard::SelectionGetCB),
00209                      nsnull);
00210 
00211   // When someone else takes the selection away:
00212   gtk_signal_connect(GTK_OBJECT(sWidget), "selection_clear_event",
00213                      GTK_SIGNAL_FUNC(nsClipboard::SelectionClearCB),
00214                      nsnull);
00215 
00216   // Set up the paste handler:
00217   gtk_signal_connect(GTK_OBJECT(sWidget), "selection_received",
00218                      GTK_SIGNAL_FUNC(nsClipboard::SelectionReceivedCB),
00219                      nsnull);
00220 }
00221 
00222 
00223 
00224 
00229 NS_IMETHODIMP nsClipboard::SetData(nsITransferable * aTransferable,
00230                                    nsIClipboardOwner * anOwner,
00231                                    PRInt32 aWhichClipboard)
00232 {
00233 
00234   if ((aTransferable == mGlobalTransferable.get() &&
00235        anOwner == mGlobalOwner.get() &&
00236        aWhichClipboard == kGlobalClipboard ) ||
00237       (aTransferable == mSelectionTransferable.get() &&
00238        anOwner == mSelectionOwner.get() &&
00239        aWhichClipboard == kSelectionClipboard)
00240       )
00241   {
00242     return NS_OK;
00243   }
00244 
00245   EmptyClipboard(aWhichClipboard);
00246 
00247   switch(aWhichClipboard) {
00248   case kSelectionClipboard:
00249     mSelectionOwner = anOwner;
00250     mSelectionTransferable = aTransferable;
00251     break;
00252   case kGlobalClipboard:
00253     mGlobalOwner = anOwner;
00254     mGlobalTransferable = aTransferable;
00255     SetCutBuffer();
00256     break;
00257   }
00258 
00259   return SetNativeClipboardData(aWhichClipboard);
00260 }
00261 
00266 NS_IMETHODIMP nsClipboard::GetData(nsITransferable * aTransferable, PRInt32 aWhichClipboard)
00267 {
00268   if (nsnull != aTransferable) {
00269     return GetNativeClipboardData(aTransferable, aWhichClipboard);
00270   } else {
00271 #ifdef DEBUG_CLIPBOARD
00272     printf("  nsClipboard::GetData(), aTransferable is NULL.\n");
00273 #endif
00274   }
00275 
00276   return NS_ERROR_FAILURE;
00277 }
00278 
00279 
00284 NS_IMETHODIMP nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
00285 {
00286   if (mIgnoreEmptyNotification) {
00287     return NS_OK;
00288   }
00289 
00290   switch(aWhichClipboard) {
00291   case kSelectionClipboard:
00292     if (mSelectionOwner) {
00293       mSelectionOwner->LosingOwnership(mSelectionTransferable);
00294       mSelectionOwner = nsnull;
00295     }
00296     mSelectionTransferable = nsnull;
00297     break;
00298   case kGlobalClipboard:
00299     if (mGlobalOwner) {
00300       mGlobalOwner->LosingOwnership(mGlobalTransferable);
00301       mGlobalOwner = nsnull;
00302     }
00303     mGlobalTransferable = nsnull;
00304     break;
00305   }
00306 
00307   return NS_OK;
00308 }
00309 
00310 NS_IMETHODIMP nsClipboard::SupportsSelectionClipboard(PRBool *_retval)
00311 {
00312   NS_ENSURE_ARG_POINTER(_retval);
00313 
00314   *_retval = PR_TRUE; // we support the selection clipboard on unix.
00315   return NS_OK;
00316 }
00317 
00318 //-------------------------------------------------------------------------
00319 NS_IMETHODIMP nsClipboard::SetNativeClipboardData(PRInt32 aWhichClipboard)
00320 {
00321   mIgnoreEmptyNotification = PR_TRUE;
00322 
00323 #ifdef DEBUG_CLIPBOARD
00324   g_print("  nsClipboard::SetNativeClipboardData(%i)\n", aWhichClipboard);
00325 #endif /* DEBUG_CLIPBOARD */
00326 
00327 
00328   GdkAtom selectionAtom = GetSelectionAtom(aWhichClipboard);
00329   nsCOMPtr<nsITransferable> transferable(GetTransferable(aWhichClipboard));
00330 
00331   // make sure we have a good transferable
00332   if (nsnull == transferable) {
00333 #ifdef DEBUG_CLIPBOARD
00334     printf("nsClipboard::SetNativeClipboardData(): no transferable!\n");
00335 #endif
00336     return NS_ERROR_FAILURE;
00337   }
00338 
00339   // are we already the owner?
00340   if (gdk_selection_owner_get(selectionAtom) == sWidget->window)
00341   {
00342     // if so, clear all the targets
00343     __gtk_selection_target_list_remove(sWidget, selectionAtom);
00344     //    gtk_selection_remove_all(sWidget);
00345   }
00346 
00347   // we arn't already the owner, so we will become it
00348   gint have_selection = gtk_selection_owner_set(sWidget,
00349                                                 selectionAtom,
00350                                                 GDK_CURRENT_TIME);
00351   if (have_selection == 0)
00352     return NS_ERROR_FAILURE;
00353 
00354   // get flavor list that includes all flavors that can be written (including ones 
00355   // obtained through conversion)
00356   nsCOMPtr<nsISupportsArray> flavorList;
00357   nsresult errCode = transferable->FlavorsTransferableCanExport ( getter_AddRefs(flavorList) );
00358   if ( NS_FAILED(errCode) )
00359     return NS_ERROR_FAILURE;
00360 
00361   PRUint32 cnt;
00362   flavorList->Count(&cnt);
00363   for ( PRUint32 i=0; i<cnt; ++i )
00364   {
00365     nsCOMPtr<nsISupports> genericFlavor;
00366     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00367     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00368     if ( currentFlavor ) {
00369       nsXPIDLCString flavorStr;
00370       currentFlavor->ToString(getter_Copies(flavorStr));
00371 
00372       // add these types as selection targets
00373       RegisterFormat(flavorStr, selectionAtom);
00374     }
00375   }
00376 
00377   mIgnoreEmptyNotification = PR_FALSE;
00378 
00379   return NS_OK;
00380 }
00381 
00382 
00383 PRBool nsClipboard::DoRealConvert(GdkAtom type, GdkAtom aSelectionAtom)
00384 {
00385 #ifdef DEBUG_CLIPBOARD
00386   g_print("    nsClipboard::DoRealConvert(%li)\n    {\n", type);
00387 #endif
00388 
00389   // Set a flag saying that we're blocking waiting for the callback:
00390   mBlocking = PR_TRUE;
00391 
00392   //
00393   // ask X what kind of data we can get
00394   //
00395 #ifdef DEBUG_CLIPBOARD
00396   g_print("     Doing real conversion of atom type '%s'\n", gdk_atom_name(type));
00397 #endif
00398   gtk_selection_convert(sWidget,
00399                         aSelectionAtom,
00400                         type,
00401                         GDK_CURRENT_TIME);
00402 
00403 
00404   /* because Gtk is smart, it checks in gtk_selection_convert to see if
00405      the widget we are converting owns the selection, and if it does it
00406      doesn't bother going to X, so it will be done and mBlocking will be
00407      set to FALSE by this point, so only poll X if we have to.  (pav)
00408   */
00409   if (mBlocking) {
00410     // Now we need to wait until the callback comes in ...
00411     // i is in case we get a runaway (yuck).
00412 #ifdef DEBUG_CLIPBOARD
00413     g_print("      Waiting for the callback... mBlocking = %d\n", mBlocking);
00414 #endif /* DEBUG_CLIPBOARD */
00415 
00416     if (!FindSelectionNotifyEvent())
00417       return PR_FALSE;
00418 
00419 #ifdef DEBUG_CLIPBOARD
00420     g_print("    }\n");
00421 #endif
00422   }
00423 
00424   if (mSelectionData.length > 0)
00425     return PR_TRUE;
00426 
00427   return PR_FALSE;
00428 }
00429 
00430 
00431 //-------------------------------------------------------------------------
00432 //
00433 // The blocking Paste routine
00434 //
00435 //-------------------------------------------------------------------------
00436 NS_IMETHODIMP
00437 nsClipboard::GetNativeClipboardData(nsITransferable * aTransferable, 
00438                                     PRInt32 aWhichClipboard)
00439 {
00440   GdkAtom selectionAtom = GetSelectionAtom(aWhichClipboard);
00441 
00442 #ifdef DEBUG_CLIPBOARD
00443   g_print("nsClipboard::GetNativeClipboardData(%i)\n", aWhichClipboard);
00444 #endif /* DEBUG_CLIPBOARD */
00445 
00446   // make sure we have a good transferable
00447   if (nsnull == aTransferable) {
00448 #ifdef DEBUG_CLIPBOARD
00449     printf("  GetNativeClipboardData: Transferable is null!\n");
00450 #endif
00451     return NS_ERROR_FAILURE;
00452   }
00453 
00454   // get flavor list that includes all acceptable flavors (including ones obtained through
00455   // conversion)
00456   nsCOMPtr<nsISupportsArray> flavorList;
00457   nsresult errCode = aTransferable->FlavorsTransferableCanImport ( getter_AddRefs(flavorList) );
00458   if ( NS_FAILED(errCode) )
00459     return NS_ERROR_FAILURE;
00460 
00461   // Walk through flavors and see which flavor matches the one being pasted:
00462   PRUint32 cnt;
00463   flavorList->Count(&cnt);
00464   nsCAutoString foundFlavor;
00465   PRBool foundData = PR_FALSE;
00466   for ( PRUint32 i = 0; i < cnt; ++i ) {
00467     nsCOMPtr<nsISupports> genericFlavor;
00468     flavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00469     nsCOMPtr<nsISupportsCString> currentFlavor ( do_QueryInterface(genericFlavor) );
00470     if ( currentFlavor ) {
00471       nsXPIDLCString flavorStr;
00472       currentFlavor->ToString ( getter_Copies(flavorStr) );
00473       if (DoConvert(flavorStr, selectionAtom)) {
00474         foundFlavor = flavorStr;
00475         foundData = PR_TRUE;
00476         break;
00477       }
00478     }
00479   }
00480   
00481 #ifdef DEBUG_CLIPBOARD
00482   g_print("  Got the callback: '%s', %d\n",
00483          mSelectionData.data, mSelectionData.length);
00484 #endif /* DEBUG_CLIPBOARD */
00485 
00486   // We're back from the callback, no longer blocking:
00487   mBlocking = PR_FALSE;
00488 
00489   // 
00490   // Now we have data in mSelectionData.data.
00491   // We just have to copy it to the transferable.
00492   // 
00493 
00494   if ( foundData ) {
00495     nsCOMPtr<nsISupports> genericDataWrapper;
00496     nsPrimitiveHelpers::CreatePrimitiveForData(foundFlavor.get(), mSelectionData.data,
00497                                                mSelectionData.length, getter_AddRefs(genericDataWrapper));
00498     aTransferable->SetTransferData(foundFlavor.get(),
00499                                    genericDataWrapper,
00500                                    mSelectionData.length);
00501   }
00502     
00503   // transferable is now owning the data, so we can free it.
00504   nsMemory::Free(mSelectionData.data);
00505   mSelectionData.data = nsnull;
00506   mSelectionData.length = 0;
00507 
00508   return NS_OK;
00509 }
00510 
00518 void
00519 nsClipboard::SelectionReceivedCB (GtkWidget        *aWidget,
00520                                   GtkSelectionData *aSelectionData,
00521                                   guint             aTime)
00522 {
00523 #ifdef DEBUG_CLIPBOARD
00524   g_print("      nsClipboard::SelectionReceivedCB\n      {\n");
00525 #endif /* DEBUG_CLIPBOARD */
00526   nsClipboard *cb =(nsClipboard *)gtk_object_get_data(GTK_OBJECT(aWidget),
00527                                                       "cb");
00528   if (!cb)
00529   {
00530 #ifdef DEBUG_CLIPBOARD
00531     g_print("no clipboard found.. this is bad.\n");
00532 #endif
00533     return;
00534   }
00535   cb->SelectionReceiver(aWidget, aSelectionData);
00536 #ifdef DEBUG_CLIPBOARD
00537   g_print("      }\n");
00538 #endif
00539 }
00540 
00541 
00548 void
00549 nsClipboard::SelectionReceiver (GtkWidget *aWidget,
00550                                 GtkSelectionData *aSD)
00551 {
00552   mBlocking = PR_FALSE;
00553 
00554   if (aSD->length <= 0)
00555   {
00556 #ifdef DEBUG_CLIPBOARD
00557     g_print("        Error retrieving selection: length was %d\n", aSD->length);
00558 #endif
00559     mSelectionData.length = aSD->length;
00560     return;
00561   }
00562 
00563   char *str = gdk_atom_name(aSD->type);
00564   nsCAutoString type(str);
00565   g_free(str);
00566 
00567 #ifdef DEBUG_CLIPBOARD
00568   g_print("        Type is %s\n", type.mBuffer);
00569 
00570   if (type.Equals("ATOM")) {
00571     g_print("        Asked for TARGETS\n");
00572   }
00573 #endif
00574 
00575   if (type.Equals("COMPOUND_TEXT")) {
00576 #ifdef DEBUG_CLIPBOARD
00577     g_print("        Copying mSelectionData pointer -- \n");
00578 #endif
00579     mSelectionData = *aSD;
00580 
00581     char *data = (char*)aSD->data;
00582     PRInt32 len = (PRInt32)aSD->length;
00583 
00584     int status = 0;
00585     XTextProperty prop;
00586 
00587 #ifdef DEBUG_CLIPBOARD
00588     g_print("        Converted text from COMPOUND_TEXT to platform locale\n");
00589     g_print("        data is %s\n", data);
00590     g_print("        len is %d\n", len);
00591 #endif
00592 
00593     prop.value = (unsigned char *)data;
00594     prop.nitems = len;
00595     prop.encoding = XInternAtom(GDK_DISPLAY(), "COMPOUND_TEXT", FALSE);
00596     prop.format = 8;
00597 
00598     char **tmpData;
00599     int foo;
00600     status = XmbTextPropertyToTextList(GDK_DISPLAY(), &prop, &tmpData, &foo);
00601 
00602 #ifdef DEBUG_CLIPBOARD
00603     if (foo > 1)
00604       printf("Got multiple strings from XmbTextPropertyToTextList.. don't know how to handle this yet\n");
00605 #endif
00606 
00607     PRInt32 numberOfBytes = 0;
00608 
00609     if (status == XNoMemory || status == XLocaleNotSupported ||
00610         status == XConverterNotFound) {
00611 #ifdef DEBUG_CLIPBOARD
00612       g_print("\n         XmbTextListToTextProperty failed.  returned %d\n", status);
00613       g_print("          text is \"%s\"\n", tmpData[0]);
00614 #endif
00615       numberOfBytes = strlen(NS_REINTERPRET_CAST(const char *, data));
00616     } else {
00617       if (foo > 0 && tmpData[0] != 0 && (*tmpData[0]) != 0) {
00618         data = tmpData[0];
00619       }
00620       numberOfBytes = strlen(NS_REINTERPRET_CAST(const char *, data));
00621 #ifdef DEBUG_CLIPBOARD
00622       g_print("\n        XmbTextListToTextProperty succeeded\n");
00623       g_print("          text is \"%s\"\n", data);
00624       g_print("          numberOfBytes is %d\n", numberOfBytes);
00625 #endif
00626     }
00627 
00628     nsresult rv;
00629     PRInt32 outUnicodeLen;
00630     PRUnichar *unicodeData = nsnull;
00631 
00632 #ifdef DEBUG_CLIPBOARD
00633     g_print("        Converting from current locale to unicode\n");
00634 #endif
00635 
00636     nsCOMPtr<nsIUnicodeDecoder> decoder;
00637     // get the charset
00638     nsCAutoString platformCharset;
00639     nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
00640     if (NS_SUCCEEDED(rv))
00641       rv = platformCharsetService->GetCharset(kPlatformCharsetSel_Menu, platformCharset);
00642     if (NS_FAILED(rv))
00643       platformCharset.AssignLiteral("ISO-8859-1");
00644       
00645     // get the decoder
00646     nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00647     rv = ccm->GetUnicodeDecoderRaw(platformCharset.get(),
00648                                    getter_AddRefs(decoder));
00649     NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed");
00650     if (NS_FAILED(rv)) {
00651       if (tmpData) XFreeStringList(tmpData);
00652       return;
00653     }
00654       
00655     // Estimate out length and allocate the buffer based on a worst-case estimate, then do
00656     // the conversion. 
00657     decoder->GetMaxLength(data, numberOfBytes, &outUnicodeLen);   // |outUnicodeLen| is number of chars
00658     if (outUnicodeLen) {
00659       unicodeData = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc((outUnicodeLen + 1) * sizeof(PRUnichar)));
00660       if ( unicodeData ) {
00661         PRInt32 numberTmp = numberOfBytes;
00662         rv = decoder->Convert(data, &numberTmp, unicodeData, &outUnicodeLen);
00663 #ifdef DEBUG_CLIPBOARD
00664         if (numberTmp != numberOfBytes)
00665           printf("didn't consume all the bytes\n");
00666 #endif
00667 
00668         (unicodeData)[outUnicodeLen] = '\0';    // null terminate. Convert() doesn't do it for us
00669       }
00670     } // if valid length
00671 
00672 
00673     mSelectionData.data = NS_REINTERPRET_CAST(guchar*,unicodeData);
00674     mSelectionData.length = outUnicodeLen * 2;
00675     if (tmpData) XFreeStringList(tmpData);
00676   }
00677   else if (type.Equals("UTF8_STRING")) {
00678     mSelectionData = *aSD;
00679 
00680     nsresult rv;
00681     PRInt32 outUnicodeLen;
00682     PRUnichar *unicodeData = nsnull;
00683 
00684     char *data = (char*)aSD->data;
00685     PRInt32 numberOfBytes = (PRInt32)aSD->length;
00686 
00687 #ifdef DEBUG_CLIPBOARD
00688     printf("UTF8_STRING is %s\nlength is %i\n", aSD->data, aSD->length);
00689 #endif
00690 
00691     // get the decoder
00692     nsCOMPtr<nsIUnicodeDecoder> decoder;
00693     nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00694     rv = ccm->GetUnicodeDecoderRaw("UTF-8", getter_AddRefs(decoder));
00695 
00696     g_return_if_fail(NS_SUCCEEDED(rv));
00697 
00698     decoder->GetMaxLength(data, numberOfBytes, &outUnicodeLen);   // |outUnicodeLen| is number of chars
00699     if (outUnicodeLen) {
00700       unicodeData = NS_REINTERPRET_CAST(PRUnichar*, nsMemory::Alloc((outUnicodeLen + 1) * sizeof(PRUnichar)));
00701       if ( unicodeData ) {
00702         PRInt32 numberTmp = numberOfBytes;
00703         rv = decoder->Convert(data, &numberTmp, unicodeData, &outUnicodeLen);
00704 #ifdef DEBUG_CLIPBOARD
00705         if (numberTmp != numberOfBytes)
00706           printf("didn't consume all the bytes\n");
00707 #endif
00708 
00709         (unicodeData)[outUnicodeLen] = '\0';    // null terminate. Convert() doesn't do it for us
00710       }
00711     } // if valid length
00712 
00713 
00714     mSelectionData.data = NS_REINTERPRET_CAST(guchar*,unicodeData);
00715     mSelectionData.length = outUnicodeLen * 2;
00716     mSelectionData.type = gdk_atom_intern(kUnicodeMime, FALSE);
00717 
00718   } else if (type.Equals("STRING")) {
00719 #ifdef DEBUG_CLIPBOARD
00720     g_print("        Copying mSelectionData pointer -- \n");
00721     g_print("         Data = %s\n         Length = %i\n", aSD->data, aSD->length);
00722 #endif
00723     mSelectionData = *aSD;
00724 
00725     // convert our plain text to unicode
00726     const char* castedText = NS_REINTERPRET_CAST(char*, mSelectionData.data);          
00727     PRUnichar* convertedText = nsnull;
00728     PRInt32 convertedTextLen = 0;
00729     nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode (castedText, mSelectionData.length, 
00730                                                            &convertedText, &convertedTextLen);
00731     if (convertedText) {
00732       // out with the old, in with the new 
00733       mSelectionData.data = NS_REINTERPRET_CAST(guchar*, convertedText);
00734       mSelectionData.length = convertedTextLen * 2;
00735     }
00736   } else if (type.Equals(kHTMLMime)) {
00737     mSelectionData = *aSD;
00738     PRUnichar* htmlBody= nsnull;
00739     PRInt32 htmlBodyLen = 0;
00740     ConvertHTMLtoUCS2((char*)aSD->data, aSD->length, &htmlBody, htmlBodyLen);
00741     if (htmlBodyLen) {
00742       mSelectionData.data = NS_REINTERPRET_CAST(guchar*, htmlBody);
00743       mSelectionData.length = htmlBodyLen * 2;
00744     }
00745   } else {
00746     mSelectionData = *aSD;
00747     mSelectionData.data = g_new(guchar, aSD->length + 1);
00748     memcpy(mSelectionData.data,
00749            aSD->data,
00750            aSD->length);
00751     mSelectionData.length = aSD->length;
00752   }
00753 }
00754 
00755 NS_IMETHODIMP
00756 nsClipboard::HasDataMatchingFlavors(nsISupportsArray* aFlavorList, 
00757                                     PRInt32 aWhichClipboard, 
00758                                     PRBool * outResult)
00759 {
00760   // XXX this doesn't work right.  need to fix it.
00761   
00762   // Note to implementor...(from pink the clipboard bitch).
00763   //
00764   // If a client asks for unicode, first check if unicode is present. If not, then 
00765   // check for plain text. If it's there, say "yes" as we will do the conversion
00766   // in GetNativeClipboardData(). From this point on, no client will
00767   // ever ask for text/plain explicitly. If they do, you must ASSERT!
00768 #ifdef DEBUG_CLIPBOARD
00769   g_print("  nsClipboard::HasDataMatchingFlavors()\n  {\n");
00770 #endif
00771 
00772 
00773   GetTargets(GetSelectionAtom(aWhichClipboard));
00774 
00775   guchar *data = mSelectionData.data;
00776   PRInt32 dataLength = mSelectionData.length;
00777   int position = 0;
00778   gchar *str;
00779 
00780 
00781   *outResult = PR_FALSE;
00782   PRUint32 length;
00783   aFlavorList->Count(&length);
00784   for ( PRUint32 i = 0; i < length; ++i ) {
00785     nsCOMPtr<nsISupports> genericFlavor;
00786     aFlavorList->GetElementAt ( i, getter_AddRefs(genericFlavor) );
00787     nsCOMPtr<nsISupportsCString> flavorWrapper ( do_QueryInterface(genericFlavor) );
00788     if ( flavorWrapper ) {
00789       nsCAutoString flavorStr;
00790       nsXPIDLCString myStr;
00791       flavorWrapper->ToString(getter_Copies(myStr));
00792       flavorStr = nsCAutoString(myStr);
00793 
00794       position = 0;
00795       while (position < dataLength) {
00796         str = gdk_atom_name(*(GdkAtom*)(data+position));
00797         position += sizeof(GdkAtom);
00798         nsCAutoString atomName(str);
00799         g_free(str);
00800         
00801         if (flavorStr.Equals(kUnicodeMime)) {
00802           if (atomName.Equals("COMPOUND_TEXT") ||
00803               atomName.Equals("UTF8_STRING") ||
00804               atomName.Equals("STRING")) {
00805 #ifdef DEBUG_CLIPBOARD
00806             g_print("    Selection owner has matching type: %s\n", atomName.mBuffer);
00807 #endif
00808             *outResult = PR_TRUE;
00809             break;
00810           }
00811         }
00812         if (flavorStr.Equals(atomName)) {
00813 #ifdef DEBUG_CLIPBOARD
00814           g_print("    Selection owner has matching type: %s\n", atomName.mBuffer);
00815 #endif
00816           *outResult = PR_TRUE;
00817           break;
00818         }
00819       }
00820     }
00821   }
00822 #ifdef DEBUG_CLIPBOARD
00823   g_print("    returning %i\n  }\n", *outResult);
00824 #endif
00825 
00826   nsMemory::Free(mSelectionData.data);
00827   mSelectionData.data = nsnull;
00828   mSelectionData.length = 0;
00829 
00830   return NS_OK;
00831 
00832 }
00833 
00843 void nsClipboard::SelectionGetCB(GtkWidget        *widget,
00844                                  GtkSelectionData *aSelectionData,
00845                                  guint            aInfo,
00846                                  guint            aTime)
00847 {
00848 #ifdef DEBUG_CLIPBOARD
00849   g_print("nsClipboard::SelectionGetCB\n"); 
00850 #endif
00851   nsClipboard *cb = (nsClipboard *)gtk_object_get_data(GTK_OBJECT(widget),
00852                                                        "cb");
00853 
00854   void     *clipboardData;
00855   PRUint32 dataLength;
00856   nsresult rv;
00857 
00858   PRInt32 whichClipboard = -1;
00859 
00860   if (aSelectionData->selection == GDK_SELECTION_PRIMARY)
00861     whichClipboard = kSelectionClipboard;
00862   else if (aSelectionData->selection == GDK_SELECTION_CLIPBOARD)
00863     whichClipboard = kGlobalClipboard;
00864 
00865 #ifdef DEBUG_CLIPBOARD
00866   g_print("  whichClipboard = %d\n", whichClipboard);
00867 #endif
00868   nsCOMPtr<nsITransferable> transferable(cb->GetTransferable(whichClipboard));
00869 
00870   // Make sure we have a transferable:
00871   if (!transferable) {
00872 #ifdef DEBUG_CLIPBOARD
00873     g_print("Clipboard has no transferable!\n");
00874 #endif
00875     return;
00876   }
00877 #ifdef DEBUG_CLIPBOARD
00878   g_print("  aInfo == %s\n  transferable == %p\n", gdk_atom_name(aInfo), transferable.get());
00879   g_print("  aSD->type == %s\n  aSD->target == %s\n", gdk_atom_name(aSelectionData->type),
00880           gdk_atom_name(aSelectionData->target));
00881 #endif
00882   const char *dataFlavor = nsnull;
00883   char *tstr = gdk_atom_name(aInfo);
00884   nsCAutoString type(tstr);
00885   g_free(tstr);
00886 
00887   if (type.Equals("STRING") ||
00888       type.Equals("UTF8_STRING") ||
00889       type.Equals("COMPOUND_TEXT") ||
00890       type.Equals("TEXT"))
00891   {
00892     dataFlavor = kUnicodeMime;
00893   } else {
00894     dataFlavor = type.get();
00895   }
00896 
00897   // Get data out of transferable.
00898   nsCOMPtr<nsISupports> genericDataWrapper;
00899   rv = transferable->GetTransferData(dataFlavor,
00900                                      getter_AddRefs(genericDataWrapper),
00901                                      &dataLength);
00902   nsPrimitiveHelpers::CreateDataFromPrimitive ( dataFlavor, genericDataWrapper, &clipboardData, dataLength );
00903   if (NS_SUCCEEDED(rv) && clipboardData && dataLength > 0) {
00904     size_t size = 1;
00905     // find the number of bytes in the data for the below thing
00906     //    size_t size = sizeof((void*)((unsigned char)clipboardData[0]));
00907     //    g_print("************  *****************      ******************* %i\n", size);
00908     
00909 
00910 #ifdef DEBUG_CLIPBOARD
00911     printf("got data from transferable\n");
00912     printf("clipboardData is %s\n", clipboardData);
00913     printf("length is %d\n", dataLength);
00914 #endif
00915 
00916     if (type.Equals("STRING")) {
00917       // if someone was asking for text/plain, lookup unicode instead so we can convert it.
00918       char* plainTextData = nsnull;
00919       PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, clipboardData);
00920       PRInt32 plainTextLen = 0;
00921       nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText (castedUnicode, dataLength / 2,
00922                                                              &plainTextData, &plainTextLen);
00923       if (clipboardData) {
00924         nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
00925         clipboardData = plainTextData;
00926         dataLength = plainTextLen;
00927       }
00928     } else if (type.Equals("UTF8_STRING")) {
00929       if (clipboardData) {
00930         PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, clipboardData);
00931         char *utf8String =
00932             ToNewUTF8String(nsDependentString(castedUnicode, dataLength/2));
00933         nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
00934         clipboardData = utf8String;
00935         dataLength = strlen(utf8String);
00936       }
00937     } else if (type.Equals(kHTMLMime)) {
00938       if (clipboardData) {
00939         /*
00940          * "text/html" can be encoded UCS2. It is recommended that
00941          * documents transmitted as UCS2 always begin with a ZERO-WIDTH
00942          * NON-BREAKING SPACE character (hexadecimal FEFF, also called
00943          * Byte Order Mark (BOM)). Adding BOM can help other app to
00944          * detect mozilla use UCS2 encoding when copy-paste.
00945          */
00946 
00947         char *buffer = NS_STATIC_CAST(char *,
00948                          nsMemory::Alloc((dataLength + 2) * sizeof(char)));
00949         if (buffer) {
00950           PRUnichar prefix = 0xFEFF;
00951           memcpy(buffer, &prefix, 2);
00952           memcpy(buffer + 2, clipboardData, dataLength);
00953           nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
00954           clipboardData = NS_REINTERPRET_CAST(char*, buffer);
00955           dataLength = dataLength + 2;
00956         }
00957       }
00958     } else if (type.Equals("COMPOUND_TEXT") || type.Equals("TEXT")) {
00959       if (type.Equals("TEXT")) {
00960         // if we get a request for  TEXT, return COMPOUND_TEXT
00961         aInfo = gdk_atom_intern("COMPOUND_TEXT", FALSE);
00962       }
00963       char *platformText;
00964       PRInt32 platformLen;
00965       // Get the appropriate unicode encoder. We're guaranteed that this won't change
00966       // through the life of the app so we can cache it.
00967 
00968       nsCOMPtr<nsIUnicodeEncoder> encoder;
00969       // get the charset
00970       nsCAutoString platformCharset;
00971       nsCOMPtr <nsIPlatformCharset> platformCharsetService = do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv);
00972       if (NS_SUCCEEDED(rv))
00973         rv = platformCharsetService->GetCharset(kPlatformCharsetSel_Menu, platformCharset);
00974       if (NS_FAILED(rv))
00975         platformCharset.AssignLiteral("ISO-8859-1");
00976       
00977       // get the encoder
00978       nsCOMPtr<nsICharsetConverterManager> ccm = do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
00979       rv = ccm->GetUnicodeEncoderRaw(platformCharset.get(), getter_AddRefs(encoder));
00980       NS_ASSERTION(NS_SUCCEEDED(rv), "GetUnicodeEncoderRaw failed");
00981       if (NS_FAILED(rv)) {
00982         nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
00983         return;
00984       }
00985 
00986       encoder->SetOutputErrorBehavior(nsIUnicodeEncoder::kOnError_Replace, nsnull, '?');
00987 
00988       // Estimate out length and allocate the buffer based on a worst-case estimate, then do
00989       // the conversion.
00990       PRUnichar *castedData = NS_REINTERPRET_CAST(PRUnichar*, clipboardData);
00991       encoder->GetMaxLength(castedData, dataLength/2, &platformLen);
00992       if ( platformLen ) {
00993         platformText = NS_REINTERPRET_CAST(char*, nsMemory::Alloc(platformLen + sizeof(char)));
00994         if ( platformText ) {
00995           PRInt32 len = (PRInt32)dataLength/2;
00996           rv = encoder->Convert(castedData, &len, platformText, &platformLen);
00997           (platformText)[platformLen] = '\0';  // null terminate. Convert() doesn't do it for us
00998         }
00999  } // if valid length
01000 
01001       if (platformLen > 0) {
01002         int status = 0;
01003         XTextProperty prop;
01004 
01005 #ifdef DEBUG_CLIPBOARD
01006         g_print("\nConverted text from unicode to platform locale\n");
01007         g_print("platformText is %s\n", platformText);
01008         g_print("platformLen is %d\n", platformLen);
01009 #endif
01010 
01011         status = XmbTextListToTextProperty(GDK_DISPLAY(), &platformText, 1, XCompoundTextStyle,
01012                                            &prop);
01013 
01014         if (status == Success) {
01015 #ifdef DEBUG_CLIPBOARD
01016           g_print("\nXmbTextListToTextProperty succeeded\n  text is %s\n  length is %d\n", prop.value,
01017                   prop.nitems);
01018 #endif
01019           nsMemory::Free(platformText);
01020           platformText = (char *)prop.value;
01021           platformLen = prop.nitems;
01022         }
01023       }
01024 
01025 #ifdef DEBUG_CLIPBOARD
01026       g_print("\nFinished trying to convert to platform charset\n");
01027 #endif
01028 
01029       if (clipboardData) {
01030         nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
01031         clipboardData = platformText;
01032         dataLength = platformLen;
01033       }
01034     }
01035 #ifdef DEBUG_CLIPBOARD
01036     g_print("\nPutting data on clipboard:\n");
01037     g_print("  clipboardData is %s\n", clipboardData);
01038     g_print("  length is %d\n", dataLength);
01039 #endif
01040     if (clipboardData && dataLength > 0)
01041       gtk_selection_data_set(aSelectionData,
01042                              aInfo, size*8,
01043                              NS_REINTERPRET_CAST(unsigned char *, clipboardData),
01044                              dataLength);
01045     else
01046       gtk_selection_data_set(aSelectionData,
01047                              gdk_atom_intern("NULL", FALSE), 8,
01048                              nsnull, 0);
01049     nsMemory::Free ( NS_REINTERPRET_CAST(char*, clipboardData) );
01050   }
01051 #ifdef DEBUG_pavlov
01052   else
01053     printf("Transferable didn't support data flavor %s (type = %d)\n",
01054            dataFlavor ? dataFlavor : "None", aInfo);
01055 #endif
01056 }
01057 
01058 
01066 void nsClipboard::SelectionClearCB(GtkWidget *aWidget,
01067                                    GdkEventSelection *aEvent,
01068                                    gpointer aData)
01069 {
01070 #ifdef DEBUG_CLIPBOARD
01071   g_print("  nsClipboard::SelectionClearCB\n");
01072 #endif /* DEBUG_CLIPBOARD */
01073 
01074   if (!aWidget) {
01075     NS_ASSERTION(PR_FALSE, "Null widget passed to SelectionClearCB)\n");
01076     return;
01077   }
01078 
01079   if (!aEvent) {
01080     NS_ASSERTION(PR_FALSE, "Null event passed to SelectionClearCB)\n");
01081     return;
01082   }
01083 
01084   nsClipboard *cb = (nsClipboard *)gtk_object_get_data(GTK_OBJECT(aWidget),
01085                                                        "cb");
01086 
01087   if (aEvent->selection == GDK_SELECTION_PRIMARY) {
01088 #ifdef DEBUG_CLIPBOARD
01089     g_print("clearing PRIMARY clipboard\n");
01090 #endif
01091     cb->EmptyClipboard(kSelectionClipboard);
01092   } else if (aEvent->selection == GDK_SELECTION_CLIPBOARD) {
01093 #ifdef DEBUG_CLIPBOARD
01094     g_print("clearing CLIPBOARD clipboard\n");
01095 #endif
01096     cb->EmptyClipboard(kGlobalClipboard);
01097   }
01098 }
01099 
01100 
01108 void
01109 nsClipboard::SelectionRequestCB (GtkWidget *aWidget,
01110                                  GtkSelectionData *aSelectionData,
01111                                  gpointer aData)
01112 {
01113 #ifdef DEBUG_CLIPBOARD
01114   g_print("  nsClipboard::SelectionRequestCB\n");
01115 #endif /* DEBUG_CLIPBOARD */
01116 }
01117 
01125 void
01126 nsClipboard::SelectionNotifyCB (GtkWidget *aWidget,
01127                                 GtkSelectionData *aSelectionData,
01128                                 gpointer aData)
01129 {
01130 #ifdef DEBUG_CLIPBOARD
01131   g_print("  nsClipboard::SelectionNotifyCB\n");
01132 #endif /* DEBUG_CLIPBOARD */
01133 }
01134 
01135 /* helper functions*/
01136 
01137 /* inline */
01138 GdkAtom nsClipboard::GetSelectionAtom(PRInt32 aWhichClipboard)
01139 {
01140   switch (aWhichClipboard)
01141   {
01142   case kGlobalClipboard:
01143     return GDK_SELECTION_CLIPBOARD;
01144   case kSelectionClipboard:
01145   default:
01146     return GDK_SELECTION_PRIMARY;
01147   }
01148 }
01149 
01150 /* inline */
01151 nsITransferable *nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
01152 {
01153   nsITransferable *transferable = nsnull;
01154   switch (aWhichClipboard)
01155   {
01156   case kGlobalClipboard:
01157     transferable = mGlobalTransferable;
01158     break;
01159   case kSelectionClipboard:
01160     transferable = mSelectionTransferable;
01161     break;
01162   }
01163   return transferable;
01164 }
01165 
01166 /* inline */
01167 void nsClipboard::AddTarget(GdkAtom aAtom, GdkAtom aSelectionAtom)
01168 {
01169   gtk_selection_add_target(sWidget, aSelectionAtom, aAtom, aAtom);
01170 }
01171 
01172 void nsClipboard::RegisterFormat(const char *aMimeStr, GdkAtom aSelectionAtom)
01173 {
01174 #ifdef DEBUG_CLIPBOARD
01175   g_print("  nsClipboard::RegisterFormat(%s)\n", aMimeStr);
01176 #endif
01177   nsCAutoString mimeStr(aMimeStr);
01178 
01179   GdkAtom atom = gdk_atom_intern(aMimeStr, FALSE);
01180 
01181   // for Text and Unicode we want to add some extra types to the X clipboard
01182   if (mimeStr.Equals(kUnicodeMime)) {
01183     // we will do the conversions to and from unicode internally
01184     // anyone asking for TEXT will get COMPOUND_TEXT
01185     AddTarget(gdk_atom_intern("TEXT", FALSE), aSelectionAtom); 
01186     AddTarget(gdk_atom_intern("COMPOUND_TEXT", FALSE), aSelectionAtom);
01187     AddTarget(gdk_atom_intern("UTF8_STRING", FALSE), aSelectionAtom);
01188     AddTarget(GDK_SELECTION_TYPE_STRING, aSelectionAtom);
01189   }
01190 
01191   AddTarget(atom, aSelectionAtom);
01192 }
01193 
01194 /* return PR_TRUE if we have converted or PR_FALSE if we havn't and need to keep being called */
01195 PRBool nsClipboard::DoConvert(const char *aMimeStr, GdkAtom aSelectionAtom)
01196 {
01197 #ifdef DEBUG_CLIPBOARD
01198   g_print("  nsClipboard::DoConvert(%s, %s)\n", aMimeStr, gdk_atom_name(aSelectionAtom));
01199 #endif
01200   /* when doing the selection_add_target, each case should have the same last parameter
01201      which matches the case match */
01202   PRBool r = PR_FALSE;
01203 
01204   nsCAutoString mimeStr(aMimeStr);
01205 
01206   if (mimeStr.Equals(kUnicodeMime)) {
01207     r = DoRealConvert(gdk_atom_intern("UTF8_STRING", FALSE), aSelectionAtom);
01208     if (r) return r;
01209     r = DoRealConvert(gdk_atom_intern("COMPOUND_TEXT", FALSE), aSelectionAtom);
01210     if (r) return r;
01211     r = DoRealConvert(GDK_SELECTION_TYPE_STRING, aSelectionAtom);
01212     if (r) return r;
01213   }
01214 
01215   GdkAtom atom = gdk_atom_intern(aMimeStr, FALSE);
01216   r = DoRealConvert(atom, aSelectionAtom);
01217   if (r) return r;
01218 
01219   return r;
01220 }
01221 
01222 PRBool nsClipboard::GetTargets(GdkAtom aSelectionAtom)
01223 {
01224 #ifdef DEBUG_CLIPBOARD
01225   g_print("    nsClipboard::GetTargets(%d)\n    {\n", aSelectionAtom);
01226 #endif
01227 
01228   // Set a flag saying that we're blocking waiting for the callback:
01229   mBlocking = PR_TRUE;
01230 
01231   //
01232   // ask X what kind of data we can get
01233   //
01234   static GdkAtom targetsAtom = gdk_atom_intern("TARGETS", PR_FALSE);
01235 
01236   gtk_selection_convert(sWidget,
01237                         aSelectionAtom,
01238                         targetsAtom,
01239                         GDK_CURRENT_TIME);
01240 
01241   /* see comment in DoRealConvert for why we check for mBlocking here */
01242   if (mBlocking) {
01243     // Now we need to wait until the callback comes in ...
01244 #ifdef DEBUG_CLIPBOARD
01245     g_print("      Waiting for the callback... mBlocking = %d\n", mBlocking);
01246 #endif /* DEBUG_CLIPBOARD */
01247 
01248     if (!FindSelectionNotifyEvent())
01249       return PR_FALSE;
01250 
01251   }
01252 
01253 #ifdef DEBUG_CLIPBOARD
01254   g_print("    }\n");
01255 #endif
01256 
01257   if (mSelectionData.length <= 0)
01258     return PR_FALSE;
01259 
01260   return PR_TRUE;
01261 }
01262 
01263 void nsClipboard::SetCutBuffer()
01264 {
01265 #ifdef USE_CUTBUFFERS
01266   void *clipboardData;
01267   PRUint32 dataLength;
01268   nsresult rv;
01269 
01270   nsCOMPtr<nsITransferable> transferable(GetTransferable(kGlobalClipboard));
01271 
01272   // Make sure we have a transferable:
01273   if (!transferable) {
01274 #ifdef DEBUG_CLIPBOARD
01275     g_print("Clipboard has no transferable!\n");
01276 #endif
01277     return;
01278   }
01279 
01280   nsCOMPtr<nsISupports> genericDataWrapper;
01281   rv = transferable->GetTransferData(kUnicodeMime, getter_AddRefs(genericDataWrapper), &dataLength);
01282   nsPrimitiveHelpers::CreateDataFromPrimitive(kUnicodeMime, genericDataWrapper, &clipboardData, dataLength);
01283   if (NS_SUCCEEDED(rv) && clipboardData && dataLength > 0) {
01284     char* plainTextData = nsnull;
01285     PRUnichar* castedUnicode = NS_REINTERPRET_CAST(PRUnichar*, clipboardData);
01286     PRInt32 plainTextLen = 0;
01287     nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText (castedUnicode, dataLength / 2,
01288                                                            &plainTextData, &plainTextLen);
01289     if (clipboardData) {
01290       nsMemory::Free(NS_REINTERPRET_CAST(char*, clipboardData));
01291       clipboardData = plainTextData;
01292       dataLength = plainTextLen;
01293     }
01294   }
01295 
01296   XRotateBuffers(GDK_DISPLAY(), 1);
01297   XStoreBytes(GDK_DISPLAY(), (const char *) clipboardData, nsCRT::strlen((const char *)clipboardData));
01298 #endif
01299 }
01300 
01301 static void
01302 DispatchSelectionNotifyEvent(GtkWidget *widget, XEvent *xevent)
01303 {
01304   GdkEvent event;
01305   event.selection.type = GDK_SELECTION_NOTIFY;
01306   event.selection.window = widget->window;
01307   event.selection.selection = xevent->xselection.selection;
01308   event.selection.target = xevent->xselection.target;
01309   event.selection.property = xevent->xselection.property;
01310   event.selection.time = xevent->xselection.time;
01311 
01312   gtk_widget_event(widget, &event);
01313 }
01314 
01315 static void
01316 DispatchPropertyNotifyEvent(GtkWidget *widget, XEvent *xevent)
01317 {
01318   if (gdk_window_get_events(widget->window) & GDK_PROPERTY_CHANGE_MASK) {
01319     GdkEvent event;
01320     event.property.type = GDK_PROPERTY_NOTIFY;
01321     event.property.window = widget->window;
01322     event.property.atom = xevent->xproperty.atom;
01323     event.property.time = xevent->xproperty.time;
01324     event.property.state = xevent->xproperty.state;
01325 
01326     gtk_widget_event(widget, &event);
01327   }
01328 }
01329 
01330 struct checkEventContext
01331 {
01332   GtkWidget *cbWidget;
01333   Atom       selAtom;
01334 };
01335 
01336 static Bool
01337 checkEventProc(Display *display, XEvent *event, XPointer arg)
01338 {
01339   checkEventContext *context = (checkEventContext *) arg;
01340 
01341   if (event->xany.type == SelectionNotify ||
01342       (event->xany.type == PropertyNotify &&
01343        event->xproperty.atom == context->selAtom)) {
01344 
01345     GdkWindow *cbWindow = gdk_window_lookup(event->xany.window);
01346     if (cbWindow) {
01347       GtkWidget *cbWidget = nsnull;
01348       gdk_window_get_user_data(cbWindow, (gpointer *)&cbWidget);
01349       if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
01350         context->cbWidget = cbWidget;
01351         return True;
01352       }
01353     }
01354   }
01355 
01356   return False;
01357 }
01358 
01359 // Idle timeout for receiving selection and property notify events (microsec)
01360 static const int kClipboardTimeout = 500000;
01361 
01362 PRBool nsClipboard::FindSelectionNotifyEvent()
01363 {
01364   Display *xDisplay = GDK_DISPLAY();
01365   checkEventContext context;
01366   context.cbWidget = nsnull;
01367   context.selAtom = gdk_atom_intern("GDK_SELECTION", FALSE);
01368 
01369   // Send X events which are relevant to the ongoing selection retrieval
01370   // to the clipboard widget.  Wait until either the operation completes, or
01371   // we hit our timeout.  All other X events remain queued.
01372 
01373   int select_result;
01374 
01375 #ifdef POLL_WITH_XCONNECTIONNUMBER
01376   struct pollfd fds[1];
01377   fds[0].fd = XConnectionNumber(xDisplay);
01378   fds[0].events = POLLIN;
01379 #else
01380   int cnumber = ConnectionNumber(xDisplay);
01381   fd_set select_set;
01382   FD_ZERO(&select_set);
01383   FD_SET(cnumber, &select_set);
01384   ++cnumber;
01385   struct timeval tv;
01386 #endif
01387 
01388   do {
01389     XEvent xevent;
01390 
01391     while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
01392                          (XPointer) &context)) {
01393 
01394       if (xevent.xany.type == SelectionNotify)
01395         DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
01396       else
01397         DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
01398 
01399       if (!mBlocking)
01400         return PR_TRUE;
01401     }
01402 
01403 #ifdef POLL_WITH_XCONNECTIONNUMBER
01404     select_result = poll(fds, 1, kClipboardTimeout / 1000);
01405 #else
01406     tv.tv_sec = 0;
01407     tv.tv_usec = kClipboardTimeout;
01408     select_result = select(cnumber, &select_set, nsnull, nsnull, &tv);
01409 #endif
01410   } while (select_result == 1);
01411 
01412 #ifdef DEBUG_CLIPBOARD
01413   printf("exceeded clipboard timeout\n");
01414 #endif
01415   return PR_FALSE;
01416 }
01417 
01418 /*
01419  * when copy-paste, mozilla wants data encoded using UCS2,
01420  * other app such as StarOffice use "text/html"(RFC2854).
01421  * This function convert data(got from GTK clipboard)
01422  * to data mozilla wanted.
01423  *
01424  * data from GTK clipboard can be 3 forms:
01425  *  1. From current mozilla
01426  *     "text/html", charset = utf-16
01427  *  2. From old version mozilla or mozilla-based app
01428  *     content("body" only), charset = utf-16
01429  *  3. From other app who use "text/html" when copy-paste
01430  *     "text/html", has "charset" info
01431  *
01432  * data      : got from GTK clipboard
01433  * dataLength: got from GTK clipboard
01434  * body      : pass to Mozilla
01435  * bodyLength: pass to Mozilla
01436  */
01437 void ConvertHTMLtoUCS2(char* data, PRInt32 dataLength,
01438                        PRUnichar** unicodeData, PRInt32& outUnicodeLen)
01439 {
01440   nsCAutoString charset;
01441   GetHTMLCharset(data, dataLength, charset);// get charset of HTML
01442   if (charset.EqualsLiteral("UTF-16")) {//current mozilla
01443     outUnicodeLen = dataLength / 2 - 1;
01444     *unicodeData = NS_REINTERPRET_CAST(PRUnichar*,
01445                    nsMemory::Alloc((outUnicodeLen + 1) * sizeof(PRUnichar)));
01446     if ( unicodeData ) {
01447       memcpy(*unicodeData, data + sizeof(PRUnichar),
01448              outUnicodeLen * sizeof(PRUnichar));
01449       (*unicodeData)[outUnicodeLen] = '\0';
01450     }
01451   }
01452   else if (charset.EqualsLiteral("OLD-MOZILLA")) {// old mozilla
01453     outUnicodeLen = dataLength / 2;
01454     *unicodeData = NS_REINTERPRET_CAST(PRUnichar*,
01455                    nsMemory::Alloc((outUnicodeLen + 1) * sizeof(PRUnichar)));
01456     if ( unicodeData ) {
01457       memcpy(*unicodeData, data, outUnicodeLen * sizeof(PRUnichar));
01458       (*unicodeData)[outUnicodeLen] = '\0';
01459     }
01460   }
01461   else {// app which use "text/html" to copy&paste
01462     nsCOMPtr<nsIUnicodeDecoder> decoder;
01463     nsresult rv;
01464     // get the decoder
01465     nsCOMPtr<nsICharsetConverterManager> ccm =
01466             do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
01467     if (NS_FAILED(rv)) {
01468 #ifdef DEBUG_CLIPBOARD
01469       g_print("        can't get CHARSET CONVERTER MANAGER service\n");
01470 #endif
01471       outUnicodeLen = 0;
01472       return;
01473     }
01474     rv = ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(decoder));
01475     if (NS_FAILED(rv)) {
01476 #ifdef DEBUG_CLIPBOARD
01477       g_print("        get unicode decoder error\n");
01478 #endif
01479       outUnicodeLen = 0;
01480       return;
01481     }
01482     // converting
01483     decoder->GetMaxLength(data, dataLength, &outUnicodeLen);
01484     // |outUnicodeLen| is number of chars
01485     if (outUnicodeLen) {
01486       *unicodeData = NS_REINTERPRET_CAST(PRUnichar*,
01487                      nsMemory::Alloc((outUnicodeLen + 1) * sizeof(PRUnichar)));
01488       if ( unicodeData ) {
01489         PRInt32 numberTmp = dataLength;
01490         decoder->Convert(data, &numberTmp, *unicodeData, &outUnicodeLen);
01491 #ifdef DEBUG_CLIPBOARD
01492         if (numberTmp != dataLength)
01493           printf("didn't consume all the bytes\n");
01494 #endif
01495         // null terminate. Convert() doesn't do it for us
01496         (*unicodeData)[outUnicodeLen] = '\0';
01497       }
01498     } // if valid length
01499   }
01500 }
01501 
01502 /*
01503  * get "charset" information from clipboard data
01504  * return value can be:
01505  *  1. "UTF-16":      current mozilla or "text/html" with "charset=utf-16"
01506  *  2. "OLD-MOZILLA": UCS2 encoded data
01507  *  3. other:         "text/html" with other charset than utf-16
01508  */
01509 void GetHTMLCharset(char* data, PRInt32 dataLength, nsACString& str)
01510 {
01511   // if detect "FFFE" or "FEFF", assume utf-16
01512   PRUnichar* beginChar =  (PRUnichar*)data;
01513   if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
01514     str.AssignLiteral("UTF-16");
01515     return;
01516   }
01517   // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
01518   nsDependentCString htmlStr = nsDependentCString(data, dataLength);
01519   nsACString::const_iterator start, end, valueStart, valueEnd;
01520 
01521   htmlStr.BeginReading(start);
01522   htmlStr.EndReading(end);
01523   htmlStr.BeginReading(valueStart);
01524   htmlStr.BeginReading(valueEnd);
01525   
01526   if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
01527                                     start, end)) {
01528     start = end;
01529     htmlStr.EndReading(end);
01530 
01531     if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("charset="),
01532                                       start, end)) {
01533       valueStart = end;
01534       start = end;
01535       htmlStr.EndReading(end);
01536           
01537       if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("\""), start, end))
01538         valueEnd = start;
01539     }
01540   }
01541   // find "charset" in HTML
01542   if (valueStart != valueEnd) {
01543     const nsACString & charsetStr = Substring(valueStart, valueEnd);
01544     if ( !charsetStr.IsEmpty() ) {
01545       nsCString charsetUpperStr;
01546       ToUpperCase(charsetStr, charsetUpperStr);
01547 #ifdef DEBUG_CLIPBOARD
01548       printf("Charset of HTML = %s\n", charsetUpperStr.get());
01549 #endif
01550       str.Assign(charsetUpperStr);
01551       return;
01552     }
01553   }
01554   // no "charset" info, assume data come from old-version mozilla
01555   // [UCS2 encoding (without "FFFE" or "FEFF" at the beginning)]
01556   //
01557   // TODO: it may also be "text/html" without "charset".
01558   // can't distinguish between them. Sochoose OLD-MOZILLA here to
01559   // make compitable with old-version mozilla
01560   str.AssignLiteral("OLD-MOZILLA");
01561 }
01562