1/*
2    PPDocumentWindowController_MenuValidation.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 "PPDocumentWindowController.h"
26
27#import "PPDocument.h"
28#import "PPCanvasView.h"
29#import "PPDocumentLayer.h"
30#import "PPPanelsController.h"
31
32
33static void SetupGlobals(void);
34static SEL SelectorFromDictForMenuActionName(NSDictionary *selectorDict, NSString *actionName);
35static NSDictionary *MenuItemValidationSelectorDict(void);
36static NSDictionary *MenuItemModificationSelectorDict(void);
37static bool MenuActionNameIsAllowedDuringMouseTracking(NSString *actionName);
38static bool MenuActionNameIsAllowedDuringPopupPanel(NSString *actionName);
39
40static bool gNeedToSetupGlobals = YES;
41static NSDictionary *gMenuItemModificationSelectorDict = nil;
42static NSDictionary *gMenuItemValidationSelectorDict = nil;
43static NSNumber *gNumber_YES = nil, *gNumber_NO = nil;
44
45
46@interface PPDocumentWindowController (MenuValidationPrivateMethods)
47
48- (NSNumber *) documentIsValid;
49- (NSNumber *) documentHasSelection;
50- (NSNumber *) documentHasSelectionAndDrawLayerIsEnabled;
51- (NSNumber *) documentHasSelectionAndEnabledTargetLayer;
52- (NSNumber *) documentHasSelectionOrEnabledTargetLayer;
53- (NSNumber *) documentHasMultipleLayers;
54- (NSNumber *) documentHasValidLayerAboveDrawingLayer;
55- (NSNumber *) documentHasValidLayerBelowDrawingLayer;
56- (NSNumber *) documentActiveLayerCanIncreaseOpacity;
57- (NSNumber *) documentActiveLayerCanDecreaseOpacity;
58- (NSNumber *) documentHasVisibleGrid;
59- (NSNumber *) documentHasBackgroundImage;
60- (NSNumber *) documentHasVisibleBackgroundImage;
61- (NSNumber *) documentHasActiveSamplerImageForPanel;
62- (NSNumber *) documentHasMultipleSamplerImagesAndSamplerPanelIsVisible;
63- (NSNumber *) documentCanReadFromPasteboard;
64- (NSNumber *) canvasViewCanZoomIn;
65- (NSNumber *) canvasViewCanZoomOut;
66
67- (void) modifyMenuItemForLayerEnabled: (NSMenuItem *) menuItem;
68- (void) modifyMenuItemForCanvasDisplayMode: (NSMenuItem *) menuItem;
69- (void) modifyMenuItemForLayerBlendingMode: (NSMenuItem *) menuItem;
70- (void) modifyMenuItemForGridVisibilityStatus: (NSMenuItem *) menuItem;
71- (void) modifyMenuItemForGridType: (NSMenuItem *) menuItem;
72- (void) modifyMenuItemForGridGuidelinesVisibilityStatus: (NSMenuItem *) menuItem;
73- (void) modifyMenuItemForBackgroundImageVisibilityStatus: (NSMenuItem *) menuItem;
74- (void) modifyMenuItemForBackgroundImageSmoothingStatus: (NSMenuItem *) menuItem;
75- (void) modifyMenuItemForLayerOperationTarget: (NSMenuItem *) menuItem;
76- (void) modifyMenuItemForToolsPanelVisibilityStatus: (NSMenuItem *) menuItem;
77- (void) modifyMenuItemForLayersPanelVisibilityStatus: (NSMenuItem *) menuItem;
78- (void) modifyMenuItemForPreviewPanelVisibilityStatus: (NSMenuItem *) menuItem;
79- (void) modifyMenuItemForSamplerImagePanelVisibilityStatus: (NSMenuItem *) menuItem;
80- (void) modifyMenuItemForToolModifierTipsPanelVisibilityStatus: (NSMenuItem *) menuItem;
81
82@end
83
84@interface NSMenuItem (PPUtilities_MenuValidationPrivateMethods)
85
86- (void) ppSetTitleModeNameSuffix: (NSString *) modeName;
87
88@end
89
90@implementation PPDocumentWindowController (MenuValidation)
91
92- (BOOL) validateMenuItem: (PPSDKNativeType_NSMenuItemPtr) menuItem
93{
94    SEL menuItemAction, modificationSelector, validationSelector;
95    NSString *actionName;
96
97    if (gNeedToSetupGlobals)
98    {
99        SetupGlobals();
100    }
101
102    if (!_documentWindowIsKey)
103    {
104        return NO;
105    }
106
107    menuItemAction = [menuItem action];
108
109    if (!menuItemAction)
110        goto ERROR;
111
112    actionName = NSStringFromSelector(menuItemAction);
113
114    if (!actionName)
115        goto ERROR;
116
117    modificationSelector =
118        SelectorFromDictForMenuActionName(gMenuItemModificationSelectorDict, actionName);
119
120    if (modificationSelector)
121    {
122        [self performSelector: modificationSelector withObject: menuItem];
123    }
124
125    if (_isTrackingMouseInCanvasView
126        && !MenuActionNameIsAllowedDuringMouseTracking(actionName))
127    {
128        return NO;
129    }
130
131    if (_pressedHotkeyForActivePopupPanel
132        && !MenuActionNameIsAllowedDuringPopupPanel(actionName))
133    {
134        return NO;
135    }
136
137    validationSelector =
138        SelectorFromDictForMenuActionName(gMenuItemValidationSelectorDict, actionName);
139
140    if (validationSelector)
141    {
142        return [[self performSelector: validationSelector] boolValue];
143    }
144
145    return NO;
146
147ERROR:
148    return NO;
149}
150
151- (NSNumber *) documentIsValid
152{
153    return ([_ppDocument numLayers]) ? gNumber_YES : gNumber_NO;
154}
155
156- (NSNumber *) documentHasSelection
157{
158    return ([_ppDocument hasSelection]) ? gNumber_YES : gNumber_NO;
159}
160
161- (NSNumber *) documentHasSelectionAndDrawLayerIsEnabled
162{
163    return ([_ppDocument hasSelection] && [[_ppDocument drawingLayer] isEnabled]) ?
164                gNumber_YES : gNumber_NO;
165}
166
167- (NSNumber *) documentHasSelectionAndEnabledTargetLayer
168{
169    return ([_ppDocument hasSelection] && [_ppDocument layerOperationTargetHasEnabledLayer]) ?
170                gNumber_YES : gNumber_NO;
171}
172
173- (NSNumber *) documentHasSelectionOrEnabledTargetLayer
174{
175    return ([_ppDocument hasSelection] || [_ppDocument layerOperationTargetHasEnabledLayer]) ?
176                gNumber_YES : gNumber_NO;
177}
178
179- (NSNumber *) documentHasMultipleLayers
180{
181    return ([_ppDocument numLayers] > 1) ? gNumber_YES : gNumber_NO;
182}
183
184- (NSNumber *) documentHasValidLayerAboveDrawingLayer
185{
186    return ([_ppDocument indexOfDrawingLayer] < ([_ppDocument numLayers] - 1)) ?
187                gNumber_YES : gNumber_NO;
188}
189
190- (NSNumber *) documentHasValidLayerBelowDrawingLayer
191{
192    return ([_ppDocument indexOfDrawingLayer] > 0) ? gNumber_YES : gNumber_NO;
193}
194
195- (NSNumber *) documentActiveLayerCanIncreaseOpacity
196{
197    return ([[_ppDocument drawingLayer] canIncreaseOpacity]) ? gNumber_YES : gNumber_NO;
198}
199
200- (NSNumber *) documentActiveLayerCanDecreaseOpacity
201{
202    return ([[_ppDocument drawingLayer] canDecreaseOpacity]) ? gNumber_YES : gNumber_NO;
203}
204
205- (NSNumber *) documentHasVisibleGrid
206{
207    return ([_ppDocument shouldDisplayGrid]) ? gNumber_YES : gNumber_NO;
208}
209
210- (NSNumber *) documentHasBackgroundImage
211{
212    return ([_ppDocument backgroundImage]) ? gNumber_YES : gNumber_NO;
213}
214
215- (NSNumber *) documentHasVisibleBackgroundImage
216{
217    return ([_ppDocument backgroundImage] && [_ppDocument shouldDisplayBackgroundImage]) ?
218                gNumber_YES : gNumber_NO;
219}
220
221- (NSNumber *) documentHasActiveSamplerImageForPanel
222{
223    return ([_ppDocument hasActiveSamplerImageForPanelType: kPPSamplerImagePanelType_Panel]) ?
224                gNumber_YES : gNumber_NO;
225}
226
227- (NSNumber *) documentHasMultipleSamplerImagesAndSamplerPanelIsVisible
228{
229    return (([_ppDocument numSamplerImages] > 1)
230                && [_panelsController panelOfTypeIsVisible: kPPPanelType_SamplerImage]) ?
231                    gNumber_YES : gNumber_NO;
232}
233
234- (NSNumber *) documentCanReadFromPasteboard
235{
236    return ([_ppDocument canReadFromPasteboard]) ? gNumber_YES : gNumber_NO;
237}
238
239- (NSNumber *) canvasViewCanZoomIn
240{
241    return ([_canvasView canIncreaseZoomFactor]) ? gNumber_YES : gNumber_NO;
242}
243
244- (NSNumber *) canvasViewCanZoomOut
245{
246    return ([_canvasView canDecreaseZoomFactor]) ? gNumber_YES : gNumber_NO;
247}
248
249#pragma mark Menu item modifications
250
251- (void) modifyMenuItemForLayerEnabled: (NSMenuItem *) menuItem
252{
253    [menuItem setState: ([[_ppDocument drawingLayer] isEnabled]) ? NSOnState : NSOffState];
254}
255
256- (void) modifyMenuItemForLayerBlendingMode: (NSMenuItem *) menuItem
257{
258    NSString *modeName = nil;
259
260    if ([self documentIsValid])
261    {
262        if ([_ppDocument layerBlendingMode] == kPPLayerBlendingMode_Linear)
263        {
264            modeName = @"LINEAR";
265        }
266        else
267        {
268            modeName = @"STANDARD";
269        }
270    }
271
272    [menuItem ppSetTitleModeNameSuffix: modeName];
273}
274
275- (void) modifyMenuItemForCanvasDisplayMode: (NSMenuItem *) menuItem
276{
277    NSString *modeName = nil;
278
279    if ([self documentIsValid])
280    {
281        if (_canvasDisplayMode == kPPLayerDisplayMode_DrawingLayerOnly)
282        {
283            modeName = @"DRAW Layer";
284        }
285        else
286        {
287            modeName = @"ENABLED Layers";
288        }
289    }
290
291    [menuItem ppSetTitleModeNameSuffix: modeName];
292}
293
294- (void) modifyMenuItemForGridVisibilityStatus: (NSMenuItem *) menuItem
295{
296    [menuItem setState: ([_ppDocument shouldDisplayGrid]) ? NSOnState : NSOffState];
297}
298
299- (void) modifyMenuItemForGridType: (NSMenuItem *) menuItem
300{
301    NSString *modeName = nil;
302
303    if ([self documentIsValid])
304    {
305        switch ([_ppDocument pixelGridPatternType])
306        {
307            case kPPGridType_Crosshairs:
308            {
309                modeName = @"Crosshairs";
310            }
311            break;
312
313            case kPPGridType_LargeDots:
314            {
315                modeName = @"Large Dots";
316            }
317            break;
318
319            case kPPGridType_Dots:
320            {
321                modeName = @"Dots";
322            }
323            break;
324
325            case kPPGridType_Lines:
326            default:
327            {
328                modeName = @"Lines";
329            }
330            break;
331        }
332    }
333
334    [menuItem ppSetTitleModeNameSuffix: modeName];
335}
336
337- (void) modifyMenuItemForGridGuidelinesVisibilityStatus: (NSMenuItem *) menuItem
338{
339    [menuItem setState:
340                ([_ppDocument gridPatternShouldDisplayGuidelines]) ? NSOnState : NSOffState];
341}
342
343- (void) modifyMenuItemForBackgroundImageVisibilityStatus: (NSMenuItem *) menuItem
344{
345    [menuItem setState: ([_ppDocument shouldDisplayBackgroundImage]) ? NSOnState : NSOffState];
346}
347
348- (void) modifyMenuItemForBackgroundImageSmoothingStatus: (NSMenuItem *) menuItem
349{
350    [menuItem setState: ([_ppDocument shouldSmoothenBackgroundImage]) ? NSOnState : NSOffState];
351}
352
353- (void) modifyMenuItemForLayerOperationTarget: (NSMenuItem *) menuItem
354{
355    NSString *modeName = nil;
356
357    if ([self documentIsValid])
358    {
359        if ([_ppDocument layerOperationTarget] == kPPLayerOperationTarget_DrawingLayerOnly)
360        {
361            modeName = @"DRAW Layer";
362        }
363        else
364        {
365            modeName = @"ENABLED Layers";
366        }
367    }
368
369    [menuItem ppSetTitleModeNameSuffix: modeName];
370}
371
372- (void) modifyMenuItemForToolsPanelVisibilityStatus: (NSMenuItem *) menuItem
373{
374    [menuItem setState:
375                ([_panelsController panelOfTypeIsVisible: kPPPanelType_Tools]) ?
376                                                                    NSOnState : NSOffState];
377}
378
379- (void) modifyMenuItemForLayersPanelVisibilityStatus: (NSMenuItem *) menuItem
380{
381    [menuItem setState:
382                ([_panelsController panelOfTypeIsVisible: kPPPanelType_Layers]) ?
383                                                                    NSOnState : NSOffState];
384}
385
386- (void) modifyMenuItemForPreviewPanelVisibilityStatus: (NSMenuItem *) menuItem
387{
388    [menuItem setState:
389                ([_panelsController panelOfTypeIsVisible: kPPPanelType_Preview]) ?
390                                                                    NSOnState : NSOffState];
391}
392
393- (void) modifyMenuItemForSamplerImagePanelVisibilityStatus: (NSMenuItem *) menuItem
394{
395    [menuItem setState:
396                ([_panelsController panelOfTypeIsVisible: kPPPanelType_SamplerImage]) ?
397                                                                    NSOnState : NSOffState];
398}
399
400- (void) modifyMenuItemForToolModifierTipsPanelVisibilityStatus: (NSMenuItem *) menuItem
401{
402    [menuItem setState:
403                ([_panelsController panelOfTypeIsVisible: kPPPanelType_ToolModifierTips]) ?
404                                                                    NSOnState : NSOffState];
405}
406
407@end
408
409@implementation NSMenuItem (PPUtilities_MenuValidationPrivateMethods)
410
411- (void) ppSetTitleModeNameSuffix: (NSString *) modeName
412{
413    NSString *currentTitle, *newTitle;
414    NSRange colonRange;
415
416    currentTitle = [self title];
417
418    if (!currentTitle)
419    {
420        currentTitle = @"";
421    }
422
423    colonRange = [currentTitle rangeOfString: @":"];
424
425    if (colonRange.length)
426    {
427        newTitle = [currentTitle substringToIndex: colonRange.location];
428    }
429    else
430    {
431        newTitle = currentTitle;
432    }
433
434    if (modeName)
435    {
436        newTitle = [newTitle stringByAppendingFormat: @": %@", modeName];
437    }
438
439    if (![newTitle isEqualToString: currentTitle])
440    {
441        [self setTitle: newTitle];
442    }
443}
444
445@end
446
447#pragma mark Private functions
448
449static void SetupGlobals(void)
450{
451    if (!gNeedToSetupGlobals)
452        return;
453
454    gMenuItemModificationSelectorDict = [MenuItemModificationSelectorDict() retain];
455    gMenuItemValidationSelectorDict = [MenuItemValidationSelectorDict() retain];
456
457    gNumber_YES = [[NSNumber numberWithBool: YES] retain];
458    gNumber_NO = [[NSNumber numberWithBool: NO] retain];
459
460    gNeedToSetupGlobals = NO;
461}
462
463static SEL SelectorFromDictForMenuActionName(NSDictionary *selectorDict, NSString *actionName)
464{
465    NSString *dictSelectorName;
466
467    if (!selectorDict || !actionName)
468    {
469        return NULL;
470    }
471
472    dictSelectorName = [selectorDict objectForKey: actionName];
473
474    if (!dictSelectorName)
475    {
476        return NULL;
477    }
478
479    return NSSelectorFromString(dictSelectorName);
480}
481
482static NSDictionary *MenuItemValidationSelectorDict(void)
483{
484    return [NSDictionary dictionaryWithObjectsAndKeys:
485
486                                                // File menu
487
488                                                    @"documentHasSelectionAndEnabledTargetLayer",
489                                                @"newDocumentFromSelection:",
490
491                                                    @"documentIsValid",
492                                                @"exportImage:",
493
494                                                // Edit menu
495
496                                                    @"documentHasSelection",
497                                                @"cut:",
498
499                                                    @"documentHasSelection",
500                                                @"copy:",
501
502                                                    @"documentCanReadFromPasteboard",
503                                                @"paste:",
504
505                                                    @"documentCanReadFromPasteboard",
506                                                @"pasteIntoActiveLayer:",
507
508                                                    @"documentHasSelection",
509                                                @"delete:",
510
511                                                    @"documentIsValid",
512                                                @"selectAll:",
513
514                                                    @"documentHasSelection",
515                                                @"deselectAll:",
516
517                                                    @"documentIsValid",
518                                                @"selectVisibleTargetPixels:",
519
520                                                    @"documentHasSelection",
521                                                @"deselectInvisibleTargetPixels:",
522
523                                                    @"documentIsValid",
524                                                @"invertSelection:",
525
526                                                    @"documentHasSelection",
527                                                @"nudgeSelectionOutlineLeft:",
528
529                                                    @"documentHasSelection",
530                                                @"nudgeSelectionOutlineRight:",
531
532                                                    @"documentHasSelection",
533                                                @"nudgeSelectionOutlineUp:",
534
535                                                    @"documentHasSelection",
536                                                @"nudgeSelectionOutlineDown:",
537
538                                                    @"documentHasSelection",
539                                                @"closeHolesInSelection:",
540
541                                                    @"documentHasSelectionAndDrawLayerIsEnabled",
542                                                @"fillSelectedPixels:",
543
544                                                    @"documentHasSelectionAndDrawLayerIsEnabled",
545                                                @"eraseSelectedPixels:",
546
547                                                    @"documentHasSelectionAndEnabledTargetLayer",
548                                                @"tileSelection:",
549
550                                                    @"documentHasSelectionAndEnabledTargetLayer",
551                                                @"tileSelectionAsNewLayer:",
552
553                                                // Layer menu
554
555                                                     @"documentIsValid",
556                                                @"newLayer:",
557
558                                                    @"documentIsValid",
559                                                @"duplicateActiveLayer:",
560
561                                                    @"documentIsValid",
562                                                @"deleteActiveLayer:",
563
564                                                   @"documentIsValid",
565                                                @"toggleActiveLayerEnabledFlag:",
566
567                                                    @"documentIsValid",
568                                                @"enableAllLayers:",
569
570                                                    @"documentIsValid",
571                                                @"disableAllLayers:",
572
573                                                    @"documentActiveLayerCanIncreaseOpacity",
574                                                @"increaseActiveLayerOpacity:",
575
576                                                    @"documentActiveLayerCanDecreaseOpacity",
577                                                @"decreaseActiveLayerOpacity:",
578
579                                                    @"documentHasValidLayerAboveDrawingLayer",
580                                                @"makePreviousLayerActive:",
581
582                                                    @"documentHasValidLayerBelowDrawingLayer",
583                                                @"makeNextLayerActive:",
584
585                                                    @"documentHasValidLayerAboveDrawingLayer",
586                                                @"moveActiveLayerUp:",
587
588                                                    @"documentHasValidLayerBelowDrawingLayer",
589                                                @"moveActiveLayerDown:",
590
591                                                    @"documentHasValidLayerAboveDrawingLayer",
592                                                @"mergeWithLayerAbove:",
593
594                                                    @"documentHasValidLayerBelowDrawingLayer",
595                                                @"mergeWithLayerBelow:",
596
597                                                    @"documentHasMultipleLayers",
598                                                @"mergeAllLayers:",
599
600                                                    @"documentIsValid",
601                                                @"toggleLayerBlendingMode:",
602
603                                                // Canvas menu
604
605                                                    @"documentHasMultipleLayers",
606                                                @"toggleCanvasDisplayMode:",
607
608                                                    @"canvasViewCanZoomIn",
609                                                @"increaseZoom:",
610
611                                                    @"canvasViewCanZoomOut",
612                                                @"decreaseZoom:",
613
614                                                    @"documentIsValid",
615                                                @"zoomToFit:",
616
617                                                    @"documentIsValid",
618                                                @"editGridSettings:",
619
620                                                    @"documentIsValid",
621                                                @"toggleGridVisibility:",
622
623                                                    @"documentHasVisibleGrid",
624                                                @"toggleGridType:",
625
626                                                    @"documentHasVisibleGrid",
627                                                @"toggleGridGuidelinesVisibility:",
628
629                                                    @"documentIsValid",
630                                                @"editBackgroundSettings:",
631
632                                                    @"documentHasBackgroundImage",
633                                                @"toggleBackgroundImageVisibility:",
634
635                                                    @"documentHasVisibleBackgroundImage",
636                                                @"toggleBackgroundImageSmoothing:",
637
638                                                    @"documentIsValid",
639                                                @"flipCanvasHorizontally:",
640
641                                                    @"documentIsValid",
642                                                @"flipCanvasVertically:",
643
644                                                    @"documentIsValid",
645                                                @"rotateCanvas90Clockwise:",
646
647                                                    @"documentIsValid",
648                                                @"rotateCanvas90Counterclockwise:",
649
650                                                    @"documentIsValid",
651                                                @"rotateCanvas180:",
652
653                                                    @"documentIsValid",
654                                                @"resize:",
655
656                                                    @"documentIsValid",
657                                                @"scale:",
658
659                                                    @"documentHasSelection",
660                                                @"cropToSelection:",
661
662                                                // Operation menu
663
664                                                    @"documentHasMultipleLayers",
665                                                @"toggleLayerOperationTarget:",
666
667                                                    @"documentHasSelectionOrEnabledTargetLayer",
668                                                @"flipHorizontally:",
669
670                                                    @"documentHasSelectionOrEnabledTargetLayer",
671                                                @"flipVertically:",
672
673                                                    @"documentHasSelectionOrEnabledTargetLayer",
674                                                @"rotate90Clockwise:",
675
676                                                    @"documentHasSelectionOrEnabledTargetLayer",
677                                                @"rotate90Counterclockwise:",
678
679                                                    @"documentHasSelectionOrEnabledTargetLayer",
680                                                @"rotate180:",
681
682                                                    @"documentHasSelectionOrEnabledTargetLayer",
683                                                @"nudgeLeft:",
684
685                                                    @"documentHasSelectionOrEnabledTargetLayer",
686                                                @"nudgeRight:",
687
688                                                    @"documentHasSelectionOrEnabledTargetLayer",
689                                                @"nudgeUp:",
690
691                                                    @"documentHasSelectionOrEnabledTargetLayer",
692                                                @"nudgeDown:",
693
694                                                // Panels menu
695
696                                                    @"documentIsValid",
697                                                @"toggleToolsPanelVisibility:",
698
699                                                    @"documentIsValid",
700                                                @"toggleLayersPanelVisibility:",
701
702                                                    @"documentIsValid",
703                                                @"togglePreviewPanelVisibility:",
704
705                                                    @"documentHasActiveSamplerImageForPanel",
706                                                @"toggleSamplerImagePanelVisibility:",
707
708                                                    @"documentIsValid",
709                                                @"toggleToolModifierTipsPanelVisibility:",
710
711                                                    @"documentIsValid",
712                                                @"toggleActivePanelsVisibility:",
713
714                                                    @"documentIsValid",
715                                                @"toggleColorPickerVisibility:",
716
717                                                    @"documentIsValid",
718                                                @"editSamplerImagesSettings:",
719
720                                    @"documentHasMultipleSamplerImagesAndSamplerPanelIsVisible",
721                                                @"nextSamplerPanelImage:",
722
723                                    @"documentHasMultipleSamplerImagesAndSamplerPanelIsVisible",
724                                                @"previousSamplerPanelImage:",
725
726                                                    nil];
727}
728
729static NSDictionary *MenuItemModificationSelectorDict(void)
730{
731    return [NSDictionary dictionaryWithObjectsAndKeys:
732
733                                    @"modifyMenuItemForLayerEnabled:",
734                                @"toggleActiveLayerEnabledFlag:",
735
736                                    @"modifyMenuItemForLayerBlendingMode:",
737                                @"toggleLayerBlendingMode:",
738
739                                    @"modifyMenuItemForCanvasDisplayMode:",
740                                @"toggleCanvasDisplayMode:",
741
742                                    @"modifyMenuItemForGridVisibilityStatus:",
743                                @"toggleGridVisibility:",
744
745                                    @"modifyMenuItemForGridType:",
746                                @"toggleGridType:",
747
748                                    @"modifyMenuItemForGridGuidelinesVisibilityStatus:",
749                                @"toggleGridGuidelinesVisibility:",
750
751                                    @"modifyMenuItemForBackgroundImageVisibilityStatus:",
752                                @"toggleBackgroundImageVisibility:",
753
754                                    @"modifyMenuItemForBackgroundImageSmoothingStatus:",
755                                @"toggleBackgroundImageSmoothing:",
756
757                                    @"modifyMenuItemForLayerOperationTarget:",
758                                @"toggleLayerOperationTarget:",
759
760                                    @"modifyMenuItemForToolsPanelVisibilityStatus:",
761                                @"toggleToolsPanelVisibility:",
762
763                                    @"modifyMenuItemForLayersPanelVisibilityStatus:",
764                                @"toggleLayersPanelVisibility:",
765
766                                    @"modifyMenuItemForPreviewPanelVisibilityStatus:",
767                                @"togglePreviewPanelVisibility:",
768
769                                    @"modifyMenuItemForSamplerImagePanelVisibilityStatus:",
770                                @"toggleSamplerImagePanelVisibility:",
771
772                                    @"modifyMenuItemForToolModifierTipsPanelVisibilityStatus:",
773                                @"toggleToolModifierTipsPanelVisibility:",
774
775                                    nil];
776}
777
778static bool MenuActionNameIsAllowedDuringMouseTracking(NSString *actionName)
779{
780    static NSSet *allowedMouseTrackingActionNamesSet = nil;
781
782    if (!actionName)
783        goto ERROR;
784
785    if (!allowedMouseTrackingActionNamesSet)
786    {
787        allowedMouseTrackingActionNamesSet =
788                                [[NSSet setWithObjects:
789                                                    @"toggleGridVisibility:",
790                                                    @"toggleGridType:",
791                                                    @"toggleGridGuidelinesVisibility:",
792                                                    @"toggleBackgroundImageVisibility:",
793                                                    @"toggleBackgroundImageSmoothing:",
794                                                    @"toggleToolsPanelVisibility:",
795                                                    @"toggleLayersPanelVisibility:",
796                                                    @"togglePreviewPanelVisibility:",
797                                                    @"toggleSamplerImagePanelVisibility:",
798                                                    @"toggleToolModifierTipsPanelVisibility:",
799                                                    @"toggleActivePanelsVisibility:",
800                                                    @"toggleColorPickerVisibility:",
801                                                    @"nextSamplerPanelImage:",
802                                                    @"previousSamplerPanelImage:",
803                                                    nil]
804                                            retain];
805    }
806
807    return ([allowedMouseTrackingActionNamesSet containsObject: actionName]) ? YES : NO;
808
809ERROR:
810    return NO;
811}
812
813static bool MenuActionNameIsAllowedDuringPopupPanel(NSString *actionName)
814{
815    static NSSet *disallowedPopupPanelActionNamesSet = nil;
816
817    if (!actionName)
818        goto ERROR;
819
820    if (!disallowedPopupPanelActionNamesSet)
821    {
822        disallowedPopupPanelActionNamesSet =
823                                [[NSSet setWithObjects:
824                                                    @"nextSamplerPanelImage:",
825                                                    @"previousSamplerPanelImage:",
826                                                    nil]
827                                            retain];
828    }
829
830    return ([disallowedPopupPanelActionNamesSet containsObject: actionName]) ? NO : YES;
831
832ERROR:
833    return NO;
834}
835