|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
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/. */ |
|
6 |
|
7 #include "nsColorPicker.h" |
|
8 |
|
9 #include <shlwapi.h> |
|
10 |
|
11 #include "mozilla/AutoRestore.h" |
|
12 #include "nsIWidget.h" |
|
13 #include "WidgetUtils.h" |
|
14 |
|
15 using namespace mozilla::widget; |
|
16 |
|
17 namespace |
|
18 { |
|
19 // Manages NS_NATIVE_TMP_WINDOW child windows. NS_NATIVE_TMP_WINDOWs are |
|
20 // temporary child windows of mParentWidget created to address RTL issues |
|
21 // in picker dialogs. We are responsible for destroying these. |
|
22 class AutoDestroyTmpWindow |
|
23 { |
|
24 public: |
|
25 explicit AutoDestroyTmpWindow(HWND aTmpWnd) : |
|
26 mWnd(aTmpWnd) { |
|
27 } |
|
28 |
|
29 ~AutoDestroyTmpWindow() { |
|
30 if (mWnd) |
|
31 DestroyWindow(mWnd); |
|
32 } |
|
33 |
|
34 inline HWND get() const { return mWnd; } |
|
35 private: |
|
36 HWND mWnd; |
|
37 }; |
|
38 |
|
39 static DWORD ColorStringToRGB(const nsAString& aColor) |
|
40 { |
|
41 DWORD result = 0; |
|
42 |
|
43 for (uint32_t i = 1; i < aColor.Length(); ++i) { |
|
44 result *= 16; |
|
45 |
|
46 char16_t c = aColor[i]; |
|
47 if (c >= '0' && c <= '9') { |
|
48 result += c - '0'; |
|
49 } else if (c >= 'a' && c <= 'f') { |
|
50 result += 10 + (c - 'a'); |
|
51 } else { |
|
52 result += 10 + (c - 'A'); |
|
53 } |
|
54 } |
|
55 |
|
56 DWORD r = result & 0x00FF0000; |
|
57 DWORD g = result & 0x0000FF00; |
|
58 DWORD b = result & 0x000000FF; |
|
59 |
|
60 r = r >> 16; |
|
61 b = b << 16; |
|
62 |
|
63 result = r | g | b; |
|
64 |
|
65 return result; |
|
66 } |
|
67 |
|
68 static nsString ToHexString(BYTE n) |
|
69 { |
|
70 nsString result; |
|
71 if (n <= 0x0F) { |
|
72 result.Append('0'); |
|
73 } |
|
74 result.AppendInt(n, 16); |
|
75 return result; |
|
76 } |
|
77 |
|
78 |
|
79 static void |
|
80 BGRIntToRGBString(DWORD color, nsAString& aResult) |
|
81 { |
|
82 BYTE r = GetRValue(color); |
|
83 BYTE g = GetGValue(color); |
|
84 BYTE b = GetBValue(color); |
|
85 |
|
86 aResult.AssignLiteral("#"); |
|
87 aResult.Append(ToHexString(r)); |
|
88 aResult.Append(ToHexString(g)); |
|
89 aResult.Append(ToHexString(b)); |
|
90 } |
|
91 } // anonymous namespace |
|
92 |
|
93 AsyncColorChooser::AsyncColorChooser(const nsAString& aInitialColor, |
|
94 nsIWidget* aParentWidget, |
|
95 nsIColorPickerShownCallback* aCallback) |
|
96 : mInitialColor(aInitialColor) |
|
97 , mParentWidget(aParentWidget) |
|
98 , mCallback(aCallback) |
|
99 { |
|
100 } |
|
101 |
|
102 NS_IMETHODIMP |
|
103 AsyncColorChooser::Run() |
|
104 { |
|
105 static COLORREF sCustomColors[16] = {0} ; |
|
106 |
|
107 MOZ_ASSERT(NS_IsMainThread(), |
|
108 "Color pickers can only be opened from main thread currently"); |
|
109 |
|
110 static bool sColorPickerOpen = false; |
|
111 // Allow only one color picker to be opened at a time, to workaround bug 944737 |
|
112 if (!sColorPickerOpen) { |
|
113 mozilla::AutoRestore<bool> autoRestoreColorPickerOpen(sColorPickerOpen); |
|
114 sColorPickerOpen = true; |
|
115 |
|
116 AutoDestroyTmpWindow adtw((HWND) (mParentWidget.get() ? |
|
117 mParentWidget->GetNativeData(NS_NATIVE_TMP_WINDOW) : nullptr)); |
|
118 |
|
119 CHOOSECOLOR options; |
|
120 options.lStructSize = sizeof(options); |
|
121 options.hwndOwner = adtw.get(); |
|
122 options.Flags = CC_RGBINIT | CC_FULLOPEN; |
|
123 options.rgbResult = ColorStringToRGB(mInitialColor); |
|
124 options.lpCustColors = sCustomColors; |
|
125 |
|
126 if (ChooseColor(&options)) { |
|
127 BGRIntToRGBString(options.rgbResult, mColor); |
|
128 } |
|
129 } else { |
|
130 NS_WARNING("Currently, it's not possible to open more than one color " |
|
131 "picker at a time"); |
|
132 mColor = mInitialColor; |
|
133 } |
|
134 |
|
135 if (mCallback) { |
|
136 mCallback->Done(mColor); |
|
137 } |
|
138 |
|
139 return NS_OK; |
|
140 } |
|
141 |
|
142 /////////////////////////////////////////////////////////////////////////////// |
|
143 // nsIColorPicker |
|
144 |
|
145 nsColorPicker::nsColorPicker() |
|
146 { |
|
147 } |
|
148 |
|
149 nsColorPicker::~nsColorPicker() |
|
150 { |
|
151 } |
|
152 |
|
153 NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker) |
|
154 |
|
155 NS_IMETHODIMP |
|
156 nsColorPicker::Init(nsIDOMWindow* parent, |
|
157 const nsAString& title, |
|
158 const nsAString& aInitialColor) |
|
159 { |
|
160 NS_PRECONDITION(parent, |
|
161 "Null parent passed to colorpicker, no color picker for you!"); |
|
162 mParentWidget = WidgetUtils::DOMWindowToWidget(parent); |
|
163 mInitialColor = aInitialColor; |
|
164 return NS_OK; |
|
165 } |
|
166 |
|
167 NS_IMETHODIMP |
|
168 nsColorPicker::Open(nsIColorPickerShownCallback* aCallback) |
|
169 { |
|
170 NS_ENSURE_ARG(aCallback); |
|
171 nsCOMPtr<nsIRunnable> event = new AsyncColorChooser(mInitialColor, mParentWidget, aCallback); |
|
172 return NS_DispatchToMainThread(event); |
|
173 } |