1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #include <gtk/gtk.h>
7
8 #include "nsColor.h"
9 #include "nsColorPicker.h"
10 #include "nsGtkUtils.h"
11 #include "nsIWidget.h"
12 #include "WidgetUtils.h"
13 #include "nsPIDOMWindow.h"
14
NS_IMPL_ISUPPORTS(nsColorPicker,nsIColorPicker)15 NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker)
16
17 #if defined(ACTIVATE_GTK3_COLOR_PICKER)
18 int nsColorPicker::convertGdkRgbaComponent(gdouble color_component) {
19 // GdkRGBA value is in range [0.0..1.0]. We need something in range [0..255]
20 return color_component * 255 + 0.5;
21 }
22
convertToGdkRgbaComponent(int color_component)23 gdouble nsColorPicker::convertToGdkRgbaComponent(int color_component) {
24 return color_component / 255.0;
25 }
26
convertToRgbaColor(nscolor color)27 GdkRGBA nsColorPicker::convertToRgbaColor(nscolor color) {
28 GdkRGBA result = {convertToGdkRgbaComponent(NS_GET_R(color)),
29 convertToGdkRgbaComponent(NS_GET_G(color)),
30 convertToGdkRgbaComponent(NS_GET_B(color)),
31 convertToGdkRgbaComponent(NS_GET_A(color))};
32
33 return result;
34 }
35 #else
36 int nsColorPicker::convertGdkColorComponent(guint16 color_component) {
37 // GdkColor value is in range [0..65535]. We need something in range [0..255]
38 return (color_component * 255 + 127) / 65535;
39 }
40
41 guint16 nsColorPicker::convertToGdkColorComponent(int color_component) {
42 return color_component * 65535 / 255;
43 }
44
45 GdkColor nsColorPicker::convertToGdkColor(nscolor color) {
46 GdkColor result = {0 /* obsolete, unused 'pixel' value */,
47 convertToGdkColorComponent(NS_GET_R(color)),
48 convertToGdkColorComponent(NS_GET_G(color)),
49 convertToGdkColorComponent(NS_GET_B(color))};
50
51 return result;
52 }
53
54 GtkColorSelection* nsColorPicker::WidgetGetColorSelection(GtkWidget* widget) {
55 return GTK_COLOR_SELECTION(gtk_color_selection_dialog_get_color_selection(
56 GTK_COLOR_SELECTION_DIALOG(widget)));
57 }
58 #endif
59
Init(mozIDOMWindowProxy * aParent,const nsAString & title,const nsAString & initialColor)60 NS_IMETHODIMP nsColorPicker::Init(mozIDOMWindowProxy* aParent,
61 const nsAString& title,
62 const nsAString& initialColor) {
63 auto* parent = nsPIDOMWindowOuter::From(aParent);
64 mParentWidget = mozilla::widget::WidgetUtils::DOMWindowToWidget(parent);
65 mTitle = title;
66 mInitialColor = initialColor;
67
68 return NS_OK;
69 }
70
Open(nsIColorPickerShownCallback * aColorPickerShownCallback)71 NS_IMETHODIMP nsColorPicker::Open(
72 nsIColorPickerShownCallback* aColorPickerShownCallback) {
73 // Input color string should be 7 length (i.e. a string representing a valid
74 // simple color)
75 if (mInitialColor.Length() != 7) {
76 return NS_ERROR_FAILURE;
77 }
78
79 const nsAString& withoutHash = StringTail(mInitialColor, 6);
80 nscolor color;
81 if (!NS_HexToRGBA(withoutHash, nsHexColorType::NoAlpha, &color)) {
82 return NS_ERROR_FAILURE;
83 }
84
85 if (mCallback) {
86 // It means Open has already been called: this is not allowed
87 NS_WARNING("mCallback is already set. Open called twice?");
88 return NS_ERROR_FAILURE;
89 }
90 mCallback = aColorPickerShownCallback;
91
92 NS_ConvertUTF16toUTF8 title(mTitle);
93 GtkWindow* parent_window =
94 GTK_WINDOW(mParentWidget->GetNativeData(NS_NATIVE_SHELLWIDGET));
95
96 #if defined(ACTIVATE_GTK3_COLOR_PICKER)
97 GtkWidget* color_chooser = gtk_color_chooser_dialog_new(title, parent_window);
98
99 if (parent_window) {
100 GtkWindow* window = GTK_WINDOW(color_chooser);
101 gtk_window_set_destroy_with_parent(window, TRUE);
102 if (gtk_window_get_modal(parent_window)) {
103 gtk_window_set_modal(window, TRUE);
104 }
105 }
106
107 gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE);
108 GdkRGBA color_rgba = convertToRgbaColor(color);
109 gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(color_chooser), &color_rgba);
110
111 g_signal_connect(GTK_COLOR_CHOOSER(color_chooser), "color-activated",
112 G_CALLBACK(OnColorChanged), this);
113 #else
114 GtkWidget* color_chooser = gtk_color_selection_dialog_new(title.get());
115
116 if (parent_window) {
117 GtkWindow* window = GTK_WINDOW(color_chooser);
118 gtk_window_set_transient_for(window, parent_window);
119 gtk_window_set_destroy_with_parent(window, TRUE);
120 if (gtk_window_get_modal(parent_window)) {
121 gtk_window_set_modal(window, TRUE);
122 }
123 }
124
125 GdkColor color_gdk = convertToGdkColor(color);
126 gtk_color_selection_set_current_color(WidgetGetColorSelection(color_chooser),
127 &color_gdk);
128
129 g_signal_connect(WidgetGetColorSelection(color_chooser), "color-changed",
130 G_CALLBACK(OnColorChanged), this);
131 #endif
132
133 NS_ADDREF_THIS();
134
135 g_signal_connect(color_chooser, "response", G_CALLBACK(OnResponse), this);
136 g_signal_connect(color_chooser, "destroy", G_CALLBACK(OnDestroy), this);
137 gtk_widget_show(color_chooser);
138
139 return NS_OK;
140 }
141
142 #if defined(ACTIVATE_GTK3_COLOR_PICKER)
143 /* static */
OnColorChanged(GtkColorChooser * color_chooser,GdkRGBA * color,gpointer user_data)144 void nsColorPicker::OnColorChanged(GtkColorChooser* color_chooser,
145 GdkRGBA* color, gpointer user_data) {
146 static_cast<nsColorPicker*>(user_data)->Update(color);
147 }
148
Update(GdkRGBA * color)149 void nsColorPicker::Update(GdkRGBA* color) {
150 SetColor(color);
151 if (mCallback) {
152 mCallback->Update(mColor);
153 }
154 }
155
SetColor(const GdkRGBA * color)156 void nsColorPicker::SetColor(const GdkRGBA* color) {
157 mColor.Assign('#');
158 mColor += ToHexString(convertGdkRgbaComponent(color->red));
159 mColor += ToHexString(convertGdkRgbaComponent(color->green));
160 mColor += ToHexString(convertGdkRgbaComponent(color->blue));
161 }
162 #else
163 /* static */
OnColorChanged(GtkColorSelection * colorselection,gpointer user_data)164 void nsColorPicker::OnColorChanged(GtkColorSelection* colorselection,
165 gpointer user_data) {
166 static_cast<nsColorPicker*>(user_data)->Update(colorselection);
167 }
168
Update(GtkColorSelection * colorselection)169 void nsColorPicker::Update(GtkColorSelection* colorselection) {
170 ReadValueFromColorSelection(colorselection);
171 if (mCallback) {
172 mCallback->Update(mColor);
173 }
174 }
175
ReadValueFromColorSelection(GtkColorSelection * colorselection)176 void nsColorPicker::ReadValueFromColorSelection(
177 GtkColorSelection* colorselection) {
178 GdkColor rgba;
179 gtk_color_selection_get_current_color(colorselection, &rgba);
180
181 mColor.Assign('#');
182 mColor += ToHexString(convertGdkColorComponent(rgba.red));
183 mColor += ToHexString(convertGdkColorComponent(rgba.green));
184 mColor += ToHexString(convertGdkColorComponent(rgba.blue));
185 }
186 #endif
187
188 /* static */
OnResponse(GtkWidget * color_chooser,gint response_id,gpointer user_data)189 void nsColorPicker::OnResponse(GtkWidget* color_chooser, gint response_id,
190 gpointer user_data) {
191 static_cast<nsColorPicker*>(user_data)->Done(color_chooser, response_id);
192 }
193
194 /* static */
OnDestroy(GtkWidget * color_chooser,gpointer user_data)195 void nsColorPicker::OnDestroy(GtkWidget* color_chooser, gpointer user_data) {
196 static_cast<nsColorPicker*>(user_data)->Done(color_chooser,
197 GTK_RESPONSE_CANCEL);
198 }
199
Done(GtkWidget * color_chooser,gint response)200 void nsColorPicker::Done(GtkWidget* color_chooser, gint response) {
201 switch (response) {
202 case GTK_RESPONSE_OK:
203 case GTK_RESPONSE_ACCEPT:
204 #if defined(ACTIVATE_GTK3_COLOR_PICKER)
205 GdkRGBA color;
206 gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(color_chooser), &color);
207 SetColor(&color);
208 #else
209 ReadValueFromColorSelection(WidgetGetColorSelection(color_chooser));
210 #endif
211 break;
212 case GTK_RESPONSE_CANCEL:
213 case GTK_RESPONSE_CLOSE:
214 case GTK_RESPONSE_DELETE_EVENT:
215 mColor = mInitialColor;
216 break;
217 default:
218 NS_WARNING("Unexpected response");
219 break;
220 }
221
222 // A "response" signal won't be sent again but "destroy" will be.
223 g_signal_handlers_disconnect_by_func(color_chooser, FuncToGpointer(OnDestroy),
224 this);
225
226 gtk_widget_destroy(color_chooser);
227 if (mCallback) {
228 mCallback->Done(mColor);
229 mCallback = nullptr;
230 }
231
232 NS_RELEASE_THIS();
233 }
234
ToHexString(int n)235 nsString nsColorPicker::ToHexString(int n) {
236 nsString result;
237 if (n <= 0x0F) {
238 result.Append('0');
239 }
240 result.AppendInt(n, 16);
241 return result;
242 }
243