diff options
Diffstat (limited to 'widget/gtk/nsColorPicker.cpp')
-rw-r--r-- | widget/gtk/nsColorPicker.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/widget/gtk/nsColorPicker.cpp b/widget/gtk/nsColorPicker.cpp new file mode 100644 index 0000000000..4719710d9e --- /dev/null +++ b/widget/gtk/nsColorPicker.cpp @@ -0,0 +1,253 @@ +/* -*- 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/Maybe.h" +#include "mozilla/dom/HTMLInputElement.h" +#include "nsColor.h" +#include "nsColorPicker.h" +#include "nsGtkUtils.h" +#include "nsIWidget.h" +#include "WidgetUtils.h" +#include "nsPIDOMWindow.h" + +using mozilla::dom::HTMLInputElement; + +NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) + +#if defined(ACTIVATE_GTK3_COLOR_PICKER) +int nsColorPicker::convertGdkRgbaComponent(gdouble color_component) { + // GdkRGBA value is in range [0.0..1.0]. We need something in range [0..255] + return color_component * 255 + 0.5; +} + +gdouble nsColorPicker::convertToGdkRgbaComponent(int color_component) { + return color_component / 255.0; +} + +GdkRGBA nsColorPicker::convertToRgbaColor(nscolor color) { + GdkRGBA result = {convertToGdkRgbaComponent(NS_GET_R(color)), + convertToGdkRgbaComponent(NS_GET_G(color)), + convertToGdkRgbaComponent(NS_GET_B(color)), + convertToGdkRgbaComponent(NS_GET_A(color))}; + + return result; +} +#else +int nsColorPicker::convertGdkColorComponent(guint16 color_component) { + // GdkColor value is in range [0..65535]. We need something in range [0..255] + return (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; +} + +GtkColorSelection* nsColorPicker::WidgetGetColorSelection(GtkWidget* widget) { + return GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection( + GTK_COLOR_SELECTION_DIALOG(widget))); +} +#endif + +NS_IMETHODIMP nsColorPicker::Init(mozIDOMWindowProxy* aParent, + const nsAString& title, + const nsAString& initialColor, + const nsTArray<nsString>& aDefaultColors) { + auto* parent = nsPIDOMWindowOuter::From(aParent); + mParentWidget = mozilla::widget::WidgetUtils::DOMWindowToWidget(parent); + mTitle = title; + mInitialColor = initialColor; + mDefaultColors.Assign(aDefaultColors); + + return NS_OK; +} + +NS_IMETHODIMP nsColorPicker::Open( + nsIColorPickerShownCallback* aColorPickerShownCallback) { + auto maybeColor = HTMLInputElement::ParseSimpleColor(mInitialColor); + if (maybeColor.isNothing()) { + return NS_ERROR_FAILURE; + } + nscolor color = maybeColor.value(); + + 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; + + NS_ConvertUTF16toUTF8 title(mTitle); + GtkWindow* parent_window = + GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET)); + +#if defined(ACTIVATE_GTK3_COLOR_PICKER) + GtkWidget* color_chooser = + gtk_color_chooser_dialog_new(title.get(), parent_window); + + if (parent_window) { + GtkWindow* window = GTK_WINDOW(color_chooser); + gtk_window_set_destroy_with_parent(window, TRUE); + if (gtk_window_get_modal(parent_window)) { + gtk_window_set_modal(window, TRUE); + } + } + + gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE); + + // Setting the default colors will put them into "Custom" colors list. + for (const nsString& defaultColor : mDefaultColors) { + if (auto color = HTMLInputElement::ParseSimpleColor(defaultColor)) { + GdkRGBA color_rgba = convertToRgbaColor(*color); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), &color_rgba); + } + } + + // The initial color needs to be set last. + GdkRGBA color_rgba = convertToRgbaColor(color); + gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), &color_rgba); + + g_signal_connect(GTK_COLOR_CHOOSER(color_chooser), "color-activated", + G_CALLBACK(OnColorChanged), this); +#else + GtkWidget* color_chooser = gtk_color_selection_dialog_new(title.get()); + + 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); + if (gtk_window_get_modal(parent_window)) { + gtk_window_set_modal(window, TRUE); + } + } + + GdkColor color_gdk = convertToGdkColor(color); + gtk_color_selection_set_current_color(WidgetGetColorSelection(color_chooser), + &color_gdk); + + g_signal_connect(WidgetGetColorSelection(color_chooser), "color-changed", + G_CALLBACK(OnColorChanged), this); +#endif + + 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; +} + +#if defined(ACTIVATE_GTK3_COLOR_PICKER) +/* static */ +void nsColorPicker::OnColorChanged(GtkColorChooser* color_chooser, + GdkRGBA* color, gpointer user_data) { + static_cast<nsColorPicker*>(user_data)->Update(color); +} + +void nsColorPicker::Update(GdkRGBA* color) { + SetColor(color); + if (mCallback) { + mCallback->Update(mColor); + } +} + +void nsColorPicker::SetColor(const GdkRGBA* color) { + mColor.Assign('#'); + mColor += ToHexString(convertGdkRgbaComponent(color->red)); + mColor += ToHexString(convertGdkRgbaComponent(color->green)); + mColor += ToHexString(convertGdkRgbaComponent(color->blue)); +} +#else +/* static */ +void nsColorPicker::OnColorChanged(GtkColorSelection* colorselection, + gpointer user_data) { + static_cast<nsColorPicker*>(user_data)->Update(colorselection); +} + +void nsColorPicker::Update(GtkColorSelection* colorselection) { + ReadValueFromColorSelection(colorselection); + if (mCallback) { + mCallback->Update(mColor); + } +} + +void nsColorPicker::ReadValueFromColorSelection( + GtkColorSelection* colorselection) { + GdkColor rgba; + gtk_color_selection_get_current_color(colorselection, &rgba); + + mColor.Assign('#'); + mColor += ToHexString(convertGdkColorComponent(rgba.red)); + mColor += ToHexString(convertGdkColorComponent(rgba.green)); + mColor += ToHexString(convertGdkColorComponent(rgba.blue)); +} +#endif + +/* 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: +#if defined(ACTIVATE_GTK3_COLOR_PICKER) + GdkRGBA color; + gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(color_chooser), &color); + SetColor(&color); +#else + ReadValueFromColorSelection(WidgetGetColorSelection(color_chooser)); +#endif + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_CLOSE: + case GTK_RESPONSE_DELETE_EVENT: + mColor = mInitialColor; + 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; +} |