1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsColorPicker.h"
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/AutoRestore.h"
13 #include "nsIWidget.h"
15 #include "WidgetUtils.h"
17 #include "nsPIDOMWindow.h"
19 using namespace mozilla::widget
;
22 static DWORD
ColorStringToRGB(const nsAString
& aColor
) {
25 for (uint32_t i
= 1; i
< aColor
.Length(); ++i
) {
28 char16_t c
= aColor
[i
];
29 if (c
>= '0' && c
<= '9') {
31 } else if (c
>= 'a' && c
<= 'f') {
32 result
+= 10 + (c
- 'a');
34 result
+= 10 + (c
- 'A');
38 DWORD r
= result
& 0x00FF0000;
39 DWORD g
= result
& 0x0000FF00;
40 DWORD b
= result
& 0x000000FF;
50 static nsString
ToHexString(BYTE n
) {
55 result
.AppendInt(n
, 16);
59 static void BGRIntToRGBString(DWORD color
, nsAString
& aResult
) {
60 BYTE r
= GetRValue(color
);
61 BYTE g
= GetGValue(color
);
62 BYTE b
= GetBValue(color
);
65 aResult
.Append(ToHexString(r
));
66 aResult
.Append(ToHexString(g
));
67 aResult
.Append(ToHexString(b
));
71 static AsyncColorChooser
* gColorChooser
;
73 AsyncColorChooser::AsyncColorChooser(COLORREF aInitialColor
,
74 const nsTArray
<nsString
>& aDefaultColors
,
75 nsIWidget
* aParentWidget
,
76 nsIColorPickerShownCallback
* aCallback
)
77 : mozilla::Runnable("AsyncColorChooser"),
78 mInitialColor(aInitialColor
),
79 mDefaultColors(aDefaultColors
.Clone()),
80 mColor(aInitialColor
),
81 mParentWidget(aParentWidget
),
82 mCallback(aCallback
) {}
85 AsyncColorChooser::Run() {
86 MOZ_ASSERT(NS_IsMainThread(),
87 "Color pickers can only be opened from main thread currently");
89 // Allow only one color picker to be opened at a time, to workaround bug
92 mozilla::AutoRestore
<AsyncColorChooser
*> restoreColorChooser(gColorChooser
);
95 ScopedRtlShimWindow
shim(mParentWidget
.get());
97 COLORREF customColors
[16];
98 for (size_t i
= 0; i
< mozilla::ArrayLength(customColors
); i
++) {
99 if (i
< mDefaultColors
.Length()) {
100 customColors
[i
] = ColorStringToRGB(mDefaultColors
[i
]);
102 customColors
[i
] = 0x00FFFFFF;
107 options
.lStructSize
= sizeof(options
);
108 options
.hwndOwner
= shim
.get();
109 options
.Flags
= CC_RGBINIT
| CC_FULLOPEN
| CC_ENABLEHOOK
;
110 options
.rgbResult
= mInitialColor
;
111 options
.lpCustColors
= customColors
;
112 options
.lpfnHook
= HookProc
;
114 mColor
= ChooseColor(&options
) ? options
.rgbResult
: mInitialColor
;
117 "Currently, it's not possible to open more than one color "
119 mColor
= mInitialColor
;
123 nsAutoString colorStr
;
124 BGRIntToRGBString(mColor
, colorStr
);
125 mCallback
->Done(colorStr
);
131 void AsyncColorChooser::Update(COLORREF aColor
) {
132 if (mColor
!= aColor
) {
135 nsAutoString colorStr
;
136 BGRIntToRGBString(mColor
, colorStr
);
137 mCallback
->Update(colorStr
);
141 /* static */ UINT_PTR CALLBACK
AsyncColorChooser::HookProc(HWND aDialog
,
145 if (!gColorChooser
) {
149 if (aMsg
== WM_INITDIALOG
) {
150 // "The default dialog box procedure processes the WM_INITDIALOG message
151 // before passing it to the hook procedure.
152 // For all other messages, the hook procedure receives the message first."
153 // https://docs.microsoft.com/en-us/windows/win32/api/commdlg/nc-commdlg-lpcchookproc
154 // "The dialog box procedure should return TRUE to direct the system to
155 // set the keyboard focus to the control specified by wParam."
156 // https://docs.microsoft.com/en-us/windows/win32/dlgbox/wm-initdialog
160 if (aMsg
== WM_CTLCOLORSTATIC
) {
161 // The color picker does not expose a proper way to retrieve the current
162 // color, so we need to obtain it from the static control displaying the
163 // current color instead.
164 const int kCurrentColorBoxID
= 709;
165 if ((HWND
)aLParam
== GetDlgItem(aDialog
, kCurrentColorBoxID
)) {
166 gColorChooser
->Update(GetPixel((HDC
)aWParam
, 0, 0));
170 // Let the default dialog box procedure processes the message.
174 ///////////////////////////////////////////////////////////////////////////////
177 nsColorPicker::nsColorPicker() {}
179 nsColorPicker::~nsColorPicker() {}
181 NS_IMPL_ISUPPORTS(nsColorPicker
, nsIColorPicker
)
184 nsColorPicker::Init(mozIDOMWindowProxy
* parent
, const nsAString
& title
,
185 const nsAString
& aInitialColor
,
186 const nsTArray
<nsString
>& aDefaultColors
) {
188 "Null parent passed to colorpicker, no color picker for you!");
190 WidgetUtils::DOMWindowToWidget(nsPIDOMWindowOuter::From(parent
));
191 mInitialColor
= ColorStringToRGB(aInitialColor
);
192 mDefaultColors
.Assign(aDefaultColors
);
197 nsColorPicker::Open(nsIColorPickerShownCallback
* aCallback
) {
198 NS_ENSURE_ARG(aCallback
);
199 nsCOMPtr
<nsIRunnable
> event
= new AsyncColorChooser(
200 mInitialColor
, mDefaultColors
, mParentWidget
, aCallback
);
201 return NS_DispatchToMainThread(event
);