1479 lines
53 KiB
Diff
1479 lines
53 KiB
Diff
From fb41f028badb4dfddfc47fb2a1a68c1aa90dcef5 Mon Sep 17 00:00:00 2001
|
|
From: Robert Bragg <robert@linux.intel.com>
|
|
Date: Fri, 8 May 2009 13:57:22 +0100
|
|
Subject: [PATCH] Adds initial Gtk clipboard support to moz-headless
|
|
|
|
If build with MOZ_X11 enabled then this now builds the clipboard code taken
|
|
from the gtk2 backend. This doesn't provide any embedding API yet to expose
|
|
the clipboard.
|
|
---
|
|
widget/src/headless/Makefile.in | 6 +
|
|
widget/src/headless/nsClipboard.cpp | 948 +++++++++++++++++++++++++++++++
|
|
widget/src/headless/nsClipboard.h | 93 +++
|
|
widget/src/headless/nsIImageToPixbuf.h | 62 ++
|
|
widget/src/headless/nsImageToPixbuf.cpp | 196 +++++++
|
|
widget/src/headless/nsImageToPixbuf.h | 71 +++
|
|
widget/src/headless/nsWidgetFactory.cpp | 18 +
|
|
7 files changed, 1394 insertions(+), 0 deletions(-)
|
|
create mode 100644 widget/src/headless/nsClipboard.cpp
|
|
create mode 100644 widget/src/headless/nsClipboard.h
|
|
create mode 100644 widget/src/headless/nsIImageToPixbuf.h
|
|
create mode 100644 widget/src/headless/nsImageToPixbuf.cpp
|
|
create mode 100644 widget/src/headless/nsImageToPixbuf.h
|
|
|
|
Index: offscreen/widget/src/headless/Makefile.in
|
|
===================================================================
|
|
--- offscreen.orig/widget/src/headless/Makefile.in 2009-05-16 18:23:25.000000000 +0100
|
|
+++ offscreen/widget/src/headless/Makefile.in 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -95,6 +95,12 @@
|
|
nsScreenManagerHeadless.cpp \
|
|
$(NULL)
|
|
|
|
+ifdef MOZ_X11
|
|
+CPPSRCS += nsClipboard.cpp \
|
|
+ nsImageToPixbuf.cpp \
|
|
+ $(NULL)
|
|
+endif
|
|
+
|
|
# build our subdirs, too
|
|
SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a
|
|
|
|
Index: offscreen/widget/src/headless/nsClipboard.cpp
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ offscreen/widget/src/headless/nsClipboard.cpp 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -0,0 +1,948 @@
|
|
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
+/* vim:expandtab:shiftwidth=4:tabstop=4:
|
|
+ */
|
|
+/* ***** BEGIN LICENSE BLOCK *****
|
|
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
+ *
|
|
+ * The contents of this file are subject to the Mozilla 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/MPL/
|
|
+ *
|
|
+ * 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 Christopher Blizzard
|
|
+ * <blizzard@mozilla.org>. Portions created by the Initial Developer
|
|
+ * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
|
|
+ *
|
|
+ * Contributor(s):
|
|
+ *
|
|
+ * Alternatively, the contents of this file may be used under the terms of
|
|
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
+ * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
+ * of those above. If you wish to allow use of your version of this file only
|
|
+ * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
+ * use your version of this file under the terms of the MPL, indicate your
|
|
+ * decision by deleting the provisions above and replace them with the notice
|
|
+ * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
+ * the provisions above, a recipient may use your version of this file under
|
|
+ * the terms of any one of the MPL, the GPL or the LGPL.
|
|
+ *
|
|
+ * ***** END LICENSE BLOCK ***** */
|
|
+
|
|
+#include "nsClipboard.h"
|
|
+#include "nsSupportsPrimitives.h"
|
|
+#include "nsString.h"
|
|
+#include "nsReadableUtils.h"
|
|
+#include "nsXPIDLString.h"
|
|
+#include "nsPrimitiveHelpers.h"
|
|
+#include "nsICharsetConverterManager.h"
|
|
+#include "nsIServiceManager.h"
|
|
+#include "nsIImage.h"
|
|
+#include "nsImageToPixbuf.h"
|
|
+#include "nsStringStream.h"
|
|
+
|
|
+#include <gtk/gtk.h>
|
|
+
|
|
+// For manipulation of the X event queue
|
|
+#include <X11/Xlib.h>
|
|
+#include <gdk/gdkx.h>
|
|
+#include <sys/time.h>
|
|
+#include <sys/types.h>
|
|
+#include <unistd.h>
|
|
+
|
|
+#ifdef POLL_WITH_XCONNECTIONNUMBER
|
|
+#include <poll.h>
|
|
+#endif
|
|
+
|
|
+// Callback when someone asks us for the selection
|
|
+void
|
|
+invisible_selection_get_cb (GtkWidget *aWidget,
|
|
+ GtkSelectionData *aSelectionData,
|
|
+ guint aTime,
|
|
+ guint aInfo,
|
|
+ nsClipboard *aClipboard);
|
|
+
|
|
+gboolean
|
|
+selection_clear_event_cb (GtkWidget *aWidget,
|
|
+ GdkEventSelection *aEvent,
|
|
+ nsClipboard *aClipboard);
|
|
+
|
|
+static void
|
|
+ConvertHTMLtoUCS2 (guchar *data,
|
|
+ PRInt32 dataLength,
|
|
+ PRUnichar **unicodeData,
|
|
+ PRInt32 &outUnicodeLen);
|
|
+
|
|
+static void
|
|
+GetHTMLCharset (guchar * data, PRInt32 dataLength, nsCString& str);
|
|
+
|
|
+
|
|
+// Our own versions of gtk_clipboard_wait_for_contents and
|
|
+// gtk_clipboard_wait_for_text, which don't run the event loop while
|
|
+// waiting for the data. This prevents a lot of problems related to
|
|
+// dispatching events at unexpected times.
|
|
+
|
|
+static GtkSelectionData *
|
|
+wait_for_contents (GtkClipboard *clipboard, GdkAtom target);
|
|
+
|
|
+static gchar *
|
|
+wait_for_text (GtkClipboard *clipboard);
|
|
+
|
|
+static Bool
|
|
+checkEventProc(Display *display, XEvent *event, XPointer arg);
|
|
+
|
|
+struct retrieval_context
|
|
+{
|
|
+ PRBool completed;
|
|
+ void *data;
|
|
+
|
|
+ retrieval_context() : completed(PR_FALSE), data(nsnull) { }
|
|
+};
|
|
+
|
|
+static void
|
|
+wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *transferData);
|
|
+
|
|
+static void
|
|
+clipboard_contents_received(GtkClipboard *clipboard,
|
|
+ GtkSelectionData *selection_data,
|
|
+ gpointer data);
|
|
+
|
|
+static void
|
|
+clipboard_text_received(GtkClipboard *clipboard,
|
|
+ const gchar *text,
|
|
+ gpointer data);
|
|
+
|
|
+nsClipboard::nsClipboard()
|
|
+{
|
|
+ mWidget = nsnull;
|
|
+}
|
|
+
|
|
+nsClipboard::~nsClipboard()
|
|
+{
|
|
+ if (mWidget)
|
|
+ gtk_widget_destroy(mWidget);
|
|
+}
|
|
+
|
|
+NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
|
|
+
|
|
+nsresult
|
|
+nsClipboard::Init(void)
|
|
+{
|
|
+ mWidget = gtk_invisible_new();
|
|
+ if (!mWidget)
|
|
+ return NS_ERROR_FAILURE;
|
|
+
|
|
+ g_signal_connect(G_OBJECT(mWidget), "selection_get",
|
|
+ G_CALLBACK(invisible_selection_get_cb), this);
|
|
+
|
|
+ g_signal_connect(G_OBJECT(mWidget), "selection_clear_event",
|
|
+ G_CALLBACK(selection_clear_event_cb), this);
|
|
+
|
|
+ // XXX make sure to set up the selection_clear event
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+nsClipboard::SetData(nsITransferable *aTransferable,
|
|
+ nsIClipboardOwner *aOwner, PRInt32 aWhichClipboard)
|
|
+{
|
|
+ // See if we can short cut
|
|
+ if ((aWhichClipboard == kGlobalClipboard &&
|
|
+ aTransferable == mGlobalTransferable.get() &&
|
|
+ aOwner == mGlobalOwner.get()) ||
|
|
+ (aWhichClipboard == kSelectionClipboard &&
|
|
+ aTransferable == mSelectionTransferable.get() &&
|
|
+ aOwner == mSelectionOwner.get())) {
|
|
+ return NS_OK;
|
|
+ }
|
|
+
|
|
+ nsresult rv;
|
|
+ if (!mPrivacyHandler) {
|
|
+ rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+ }
|
|
+ rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable);
|
|
+ NS_ENSURE_SUCCESS(rv, rv);
|
|
+
|
|
+ // Clear out the clipboard in order to set the new data
|
|
+ EmptyClipboard(aWhichClipboard);
|
|
+
|
|
+ if (aWhichClipboard == kSelectionClipboard) {
|
|
+ mSelectionOwner = aOwner;
|
|
+ mSelectionTransferable = aTransferable;
|
|
+ }
|
|
+ else {
|
|
+ mGlobalOwner = aOwner;
|
|
+ mGlobalTransferable = aTransferable;
|
|
+ }
|
|
+
|
|
+ // Which selection are we about to claim, CLIPBOARD or PRIMARY?
|
|
+ GdkAtom selectionAtom = GetSelectionAtom(aWhichClipboard);
|
|
+
|
|
+ // Make ourselves the owner. If we fail to, return.
|
|
+ if (!gtk_selection_owner_set(mWidget, selectionAtom, GDK_CURRENT_TIME))
|
|
+ return NS_ERROR_FAILURE;
|
|
+
|
|
+ // Clear the old selection target list.
|
|
+ gtk_selection_clear_targets(mWidget, selectionAtom);
|
|
+
|
|
+ // Get the types of supported flavors
|
|
+ nsCOMPtr<nsISupportsArray> flavors;
|
|
+
|
|
+ rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors));
|
|
+ if (!flavors || NS_FAILED(rv))
|
|
+ return NS_ERROR_FAILURE;
|
|
+
|
|
+ // Add all the flavors to this widget's supported type.
|
|
+ PRUint32 count;
|
|
+ flavors->Count(&count);
|
|
+ for (PRUint32 i=0; i < count; i++) {
|
|
+ nsCOMPtr<nsISupports> tastesLike;
|
|
+ flavors->GetElementAt(i, getter_AddRefs(tastesLike));
|
|
+ nsCOMPtr<nsISupportsCString> flavor = do_QueryInterface(tastesLike);
|
|
+
|
|
+ if (flavor) {
|
|
+ nsXPIDLCString flavorStr;
|
|
+ flavor->ToString(getter_Copies(flavorStr));
|
|
+
|
|
+ // special case text/unicode since we can handle all of
|
|
+ // the string types
|
|
+ if (!strcmp(flavorStr, kUnicodeMime)) {
|
|
+ AddTarget(gdk_atom_intern("UTF8_STRING", FALSE),
|
|
+ selectionAtom);
|
|
+ AddTarget(gdk_atom_intern("COMPOUND_TEXT", FALSE),
|
|
+ selectionAtom);
|
|
+ AddTarget(gdk_atom_intern("TEXT", FALSE), selectionAtom);
|
|
+ AddTarget(GDK_SELECTION_TYPE_STRING, selectionAtom);
|
|
+ // next loop iteration
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // very special case for this one. since our selection mechanism doesn't work for images,
|
|
+ // we must use GTK's clipboard utility functions
|
|
+ if (!strcmp(flavorStr, kNativeImageMime) || !strcmp(flavorStr, kPNGImageMime) ||
|
|
+ !strcmp(flavorStr, kJPEGImageMime) || !strcmp(flavorStr, kGIFImageMime)) {
|
|
+ nsCOMPtr<nsISupports> item;
|
|
+ PRUint32 len;
|
|
+ rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(item), &len);
|
|
+ nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(item));
|
|
+ if (!ptrPrimitive)
|
|
+ continue;
|
|
+
|
|
+ nsCOMPtr<nsISupports> primitiveData;
|
|
+ ptrPrimitive->GetData(getter_AddRefs(primitiveData));
|
|
+ nsCOMPtr<nsIImage> image(do_QueryInterface(primitiveData));
|
|
+ if (!image) // Not getting an image for an image mime type!?
|
|
+ continue;
|
|
+
|
|
+ if (NS_FAILED(image->LockImagePixels(PR_FALSE)))
|
|
+ continue;
|
|
+ GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
|
|
+ if (!pixbuf) {
|
|
+ image->UnlockImagePixels(PR_FALSE);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ GtkClipboard *aClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
|
|
+ gtk_clipboard_set_image(aClipboard, pixbuf);
|
|
+ g_object_unref(pixbuf);
|
|
+ image->UnlockImagePixels(PR_FALSE);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // Add this to our list of valid targets
|
|
+ GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
|
|
+ AddTarget(atom, selectionAtom);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
|
|
+{
|
|
+ if (!aTransferable)
|
|
+ return NS_ERROR_FAILURE;
|
|
+
|
|
+ GtkClipboard *clipboard;
|
|
+ clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
|
|
+
|
|
+ guchar *data = NULL;
|
|
+ gint length = 0;
|
|
+ PRBool foundData = PR_FALSE;
|
|
+ nsCAutoString foundFlavor;
|
|
+
|
|
+ // Get a list of flavors this transferable can import
|
|
+ nsCOMPtr<nsISupportsArray> flavors;
|
|
+ nsresult rv;
|
|
+ rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors));
|
|
+ if (!flavors || NS_FAILED(rv))
|
|
+ return NS_ERROR_FAILURE;
|
|
+
|
|
+ PRUint32 count;
|
|
+ flavors->Count(&count);
|
|
+ for (PRUint32 i=0; i < count; i++) {
|
|
+ nsCOMPtr<nsISupports> genericFlavor;
|
|
+ flavors->GetElementAt(i, getter_AddRefs(genericFlavor));
|
|
+
|
|
+ nsCOMPtr<nsISupportsCString> currentFlavor;
|
|
+ currentFlavor = do_QueryInterface(genericFlavor);
|
|
+
|
|
+ if (currentFlavor) {
|
|
+ nsXPIDLCString flavorStr;
|
|
+ currentFlavor->ToString(getter_Copies(flavorStr));
|
|
+
|
|
+ // Special case text/unicode since we can convert any
|
|
+ // string into text/unicode
|
|
+ if (!strcmp(flavorStr, kUnicodeMime)) {
|
|
+ gchar* new_text = wait_for_text(clipboard);
|
|
+ if (new_text) {
|
|
+ // Convert utf-8 into our unicode format.
|
|
+ NS_ConvertUTF8toUTF16 ucs2string(new_text);
|
|
+ data = (guchar *)ToNewUnicode(ucs2string);
|
|
+ length = ucs2string.Length() * 2;
|
|
+ g_free(new_text);
|
|
+ foundData = PR_TRUE;
|
|
+ foundFlavor = kUnicodeMime;
|
|
+ break;
|
|
+ }
|
|
+ // If the type was text/unicode and we couldn't get
|
|
+ // text off the clipboard, run the next loop
|
|
+ // iteration.
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ // For images, we must wrap the data in an nsIInputStream then return instead of break,
|
|
+ // because that code below won't help us.
|
|
+ if (!strcmp(flavorStr, kJPEGImageMime) || !strcmp(flavorStr, kPNGImageMime) || !strcmp(flavorStr, kGIFImageMime)) {
|
|
+ GdkAtom atom;
|
|
+ if (!strcmp(flavorStr, kJPEGImageMime)) // This is image/jpg, but X only understands image/jpeg
|
|
+ atom = gdk_atom_intern("image/jpeg", FALSE);
|
|
+ else
|
|
+ atom = gdk_atom_intern(flavorStr, FALSE);
|
|
+
|
|
+ GtkSelectionData *selectionData = wait_for_contents(clipboard, atom);
|
|
+ if (!selectionData)
|
|
+ continue;
|
|
+
|
|
+ nsCOMPtr<nsIInputStream> byteStream;
|
|
+ NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)selectionData->data,
|
|
+ selectionData->length, NS_ASSIGNMENT_COPY);
|
|
+ aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
|
|
+ gtk_selection_data_free(selectionData);
|
|
+ return NS_OK;
|
|
+ }
|
|
+
|
|
+ // Get the atom for this type and try to request it off
|
|
+ // the clipboard.
|
|
+ GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
|
|
+ GtkSelectionData *selectionData;
|
|
+ selectionData = wait_for_contents(clipboard, atom);
|
|
+ if (selectionData) {
|
|
+ length = selectionData->length;
|
|
+ // Special case text/html since we can convert into UCS2
|
|
+ if (!strcmp(flavorStr, kHTMLMime)) {
|
|
+ PRUnichar* htmlBody= nsnull;
|
|
+ PRInt32 htmlBodyLen = 0;
|
|
+ // Convert text/html into our unicode format
|
|
+ ConvertHTMLtoUCS2((guchar *)selectionData->data, length,
|
|
+ &htmlBody, htmlBodyLen);
|
|
+ if (!htmlBodyLen)
|
|
+ break;
|
|
+ data = (guchar *)htmlBody;
|
|
+ length = htmlBodyLen * 2;
|
|
+ } else {
|
|
+ data = (guchar *)nsMemory::Alloc(length);
|
|
+ if (!data)
|
|
+ break;
|
|
+ memcpy(data, selectionData->data, length);
|
|
+ }
|
|
+ foundData = PR_TRUE;
|
|
+ foundFlavor = flavorStr;
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (foundData) {
|
|
+ nsCOMPtr<nsISupports> wrapper;
|
|
+ nsPrimitiveHelpers::CreatePrimitiveForData(foundFlavor.get(),
|
|
+ data, length,
|
|
+ getter_AddRefs(wrapper));
|
|
+ aTransferable->SetTransferData(foundFlavor.get(),
|
|
+ wrapper, length);
|
|
+ }
|
|
+
|
|
+ if (data)
|
|
+ nsMemory::Free(data);
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
|
|
+{
|
|
+ if (aWhichClipboard == kSelectionClipboard) {
|
|
+ if (mSelectionOwner) {
|
|
+ mSelectionOwner->LosingOwnership(mSelectionTransferable);
|
|
+ mSelectionOwner = nsnull;
|
|
+ }
|
|
+ mSelectionTransferable = nsnull;
|
|
+ }
|
|
+ else {
|
|
+ if (mGlobalOwner) {
|
|
+ mGlobalOwner->LosingOwnership(mGlobalTransferable);
|
|
+ mGlobalOwner = nsnull;
|
|
+ }
|
|
+ mGlobalTransferable = nsnull;
|
|
+ }
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, PRUint32 aLength,
|
|
+ PRInt32 aWhichClipboard, PRBool *_retval)
|
|
+{
|
|
+ if (!aFlavorList || !_retval)
|
|
+ return NS_ERROR_NULL_POINTER;
|
|
+
|
|
+ *_retval = PR_FALSE;
|
|
+
|
|
+ GtkSelectionData *selection_data =
|
|
+ GetTargets(GetSelectionAtom(aWhichClipboard));
|
|
+ if (!selection_data)
|
|
+ return NS_OK;
|
|
+
|
|
+ gint n_targets = 0;
|
|
+ GdkAtom *targets = NULL;
|
|
+
|
|
+ if (!gtk_selection_data_get_targets(selection_data,
|
|
+ &targets, &n_targets) ||
|
|
+ !n_targets)
|
|
+ return NS_OK;
|
|
+
|
|
+ // Walk through the provided types and try to match it to a
|
|
+ // provided type.
|
|
+ for (PRUint32 i = 0; i < aLength && !*_retval; i++) {
|
|
+ // We special case text/unicode here.
|
|
+ if (!strcmp(aFlavorList[i], kUnicodeMime) &&
|
|
+ gtk_selection_data_targets_include_text(selection_data)) {
|
|
+ *_retval = PR_TRUE;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ for (PRInt32 j = 0; j < n_targets; j++) {
|
|
+ gchar *atom_name = gdk_atom_name(targets[j]);
|
|
+ if (!strcmp(atom_name, aFlavorList[i]))
|
|
+ *_retval = PR_TRUE;
|
|
+
|
|
+ // X clipboard wants image/jpeg, not image/jpg
|
|
+ if (!strcmp(aFlavorList[i], kJPEGImageMime) && !strcmp(atom_name, "image/jpeg"))
|
|
+ *_retval = PR_TRUE;
|
|
+
|
|
+ g_free(atom_name);
|
|
+
|
|
+ if (*_retval)
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ gtk_selection_data_free(selection_data);
|
|
+ g_free(targets);
|
|
+
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP
|
|
+nsClipboard::SupportsSelectionClipboard(PRBool *_retval)
|
|
+{
|
|
+ *_retval = PR_TRUE; // yeah, unix supports the selection clipboard
|
|
+ return NS_OK;
|
|
+}
|
|
+
|
|
+/* static */
|
|
+GdkAtom
|
|
+nsClipboard::GetSelectionAtom(PRInt32 aWhichClipboard)
|
|
+{
|
|
+ if (aWhichClipboard == kGlobalClipboard)
|
|
+ return GDK_SELECTION_CLIPBOARD;
|
|
+
|
|
+ return GDK_SELECTION_PRIMARY;
|
|
+}
|
|
+
|
|
+/* static */
|
|
+GtkSelectionData *
|
|
+nsClipboard::GetTargets(GdkAtom aWhichClipboard)
|
|
+{
|
|
+ GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard);
|
|
+ return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE));
|
|
+}
|
|
+
|
|
+nsITransferable *
|
|
+nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
|
|
+{
|
|
+ nsITransferable *retval;
|
|
+
|
|
+ if (aWhichClipboard == kSelectionClipboard)
|
|
+ retval = mSelectionTransferable.get();
|
|
+ else
|
|
+ retval = mGlobalTransferable.get();
|
|
+
|
|
+ return retval;
|
|
+}
|
|
+
|
|
+void
|
|
+nsClipboard::AddTarget(GdkAtom aName, GdkAtom aClipboard)
|
|
+{
|
|
+ gtk_selection_add_target(mWidget, aClipboard, aName, 0);
|
|
+}
|
|
+
|
|
+void
|
|
+nsClipboard::SelectionGetEvent (GtkWidget *aWidget,
|
|
+ GtkSelectionData *aSelectionData,
|
|
+ guint aTime)
|
|
+{
|
|
+ // Someone has asked us to hand them something. The first thing
|
|
+ // that we want to do is see if that something includes text. If
|
|
+ // it does, try to give it text/unicode after converting it to
|
|
+ // utf-8.
|
|
+
|
|
+ PRInt32 whichClipboard;
|
|
+
|
|
+ // which clipboard?
|
|
+ if (aSelectionData->selection == GDK_SELECTION_PRIMARY)
|
|
+ whichClipboard = kSelectionClipboard;
|
|
+ else if (aSelectionData->selection == GDK_SELECTION_CLIPBOARD)
|
|
+ whichClipboard = kGlobalClipboard;
|
|
+ else
|
|
+ return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
|
|
+
|
|
+ nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
|
|
+
|
|
+ nsresult rv;
|
|
+ nsCOMPtr<nsISupports> item;
|
|
+ PRUint32 len;
|
|
+
|
|
+ // Check to see if the selection data includes any of the string
|
|
+ // types that we support.
|
|
+ if (aSelectionData->target == gdk_atom_intern ("STRING", FALSE) ||
|
|
+ aSelectionData->target == gdk_atom_intern ("TEXT", FALSE) ||
|
|
+ aSelectionData->target == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
|
|
+ aSelectionData->target == gdk_atom_intern ("UTF8_STRING", FALSE)) {
|
|
+ // Try to convert our internal type into a text string. Get
|
|
+ // the transferable for this clipboard and try to get the
|
|
+ // text/unicode type for it.
|
|
+ rv = trans->GetTransferData("text/unicode", getter_AddRefs(item),
|
|
+ &len);
|
|
+ if (!item || NS_FAILED(rv))
|
|
+ return;
|
|
+
|
|
+ nsCOMPtr<nsISupportsString> wideString;
|
|
+ wideString = do_QueryInterface(item);
|
|
+ if (!wideString)
|
|
+ return;
|
|
+
|
|
+ nsAutoString ucs2string;
|
|
+ wideString->GetData(ucs2string);
|
|
+ char *utf8string = ToNewUTF8String(ucs2string);
|
|
+ if (!utf8string)
|
|
+ return;
|
|
+
|
|
+ gtk_selection_data_set_text (aSelectionData, utf8string,
|
|
+ strlen(utf8string));
|
|
+
|
|
+ nsMemory::Free(utf8string);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ // Try to match up the selection data target to something our
|
|
+ // transferable provides.
|
|
+ gchar *target_name = gdk_atom_name(aSelectionData->target);
|
|
+ if (!target_name)
|
|
+ return;
|
|
+
|
|
+ rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len);
|
|
+ // nothing found?
|
|
+ if (!item || NS_FAILED(rv)) {
|
|
+ g_free(target_name);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ void *primitive_data = nsnull;
|
|
+ nsPrimitiveHelpers::CreateDataFromPrimitive(target_name, item,
|
|
+ &primitive_data, len);
|
|
+
|
|
+ if (primitive_data) {
|
|
+ // Check to see if the selection data is text/html
|
|
+ if (aSelectionData->target == gdk_atom_intern (kHTMLMime, FALSE)) {
|
|
+ /*
|
|
+ * "text/html" can be encoded UCS2. It is recommended that
|
|
+ * documents transmitted as UCS2 always begin with a ZERO-WIDTH
|
|
+ * NON-BREAKING SPACE character (hexadecimal FEFF, also called
|
|
+ * Byte Order Mark (BOM)). Adding BOM can help other app to
|
|
+ * detect mozilla use UCS2 encoding when copy-paste.
|
|
+ */
|
|
+ guchar *buffer = (guchar *)
|
|
+ nsMemory::Alloc((len * sizeof(guchar)) + sizeof(PRUnichar));
|
|
+ if (!buffer)
|
|
+ return;
|
|
+ PRUnichar prefix = 0xFEFF;
|
|
+ memcpy(buffer, &prefix, sizeof(prefix));
|
|
+ memcpy(buffer + sizeof(prefix), primitive_data, len);
|
|
+ nsMemory::Free((guchar *)primitive_data);
|
|
+ primitive_data = (guchar *)buffer;
|
|
+ len += sizeof(prefix);
|
|
+ }
|
|
+
|
|
+ gtk_selection_data_set(aSelectionData, aSelectionData->target,
|
|
+ 8, /* 8 bits in a unit */
|
|
+ (const guchar *)primitive_data, len);
|
|
+ nsMemory::Free(primitive_data);
|
|
+ }
|
|
+
|
|
+ g_free(target_name);
|
|
+
|
|
+}
|
|
+
|
|
+void
|
|
+nsClipboard::SelectionClearEvent (GtkWidget *aWidget,
|
|
+ GdkEventSelection *aEvent)
|
|
+{
|
|
+ PRInt32 whichClipboard;
|
|
+
|
|
+ // which clipboard?
|
|
+ if (aEvent->selection == GDK_SELECTION_PRIMARY)
|
|
+ whichClipboard = kSelectionClipboard;
|
|
+ else if (aEvent->selection == GDK_SELECTION_CLIPBOARD)
|
|
+ whichClipboard = kGlobalClipboard;
|
|
+ else
|
|
+ return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
|
|
+
|
|
+ EmptyClipboard(whichClipboard);
|
|
+}
|
|
+
|
|
+void
|
|
+invisible_selection_get_cb (GtkWidget *aWidget,
|
|
+ GtkSelectionData *aSelectionData,
|
|
+ guint aTime,
|
|
+ guint aInfo,
|
|
+ nsClipboard *aClipboard)
|
|
+{
|
|
+ aClipboard->SelectionGetEvent(aWidget, aSelectionData, aTime);
|
|
+}
|
|
+
|
|
+gboolean
|
|
+selection_clear_event_cb (GtkWidget *aWidget,
|
|
+ GdkEventSelection *aEvent,
|
|
+ nsClipboard *aClipboard)
|
|
+{
|
|
+ aClipboard->SelectionClearEvent(aWidget, aEvent);
|
|
+ return TRUE;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * when copy-paste, mozilla wants data encoded using UCS2,
|
|
+ * other app such as StarOffice use "text/html"(RFC2854).
|
|
+ * This function convert data(got from GTK clipboard)
|
|
+ * to data mozilla wanted.
|
|
+ *
|
|
+ * data from GTK clipboard can be 3 forms:
|
|
+ * 1. From current mozilla
|
|
+ * "text/html", charset = utf-16
|
|
+ * 2. From old version mozilla or mozilla-based app
|
|
+ * content("body" only), charset = utf-16
|
|
+ * 3. From other app who use "text/html" when copy-paste
|
|
+ * "text/html", has "charset" info
|
|
+ *
|
|
+ * data : got from GTK clipboard
|
|
+ * dataLength: got from GTK clipboard
|
|
+ * body : pass to Mozilla
|
|
+ * bodyLength: pass to Mozilla
|
|
+ */
|
|
+void ConvertHTMLtoUCS2(guchar * data, PRInt32 dataLength,
|
|
+ PRUnichar** unicodeData, PRInt32& outUnicodeLen)
|
|
+{
|
|
+ nsCAutoString charset;
|
|
+ GetHTMLCharset(data, dataLength, charset);// get charset of HTML
|
|
+ if (charset.EqualsLiteral("UTF-16")) {//current mozilla
|
|
+ outUnicodeLen = (dataLength / 2) - 1;
|
|
+ *unicodeData = reinterpret_cast<PRUnichar*>
|
|
+ (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
|
|
+ sizeof(PRUnichar)));
|
|
+ if (*unicodeData) {
|
|
+ memcpy(*unicodeData, data + sizeof(PRUnichar),
|
|
+ outUnicodeLen * sizeof(PRUnichar));
|
|
+ (*unicodeData)[outUnicodeLen] = '\0';
|
|
+ }
|
|
+ } else if (charset.EqualsLiteral("UNKNOWN")) {
|
|
+ outUnicodeLen = 0;
|
|
+ return;
|
|
+ } else {
|
|
+ // app which use "text/html" to copy&paste
|
|
+ nsCOMPtr<nsIUnicodeDecoder> decoder;
|
|
+ nsresult rv;
|
|
+ // get the decoder
|
|
+ nsCOMPtr<nsICharsetConverterManager> ccm =
|
|
+ do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
|
|
+ if (NS_FAILED(rv)) {
|
|
+#ifdef DEBUG_CLIPBOARD
|
|
+ g_print(" can't get CHARSET CONVERTER MANAGER service\n");
|
|
+#endif
|
|
+ outUnicodeLen = 0;
|
|
+ return;
|
|
+ }
|
|
+ rv = ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(decoder));
|
|
+ if (NS_FAILED(rv)) {
|
|
+#ifdef DEBUG_CLIPBOARD
|
|
+ g_print(" get unicode decoder error\n");
|
|
+#endif
|
|
+ outUnicodeLen = 0;
|
|
+ return;
|
|
+ }
|
|
+ // converting
|
|
+ decoder->GetMaxLength((const char *)data, dataLength, &outUnicodeLen);
|
|
+ // |outUnicodeLen| is number of chars
|
|
+ if (outUnicodeLen) {
|
|
+ *unicodeData = reinterpret_cast<PRUnichar*>
|
|
+ (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
|
|
+ sizeof(PRUnichar)));
|
|
+ if (*unicodeData) {
|
|
+ PRInt32 numberTmp = dataLength;
|
|
+ decoder->Convert((const char *)data, &numberTmp,
|
|
+ *unicodeData, &outUnicodeLen);
|
|
+#ifdef DEBUG_CLIPBOARD
|
|
+ if (numberTmp != dataLength)
|
|
+ printf("didn't consume all the bytes\n");
|
|
+#endif
|
|
+ // null terminate. Convert() doesn't do it for us
|
|
+ (*unicodeData)[outUnicodeLen] = '\0';
|
|
+ }
|
|
+ } // if valid length
|
|
+ }
|
|
+}
|
|
+
|
|
+/*
|
|
+ * get "charset" information from clipboard data
|
|
+ * return value can be:
|
|
+ * 1. "UTF-16": mozilla or "text/html" with "charset=utf-16"
|
|
+ * 2. "UNKNOWN": mozilla can't detect what encode it use
|
|
+ * 3. other: "text/html" with other charset than utf-16
|
|
+ */
|
|
+void GetHTMLCharset(guchar * data, PRInt32 dataLength, nsCString& str)
|
|
+{
|
|
+ // if detect "FFFE" or "FEFF", assume UTF-16
|
|
+ PRUnichar* beginChar = (PRUnichar*)data;
|
|
+ if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
|
|
+ str.AssignLiteral("UTF-16");
|
|
+ return;
|
|
+ }
|
|
+ // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
|
|
+ const nsDependentCString htmlStr((const char *)data, dataLength);
|
|
+ nsACString::const_iterator start, end;
|
|
+ htmlStr.BeginReading(start);
|
|
+ htmlStr.EndReading(end);
|
|
+ nsACString::const_iterator valueStart(start), valueEnd(start);
|
|
+
|
|
+ if (CaseInsensitiveFindInReadable(
|
|
+ NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
|
|
+ start, end)) {
|
|
+ start = end;
|
|
+ htmlStr.EndReading(end);
|
|
+
|
|
+ if (CaseInsensitiveFindInReadable(
|
|
+ NS_LITERAL_CSTRING("charset="),
|
|
+ start, end)) {
|
|
+ valueStart = end;
|
|
+ start = end;
|
|
+ htmlStr.EndReading(end);
|
|
+
|
|
+ if (FindCharInReadable('"', start, end))
|
|
+ valueEnd = start;
|
|
+ }
|
|
+ }
|
|
+ // find "charset" in HTML
|
|
+ if (valueStart != valueEnd) {
|
|
+ str = Substring(valueStart, valueEnd);
|
|
+ ToUpperCase(str);
|
|
+#ifdef DEBUG_CLIPBOARD
|
|
+ printf("Charset of HTML = %s\n", charsetUpperStr.get());
|
|
+#endif
|
|
+ return;
|
|
+ }
|
|
+ str.AssignLiteral("UNKNOWN");
|
|
+}
|
|
+
|
|
+static void
|
|
+DispatchSelectionNotifyEvent(GtkWidget *widget, XEvent *xevent)
|
|
+{
|
|
+ GdkEvent event;
|
|
+ event.selection.type = GDK_SELECTION_NOTIFY;
|
|
+ event.selection.window = widget->window;
|
|
+ event.selection.selection = gdk_x11_xatom_to_atom(xevent->xselection.selection);
|
|
+ event.selection.target = gdk_x11_xatom_to_atom(xevent->xselection.target);
|
|
+ event.selection.property = gdk_x11_xatom_to_atom(xevent->xselection.property);
|
|
+ event.selection.time = xevent->xselection.time;
|
|
+
|
|
+ gtk_widget_event(widget, &event);
|
|
+}
|
|
+
|
|
+static void
|
|
+DispatchPropertyNotifyEvent(GtkWidget *widget, XEvent *xevent)
|
|
+{
|
|
+ if (((GdkWindowObject *) widget->window)->event_mask & GDK_PROPERTY_CHANGE_MASK) {
|
|
+ GdkEvent event;
|
|
+ event.property.type = GDK_PROPERTY_NOTIFY;
|
|
+ event.property.window = widget->window;
|
|
+ event.property.atom = gdk_x11_xatom_to_atom(xevent->xproperty.atom);
|
|
+ event.property.time = xevent->xproperty.time;
|
|
+ event.property.state = xevent->xproperty.state;
|
|
+
|
|
+ gtk_widget_event(widget, &event);
|
|
+ }
|
|
+}
|
|
+
|
|
+struct checkEventContext
|
|
+{
|
|
+ GtkWidget *cbWidget;
|
|
+ Atom selAtom;
|
|
+};
|
|
+
|
|
+static Bool
|
|
+checkEventProc(Display *display, XEvent *event, XPointer arg)
|
|
+{
|
|
+ checkEventContext *context = (checkEventContext *) arg;
|
|
+
|
|
+ if (event->xany.type == SelectionNotify ||
|
|
+ (event->xany.type == PropertyNotify &&
|
|
+ event->xproperty.atom == context->selAtom)) {
|
|
+
|
|
+ GdkWindow *cbWindow = gdk_window_lookup(event->xany.window);
|
|
+ if (cbWindow) {
|
|
+ GtkWidget *cbWidget = NULL;
|
|
+ gdk_window_get_user_data(cbWindow, (gpointer *)&cbWidget);
|
|
+ if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
|
|
+ context->cbWidget = cbWidget;
|
|
+ return True;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return False;
|
|
+}
|
|
+
|
|
+// Idle timeout for receiving selection and property notify events (microsec)
|
|
+static const int kClipboardTimeout = 500000;
|
|
+
|
|
+static void
|
|
+wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *r_context)
|
|
+{
|
|
+ if (r_context->completed) // the request completed synchronously
|
|
+ return;
|
|
+
|
|
+ Display *xDisplay = GDK_DISPLAY();
|
|
+ checkEventContext context;
|
|
+ context.cbWidget = NULL;
|
|
+ context.selAtom = gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION",
|
|
+ FALSE));
|
|
+
|
|
+ // Send X events which are relevant to the ongoing selection retrieval
|
|
+ // to the clipboard widget. Wait until either the operation completes, or
|
|
+ // we hit our timeout. All other X events remain queued.
|
|
+
|
|
+ int select_result;
|
|
+
|
|
+#ifdef POLL_WITH_XCONNECTIONNUMBER
|
|
+ struct pollfd fds[1];
|
|
+ fds[0].fd = XConnectionNumber(xDisplay);
|
|
+ fds[0].events = POLLIN;
|
|
+#else
|
|
+ int cnumber = ConnectionNumber(xDisplay);
|
|
+ fd_set select_set;
|
|
+ FD_ZERO(&select_set);
|
|
+ FD_SET(cnumber, &select_set);
|
|
+ ++cnumber;
|
|
+ struct timeval tv;
|
|
+#endif
|
|
+
|
|
+ do {
|
|
+ XEvent xevent;
|
|
+
|
|
+ while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
|
|
+ (XPointer) &context)) {
|
|
+
|
|
+ if (xevent.xany.type == SelectionNotify)
|
|
+ DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
|
|
+ else
|
|
+ DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
|
|
+
|
|
+ if (r_context->completed)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+#ifdef POLL_WITH_XCONNECTIONNUMBER
|
|
+ select_result = poll(fds, 1, kClipboardTimeout / 1000);
|
|
+#else
|
|
+ tv.tv_sec = 0;
|
|
+ tv.tv_usec = kClipboardTimeout;
|
|
+ select_result = select(cnumber, &select_set, NULL, NULL, &tv);
|
|
+#endif
|
|
+ } while (select_result == 1);
|
|
+
|
|
+#ifdef DEBUG_CLIPBOARD
|
|
+ printf("exceeded clipboard timeout\n");
|
|
+#endif
|
|
+}
|
|
+
|
|
+static void
|
|
+clipboard_contents_received(GtkClipboard *clipboard,
|
|
+ GtkSelectionData *selection_data,
|
|
+ gpointer data)
|
|
+{
|
|
+ retrieval_context *context = static_cast<retrieval_context *>(data);
|
|
+ context->completed = PR_TRUE;
|
|
+
|
|
+ if (selection_data->length >= 0)
|
|
+ context->data = gtk_selection_data_copy(selection_data);
|
|
+}
|
|
+
|
|
+
|
|
+static GtkSelectionData *
|
|
+wait_for_contents(GtkClipboard *clipboard, GdkAtom target)
|
|
+{
|
|
+ retrieval_context context;
|
|
+ gtk_clipboard_request_contents(clipboard, target,
|
|
+ clipboard_contents_received,
|
|
+ &context);
|
|
+
|
|
+ wait_for_retrieval(clipboard, &context);
|
|
+ return static_cast<GtkSelectionData *>(context.data);
|
|
+}
|
|
+
|
|
+static void
|
|
+clipboard_text_received(GtkClipboard *clipboard,
|
|
+ const gchar *text,
|
|
+ gpointer data)
|
|
+{
|
|
+ retrieval_context *context = static_cast<retrieval_context *>(data);
|
|
+ context->completed = PR_TRUE;
|
|
+ context->data = g_strdup(text);
|
|
+}
|
|
+
|
|
+static gchar *
|
|
+wait_for_text(GtkClipboard *clipboard)
|
|
+{
|
|
+ retrieval_context context;
|
|
+ gtk_clipboard_request_text(clipboard, clipboard_text_received, &context);
|
|
+
|
|
+ wait_for_retrieval(clipboard, &context);
|
|
+ return static_cast<gchar *>(context.data);
|
|
+}
|
|
Index: offscreen/widget/src/headless/nsClipboard.h
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ offscreen/widget/src/headless/nsClipboard.h 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -0,0 +1,93 @@
|
|
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
+/* vim:expandtab:shiftwidth=4:tabstop=4:
|
|
+ */
|
|
+/* ***** BEGIN LICENSE BLOCK *****
|
|
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
+ *
|
|
+ * The contents of this file are subject to the Mozilla 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/MPL/
|
|
+ *
|
|
+ * 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 Christopher Blizzard
|
|
+ * <blizzard@mozilla.org>. Portions created by the Initial Developer
|
|
+ * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
|
|
+ *
|
|
+ * Contributor(s):
|
|
+ *
|
|
+ * Alternatively, the contents of this file may be used under the terms of
|
|
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
+ * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
+ * of those above. If you wish to allow use of your version of this file only
|
|
+ * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
+ * use your version of this file under the terms of the MPL, indicate your
|
|
+ * decision by deleting the provisions above and replace them with the notice
|
|
+ * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
+ * the provisions above, a recipient may use your version of this file under
|
|
+ * the terms of any one of the MPL, the GPL or the LGPL.
|
|
+ *
|
|
+ * ***** END LICENSE BLOCK ***** */
|
|
+
|
|
+#ifndef __nsClipboard_h_
|
|
+#define __nsClipboard_h_
|
|
+
|
|
+#include "nsIClipboard.h"
|
|
+#include "nsClipboardPrivacyHandler.h"
|
|
+#include "nsAutoPtr.h"
|
|
+#include <gtk/gtk.h>
|
|
+
|
|
+class nsClipboard : public nsIClipboard
|
|
+{
|
|
+public:
|
|
+ nsClipboard();
|
|
+ virtual ~nsClipboard();
|
|
+
|
|
+ NS_DECL_ISUPPORTS
|
|
+
|
|
+ NS_DECL_NSICLIPBOARD
|
|
+
|
|
+ // Make sure we are initialized, called from the factory
|
|
+ // constructor
|
|
+ nsresult Init (void);
|
|
+ // Someone requested the selection from the hidden widget
|
|
+ void SelectionGetEvent (GtkWidget *aWidget,
|
|
+ GtkSelectionData *aSelectionData,
|
|
+ guint aTime);
|
|
+ void SelectionClearEvent (GtkWidget *aWidget,
|
|
+ GdkEventSelection *aEvent);
|
|
+
|
|
+
|
|
+private:
|
|
+ // Utility methods
|
|
+ static GdkAtom GetSelectionAtom (PRInt32 aWhichClipboard);
|
|
+ static GtkSelectionData *GetTargets (GdkAtom aWhichClipboard);
|
|
+
|
|
+ // Get our hands on the correct transferable, given a specific
|
|
+ // clipboard
|
|
+ nsITransferable *GetTransferable (PRInt32 aWhichClipboard);
|
|
+
|
|
+ // Add a target type to the hidden widget
|
|
+ void AddTarget (GdkAtom aName,
|
|
+ GdkAtom aClipboard);
|
|
+
|
|
+ // The hidden widget where we do all of our operations
|
|
+ GtkWidget *mWidget;
|
|
+ // Hang on to our owners and transferables so we can transfer data
|
|
+ // when asked.
|
|
+ nsCOMPtr<nsIClipboardOwner> mSelectionOwner;
|
|
+ nsCOMPtr<nsIClipboardOwner> mGlobalOwner;
|
|
+ nsCOMPtr<nsITransferable> mSelectionTransferable;
|
|
+ nsCOMPtr<nsITransferable> mGlobalTransferable;
|
|
+ nsRefPtr<nsClipboardPrivacyHandler> mPrivacyHandler;
|
|
+
|
|
+};
|
|
+
|
|
+#endif /* __nsClipboard_h_ */
|
|
Index: offscreen/widget/src/headless/nsIImageToPixbuf.h
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ offscreen/widget/src/headless/nsIImageToPixbuf.h 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -0,0 +1,62 @@
|
|
+/* ***** BEGIN LICENSE BLOCK *****
|
|
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
+ *
|
|
+ * The contents of this file are subject to the Mozilla 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/MPL/
|
|
+ *
|
|
+ * 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 widget code.
|
|
+ *
|
|
+ * The Initial Developer of the Original Code is
|
|
+ * Christian Biesinger <cbiesinger@web.de>.
|
|
+ * Portions created by the Initial Developer are Copyright (C) 2006
|
|
+ * the Initial Developer. All Rights Reserved.
|
|
+ *
|
|
+ * Contributor(s):
|
|
+ *
|
|
+ * Alternatively, the contents of this file may be used under the terms of
|
|
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
+ * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
+ * of those above. If you wish to allow use of your version of this file only
|
|
+ * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
+ * use your version of this file under the terms of the MPL, indicate your
|
|
+ * decision by deleting the provisions above and replace them with the notice
|
|
+ * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
+ * the provisions above, a recipient may use your version of this file under
|
|
+ * the terms of any one of the MPL, the GPL or the LGPL.
|
|
+ *
|
|
+ * ***** END LICENSE BLOCK ***** */
|
|
+
|
|
+#ifndef NSIIMAGETOPIXBUF_H_
|
|
+#define NSIIMAGETOPIXBUF_H_
|
|
+
|
|
+#include "nsISupports.h"
|
|
+
|
|
+// dfa4ac93-83f2-4ab8-9b2a-0ff7022aebe2
|
|
+#define NSIIMAGETOPIXBUF_IID \
|
|
+{ 0xdfa4ac93, 0x83f2, 0x4ab8, \
|
|
+ { 0x9b, 0x2a, 0x0f, 0xf7, 0x02, 0x2a, 0xeb, 0xe2 } }
|
|
+
|
|
+class nsIImage;
|
|
+typedef struct _GdkPixbuf GdkPixbuf;
|
|
+
|
|
+/**
|
|
+ * An interface that allows converting an nsIImage to a GdkPixbuf*.
|
|
+ */
|
|
+class nsIImageToPixbuf : public nsISupports {
|
|
+ public:
|
|
+ NS_DECLARE_STATIC_IID_ACCESSOR(NSIIMAGETOPIXBUF_IID)
|
|
+
|
|
+ NS_IMETHOD_(GdkPixbuf*) ConvertImageToPixbuf(nsIImage* aImage) = 0;
|
|
+};
|
|
+
|
|
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIImageToPixbuf, NSIIMAGETOPIXBUF_IID)
|
|
+
|
|
+#endif
|
|
Index: offscreen/widget/src/headless/nsImageToPixbuf.cpp
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ offscreen/widget/src/headless/nsImageToPixbuf.cpp 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -0,0 +1,196 @@
|
|
+/* vim:set sw=4 sts=4 et cin: */
|
|
+/* ***** BEGIN LICENSE BLOCK *****
|
|
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
+ *
|
|
+ * The contents of this file are subject to the Mozilla 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/MPL/
|
|
+ *
|
|
+ * 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 widget code.
|
|
+ *
|
|
+ * The Initial Developer of the Original Code is
|
|
+ * Christian Biesinger <cbiesinger@web.de>.
|
|
+ * Portions created by the Initial Developer are Copyright (C) 2006
|
|
+ * the Initial Developer. All Rights Reserved.
|
|
+ *
|
|
+ * Contributor(s):
|
|
+ *
|
|
+ * Alternatively, the contents of this file may be used under the terms of
|
|
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
+ * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
+ * of those above. If you wish to allow use of your version of this file only
|
|
+ * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
+ * use your version of this file under the terms of the MPL, indicate your
|
|
+ * decision by deleting the provisions above and replace them with the notice
|
|
+ * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
+ * the provisions above, a recipient may use your version of this file under
|
|
+ * the terms of any one of the MPL, the GPL or the LGPL.
|
|
+ *
|
|
+ * ***** END LICENSE BLOCK ***** */
|
|
+
|
|
+#include <gdk-pixbuf/gdk-pixbuf.h>
|
|
+
|
|
+#include "gfxASurface.h"
|
|
+#include "gfxImageSurface.h"
|
|
+#include "gfxContext.h"
|
|
+
|
|
+#include "nsIImage.h"
|
|
+
|
|
+#include "nsAutoPtr.h"
|
|
+
|
|
+#include "nsImageToPixbuf.h"
|
|
+
|
|
+NS_IMPL_ISUPPORTS1(nsImageToPixbuf, nsIImageToPixbuf)
|
|
+
|
|
+inline unsigned char
|
|
+unpremultiply (unsigned char color,
|
|
+ unsigned char alpha)
|
|
+{
|
|
+ if (alpha == 0)
|
|
+ return 0;
|
|
+ // plus alpha/2 to round instead of truncate
|
|
+ return (color * 255 + alpha / 2) / alpha;
|
|
+}
|
|
+
|
|
+NS_IMETHODIMP_(GdkPixbuf*)
|
|
+nsImageToPixbuf::ConvertImageToPixbuf(nsIImage* aImage)
|
|
+{
|
|
+ return ImageToPixbuf(aImage);
|
|
+}
|
|
+
|
|
+GdkPixbuf*
|
|
+nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
|
|
+{
|
|
+ PRInt32 width = aImage->GetWidth(),
|
|
+ height = aImage->GetHeight();
|
|
+
|
|
+ nsRefPtr<gfxPattern> pattern;
|
|
+ aImage->GetPattern(getter_AddRefs(pattern));
|
|
+
|
|
+ return PatternToPixbuf(pattern, width, height);
|
|
+}
|
|
+
|
|
+GdkPixbuf*
|
|
+nsImageToPixbuf::ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface, PRInt32 aWidth, PRInt32 aHeight)
|
|
+{
|
|
+ GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, PR_TRUE, 8,
|
|
+ aWidth, aHeight);
|
|
+ if (!pixbuf)
|
|
+ return nsnull;
|
|
+
|
|
+ PRUint32 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
|
|
+ guchar* pixels = gdk_pixbuf_get_pixels (pixbuf);
|
|
+
|
|
+ long cairoStride = aImgSurface->Stride();
|
|
+ unsigned char* cairoData = aImgSurface->Data();
|
|
+
|
|
+ gfxASurface::gfxImageFormat format = aImgSurface->Format();
|
|
+
|
|
+ for (PRInt32 row = 0; row < aHeight; ++row) {
|
|
+ for (PRInt32 col = 0; col < aWidth; ++col) {
|
|
+ guchar* pixel = pixels + row * rowstride + 4 * col;
|
|
+
|
|
+ PRUint32* cairoPixel = reinterpret_cast<PRUint32*>
|
|
+ ((cairoData + row * cairoStride + 4 * col));
|
|
+
|
|
+ if (format == gfxASurface::ImageFormatARGB32) {
|
|
+ const PRUint8 a = (*cairoPixel >> 24) & 0xFF;
|
|
+ const PRUint8 r = unpremultiply((*cairoPixel >> 16) & 0xFF, a);
|
|
+ const PRUint8 g = unpremultiply((*cairoPixel >> 8) & 0xFF, a);
|
|
+ const PRUint8 b = unpremultiply((*cairoPixel >> 0) & 0xFF, a);
|
|
+
|
|
+ *pixel++ = r;
|
|
+ *pixel++ = g;
|
|
+ *pixel++ = b;
|
|
+ *pixel++ = a;
|
|
+ } else {
|
|
+ NS_ASSERTION(format == gfxASurface::ImageFormatRGB24,
|
|
+ "unexpected format");
|
|
+ const PRUint8 r = (*cairoPixel >> 16) & 0xFF;
|
|
+ const PRUint8 g = (*cairoPixel >> 8) & 0xFF;
|
|
+ const PRUint8 b = (*cairoPixel >> 0) & 0xFF;
|
|
+
|
|
+ *pixel++ = r;
|
|
+ *pixel++ = g;
|
|
+ *pixel++ = b;
|
|
+ *pixel++ = 0xFF; // A
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return pixbuf;
|
|
+}
|
|
+
|
|
+GdkPixbuf*
|
|
+nsImageToPixbuf::SurfaceToPixbuf(gfxASurface* aSurface, PRInt32 aWidth, PRInt32 aHeight)
|
|
+{
|
|
+ if (aSurface->CairoStatus()) {
|
|
+ NS_ERROR("invalid surface");
|
|
+ return nsnull;
|
|
+ }
|
|
+
|
|
+ nsRefPtr<gfxImageSurface> imgSurface;
|
|
+ if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
|
|
+ imgSurface = static_cast<gfxImageSurface*>
|
|
+ (static_cast<gfxASurface*>(aSurface));
|
|
+ } else {
|
|
+ imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
|
|
+ gfxImageSurface::ImageFormatARGB32);
|
|
+
|
|
+ if (!imgSurface)
|
|
+ return nsnull;
|
|
+
|
|
+ nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
|
|
+ if (!context)
|
|
+ return nsnull;
|
|
+
|
|
+ context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
+ context->SetSource(aSurface);
|
|
+ context->Paint();
|
|
+ }
|
|
+
|
|
+ return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
|
|
+}
|
|
+
|
|
+GdkPixbuf*
|
|
+nsImageToPixbuf::PatternToPixbuf(gfxPattern* aPattern, PRInt32 aWidth, PRInt32 aHeight)
|
|
+{
|
|
+ if (aPattern->CairoStatus()) {
|
|
+ NS_ERROR("invalid pattern");
|
|
+ return nsnull;
|
|
+ }
|
|
+
|
|
+ nsRefPtr<gfxImageSurface> imgSurface;
|
|
+ if (aPattern->GetType() == gfxPattern::PATTERN_SURFACE) {
|
|
+ nsRefPtr<gfxASurface> surface = aPattern->GetSurface();
|
|
+ if (surface->GetType() == gfxASurface::SurfaceTypeImage) {
|
|
+ imgSurface = static_cast<gfxImageSurface*>
|
|
+ (static_cast<gfxASurface*>(surface.get()));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (!imgSurface) {
|
|
+ imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
|
|
+ gfxImageSurface::ImageFormatARGB32);
|
|
+
|
|
+ if (!imgSurface)
|
|
+ return nsnull;
|
|
+
|
|
+ nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
|
|
+ if (!context)
|
|
+ return nsnull;
|
|
+
|
|
+ context->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
+ context->SetPattern(aPattern);
|
|
+ context->Paint();
|
|
+ }
|
|
+
|
|
+ return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
|
|
+}
|
|
Index: offscreen/widget/src/headless/nsImageToPixbuf.h
|
|
===================================================================
|
|
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
|
|
+++ offscreen/widget/src/headless/nsImageToPixbuf.h 2009-06-12 14:14:05.000000000 +0100
|
|
@@ -0,0 +1,71 @@
|
|
+/* vim:set sw=4 sts=4 et cin: */
|
|
+/* ***** BEGIN LICENSE BLOCK *****
|
|
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
+ *
|
|
+ * The contents of this file are subject to the Mozilla 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/MPL/
|
|
+ *
|
|
+ * 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 widget code.
|
|
+ *
|
|
+ * The Initial Developer of the Original Code is
|
|
+ * Christian Biesinger <cbiesinger@web.de>.
|
|
+ * Portions created by the Initial Developer are Copyright (C) 2006
|
|
+ * the Initial Developer. All Rights Reserved.
|
|
+ *
|
|
+ * Contributor(s):
|
|
+ *
|
|
+ * Alternatively, the contents of this file may be used under the terms of
|
|
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
+ * in which case the provisions of the GPL or the LGPL are applicable instead
|
|
+ * of those above. If you wish to allow use of your version of this file only
|
|
+ * under the terms of either the GPL or the LGPL, and not to allow others to
|
|
+ * use your version of this file under the terms of the MPL, indicate your
|
|
+ * decision by deleting the provisions above and replace them with the notice
|
|
+ * and other provisions required by the GPL or the LGPL. If you do not delete
|
|
+ * the provisions above, a recipient may use your version of this file under
|
|
+ * the terms of any one of the MPL, the GPL or the LGPL.
|
|
+ *
|
|
+ * ***** END LICENSE BLOCK ***** */
|
|
+
|
|
+#ifndef NSIMAGETOPIXBUF_H_
|
|
+#define NSIMAGETOPIXBUF_H_
|
|
+
|
|
+#include "nsIImageToPixbuf.h"
|
|
+
|
|
+class gfxASurface;
|
|
+class gfxPattern;
|
|
+class gfxImageSurface;
|
|
+
|
|
+class nsImageToPixbuf : public nsIImageToPixbuf {
|
|
+ public:
|
|
+ NS_DECL_ISUPPORTS
|
|
+ NS_IMETHOD_(GdkPixbuf*) ConvertImageToPixbuf(nsIImage* aImage);
|
|
+
|
|
+ // Friendlier version of ConvertImageToPixbuf for callers inside of
|
|
+ // widget
|
|
+ static GdkPixbuf* ImageToPixbuf(nsIImage* aImage);
|
|
+ static GdkPixbuf* SurfaceToPixbuf(gfxASurface* aSurface,
|
|
+ PRInt32 aWidth, PRInt32 aHeight);
|
|
+ static GdkPixbuf* PatternToPixbuf(gfxPattern* aPattern,
|
|
+ PRInt32 aWidth, PRInt32 aHeight);
|
|
+ private:
|
|
+ static GdkPixbuf* ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface,
|
|
+ PRInt32 aWidth, PRInt32 aHeight);
|
|
+ ~nsImageToPixbuf() {}
|
|
+};
|
|
+
|
|
+
|
|
+// fc2389b8-c650-4093-9e42-b05e5f0685b7
|
|
+#define NS_IMAGE_TO_PIXBUF_CID \
|
|
+{ 0xfc2389b8, 0xc650, 0x4093, \
|
|
+ { 0x9e, 0x42, 0xb0, 0x5e, 0x5f, 0x06, 0x85, 0xb7 } }
|
|
+
|
|
+#endif
|
|
Index: offscreen/widget/src/headless/nsWidgetFactory.cpp
|
|
===================================================================
|
|
--- offscreen.orig/widget/src/headless/nsWidgetFactory.cpp 2009-06-12 14:08:56.000000000 +0100
|
|
+++ offscreen/widget/src/headless/nsWidgetFactory.cpp 2009-06-12 14:15:24.000000000 +0100
|
|
@@ -46,6 +46,10 @@
|
|
#include "nsWindow.h"
|
|
#include "nsTransferable.h"
|
|
#include "nsHTMLFormatConverter.h"
|
|
+#ifdef MOZ_X11
|
|
+#include "nsClipboardHelper.h"
|
|
+#include "nsClipboard.h"
|
|
+#endif
|
|
#include "nsSound.h"
|
|
#include "nsBidiKeyboard.h"
|
|
#include "nsScreenManagerHeadless.h"
|
|
@@ -66,6 +70,10 @@
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
|
|
+#ifdef MOZ_X11
|
|
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
|
|
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init)
|
|
+#endif
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
|
|
NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerHeadless)
|
|
//NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
|
|
@@ -142,6 +150,16 @@
|
|
NS_TRANSFERABLE_CID,
|
|
"@mozilla.org/widget/transferable;1",
|
|
nsTransferableConstructor },
|
|
+#ifdef MOZ_X11
|
|
+ { "Gtk Clipboard",
|
|
+ NS_CLIPBOARD_CID,
|
|
+ "@mozilla.org/widget/clipboard;1",
|
|
+ nsClipboardConstructor },
|
|
+ { "Clipboard Helper",
|
|
+ NS_CLIPBOARDHELPER_CID,
|
|
+ "@mozilla.org/widget/clipboardhelper;1",
|
|
+ nsClipboardHelperConstructor },
|
|
+#endif
|
|
{ "HTML Format Converter",
|
|
NS_HTMLFORMATCONVERTER_CID,
|
|
"@mozilla.org/widget/htmlformatconverter;1",
|