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