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#import <Cocoa/Cocoa.h>
7
8#include "nsColorPicker.h"
9#include "nsCocoaUtils.h"
10#include "nsThreadUtils.h"
11
12using namespace mozilla;
13
14static unsigned int HexStrToInt(NSString* str) {
15  unsigned int result = 0;
16
17  for (unsigned int i = 0; i < [str length]; ++i) {
18    char c = [str characterAtIndex:i];
19    result *= 16;
20    if (c >= '0' && c <= '9') {
21      result += c - '0';
22    } else if (c >= 'A' && c <= 'F') {
23      result += 10 + (c - 'A');
24    } else {
25      result += 10 + (c - 'a');
26    }
27  }
28
29  return result;
30}
31
32@interface NSColorPanelWrapper : NSObject <NSWindowDelegate> {
33  NSColorPanel* mColorPanel;
34  nsColorPicker* mColorPicker;
35}
36- (id)initWithPicker:(nsColorPicker*)aPicker;
37- (void)open:(NSColor*)aInitialColor title:(NSString*)aTitle;
38- (void)colorChanged:(NSColorPanel*)aPanel;
39- (void)windowWillClose:(NSNotification*)aNotification;
40- (void)close;
41@end
42
43@implementation NSColorPanelWrapper
44- (id)initWithPicker:(nsColorPicker*)aPicker {
45  mColorPicker = aPicker;
46  mColorPanel = [NSColorPanel sharedColorPanel];
47
48  self = [super init];
49  return self;
50}
51
52- (void)open:(NSColor*)aInitialColor title:(NSString*)aTitle {
53  [mColorPanel setTarget:self];
54  [mColorPanel setAction:@selector(colorChanged:)];
55  [mColorPanel setDelegate:self];
56  [mColorPanel setTitle:aTitle];
57  [mColorPanel setColor:aInitialColor];
58  [mColorPanel makeKeyAndOrderFront:nil];
59}
60
61- (void)colorChanged:(NSColorPanel*)aPanel {
62  if (!mColorPicker) {
63    return;
64  }
65  mColorPicker->Update([mColorPanel color]);
66}
67
68- (void)windowWillClose:(NSNotification*)aNotification {
69  if (!mColorPicker) {
70    return;
71  }
72  mColorPicker->Done();
73}
74
75- (void)close {
76  [mColorPanel setTarget:nil];
77  [mColorPanel setAction:nil];
78  [mColorPanel setDelegate:nil];
79
80  mColorPanel = nil;
81  mColorPicker = nullptr;
82}
83@end
84
85NS_IMPL_ISUPPORTS(nsColorPicker, nsIColorPicker)
86
87nsColorPicker::~nsColorPicker() {
88  if (mColorPanelWrapper) {
89    [mColorPanelWrapper close];
90    [mColorPanelWrapper release];
91    mColorPanelWrapper = nullptr;
92  }
93}
94
95NS_IMETHODIMP
96nsColorPicker::Init(mozIDOMWindowProxy* aParent, const nsAString& aTitle,
97                    const nsAString& aInitialColor) {
98  MOZ_ASSERT(NS_IsMainThread(), "Color pickers can only be opened from main thread currently");
99  mTitle = aTitle;
100  mColor = aInitialColor;
101  mColorPanelWrapper = [[NSColorPanelWrapper alloc] initWithPicker:this];
102  return NS_OK;
103}
104
105/* static */ NSColor* nsColorPicker::GetNSColorFromHexString(const nsAString& aColor) {
106  NSString* str = nsCocoaUtils::ToNSString(aColor);
107
108  double red = HexStrToInt([str substringWithRange:NSMakeRange(1, 2)]) / 255.0;
109  double green = HexStrToInt([str substringWithRange:NSMakeRange(3, 2)]) / 255.0;
110  double blue = HexStrToInt([str substringWithRange:NSMakeRange(5, 2)]) / 255.0;
111
112  return [NSColor colorWithDeviceRed:red green:green blue:blue alpha:1.0];
113}
114
115/* static */ void nsColorPicker::GetHexStringFromNSColor(NSColor* aColor, nsAString& aResult) {
116  CGFloat redFloat, greenFloat, blueFloat;
117
118  NSColor* color = aColor;
119  @try {
120    [color getRed:&redFloat green:&greenFloat blue:&blueFloat alpha:nil];
121  } @catch (NSException* e) {
122    color = [color colorUsingColorSpace:[NSColorSpace genericRGBColorSpace]];
123    [color getRed:&redFloat green:&greenFloat blue:&blueFloat alpha:nil];
124  }
125
126  nsCocoaUtils::GetStringForNSString(
127      [NSString stringWithFormat:@"#%02x%02x%02x", (int)(redFloat * 255), (int)(greenFloat * 255),
128                                 (int)(blueFloat * 255)],
129      aResult);
130}
131
132NS_IMETHODIMP
133nsColorPicker::Open(nsIColorPickerShownCallback* aCallback) {
134  MOZ_ASSERT(aCallback);
135  mCallback = aCallback;
136
137  [mColorPanelWrapper open:GetNSColorFromHexString(mColor) title:nsCocoaUtils::ToNSString(mTitle)];
138
139  NS_ADDREF_THIS();
140
141  return NS_OK;
142}
143
144void nsColorPicker::Update(NSColor* aColor) {
145  GetHexStringFromNSColor(aColor, mColor);
146  mCallback->Update(mColor);
147}
148
149void nsColorPicker::Done() {
150  [mColorPanelWrapper close];
151  [mColorPanelWrapper release];
152  mColorPanelWrapper = nullptr;
153  mCallback->Done(u""_ns);
154  mCallback = nullptr;
155  NS_RELEASE_THIS();
156}
157