diff --git a/gfx/src/nsColor.cpp b/gfx/src/nsColor.cpp index 0c26b3d13f09..2881f52fe646 100644 --- a/gfx/src/nsColor.cpp +++ b/gfx/src/nsColor.cpp @@ -91,10 +91,10 @@ static int ComponentValue(const PRUnichar* aColorSpec, int aLen, int color, int 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)) { diff --git a/gfx/src/nsColor.h b/gfx/src/nsColor.h index 2b75645df4fe..0ac5feefdab5 100644 --- a/gfx/src/nsColor.h +++ b/gfx/src/nsColor.h @@ -48,7 +48,7 @@ typedef uint32_t nscolor; // 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. diff --git a/widget/gtk2/compat/gtk/gtkcolorselectiondialog.h b/widget/gtk2/compat/gtk/gtkcolorselectiondialog.h new file mode 100644 index 000000000000..1c37fe1ad8dc --- /dev/null +++ b/widget/gtk2/compat/gtk/gtkcolorselectiondialog.h @@ -0,0 +1,17 @@ +#ifndef GTKCOLORSELECTIONDIALOG_WRAPPER_H +#define GTKCOLORSELECTIONDIALOG_WRAPPER_H + +#include_next +#include + +#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 diff --git a/widget/gtk2/moz.build b/widget/gtk2/moz.build index 6a7fea1d577c..778fa7dd93d6 100644 --- a/widget/gtk2/moz.build +++ b/widget/gtk2/moz.build @@ -16,6 +16,7 @@ CPP_SOURCES += [ 'WidgetTraceEvent.cpp', 'nsAppShell.cpp', 'nsBidiKeyboard.cpp', + 'nsColorPicker.cpp', 'nsFilePicker.cpp', 'nsGtkIMModule.cpp', 'nsGtkKeyUtils.cpp', diff --git a/widget/gtk2/nsColorPicker.cpp b/widget/gtk2/nsColorPicker.cpp new file mode 100644 index 000000000000..9a8f9c3e5854 --- /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 + +#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(user_data)-> + Done(color_chooser, response_id); +} + +/* static */ void +nsColorPicker::OnDestroy(GtkWidget* color_chooser, gpointer user_data) +{ + static_cast(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)); +} diff --git a/widget/gtk2/nsColorPicker.h b/widget/gtk2/nsColorPicker.h new file mode 100644 index 000000000000..cb50910c1c42 --- /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 + +#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 mParentWidget; + nsCOMPtr mCallback; + nsString mTitle; + nsString mColor; + GdkColor mDefaultColor; +}; + +#endif // nsColorPicker_h__ diff --git a/widget/gtk2/nsDragService.cpp b/widget/gtk2/nsDragService.cpp index 62067d509a63..8926610737ae 100644 --- a/widget/gtk2/nsDragService.cpp +++ b/widget/gtk2/nsDragService.cpp @@ -33,6 +33,7 @@ #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 @@ -46,17 +47,6 @@ enum { 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 static inline gpointer -FuncToGpointer(T aFunction) -{ - return reinterpret_cast - (reinterpret_cast - // This cast just provides a warning if T is not a function. - (reinterpret_cast(aFunction))); -} - static PRLogModuleInfo *sDragLm = NULL; // data used for synthetic periodic motion events sent to the source widget diff --git a/widget/gtk2/nsFilePicker.cpp b/widget/gtk2/nsFilePicker.cpp index fd32416cbcaa..3c1e348166f4 100644 --- a/widget/gtk2/nsFilePicker.cpp +++ b/widget/gtk2/nsFilePicker.cpp @@ -7,6 +7,7 @@ #include +#include "nsGtkUtils.h" #include "nsIFileURL.h" #include "nsIURI.h" #include "nsIWidget.h" @@ -32,17 +33,6 @@ using namespace mozilla; 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 static inline gpointer -FuncToGpointer(T aFunction) -{ - return reinterpret_cast - (reinterpret_cast - // This cast just provides a warning if T is not a function. - (reinterpret_cast(aFunction))); -} - void nsFilePicker::Shutdown() { diff --git a/widget/gtk2/nsGtkUtils.h b/widget/gtk2/nsGtkUtils.h new file mode 100644 index 000000000000..cb41ddaf7af9 --- /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 + +// Some gobject functions expect functions for gpointer arguments. +// gpointer is void* but C++ doesn't like casting functions to void*. +template static inline gpointer +FuncToGpointer(T aFunction) +{ + return reinterpret_cast + (reinterpret_cast + // This cast just provides a warning if T is not a function. + (reinterpret_cast(aFunction))); +} + +#endif // nsGtkUtils_h__ diff --git a/widget/gtk2/nsWidgetFactory.cpp b/widget/gtk2/nsWidgetFactory.cpp index dc1c3afcd1a1..16be8d65ae4c 100644 --- a/widget/gtk2/nsWidgetFactory.cpp +++ b/widget/gtk2/nsWidgetFactory.cpp @@ -19,6 +19,7 @@ #include "nsClipboard.h" #include "nsDragService.h" #endif +#include "nsColorPicker.h" #include "nsFilePicker.h" #include "nsSound.h" #include "nsBidiKeyboard.h" @@ -152,6 +153,24 @@ nsFilePickerConstructor(nsISupports *aOuter, REFNSIID aIID, 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 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, @@ -199,6 +218,7 @@ nsNativeKeyBindingsTextAreaConstructor(nsISupports *aOuter, REFNSIID aIID, 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); @@ -234,6 +254,7 @@ 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 }, @@ -270,6 +291,7 @@ 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 }, diff --git a/widget/gtk2/nsWindow.cpp b/widget/gtk2/nsWindow.cpp index a42ec1f821ee..5143d37c6e2f 100644 --- a/widget/gtk2/nsWindow.cpp +++ b/widget/gtk2/nsWindow.cpp @@ -67,6 +67,7 @@ #include "nsIServiceManager.h" #include "nsIStringBundle.h" #include "nsGfxCIID.h" +#include "nsGtkUtils.h" #include "nsIObserverService.h" #include "mozilla/layers/LayersTypes.h" #include "nsIIdleServiceInternal.h" @@ -283,17 +284,6 @@ static GtkWidget *gInvisibleContainer = NULL; // 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 static inline gpointer -FuncToGpointer(T aFunction) -{ - return reinterpret_cast - (reinterpret_cast - // This cast just provides a warning if T is not a function. - (reinterpret_cast(aFunction))); -} - // nsAutoRef uses nsSimpleRef<> to know how to automatically // destroy regions. template <>