/* -*- 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/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& 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(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(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(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: #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; }