1/*
2    PPLayerControlsPopupPanelController.m
3
4    Copyright 2013-2018,2020 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 "PPLayerControlsPopupPanelController.h"
26
27#import "PPDocument.h"
28#import "PPDocumentLayer.h"
29#import "PPDocumentWindowController.h"
30#import "PPThumbnailImageView.h"
31#import "NSObject_PPUtilities.h"
32#import "PPUIColors_Panels.h"
33#import "PPLayerControlButtonImagesManager.h"
34#import "PPTitleablePopUpButton.h"
35#import "PPGeometry.h"
36#import "PPBackgroundPattern.h"
37#import "PPTextAttributesDicts.h"
38#import "NSBitmapImageRep_PPUtilities.h"
39#import "NSImage_PPUtilities.h"
40#import "PPThumbnailUtilities.h"
41
42
43#define kLayerControlsPopupPanelNibName     @"LayerControlsPopupPanel"
44
45#define kPopupMenuThumbnailSize             NSMakeSize(21.0f, 21.0f)
46
47#define kDrawingLayerOpacityFormatString    @"%.1f%%"
48
49
50@interface PPLayerControlsPopupPanelController (PrivateMethods)
51
52- (void) handlePPDocumentNotification_UpdatedDrawingLayerThumbnailImage:
53                                                            (NSNotification *) notification;
54- (void) handlePPDocumentNotification_SwitchedDrawingLayer: (NSNotification *) notification;
55- (void) handlePPDocumentNotification_ReorderedLayers: (NSNotification *) notification;
56- (void) handlePPDocumentNotification_ChangedLayerAttribute: (NSNotification *) notification;
57- (void) handlePPDocumentNotification_SwitchedLayerOperationTarget:
58                                                            (NSNotification *) notification;
59- (void) handlePPDocumentNotification_UpdatedBackgroundSettings:
60                                                            (NSNotification *) notification;
61- (void) handlePPDocumentNotification_ReloadedDocument: (NSNotification *) notification;
62
63
64- (void) addAsObserverForPPDocumentWindowControllerNotifications;
65- (void) removeAsObserverForPPDocumentWindowControllerNotifications;
66- (void) handlePPDocumentWindowControllerNotification_ChangedCanvasDisplayMode:
67                                                                (NSNotification *) notification;
68
69- (void) addAsObserverForPPLayerControlButtonImagesManagerNotifications;
70- (void) removeAsObserverForPPLayerControlButtonImagesManagerNotifications;
71- (void) handlePPLayerControlButtonImagesManagerNotification_ChangedDrawLayerImages:
72                                                                (NSNotification *) notification;
73- (void) handlePPLayerControlButtonImagesManagerNotification_ChangedEnabledLayersImages:
74                                                                (NSNotification *) notification;
75
76- (void) handleOpacitySliderDidBeginTracking;
77- (void) handleOpacitySliderDidFinishTracking;
78
79- (void) setupWithPPDocumentWindowController:
80                                    (PPDocumentWindowController *) ppDocumentWindowController;
81
82- (void) setupDrawingLayerThumbnailImage;
83- (void) setupDrawingLayerThumbnailImageBackground;
84
85- (void) setupPopupMenuThumbnailDrawMembers;
86- (void) setupPopupMenuThumbnailBackgroundBitmap;
87- (void) destroyPopupMenuThumbnailBackgroundBitmap;
88
89- (NSImage *) popupMenuThumbnailImageForLayerImage: (NSImage *) layerImage;
90
91- (void) updateCanvasDisplayMode;
92- (void) updateLayerOperationTarget;
93
94- (void) updateLayerControlButtonImages;
95- (void) updateCanvasDisplayModeButtonImage;
96- (void) updateLayerOperationTargetButtonImage;
97
98- (void) updateDrawingLayerControls;
99- (void) updateDrawingLayerControlsIfPanelIsVisible;
100- (void) updateDrawingLayerAttributeControls;
101- (void) updateDrawingLayerOpacityTextField;
102
103- (void) updateDrawingLayerPopUpButtonMenu;
104
105- (int) layerIndexForPopUpButtonMenuItemAtIndex: (int) itemIndex;
106- (int) popUpButtonMenuItemIndexForLayerAtIndex: (int) layerIndex;
107
108- (float) drawingLayerOpacitySliderQuantizedValue;
109
110@end
111
112@implementation PPLayerControlsPopupPanelController
113
114- (void) dealloc
115{
116    [self removeAsObserverForPPLayerControlButtonImagesManagerNotifications];
117
118    [self setupWithPPDocumentWindowController: nil];
119
120    [self destroyPopupMenuThumbnailBackgroundBitmap];
121
122    [super dealloc];
123}
124
125#pragma mark Actions
126
127- (IBAction) canvasDisplayModeButtonPressed: (id) sender
128{
129    [_ppDocumentWindowController toggleCanvasDisplayMode: self];
130}
131
132- (IBAction) layerOperationTargetButtonPressed: (id) sender
133{
134    [_ppDocumentWindowController toggleLayerOperationTarget: self];
135}
136
137- (IBAction) drawingLayerEnabledCheckboxClicked: (id) sender
138{
139    [[_ppDocument drawingLayer] setEnabled: [_drawingLayerEnabledCheckbox intValue]];
140}
141
142- (IBAction) drawingLayerPopupMenuItemSelected: (id) sender
143{
144    int indexOfSelectedDrawingLayer =
145            [self layerIndexForPopUpButtonMenuItemAtIndex:
146                                    [_drawingLayerTitleablePopUpButton indexOfSelectedItem]];
147
148    [_ppDocument selectDrawingLayerAtIndex: indexOfSelectedDrawingLayer];
149}
150
151- (IBAction) drawingLayerOpacitySliderMoved: (id) sender
152{
153    if (!_isTrackingOpacitySlider)
154    {
155        [self handleOpacitySliderDidBeginTracking];
156    }
157
158    _ignoreNotificationForChangedLayerAttribute = YES;
159
160    [[_ppDocument drawingLayer] setOpacityWithoutRegisteringUndo:
161                                                [self drawingLayerOpacitySliderQuantizedValue]];
162
163    _ignoreNotificationForChangedLayerAttribute = NO;
164
165    [self updateDrawingLayerOpacityTextField];
166}
167
168#pragma mark NSWindowController overrrides
169
170- (void) windowDidLoad
171{
172    _needToUpdateLayerControlButtonImages = YES;
173    _needToUpdateDrawingLayerControls = YES;
174    _needToUpdateDrawingLayerPopupButtonMenu = YES;
175
176    // [super windowDidLoad] calls [self setupPanelForCurrentPPDocument], so any preliminary
177    // setup required before calling setupPanelForCurrentPPDocument should go before this call
178    [super windowDidLoad];
179
180    [_backgroundFillTextField setBackgroundColor: [NSColor windowBackgroundColor]];
181
182    [_drawingLayerTitleablePopUpButton setDelegate: self];
183
184    [self addAsObserverForPPLayerControlButtonImagesManagerNotifications];
185}
186
187#pragma mark PPPopupPanelController overrides
188
189+ (NSString *) panelNibName
190{
191    return kLayerControlsPopupPanelNibName;
192}
193
194- (void) setPPDocument: (PPDocument *) ppDocument
195{
196    [super setPPDocument: ppDocument];
197
198    if (!_ppDocument)
199    {
200        [self setupWithPPDocumentWindowController: nil];
201    }
202}
203
204- (void) addAsObserverForPPDocumentNotifications
205{
206    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
207
208    if (!_ppDocument)
209        return;
210
211    [notificationCenter addObserver: self
212                        selector:
213                            @selector(
214                            handlePPDocumentNotification_UpdatedDrawingLayerThumbnailImage:)
215                        name: PPDocumentNotification_UpdatedDrawingLayerThumbnailImage
216                        object: _ppDocument];
217
218    [notificationCenter addObserver: self
219                        selector: @selector(handlePPDocumentNotification_SwitchedDrawingLayer:)
220                        name: PPDocumentNotification_SwitchedDrawingLayer
221                        object: _ppDocument];
222
223    [notificationCenter addObserver: self
224                        selector: @selector(handlePPDocumentNotification_ReorderedLayers:)
225                        name: PPDocumentNotification_ReorderedLayers
226                        object: _ppDocument];
227
228    [notificationCenter addObserver: self
229                        selector: @selector(handlePPDocumentNotification_ChangedLayerAttribute:)
230                        name: PPDocumentNotification_ChangedLayerAttribute
231                        object: _ppDocument];
232
233    [notificationCenter addObserver: self
234                        selector:
235                            @selector(handlePPDocumentNotification_SwitchedLayerOperationTarget:)
236                        name: PPDocumentNotification_SwitchedLayerOperationTarget
237                        object: _ppDocument];
238
239    [notificationCenter addObserver: self
240                        selector:
241                            @selector(handlePPDocumentNotification_UpdatedBackgroundSettings:)
242                        name: PPDocumentNotification_UpdatedBackgroundSettings
243                        object: _ppDocument];
244
245    [notificationCenter addObserver: self
246                        selector: @selector(handlePPDocumentNotification_ReloadedDocument:)
247                        name: PPDocumentNotification_ReloadedDocument
248                        object: _ppDocument];
249}
250
251- (void) removeAsObserverForPPDocumentNotifications
252{
253    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
254
255    [notificationCenter removeObserver: self
256                        name: PPDocumentNotification_UpdatedDrawingLayerThumbnailImage
257                        object: _ppDocument];
258
259    [notificationCenter removeObserver: self
260                        name: PPDocumentNotification_SwitchedDrawingLayer
261                        object: _ppDocument];
262
263    [notificationCenter removeObserver: self
264                        name: PPDocumentNotification_ReorderedLayers
265                        object: _ppDocument];
266
267    [notificationCenter removeObserver: self
268                        name: PPDocumentNotification_ChangedLayerAttribute
269                        object: _ppDocument];
270
271    [notificationCenter removeObserver: self
272                        name: PPDocumentNotification_SwitchedLayerOperationTarget
273                        object: _ppDocument];
274
275    [notificationCenter removeObserver: self
276                        name: PPDocumentNotification_UpdatedBackgroundSettings
277                        object: _ppDocument];
278
279    [notificationCenter removeObserver: self
280                        name: PPDocumentNotification_ReloadedDocument
281                        object: _ppDocument];
282}
283
284- (void) setupPanelForCurrentPPDocument
285{
286    [super setupPanelForCurrentPPDocument];
287
288    [self setupWithPPDocumentWindowController: [_ppDocument ppDocumentWindowController]];
289
290    [self setupDrawingLayerThumbnailImage];
291    [self setupDrawingLayerThumbnailImageBackground];
292
293    [self setupPopupMenuThumbnailDrawMembers];
294    [self setupPopupMenuThumbnailBackgroundBitmap];
295
296    [self updateCanvasDisplayMode];
297    [self updateLayerOperationTarget];
298
299    [[PPLayerControlButtonImagesManager sharedManager] setPPDocument: _ppDocument];
300
301    [self updateDrawingLayerControlsIfPanelIsVisible];
302
303    _needToUpdateDrawingLayerPopupButtonMenu = YES;
304}
305
306- (void) setupPanelBeforeMakingVisible
307{
308    [super setupPanelBeforeMakingVisible];
309
310    if (_needToUpdateLayerControlButtonImages)
311    {
312        [self updateLayerControlButtonImages];
313    }
314
315    if (_needToUpdateDrawingLayerControls)
316    {
317        [self updateDrawingLayerControls];
318    }
319}
320
321- (NSColor *) backgroundColorForPopupPanel
322{
323    return kUIColor_LayerControlsPopupPanel_Background;
324}
325
326- (void) handleDirectionCommand: (PPDirectionType) directionType
327{
328    switch (directionType)
329    {
330        case kPPDirectionType_Left:
331        {
332            [_ppDocumentWindowController toggleCanvasDisplayMode: self];
333        }
334        break;
335
336        case kPPDirectionType_Right:
337        {
338            [_ppDocumentWindowController toggleLayerOperationTarget: self];
339        }
340        break;
341
342        case kPPDirectionType_Up:
343        {
344            [_ppDocumentWindowController makePreviousLayerActive: self];
345        }
346        break;
347
348        case kPPDirectionType_Down:
349        {
350            [_ppDocumentWindowController makeNextLayerActive: self];
351        }
352        break;
353
354        default:
355        break;
356    }
357}
358
359#pragma mark PPTitleablePopUpButton delegate methods
360
361- (NSDictionary *) titleTextAttributesForMenuItemAtIndex: (int) itemIndex
362                    onTitleablePopUpButton: (PPTitleablePopUpButton *) button
363{
364    int layerIndex;
365    bool layerIsEnabled;
366
367    layerIndex = [self layerIndexForPopUpButtonMenuItemAtIndex: itemIndex];
368
369    layerIsEnabled = [[_ppDocument layerAtIndex: layerIndex] isEnabled];
370
371    return (layerIsEnabled) ? nil : PPTextAttributesDict_DisabledTitle_PopupButton();
372}
373
374- (void) titleablePopUpButtonWillDisplayPopupMenu: (PPTitleablePopUpButton *) button
375{
376    if (_needToUpdateDrawingLayerPopupButtonMenu)
377    {
378        [self updateDrawingLayerPopUpButtonMenu];
379    }
380}
381
382#pragma mark PPDocument notifications
383
384- (void) handlePPDocumentNotification_UpdatedDrawingLayerThumbnailImage:
385                                                                (NSNotification *) notification
386{
387    [_drawingLayerThumbnailView handleUpdateToImage];
388
389    _needToUpdateDrawingLayerPopupButtonMenu = YES;
390}
391
392- (void) handlePPDocumentNotification_SwitchedDrawingLayer: (NSNotification *) notification
393{
394    [self setupDrawingLayerThumbnailImage];
395    [self updateDrawingLayerControlsIfPanelIsVisible];
396}
397
398- (void) handlePPDocumentNotification_ReorderedLayers: (NSNotification *) notification
399{
400    [self setupDrawingLayerThumbnailImage];
401    [self updateDrawingLayerControlsIfPanelIsVisible];
402
403    _needToUpdateDrawingLayerPopupButtonMenu = YES;
404}
405
406- (void) handlePPDocumentNotification_ChangedLayerAttribute: (NSNotification *) notification
407{
408    NSDictionary *userInfo;
409    NSNumber *layerIndexNumber;
410    int layerIndex = -1;
411
412    if (_ignoreNotificationForChangedLayerAttribute)
413        return;
414
415    userInfo = [notification userInfo];
416
417    layerIndexNumber =
418            [userInfo objectForKey: PPDocumentNotification_UserInfoKey_IndexOfChangedLayer];
419
420    if (layerIndexNumber)
421    {
422        layerIndex = [layerIndexNumber intValue];
423    }
424
425    if (layerIndex == [_ppDocument indexOfDrawingLayer])
426    {
427        if ([self panelIsVisible])
428        {
429            [self updateDrawingLayerAttributeControls];
430        }
431        else
432        {
433            _needToUpdateDrawingLayerControls = YES;
434        }
435    }
436
437    _needToUpdateDrawingLayerPopupButtonMenu = YES;
438}
439
440- (void) handlePPDocumentNotification_SwitchedLayerOperationTarget:
441                                                            (NSNotification *) notification
442{
443    [self updateLayerOperationTarget];
444
445    if ([self panelIsVisible])
446    {
447        [self updateLayerOperationTargetButtonImage];
448    }
449    else
450    {
451        _needToUpdateLayerControlButtonImages = YES;
452    }
453}
454
455- (void) handlePPDocumentNotification_UpdatedBackgroundSettings:
456                                                            (NSNotification *) notification
457{
458    [self setupDrawingLayerThumbnailImageBackground];
459
460    [self setupPopupMenuThumbnailBackgroundBitmap];
461
462    _needToUpdateDrawingLayerPopupButtonMenu = YES;
463}
464
465- (void) handlePPDocumentNotification_ReloadedDocument: (NSNotification *) notification
466{
467    [self setupPanelForCurrentPPDocument];
468}
469
470#pragma mark PPDocumentWindowController notifications
471
472- (void) addAsObserverForPPDocumentWindowControllerNotifications
473{
474    if (!_ppDocumentWindowController)
475        return;
476
477    [[NSNotificationCenter defaultCenter]
478        addObserver: self
479        selector:
480            @selector(handlePPDocumentWindowControllerNotification_ChangedCanvasDisplayMode:)
481        name: PPDocumentWindowControllerNotification_ChangedCanvasDisplayMode
482        object: _ppDocumentWindowController];
483}
484
485- (void) removeAsObserverForPPDocumentWindowControllerNotifications
486{
487    [[NSNotificationCenter defaultCenter]
488                        removeObserver: self
489                        name: PPDocumentWindowControllerNotification_ChangedCanvasDisplayMode
490                        object: _ppDocumentWindowController];
491}
492
493- (void) handlePPDocumentWindowControllerNotification_ChangedCanvasDisplayMode:
494                                                                (NSNotification *) notification
495{
496    [self updateCanvasDisplayMode];
497
498    if ([self panelIsVisible])
499    {
500        [self updateCanvasDisplayModeButtonImage];
501    }
502    else
503    {
504        _needToUpdateLayerControlButtonImages = YES;
505    }
506}
507
508#pragma mark PPLayerControlButtonImagesManager notifications
509
510- (void) addAsObserverForPPLayerControlButtonImagesManagerNotifications
511{
512    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
513
514    [notificationCenter
515        addObserver: self
516        selector:
517            @selector(
518                handlePPLayerControlButtonImagesManagerNotification_ChangedDrawLayerImages:)
519        name: PPLayerControlButtonImagesManagerNotification_ChangedDrawLayerImages
520        object: nil];
521
522    [notificationCenter
523        addObserver: self
524        selector:
525            @selector(
526                handlePPLayerControlButtonImagesManagerNotification_ChangedEnabledLayersImages:)
527        name: PPLayerControlButtonImagesManagerNotification_ChangedEnabledLayersImages
528        object: nil];
529}
530
531- (void) removeAsObserverForPPLayerControlButtonImagesManagerNotifications
532{
533    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
534
535    [notificationCenter
536                removeObserver: self
537                name: PPLayerControlButtonImagesManagerNotification_ChangedDrawLayerImages
538                object: nil];
539
540    [notificationCenter
541                removeObserver: self
542                name: PPLayerControlButtonImagesManagerNotification_ChangedEnabledLayersImages
543                object: nil];
544}
545
546- (void) handlePPLayerControlButtonImagesManagerNotification_ChangedDrawLayerImages:
547                                                                (NSNotification *) notification
548{
549    if (![self panelIsVisible])
550    {
551        _needToUpdateLayerControlButtonImages = YES;
552
553        return;
554    }
555
556    if (_canvasDisplayMode == kPPLayerDisplayMode_DrawingLayerOnly)
557    {
558        [self updateCanvasDisplayModeButtonImage];
559    }
560
561    if (_layerOperationTarget == kPPLayerOperationTarget_DrawingLayerOnly)
562    {
563        [self updateLayerOperationTargetButtonImage];
564    }
565}
566
567- (void) handlePPLayerControlButtonImagesManagerNotification_ChangedEnabledLayersImages:
568                                                                (NSNotification *) notification
569{
570    if (![self panelIsVisible])
571    {
572        _needToUpdateLayerControlButtonImages = YES;
573
574        return;
575    }
576
577    if (_canvasDisplayMode != kPPLayerDisplayMode_DrawingLayerOnly)
578    {
579        [self updateCanvasDisplayModeButtonImage];
580    }
581
582    if (_layerOperationTarget != kPPLayerOperationTarget_DrawingLayerOnly)
583    {
584        [self updateLayerOperationTargetButtonImage];
585    }
586}
587
588#pragma mark Opacity slider tracking
589
590- (void) handleOpacitySliderDidBeginTracking
591{
592    if (_isTrackingOpacitySlider)
593        return;
594
595    _isTrackingOpacitySlider = YES;
596
597    _drawingLayerInitialOpacity = [[_ppDocument drawingLayer] opacity];
598
599    [_ppDocument disableThumbnailImageUpdateNotifications: YES];
600
601    // won't return to the main run loop until the slider's done tracking, so post a message
602    // in the next stack frame to serve as notification that tracking's finished
603    [self ppPerformSelectorFromNewStackFrame: @selector(handleOpacitySliderDidFinishTracking)];
604}
605
606- (void) handleOpacitySliderDidFinishTracking
607{
608    float newOpacity = [self drawingLayerOpacitySliderQuantizedValue];
609
610    _isTrackingOpacitySlider = NO;
611
612    [_ppDocument disableThumbnailImageUpdateNotifications: NO];
613
614    if (newOpacity != _drawingLayerInitialOpacity)
615    {
616        [[_ppDocument drawingLayer] setOpacity: newOpacity];
617    }
618}
619
620#pragma mark Private methods
621
622- (void) setupWithPPDocumentWindowController:
623                                    (PPDocumentWindowController *) ppDocumentWindowController
624{
625    if (_ppDocumentWindowController == ppDocumentWindowController)
626    {
627        return;
628    }
629
630    if (_ppDocumentWindowController)
631    {
632        [self removeAsObserverForPPDocumentWindowControllerNotifications];
633    }
634
635    [_ppDocumentWindowController release];
636    _ppDocumentWindowController = [ppDocumentWindowController retain];
637
638    if (_ppDocumentWindowController)
639    {
640        [self addAsObserverForPPDocumentWindowControllerNotifications];
641    }
642}
643
644- (void) setupDrawingLayerThumbnailImage
645{
646    [_drawingLayerThumbnailView setImage: [_ppDocument drawingLayerThumbnailImage]];
647}
648
649- (void) setupDrawingLayerThumbnailImageBackground
650{
651    [_drawingLayerThumbnailView setBackgroundPattern: [_ppDocument backgroundPattern]];
652}
653
654- (void) setupPopupMenuThumbnailDrawMembers
655{
656    _popupMenuThumbnailDrawSourceRect.size = [_ppDocument canvasSize];
657    _popupMenuThumbnailDrawDestinationRect =
658        PPGeometry_ScaledBoundsForFrameOfSizeToFitFrameOfSize(
659                                                        _popupMenuThumbnailDrawSourceRect.size,
660                                                        kPopupMenuThumbnailSize);
661
662    _popupMenuThumbnailInterpolation =
663        PPThumbUtils_ImageInterpolationForSourceRectToDestinationRect(
664                                                        _popupMenuThumbnailDrawSourceRect,
665                                                        _popupMenuThumbnailDrawDestinationRect);
666}
667
668- (void) setupPopupMenuThumbnailBackgroundBitmap
669{
670    PPBackgroundPattern *documentBackgroundPattern, *thumbnailBackgroundPattern;
671    float patternScalingFactor;
672    NSColor *thumbnailBackgroundPatternColor;
673    NSBitmapImageRep *backgroundBitmap;
674
675    [self destroyPopupMenuThumbnailBackgroundBitmap];
676
677    if (!_ppDocument || NSIsEmptyRect(_popupMenuThumbnailDrawSourceRect))
678    {
679        goto ERROR;
680    }
681
682    documentBackgroundPattern = [_ppDocument backgroundPattern];
683
684    patternScalingFactor = kScalingFactorForThumbnailBackgroundPatternSize
685                                * _popupMenuThumbnailDrawDestinationRect.size.width
686                                / _popupMenuThumbnailDrawSourceRect.size.width;
687
688    if (patternScalingFactor > 1.0f)
689    {
690        patternScalingFactor = 1.0f;
691    }
692
693    thumbnailBackgroundPattern =
694            [documentBackgroundPattern backgroundPatternScaledByFactor: patternScalingFactor];
695
696    thumbnailBackgroundPatternColor = [thumbnailBackgroundPattern patternFillColor];
697
698    if (!thumbnailBackgroundPatternColor)
699        goto ERROR;
700
701    backgroundBitmap = [NSBitmapImageRep ppImageBitmapOfSize: kPopupMenuThumbnailSize];
702
703    if (!backgroundBitmap)
704        goto ERROR;
705
706    [backgroundBitmap ppSetAsCurrentGraphicsContext];
707
708    [thumbnailBackgroundPatternColor set];
709    NSRectFill(_popupMenuThumbnailDrawDestinationRect);
710
711    [backgroundBitmap ppRestoreGraphicsContext];
712
713    _popupMenuThumbnailBackgroundBitmap = [backgroundBitmap retain];
714
715    return;
716
717ERROR:
718    return;
719}
720
721- (NSImage *) popupMenuThumbnailImageForLayerImage: (NSImage *) layerImage
722{
723    NSBitmapImageRep *thumbnailBitmap;
724
725    if (!layerImage)
726        goto ERROR;
727
728    thumbnailBitmap = [[_popupMenuThumbnailBackgroundBitmap copy] autorelease];
729
730    if (!thumbnailBitmap)
731        goto ERROR;
732
733    [thumbnailBitmap ppSetAsCurrentGraphicsContext];
734
735    [[NSGraphicsContext currentContext] setImageInterpolation: _popupMenuThumbnailInterpolation];
736
737    [layerImage drawInRect: _popupMenuThumbnailDrawDestinationRect
738                fromRect: _popupMenuThumbnailDrawSourceRect
739                operation: NSCompositeSourceOver
740                fraction: 1.0f];
741
742    [thumbnailBitmap ppRestoreGraphicsContext];
743
744    return [NSImage ppImageWithBitmap: thumbnailBitmap];
745
746ERROR:
747    return nil;
748}
749
750- (void) destroyPopupMenuThumbnailBackgroundBitmap
751{
752    [_popupMenuThumbnailBackgroundBitmap release];
753    _popupMenuThumbnailBackgroundBitmap = nil;
754}
755
756- (void) updateCanvasDisplayMode
757{
758    _canvasDisplayMode = [_ppDocumentWindowController canvasDisplayMode];
759
760    if (_canvasDisplayMode != kPPLayerDisplayMode_DrawingLayerOnly)
761    {
762        _canvasDisplayMode = kPPLayerDisplayMode_VisibleLayers;
763    }
764}
765
766- (void) updateLayerOperationTarget
767{
768    _layerOperationTarget = [_ppDocument layerOperationTarget];
769
770    if (_layerOperationTarget != kPPLayerOperationTarget_DrawingLayerOnly)
771    {
772        _layerOperationTarget = kPPLayerOperationTarget_VisibleLayers;
773    }
774}
775
776- (void) updateLayerControlButtonImages
777{
778    [self updateCanvasDisplayModeButtonImage];
779    [self updateLayerOperationTargetButtonImage];
780
781    _needToUpdateLayerControlButtonImages = NO;
782}
783
784- (void) updateCanvasDisplayModeButtonImage
785{
786    NSImage *buttonImage =
787                [[PPLayerControlButtonImagesManager sharedManager]
788                                                buttonImageForDisplayMode: _canvasDisplayMode];
789
790    // button's current image may already be buttonImage, so force redraw by clearing the image
791    // first
792    [_canvasDisplayModeButton setImage: nil];
793    [_canvasDisplayModeButton setImage: buttonImage];
794}
795
796- (void) updateLayerOperationTargetButtonImage
797{
798    NSImage *buttonImage =
799                [[PPLayerControlButtonImagesManager sharedManager]
800                                        buttonImageForOperationTarget: _layerOperationTarget];
801
802    // button's current image may already be buttonImage, so force redraw by clearing the image
803    // first
804    [_layerOperationTargetButton setImage: nil];
805    [_layerOperationTargetButton setImage: buttonImage];
806}
807
808- (void) updateDrawingLayerControls
809{
810    [_drawingLayerThumbnailView handleUpdateToImage];
811
812    [self updateDrawingLayerAttributeControls];
813
814    _needToUpdateDrawingLayerControls = NO;
815}
816
817- (void) updateDrawingLayerControlsIfPanelIsVisible
818{
819    if ([self panelIsVisible])
820    {
821        [self updateDrawingLayerControls];
822    }
823    else
824    {
825        _needToUpdateDrawingLayerControls = YES;
826    }
827}
828
829- (void) updateDrawingLayerAttributeControls
830{
831    PPDocumentLayer *drawingLayer;
832    bool drawingLayerIsEnabled;
833    NSDictionary *drawingLayerTitleAttributes;
834
835    drawingLayer = [_ppDocument drawingLayer];
836    drawingLayerIsEnabled = [drawingLayer isEnabled];
837
838    [_drawingLayerEnabledCheckbox setIntValue: drawingLayerIsEnabled];
839
840    drawingLayerTitleAttributes =
841            (drawingLayerIsEnabled) ? nil : PPTextAttributesDict_DisabledTitle_PopupButton();
842
843    [_drawingLayerTitleablePopUpButton setTitle: [drawingLayer name]
844                                        withTextAttributes: drawingLayerTitleAttributes];
845
846    [self updateDrawingLayerOpacityTextField];
847
848    [_drawingLayerOpacitySlider setFloatValue: [drawingLayer opacity]];
849}
850
851- (void) updateDrawingLayerOpacityTextField
852{
853    float opacity = [[_ppDocument drawingLayer] opacity] * 100.0f;
854    NSString *opacityString =
855                        [NSString stringWithFormat: kDrawingLayerOpacityFormatString, opacity];
856
857    [_drawingLayerOpacityTextField setStringValue: opacityString];
858}
859
860- (void) updateDrawingLayerPopUpButtonMenu
861{
862    NSMenu *layersMenu;
863    int layerCounter;
864    PPDocumentLayer *layer;
865    NSString *layerName;
866        // use PPSDKNativeType_NSMenuItemPtr for menuItem, as -[NSMenu addItemWithTitle:...]
867        // could return either (NSMenuItem *) or (id <NSMenuItem>), depending on the SDK
868    PPSDKNativeType_NSMenuItemPtr menuItem;
869
870    layersMenu = [[[NSMenu alloc] init] autorelease];
871
872    layerCounter = [_ppDocument numLayers];
873
874    while (layerCounter--)
875    {
876        layer = [_ppDocument layerAtIndex: layerCounter];
877
878        layerName = [layer name];
879
880        if (!layerName)
881        {
882            layerName = @"";
883        }
884
885        menuItem = [layersMenu addItemWithTitle: layerName
886                                action: NULL
887                                keyEquivalent: @""];
888
889        [menuItem setImage: [self popupMenuThumbnailImageForLayerImage: [layer image]]];
890
891        if (![layer isEnabled])
892        {
893            NSAttributedString *attributedTitle;
894
895            attributedTitle =
896                [[[NSAttributedString alloc]
897                                        initWithString: layerName
898                                        attributes:
899                                            PPTextAttributesDict_DisabledTitle_PopupMenuItem()]
900                                    autorelease];
901
902            [menuItem setAttributedTitle: attributedTitle];
903        }
904    }
905
906    [_drawingLayerTitleablePopUpButton setMenu: layersMenu];
907
908    [_drawingLayerTitleablePopUpButton
909        selectItemAtIndex:
910            [self popUpButtonMenuItemIndexForLayerAtIndex: [_ppDocument indexOfDrawingLayer]]];
911
912    _needToUpdateDrawingLayerPopupButtonMenu = NO;
913}
914
915- (int) layerIndexForPopUpButtonMenuItemAtIndex: (int) itemIndex
916{
917    return [_ppDocument numLayers] - itemIndex - 1;
918}
919
920- (int) popUpButtonMenuItemIndexForLayerAtIndex: (int) layerIndex
921{
922    return [_ppDocument numLayers] - layerIndex - 1;
923}
924
925- (float) drawingLayerOpacitySliderQuantizedValue
926{
927    // roundoff slider value to nearest .5% (1/200)
928    return roundf(200.0f * [_drawingLayerOpacitySlider floatValue]) / 200.0f;
929}
930
931@end
932