Bug 875753 - Color input: Gtk widget. r=karlt
authorArnaud Bienner <arnaud.bienner@gmail.com>
Sat, 22 Jun 2013 15:39:43 +0200
changeset 139951 63e6f23138febde6bba10aa20858b36c163a3f7c
parent 139950 e499b8afbdc620dd5b261df4c78b8c3c728a3525
child 139952 ed851bbfe99730b15023535b07b3eaddb32648b4
push id31539
push userryanvm@gmail.com
push dateThu, 25 Jul 2013 13:34:21 +0000
treeherdermozilla-inbound@658e5f8a8daf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskarlt
bugs875753
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 875753 - Color input: Gtk widget. r=karlt
gfx/src/nsColor.cpp
gfx/src/nsColor.h
widget/gtk2/compat/gtk/gtkcolorselectiondialog.h
widget/gtk2/moz.build
widget/gtk2/nsColorPicker.cpp
widget/gtk2/nsColorPicker.h
widget/gtk2/nsDragService.cpp
widget/gtk2/nsFilePicker.cpp
widget/gtk2/nsGtkUtils.h
widget/gtk2/nsWidgetFactory.cpp
widget/gtk2/nsWindow.cpp
--- a/gfx/src/nsColor.cpp
+++ b/gfx/src/nsColor.cpp
@@ -86,20 +86,20 @@ static int ComponentValue(const PRUnicha
     }
     else {  // not a hex digit, treat it like 0
       component = (component * 16);
     }
   }
   return component;
 }
 
-NS_GFX_(bool) NS_HexToRGB(const nsString& aColorSpec,
+NS_GFX_(bool) NS_HexToRGB(const nsAString& aColorSpec,
                                        nscolor* aResult)
 {
-  const PRUnichar* buffer = aColorSpec.get();
+  const PRUnichar* buffer = aColorSpec.BeginReading();
 
   int nameLen = aColorSpec.Length();
   if ((nameLen == 3) || (nameLen == 6)) {
     // Make sure the digits are legal
     for (int i = 0; i < nameLen; i++) {
       PRUnichar ch = buffer[i];
       if (((ch >= '0') && (ch <= '9')) ||
           ((ch >= 'a') && (ch <= 'f')) ||
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -43,17 +43,17 @@ typedef uint32_t nscolor;
   PR_BEGIN_MACRO                                   \
     unsigned tmp_ = v;                             \
     target = ((tmp_ << 8) + tmp_ + 255) >> 16;     \
   PR_END_MACRO
 
 // Translate a hex string to a color. Return true if it parses ok,
 // otherwise return false.
 // This accepts only 3 or 6 digits
-NS_GFX_(bool) NS_HexToRGB(const nsString& aBuf, nscolor* aResult);
+NS_GFX_(bool) NS_HexToRGB(const nsAString& aBuf, nscolor* aResult);
 
 // Compose one NS_RGB color onto another. The result is what
 // you get if you draw aFG on top of aBG with operator OVER.
 NS_GFX_(nscolor) NS_ComposeColors(nscolor aBG, nscolor aFG);
 
 // Translate a hex string to a color. Return true if it parses ok,
 // otherwise return false.
 // This version accepts 1 to 9 digits (missing digits are 0)
new file mode 100644
--- /dev/null
+++ b/widget/gtk2/compat/gtk/gtkcolorselectiondialog.h
@@ -0,0 +1,17 @@
+#ifndef GTKCOLORSELECTIONDIALOG_WRAPPER_H
+#define GTKCOLORSELECTIONDIALOG_WRAPPER_H
+
+#include_next <gtk/gtkselection.h>
+#include <gtk/gtkversion.h>
+
+#if !GTK_CHECK_VERSION(2, 14, 0)
+static inline GtkWidget*
+gtk_color_selection_dialog_get_color_selection(GtkColorSelectionDialog* colorseldialog)
+{
+  GtkWidget* colorsel;
+  g_object_get(colorseldialog, "color-selection", &colorsel, NULL);
+  return colorsel;
+}
+#endif // GTK_CHECK_VERSION
+
+#endif // GTKCOLORSELECTIONDIALOG_WRAPPER_H
--- a/widget/gtk2/moz.build
+++ b/widget/gtk2/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'nsGTKToolkit.h',
     'nsIImageToPixbuf.h',
 ]
 
 CPP_SOURCES += [
     'WidgetTraceEvent.cpp',
     'nsAppShell.cpp',
     'nsBidiKeyboard.cpp',
+    'nsColorPicker.cpp',
     'nsFilePicker.cpp',
     'nsGtkIMModule.cpp',
     'nsGtkKeyUtils.cpp',
     'nsImageToPixbuf.cpp',
     'nsLookAndFeel.cpp',
     'nsNativeKeyBindings.cpp',
     'nsNativeThemeGTK.cpp',
     'nsScreenGtk.cpp',
new file mode 100644
--- /dev/null
+++ b/widget/gtk2/nsColorPicker.cpp
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <gtk/gtk.h>
+
+#include "mozilla/Util.h"
+#include "nsColor.h"
+#include "nsColorPicker.h"
+#include "nsGtkUtils.h"
+#include "nsIWidget.h"
+#include "WidgetUtils.h"
+
+NS_IMPL_ISUPPORTS1(nsColorPicker, nsIColorPicker)
+
+int nsColorPicker::convertGdkColorComponent(guint16 color_component) {
+  // GdkColor value is in range [0..65535]. We need something in range [0..255]
+  return (int(color_component)*255 + 127)/65535;
+}
+
+guint16 nsColorPicker::convertToGdkColorComponent(int color_component) {
+  return color_component*65535/255;
+}
+
+GdkColor nsColorPicker::convertToGdkColor(nscolor color) {
+  GdkColor result = { 0 /* obsolete, unused 'pixel' value */,
+      convertToGdkColorComponent(NS_GET_R(color)),
+      convertToGdkColorComponent(NS_GET_G(color)),
+      convertToGdkColorComponent(NS_GET_B(color)) };
+
+  return result;
+}
+
+/* void init (in nsIDOMWindow parent, in AString title, in short mode); */
+NS_IMETHODIMP nsColorPicker::Init(nsIDOMWindow *parent,
+                                  const nsAString& title,
+                                  const nsAString& initialColor)
+{
+  // Input color string should be 7 length (i.e. a string representing a valid
+  // simple color)
+  if (initialColor.Length() != 7) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  const nsAString& withoutHash  = StringTail(initialColor, 6);
+  nscolor color;
+  if (!NS_HexToRGB(withoutHash, &color)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mDefaultColor = convertToGdkColor(color);
+
+  mParentWidget = mozilla::widget::WidgetUtils::DOMWindowToWidget(parent);
+  mTitle.Assign(title);
+
+  return NS_OK;
+}
+
+/* void open (in nsIColorPickerShownCallback aColorPickerShownCallback); */
+NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShownCallback)
+{
+  if (mCallback) {
+    // It means Open has already been called: this is not allowed
+    NS_WARNING("mCallback is already set. Open called twice?");
+    return NS_ERROR_FAILURE;
+  }
+  mCallback = aColorPickerShownCallback;
+
+  nsXPIDLCString title;
+  title.Adopt(ToNewUTF8String(mTitle));
+  GtkWidget *color_chooser = gtk_color_selection_dialog_new(title);
+
+  GtkWindow *parent_window = GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
+  if (parent_window) {
+    GtkWindow *window = GTK_WINDOW(color_chooser);
+    gtk_window_set_transient_for(window, parent_window);
+    gtk_window_set_destroy_with_parent(window, TRUE);
+  }
+
+  gtk_color_selection_set_current_color(
+      GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(
+        GTK_COLOR_SELECTION_DIALOG(color_chooser))),
+      &mDefaultColor);
+
+  NS_ADDREF_THIS();
+  g_signal_connect(color_chooser, "response", G_CALLBACK(OnResponse), this);
+  g_signal_connect(color_chooser, "destroy", G_CALLBACK(OnDestroy), this);
+  gtk_widget_show(color_chooser);
+
+  return NS_OK;
+}
+
+/* static */ void
+nsColorPicker::OnResponse(GtkWidget* color_chooser, gint response_id,
+                          gpointer user_data)
+{
+  static_cast<nsColorPicker*>(user_data)->
+    Done(color_chooser, response_id);
+}
+
+/* static */ void
+nsColorPicker::OnDestroy(GtkWidget* color_chooser, gpointer user_data)
+{
+  static_cast<nsColorPicker*>(user_data)->
+    Done(color_chooser, GTK_RESPONSE_CANCEL);
+}
+
+void
+nsColorPicker::Done(GtkWidget* color_chooser, gint response)
+{
+  switch (response) {
+    case GTK_RESPONSE_OK:
+    case GTK_RESPONSE_ACCEPT:
+      ReadValueFromColorChooser(color_chooser);
+      break;
+    case GTK_RESPONSE_CANCEL:
+    case GTK_RESPONSE_CLOSE:
+    case GTK_RESPONSE_DELETE_EVENT:
+      break;
+    default:
+      NS_WARNING("Unexpected response");
+      break;
+  }
+
+  // A "response" signal won't be sent again but "destroy" will be.
+  g_signal_handlers_disconnect_by_func(color_chooser,
+                                       FuncToGpointer(OnDestroy), this);
+
+  gtk_widget_destroy(color_chooser);
+  if (mCallback) {
+    mCallback->Done(mColor);
+    mCallback = nullptr;
+  }
+
+  NS_RELEASE_THIS();
+}
+
+nsString nsColorPicker::ToHexString(int n)
+{
+  nsString result;
+  if (n <= 0x0F) {
+    result.Append('0');
+  }
+  result.AppendInt(n, 16);
+  return result;
+}
+
+void nsColorPicker::ReadValueFromColorChooser(GtkWidget* color_chooser)
+{
+  GdkColor rgba;
+  gtk_color_selection_get_current_color(
+    GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(
+      GTK_COLOR_SELECTION_DIALOG(color_chooser))),
+    &rgba);
+
+  mColor.AssignLiteral("#");
+  mColor += ToHexString(convertGdkColorComponent(rgba.red));
+  mColor += ToHexString(convertGdkColorComponent(rgba.green));
+  mColor += ToHexString(convertGdkColorComponent(rgba.blue));
+}
new file mode 100644
--- /dev/null
+++ b/widget/gtk2/nsColorPicker.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsColorPicker_h__
+#define nsColorPicker_h__
+
+#include <gtk/gtk.h>
+
+#include "nsCOMPtr.h"
+#include "nsIColorPicker.h"
+#include "nsString.h"
+
+class nsIWidget;
+
+class nsColorPicker : public nsIColorPicker
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICOLORPICKER
+
+  nsColorPicker() {};
+
+private:
+  ~nsColorPicker() {};
+
+  static void OnResponse(GtkWidget* dialog, gint response_id,
+                         gpointer user_data);
+  static void OnDestroy(GtkWidget* dialog, gpointer user_data);
+
+  // Conversion functions for color
+  static int convertGdkColorComponent(guint16 color_component);
+  static guint16 convertToGdkColorComponent(int color_component);
+  static GdkColor convertToGdkColor(nscolor color);
+  static nsString ToHexString(int n);
+
+  void Done(GtkWidget* dialog, gint response_id);
+  void ReadValueFromColorChooser(GtkWidget* color_chooser);
+
+  nsCOMPtr<nsIWidget> mParentWidget;
+  nsCOMPtr<nsIColorPickerShownCallback> mCallback;
+  nsString mTitle;
+  nsString mColor;
+  GdkColor mDefaultColor;
+};
+
+#endif // nsColorPicker_h__
--- a/widget/gtk2/nsDragService.cpp
+++ b/widget/gtk2/nsDragService.cpp
@@ -28,40 +28,30 @@
 #include "gfxXlibSurface.h"
 #include "gfxContext.h"
 #include "nsImageToPixbuf.h"
 #include "nsPresContext.h"
 #include "nsIDocument.h"
 #include "nsISelection.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
+#include "nsGtkUtils.h"
 
 // This sets how opaque the drag image is
 #define DRAG_IMAGE_ALPHA_LEVEL 0.5
 
 // These values are copied from GtkDragResult (rather than using GtkDragResult
 // directly) so that this code can be compiled against versions of GTK+ that
 // do not have GtkDragResult.
 // GtkDragResult is available from GTK+ version 2.12.
 enum {
   MOZ_GTK_DRAG_RESULT_SUCCESS,
   MOZ_GTK_DRAG_RESULT_NO_TARGET
 };
 
-// Some gobject functions expect functions for gpointer arguments.
-// gpointer is void* but C++ doesn't like casting functions to void*.
-template<class T> static inline gpointer
-FuncToGpointer(T aFunction)
-{
-    return reinterpret_cast<gpointer>
-        (reinterpret_cast<uintptr_t>
-         // This cast just provides a warning if T is not a function.
-         (reinterpret_cast<void (*)()>(aFunction)));
-}
-
 static PRLogModuleInfo *sDragLm = NULL;
 
 // data used for synthetic periodic motion events sent to the source widget
 // grabbing real events for the drag.
 static guint sMotionEventTimerID;
 static GdkEvent *sMotionEvent;
 static GtkWidget *sGrabWidget;
 
--- a/widget/gtk2/nsFilePicker.cpp
+++ b/widget/gtk2/nsFilePicker.cpp
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
 #include <gtk/gtk.h>
 
+#include "nsGtkUtils.h"
 #include "nsIFileURL.h"
 #include "nsIURI.h"
 #include "nsIWidget.h"
 #include "nsIFile.h"
 #include "nsIStringBundle.h"
 
 #include "nsArrayEnumerator.h"
 #include "nsMemory.h"
@@ -27,27 +28,16 @@
 #endif
 
 using namespace mozilla;
 
 #define MAX_PREVIEW_SIZE 180
 
 nsIFile *nsFilePicker::mPrevDisplayDirectory = nullptr;
 
-// Some GObject functions expect functions for gpointer arguments.
-// gpointer is void* but C++ doesn't like casting functions to void*.
-template<class T> static inline gpointer
-FuncToGpointer(T aFunction)
-{
-    return reinterpret_cast<gpointer>
-        (reinterpret_cast<uintptr_t>
-         // This cast just provides a warning if T is not a function.
-         (reinterpret_cast<void (*)()>(aFunction)));
-}
-
 void
 nsFilePicker::Shutdown()
 {
   NS_IF_RELEASE(mPrevDisplayDirectory);
 }
 
 static GtkFileChooserAction
 GetGtkFileChooserAction(int16_t aMode)
new file mode 100644
--- /dev/null
+++ b/widget/gtk2/nsGtkUtils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsGtkUtils_h__
+#define nsGtkUtils_h__
+
+#include <glib.h>
+
+// Some gobject functions expect functions for gpointer arguments.
+// gpointer is void* but C++ doesn't like casting functions to void*.
+template<class T> static inline gpointer
+FuncToGpointer(T aFunction)
+{
+    return reinterpret_cast<gpointer>
+        (reinterpret_cast<uintptr_t>
+         // This cast just provides a warning if T is not a function.
+         (reinterpret_cast<void (*)()>(aFunction)));
+}
+
+#endif // nsGtkUtils_h__
--- a/widget/gtk2/nsWidgetFactory.cpp
+++ b/widget/gtk2/nsWidgetFactory.cpp
@@ -14,16 +14,17 @@
 #include "nsWindow.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
 #ifdef MOZ_X11
 #include "nsClipboardHelper.h"
 #include "nsClipboard.h"
 #include "nsDragService.h"
 #endif
+#include "nsColorPicker.h"
 #include "nsFilePicker.h"
 #include "nsSound.h"
 #include "nsBidiKeyboard.h"
 #include "nsNativeKeyBindings.h"
 #include "nsScreenManagerGtk.h"
 #include "nsGTKToolkit.h"
 
 #ifdef NS_PRINTING
@@ -148,16 +149,34 @@ nsFilePickerConstructor(nsISupports *aOu
   if (!picker) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return picker->QueryInterface(aIID, aResult);
 }
 
 static nsresult
+nsColorPickerConstructor(nsISupports *aOuter, REFNSIID aIID,
+                         void **aResult)
+{
+    *aResult = nullptr;
+    if (aOuter != nullptr) {
+        return NS_ERROR_NO_AGGREGATION;
+    }
+
+    nsCOMPtr<nsIColorPicker> picker = new nsColorPicker;
+
+    if (!picker) {
+        return NS_ERROR_OUT_OF_MEMORY;
+    }
+
+    return picker->QueryInterface(aIID, aResult);
+}
+
+static nsresult
 nsNativeKeyBindingsConstructor(nsISupports *aOuter, REFNSIID aIID,
                                void **aResult,
                                NativeKeyBindingsType aKeyBindingsType)
 {
     nsresult rv;
 
     nsNativeKeyBindings *inst;
 
@@ -194,16 +213,17 @@ nsNativeKeyBindingsTextAreaConstructor(n
 {
     return nsNativeKeyBindingsConstructor(aOuter, aIID, aResult,
                                           eKeyBindings_TextArea);
 }
 
 NS_DEFINE_NAMED_CID(NS_WINDOW_CID);
 NS_DEFINE_NAMED_CID(NS_CHILD_CID);
 NS_DEFINE_NAMED_CID(NS_APPSHELL_CID);
+NS_DEFINE_NAMED_CID(NS_COLORPICKER_CID);
 NS_DEFINE_NAMED_CID(NS_FILEPICKER_CID);
 NS_DEFINE_NAMED_CID(NS_SOUND_CID);
 NS_DEFINE_NAMED_CID(NS_TRANSFERABLE_CID);
 #ifdef MOZ_X11
 NS_DEFINE_NAMED_CID(NS_CLIPBOARD_CID);
 NS_DEFINE_NAMED_CID(NS_CLIPBOARDHELPER_CID);
 NS_DEFINE_NAMED_CID(NS_DRAGSERVICE_CID);
 #endif
@@ -229,16 +249,17 @@ NS_DEFINE_NAMED_CID(NS_IDLE_SERVICE_CID)
 NS_DEFINE_NAMED_CID(NS_GFXINFO_CID);
 #endif
 
 
 static const mozilla::Module::CIDEntry kWidgetCIDs[] = {
     { &kNS_WINDOW_CID, false, NULL, nsWindowConstructor },
     { &kNS_CHILD_CID, false, NULL, nsChildWindowConstructor },
     { &kNS_APPSHELL_CID, false, NULL, nsAppShellConstructor },
+    { &kNS_COLORPICKER_CID, false, NULL, nsColorPickerConstructor },
     { &kNS_FILEPICKER_CID, false, NULL, nsFilePickerConstructor },
     { &kNS_SOUND_CID, false, NULL, nsSoundConstructor },
     { &kNS_TRANSFERABLE_CID, false, NULL, nsTransferableConstructor },
 #ifdef MOZ_X11
     { &kNS_CLIPBOARD_CID, false, NULL, nsClipboardConstructor },
     { &kNS_CLIPBOARDHELPER_CID, false, NULL, nsClipboardHelperConstructor },
     { &kNS_DRAGSERVICE_CID, false, NULL, nsDragServiceConstructor },
 #endif
@@ -265,16 +286,17 @@ static const mozilla::Module::CIDEntry k
 #endif
     { NULL }
 };
 
 static const mozilla::Module::ContractIDEntry kWidgetContracts[] = {
     { "@mozilla.org/widget/window/gtk;1", &kNS_WINDOW_CID },
     { "@mozilla.org/widgets/child_window/gtk;1", &kNS_CHILD_CID },
     { "@mozilla.org/widget/appshell/gtk;1", &kNS_APPSHELL_CID },
+    { "@mozilla.org/colorpicker;1", &kNS_COLORPICKER_CID },
     { "@mozilla.org/filepicker;1", &kNS_FILEPICKER_CID },
     { "@mozilla.org/sound;1", &kNS_SOUND_CID },
     { "@mozilla.org/widget/transferable;1", &kNS_TRANSFERABLE_CID },
 #ifdef MOZ_X11
     { "@mozilla.org/widget/clipboard;1", &kNS_CLIPBOARD_CID },
     { "@mozilla.org/widget/clipboardhelper;1", &kNS_CLIPBOARDHELPER_CID },
     { "@mozilla.org/widget/dragservice;1", &kNS_DRAGSERVICE_CID },
 #endif
--- a/widget/gtk2/nsWindow.cpp
+++ b/widget/gtk2/nsWindow.cpp
@@ -62,16 +62,17 @@
 
 #include "mozilla/Likely.h"
 #include "mozilla/Preferences.h"
 #include "nsIPrefService.h"
 #include "nsIGConfService.h"
 #include "nsIServiceManager.h"
 #include "nsIStringBundle.h"
 #include "nsGfxCIID.h"
+#include "nsGtkUtils.h"
 #include "nsIObserverService.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "nsIIdleServiceInternal.h"
 #include "nsIPropertyBag2.h"
 
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
 #include "mozilla/a11y/Platform.h"
@@ -278,27 +279,16 @@ typedef struct _GdkDisplay GdkDisplay;
 static GdkCursor *gCursorCache[eCursorCount];
 
 static GtkWidget *gInvisibleContainer = NULL;
 
 // Sometimes this actually also includes the state of the modifier keys, but
 // only the button state bits are used.
 static guint gButtonState;
 
-// Some gobject functions expect functions for gpointer arguments.
-// gpointer is void* but C++ doesn't like casting functions to void*.
-template<class T> static inline gpointer
-FuncToGpointer(T aFunction)
-{
-    return reinterpret_cast<gpointer>
-        (reinterpret_cast<uintptr_t>
-         // This cast just provides a warning if T is not a function.
-         (reinterpret_cast<void (*)()>(aFunction)));
-}
-
 // nsAutoRef<pixman_region32> uses nsSimpleRef<> to know how to automatically
 // destroy regions.
 template <>
 class nsSimpleRef<pixman_region32> : public pixman_region32 {
 protected:
     typedef pixman_region32 RawRef;
 
     nsSimpleRef() { data = nullptr; }