1/*
2    PPSamplerImagePanelController.m
3
4    Copyright 2013-2018 Josh Freeman
5    http://www.twilightedge.com
6
7    This file is part of PikoPixel for Mac OS X and GNUstep.
8    PikoPixel is a graphical application for drawing & editing pixel-art images.
9
10    PikoPixel is free software: you can redistribute it and/or modify it under
11    the terms of the GNU Affero General Public License as published by the
12    Free Software Foundation, either version 3 of the License, or (at your
13    option) any later version approved for PikoPixel by its copyright holder (or
14    an authorized proxy).
15
16    PikoPixel is distributed in the hope that it will be useful, but WITHOUT ANY
17    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18    FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19    details.
20
21    You should have received a copy of the GNU Affero General Public License
22    along with this program. If not, see <http://www.gnu.org/licenses/>.
23*/
24
25#import "PPSamplerImagePanelController.h"
26
27#import "PPUIColors_Panels.h"
28#import "PPDocument.h"
29#import "PPMiniColorWell.h"
30#import "PPSamplerImageView.h"
31#import "NSObject_PPUtilities.h"
32#import "PPDocumentWindowController.h"
33#import "PPDocumentSamplerImage.h"
34#import "PPGeometry.h"
35#import "PPPanelDefaultFramePinnings.h"
36
37
38#define kSamplerImagePanelNibName   @"SamplerImagePanel"
39
40
41#define kMinAllowedSamplerViewDimension             128
42#define kMaxAllowedSamplerViewDimension             600
43#define kDefaultImageSizeDimension                  200
44
45
46@interface PPSamplerImagePanelController (PrivateMethods)
47
48- (void) handlePPDocumentNotification_ChangedFillColor: (NSNotification *) notification;
49- (void) handlePPDocumentNotification_UpdatedSamplerImages: (NSNotification *) notification;
50- (void) handlePPDocumentNotification_SwitchedActiveSamplerImage:
51                                                            (NSNotification *) notification;
52
53- (void) updateSamplerPanelStateForCurrentDocument;
54
55- (void) setupWithActiveSamplerImageFromCurrentDocument;
56
57- (void) updateArrowButtonsVisibility;
58
59- (void) handleResizingBegin;
60- (void) handleResizingEnd;
61
62@end
63
64@implementation PPSamplerImagePanelController
65
66#pragma mark Actions
67
68- (IBAction) previousSamplerImageButtonPressed: (id) sender
69{
70    [_ppDocument activatePreviousSamplerImageForPanelType: kPPSamplerImagePanelType_Panel];
71}
72
73- (IBAction) nextSamplerImageButtonPressed: (id) sender
74{
75    [_ppDocument activateNextSamplerImageForPanelType: kPPSamplerImagePanelType_Panel];
76}
77
78#pragma mark NSWindowController overrides
79
80- (void) windowDidLoad
81{
82    NSWindow *window;
83    NSRect windowFrame, imageViewFrame;
84
85    window = [self window];
86    windowFrame = [window frame];
87    imageViewFrame = [_samplerImageView frame];
88
89    _sizeDifferenceBetweenPanelAndSamplerImageView =
90                            PPGeometry_SizeDifference(windowFrame.size, imageViewFrame.size);
91
92    // minimum size handled manually - make sure window defaults don't interfere
93    [window setContentMinSize: NSZeroSize];
94
95    [_miniColorWell setOutlineColor: kUIColor_SamplerImagePanel_ColorWellOutline];
96
97    [_samplerImageView setSamplerImagePanelType: kPPSamplerImagePanelType_Panel
98                        minViewDimension: kMinAllowedSamplerViewDimension
99                        maxViewDimension: kMaxAllowedSamplerViewDimension
100                        defaultImageDimension: kDefaultImageSizeDimension
101                        delegate: self];
102
103    // setupWithActiveSamplerImageFromCurrentDocument will resize the window frame so it has
104    // the correct dimensions when the frame is pinned (in [super windowDidLoad])
105    [self setupWithActiveSamplerImageFromCurrentDocument];
106
107    // [super windowDidLoad] may show the panel, so call as late as possible
108    [super windowDidLoad];
109}
110
111#pragma mark PPPanelController overrides
112
113+ (NSString *) panelNibName
114{
115    return kSamplerImagePanelNibName;
116}
117
118- (void) setPanelEnabled: (bool) enablePanel
119{
120    if (enablePanel
121        && ![_ppDocument hasActiveSamplerImageForPanelType: kPPSamplerImagePanelType_Panel])
122    {
123        enablePanel = NO;
124    }
125
126    [_ppDocument setShouldEnableSamplerImagePanel: enablePanel];
127
128    [self updateSamplerPanelStateForCurrentDocument];
129}
130
131- (void) addAsObserverForPPDocumentNotifications
132{
133    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
134
135    if (!_ppDocument)
136        return;
137
138    [notificationCenter addObserver: self
139                        selector: @selector(handlePPDocumentNotification_ChangedFillColor:)
140                        name: PPDocumentNotification_ChangedFillColor
141                        object: _ppDocument];
142
143    [notificationCenter addObserver: self
144                        selector: @selector(handlePPDocumentNotification_UpdatedSamplerImages:)
145                        name: PPDocumentNotification_UpdatedSamplerImages
146                        object: _ppDocument];
147
148    [notificationCenter addObserver: self
149                        selector:
150                            @selector(handlePPDocumentNotification_SwitchedActiveSamplerImage:)
151                        name: PPDocumentNotification_SwitchedActiveSamplerImage
152                        object: _ppDocument];
153}
154
155- (void) removeAsObserverForPPDocumentNotifications
156{
157    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
158
159    [notificationCenter removeObserver: self
160                        name: PPDocumentNotification_ChangedFillColor
161                        object: _ppDocument];
162
163    [notificationCenter removeObserver: self
164                        name: PPDocumentNotification_UpdatedSamplerImages
165                        object: _ppDocument];
166
167    [notificationCenter removeObserver: self
168                        name: PPDocumentNotification_SwitchedActiveSamplerImage
169                        object: _ppDocument];
170}
171
172- (PPFramePinningType) pinningTypeForDefaultWindowFrame
173{
174    return kPPPanelDefaultFramePinning_SamplerImage;
175}
176
177- (void) setupPanelForCurrentPPDocument
178{
179    // [super setupPanelForCurrentPPDocument] may show the panel, so call as late as possible
180    [super setupPanelForCurrentPPDocument];
181
182    [self updateSamplerPanelStateForCurrentDocument];
183}
184
185#pragma mark NSWindow delegate methods
186
187- (NSSize) windowWillResize: (NSWindow *) sender toSize: (NSSize) proposedFrameSize
188{
189    NSSize currentFrameSize, proposedSamplerImageViewSize, newSamplerImageViewSize;
190
191    currentFrameSize = [[self window] frame].size;
192
193    if (!_panelIsResizing)
194    {
195        [self handleResizingBegin];
196        [self ppPerformSelectorAtomicallyFromNewStackFrame: @selector(handleResizingEnd)];
197    }
198
199    if (NSEqualSizes(currentFrameSize, proposedFrameSize))
200    {
201        goto ERROR;
202    }
203
204    if (currentFrameSize.width != proposedFrameSize.width)
205    {
206        _panelResizableDirectionsMask |= kPPResizableDirectionsMask_Horizontal;
207    }
208
209    if (currentFrameSize.height != proposedFrameSize.height)
210    {
211        _panelResizableDirectionsMask |= kPPResizableDirectionsMask_Vertical;
212    }
213
214    proposedSamplerImageViewSize =
215        PPGeometry_SizeDifference(proposedFrameSize,
216                                    _sizeDifferenceBetweenPanelAndSamplerImageView);
217
218    newSamplerImageViewSize =
219        [_samplerImageView viewSizeForResizingToProposedViewSize: proposedSamplerImageViewSize
220                            resizableDirectionsMask: _panelResizableDirectionsMask];
221
222    if (PPGeometry_IsZeroSize(newSamplerImageViewSize))
223    {
224        goto ERROR;
225    }
226
227    return PPGeometry_SizeSum(newSamplerImageViewSize,
228                                _sizeDifferenceBetweenPanelAndSamplerImageView);
229
230ERROR:
231    return currentFrameSize;
232}
233
234#pragma mark PPSamplerImageView delegate methods
235
236- (void) ppSamplerImageView: (PPSamplerImageView *) samplerImageView
237            didBrowseColor: (NSColor *) color
238{
239    if (samplerImageView != _samplerImageView)
240    {
241        return;
242    }
243
244    if (!_initialDocumentFillColor)
245    {
246        _initialDocumentFillColor = [[_ppDocument fillColor] retain];
247    }
248
249    _samplerImageViewIsBrowsingOutsideImage = (color) ? NO : YES;
250
251    if (!color)
252    {
253        color = _initialDocumentFillColor;
254    }
255
256    [_ppDocument setFillColorWithoutUndoRegistration: color];
257}
258
259- (void) ppSamplerImageView: (PPSamplerImageView *) samplerImageView
260            didSelectColor: (NSColor *) color
261{
262    if (samplerImageView != _samplerImageView)
263    {
264        return;
265    }
266
267    _samplerImageViewIsBrowsingOutsideImage = NO;
268
269    [_ppDocument setFillColorWithoutUndoRegistration: _initialDocumentFillColor];
270
271    if (color)
272    {
273        [_ppDocument setFillColor: color];
274    }
275
276    [_initialDocumentFillColor release];
277    _initialDocumentFillColor = nil;
278}
279
280- (void) ppSamplerImageViewDidCancelSelection: (PPSamplerImageView *) samplerImageView
281{
282    if (samplerImageView != _samplerImageView)
283    {
284        return;
285    }
286
287    _samplerImageViewIsBrowsingOutsideImage = NO;
288
289    [_ppDocument setFillColorWithoutUndoRegistration: _initialDocumentFillColor];
290
291    [_initialDocumentFillColor release];
292    _initialDocumentFillColor = nil;
293}
294
295#pragma mark PPDocument notifications
296
297- (void) handlePPDocumentNotification_ChangedFillColor: (NSNotification *) notification
298{
299    NSColor *colorWellColor = nil;
300
301    if (!_samplerImageViewIsBrowsingOutsideImage)
302    {
303        colorWellColor = [_ppDocument fillColor];
304    }
305
306    [_miniColorWell setColor: colorWellColor];
307}
308
309- (void) handlePPDocumentNotification_UpdatedSamplerImages: (NSNotification *) notification
310{
311    [self updateSamplerPanelStateForCurrentDocument];
312}
313
314- (void) handlePPDocumentNotification_SwitchedActiveSamplerImage:
315                                                            (NSNotification *) notification
316{
317    NSNumber *samplerImagePanelTypeNumber;
318
319    samplerImagePanelTypeNumber =
320        [[notification userInfo]
321                        objectForKey: PPDocumentNotification_UserInfoKey_SamplerImagePanelType];
322
323    if (samplerImagePanelTypeNumber
324        && ([samplerImagePanelTypeNumber intValue] != kPPSamplerImagePanelType_Panel))
325    {
326        return;
327    }
328
329    [self setupWithActiveSamplerImageFromCurrentDocument];
330}
331
332#pragma mark Private methods
333
334- (void) updateSamplerPanelStateForCurrentDocument
335{
336    bool shouldDisplayPanel =
337            ([_ppDocument shouldEnableSamplerImagePanel]
338                && [_ppDocument hasActiveSamplerImageForPanelType:
339                                                            kPPSamplerImagePanelType_Panel]);
340
341    if (shouldDisplayPanel)
342    {
343        // make sure panel is loaded before accessing IBOutlets
344        if (!_panelDidLoad)
345        {
346            [self window];
347        }
348
349        _samplerImageViewIsBrowsingOutsideImage = NO;
350        [_miniColorWell setColor: [_ppDocument fillColor]];
351
352        [self updateArrowButtonsVisibility];
353
354        [self setupWithActiveSamplerImageFromCurrentDocument];
355    }
356
357    [super setPanelEnabled: shouldDisplayPanel];
358
359    if (!_panelDidLoad)
360        return;
361
362    [_samplerImageView setupMouseTracking];
363
364    if (!shouldDisplayPanel)
365    {
366        if ([_samplerImageView mouseIsSamplingImage])
367        {
368            [_samplerImageView forceStopSamplingImage];
369        }
370
371        [_samplerImageView setSamplerImage: nil];
372    }
373}
374
375- (void) setupWithActiveSamplerImageFromCurrentDocument
376{
377    PPDocumentSamplerImage *samplerImage;
378    NSSize samplerImageViewSize;
379    NSRect newWindowFrame;
380
381    samplerImage = [_ppDocument activeSamplerImageForPanelType: kPPSamplerImagePanelType_Panel];
382
383    [_samplerImageView setSamplerImage: samplerImage];
384
385    if (!samplerImage)
386        return;
387
388    samplerImageViewSize = [_samplerImageView viewSizeForScaledCurrentSamplerImage];
389
390    newWindowFrame = [[self window] frame];
391    newWindowFrame.size =
392        PPGeometry_SizeSum(samplerImageViewSize, _sizeDifferenceBetweenPanelAndSamplerImageView);
393
394    [[self window] setFrame: newWindowFrame display: NO];
395}
396
397- (void) updateArrowButtonsVisibility
398{
399    bool shouldHideButtons = ([_ppDocument numSamplerImages] < 2) ? YES : NO;
400
401    [_previousSamplerImageButton setHidden: shouldHideButtons];
402    [_nextSamplerImageButton setHidden: shouldHideButtons];
403}
404
405- (void) handleResizingBegin
406{
407    if (_panelIsResizing)
408        return;
409
410    _panelIsResizing = YES;
411
412    [_samplerImageView disableMouseTracking: YES];
413
414    _panelResizableDirectionsMask = kPPResizableDirectionsMask_None;
415}
416
417- (void) handleResizingEnd
418{
419    if (!_panelIsResizing)
420        return;
421
422    _panelIsResizing = NO;
423
424    [_samplerImageView disableMouseTracking: NO];
425}
426
427@end
428