/* |
File: Controller.m |
Abstract: The controller class, which implements the object used to control and initialize this |
application and as the NSToolbar delegate. |
Version: 1.2 |
|
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple |
Inc. ("Apple") in consideration of your agreement to the following |
terms, and your use, installation, modification or redistribution of |
this Apple software constitutes acceptance of these terms. If you do |
not agree with these terms, please do not use, install, modify or |
redistribute this Apple software. |
|
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. may |
be used to endorse or promote products derived from the Apple Software |
without specific prior written permission from Apple. Except as |
expressly stated in this notice, no other rights or licenses, express or |
implied, are granted by Apple herein, including but not limited to any |
patent rights that may be infringed by your derivative works or by other |
works in which the Apple Software may be incorporated. |
|
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
|
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
|
Copyright (C) 2011 Apple Inc. All Rights Reserved. |
|
*/ |
|
#import "Controller.h" |
|
#define kFontSizeToolbarItemID @"FontSize" |
#define kFontStyleToolbarItemID @"FontStyle" |
#define kBlueLetterToolbarItemID @"BlueLetter" |
|
#pragma mark - |
|
@implementation Controller |
|
//-------------------------------------------------------------------------------------------------- |
// Setup our toolbar at launch time. |
// Create the toolbar and add all the NSToolbarItems, and installing the toolbar in our window. |
//-------------------------------------------------------------------------------------------------- |
- (void)awakeFromNib |
{ |
// configure our toolbar (note: this can also be done in Interface Builder) |
|
// If you pass NO here, you turn off the customization palette. The palette is normally handled |
// automatically for you by NSWindow's -runToolbarCustomizationPalette: method; you'll notice |
// that the "Customize Toolbar" menu item is hooked up to that method in Interface Builder. |
// Interface Builder currently doesn't automatically show this action (or the -toggleToolbarShown: action) |
// for First Responder/NSWindow (this is a bug), so you have to manually add those methods to the |
// First Responder in Interface Builder (by hitting return on the First Responder and adding the |
// new actions in the usual way) if you want to wire up menus to them. |
// |
[toolbar setAllowsUserCustomization:YES]; |
|
// tell the toolbar that it should save any configuration changes to user defaults. ie. mode |
// changes, or reordering will persist. Specifically they will be written in the app domain using |
// the toolbar identifier as the key. |
// |
[toolbar setAutosavesConfiguration:YES]; |
|
// tell the toolbar to show icons only by default |
[toolbar setDisplayMode:NSToolbarDisplayModeIconOnly]; |
|
// initialize our font size control here to 12-point font, and set our contentView (an NSTextView) to that size |
[fontSizeStepper setIntegerValue:12]; |
NSFont *theFont = [NSFont fontWithName:@"Helvetica" size:12]; |
[contentView setFont:theFont]; |
|
// this is a state variable that keeps track of whether we're set to plain-text, bold, or italic font |
fontStylePicked = 1; |
} |
|
//-------------------------------------------------------------------------------------------------- |
- (void)dealloc |
{ |
[super dealloc]; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// Factory method to create autoreleased NSToolbarItems. |
// |
// All NSToolbarItems have a unique identifer associated with them, used to tell your delegate/controller |
// what toolbar items to initialize and return at various points. Typically, for a given identifier, |
// you need to generate a copy of your "master" toolbar item, and return it autoreleased. The function |
// creates an NSToolbarItem with a bunch of NSToolbarItem paramenters. |
// |
// It's easy to call this function repeatedly to generate lots of NSToolbarItems for your toolbar. |
// |
// The label, palettelabel, toolTip, action, and menu can all be nil, depending upon what you want |
// the item to do. |
//-------------------------------------------------------------------------------------------------- |
- (NSToolbarItem *)toolbarItemWithIdentifier:(NSString *)identifier |
label:(NSString *)label |
paleteLabel:(NSString *)paletteLabel |
toolTip:(NSString *)toolTip |
target:(id)target |
itemContent:(id)imageOrView |
action:(SEL)action |
menu:(NSMenu *)menu |
{ |
// here we create the NSToolbarItem and setup its attributes in line with the parameters |
NSToolbarItem *item = [[[NSToolbarItem alloc] initWithItemIdentifier:identifier] autorelease]; |
|
[item setLabel:label]; |
[item setPaletteLabel:paletteLabel]; |
[item setToolTip:toolTip]; |
[item setTarget:target]; |
[item setAction:action]; |
|
// Set the right attribute, depending on if we were given an image or a view |
if([imageOrView isKindOfClass:[NSImage class]]){ |
[item setImage:imageOrView]; |
} else if ([imageOrView isKindOfClass:[NSView class]]){ |
[item setView:imageOrView]; |
}else { |
assert(!"Invalid itemContent: object"); |
} |
|
|
// If this NSToolbarItem is supposed to have a menu "form representation" associated with it |
// (for text-only mode), we set it up here. Actually, you have to hand an NSMenuItem |
// (not a complete NSMenu) to the toolbar item, so we create a dummy NSMenuItem that has our real |
// menu as a submenu. |
// |
if (menu != nil) |
{ |
// we actually need an NSMenuItem here, so we construct one |
NSMenuItem *mItem = [[[NSMenuItem alloc] init] autorelease]; |
[mItem setSubmenu:menu]; |
[mItem setTitle:label]; |
[item setMenuFormRepresentation:mItem]; |
} |
|
return item; |
} |
|
|
#pragma mark - |
#pragma mark Actions |
|
//-------------------------------------------------------------------------------------------------- |
// When you have the toolbar in text-only mode, this action is called |
// by the toolbar item's menu to make the font bigger. We just change the stepper control and |
// call our -changeFontSize: action. |
//-------------------------------------------------------------------------------------------------- |
- (IBAction)fontSizeBigger:(id)sender |
{ |
[fontSizeStepper setIntegerValue:[fontSizeStepper integerValue]+1]; |
[self changeFontSize:nil]; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// When you have the toolbar in text-only mode, this action is called |
// by the toolbar item's menu to make the font smaller. We just change the stepper control and |
// call our -changeFontSize: action. |
//-------------------------------------------------------------------------------------------------- |
- (IBAction)fontSizeSmaller:(id)sender |
{ |
[fontSizeStepper setIntegerValue:[fontSizeStepper integerValue]-1]; |
[self changeFontSize:nil]; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This action is called to change the font size. It's called by the NSPopUpButton in the toolbar item's |
// custom view, and also by the above routines called from the toolbar item's menu (in text-only mode). |
//-------------------------------------------------------------------------------------------------- |
- (IBAction)changeFontSize:(id)sender |
{ |
[fontSizeField takeIntegerValueFrom:fontSizeStepper]; |
|
NSFont *theFont = [contentView font]; |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toSize:[fontSizeStepper integerValue]]; |
[contentView setFont:theFont range:[contentView selectedRange]]; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This action is called from the change font style toolbar item, both from the NSPopUpButton in the |
// custom view, and from the menuFormRepresentation menu. |
//-------------------------------------------------------------------------------------------------- |
- (IBAction)changeFontStyle:(id)sender |
{ |
NSFont *theFont; |
NSInteger itemIndex; |
|
// If the sender is an NSMenuItem then this is the menuFormRepresentation. Otherwise, we are |
// being called from the NSPopUpButton. We need to check this to find out how to calculate the |
// index of the picked menu item. |
// |
if ([NSStringFromClass([sender class]) isEqual:@"NSMenuItem"]) |
{ |
// for ordinary NSMenus, the title is item #0, so we have to offset things |
itemIndex = [[sender menu] indexOfItem:sender]; |
} |
else |
{ |
// this is an NSPopUpButton, so the first useful item really is #0 |
itemIndex = [sender indexOfSelectedItem]; |
} |
|
[fontSizeField takeIntegerValueFrom:fontSizeStepper]; |
theFont = [contentView font]; |
|
// set the font properties depending upon what was selected |
switch (itemIndex) |
{ |
case 0: |
{ |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toNotHaveTrait:NSItalicFontMask]; |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toNotHaveTrait:NSBoldFontMask]; |
break; |
} |
case 1: |
{ |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toNotHaveTrait:NSItalicFontMask]; |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toHaveTrait:NSBoldFontMask]; |
break; |
} |
case 2: |
{ |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toNotHaveTrait:NSBoldFontMask]; |
theFont = [[NSFontManager sharedFontManager] convertFont:theFont toHaveTrait:NSItalicFontMask]; |
break; |
} |
} |
|
// make sure the fontStylePicked variable matches the menu selection plus 1, which also matches |
// the menu item tags in the menuFormRepresentation (see the menu in Interface Builder), so |
// that -validateMenuItem: can do its work. |
// |
fontStylePicked = itemIndex + 1; |
[contentView setFont:theFont range:[contentView selectedRange]]; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This is called by the appropriate toolbar item to toggle blue text on/off. |
//-------------------------------------------------------------------------------------------------- |
- (IBAction)blueText:(id)sender |
{ |
if (![[contentView textColor] isEqual:[NSColor blueColor]]) |
{ |
[contentView setTextColor:[NSColor blueColor] range:[contentView selectedRange]]; |
} |
else |
{ |
[contentView setTextColor:[NSColor controlTextColor] range:[contentView selectedRange]]; |
} |
} |
|
//-------------------------------------------------------------------------------------------------- |
// The NSToolbarPrintItem NSToolbarItem will sent the -printDocument: message to its target. |
// Since we wired its target to be ourselves in -toolbarWillAddItem:, we get called here when |
// the user tries to print by clicking the toolbar item. |
//-------------------------------------------------------------------------------------------------- |
- (void)printDocument:(id)sender |
{ |
NSPrintOperation *printOperation = [NSPrintOperation printOperationWithView: contentView]; |
[printOperation runOperationModalForWindow: [contentView window] delegate:nil didRunSelector:nil contextInfo:nil]; |
} |
|
|
#pragma mark - |
#pragma mark Menu Item Validation |
|
//-------------------------------------------------------------------------------------------------- |
// Update or validate our menu items |
//-------------------------------------------------------------------------------------------------- |
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem |
{ |
// find out which menu item we are attempting to update by examining it's action selector |
// |
if ([menuItem action] == @selector(changeFontStyle:)) |
{ |
// update the menu item's checkmark state if the menu matches are tracked chosen menu item |
if ([menuItem tag] == fontStylePicked) |
{ |
[menuItem setState:NSOnState]; |
} |
else |
{ |
[menuItem setState:NSOffState]; |
} |
|
} |
return YES; |
} |
|
|
#pragma mark - |
#pragma mark NSToolbarItemValidation |
|
//-------------------------------------------------------------------------------------------------- |
// We don't do anything useful here (and we don't really have to implement this method) but you could |
// if you wanted to. If, however, you want to have validation checks on your standard NSToolbarItems |
// (with images) and have inactive ones grayed out, then this is the method for you. |
// It isn't called for custom NSToolbarItems (with custom views); you'd have to override -validate: |
// (see NSToolbarItem.h for a discussion) to get it to do so if you wanted it to. |
// If you don't implement this method, the NSToolbarItems are enabled by default. |
//-------------------------------------------------------------------------------------------------- |
- (BOOL)validateToolbarItem:(NSToolbarItem *)theItem |
{ |
// You could check [theItem itemIdentifier] here and take appropriate action if you wanted to |
return YES; |
} |
|
|
#pragma mark - |
#pragma mark NSToolbarDelegate |
|
//-------------------------------------------------------------------------------------------------- |
// This is an optional delegate method, called when a new item is about to be added to the toolbar. |
// This is a good spot to set up initial state information for toolbar items, particularly ones |
// that you don't directly control yourself (like with NSToolbarPrintItemIdentifier here). |
// The notification's object is the toolbar, and the @"item" key in the userInfo is the toolbar item |
// being added. |
//-------------------------------------------------------------------------------------------------- |
- (void)toolbarWillAddItem:(NSNotification *)notif |
{ |
NSToolbarItem *addedItem = [[notif userInfo] objectForKey:@"item"]; |
|
// Is this the printing toolbar item? If so, then we want to redirect it's action to ourselves |
// so we can handle the printing properly; hence, we give it a new target. |
// |
if ([[addedItem itemIdentifier] isEqual: NSToolbarPrintItemIdentifier]) |
{ |
[addedItem setToolTip:@"Print your document"]; |
[addedItem setTarget:self]; |
} |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This method is required of NSToolbar delegates. |
// It takes an identifier, and returns the matching NSToolbarItem. It also takes a parameter telling |
// whether this toolbar item is going into an actual toolbar, or whether it's going to be displayed |
// in a customization palette. |
//-------------------------------------------------------------------------------------------------- |
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag |
{ |
NSToolbarItem *toolbarItem = nil; |
|
// We create and autorelease a new NSToolbarItem, and then go through the process of setting up its |
// attributes from the master toolbar item matching that identifier in our dictionary of items. |
if ([itemIdentifier isEqualToString:kFontStyleToolbarItemID]) |
{ |
// 1) Font style toolbar item |
toolbarItem = [self toolbarItemWithIdentifier:kFontStyleToolbarItemID |
label:@"Font Style" |
paleteLabel:@"Font Style" |
toolTip:@"Change your font style" |
target:self |
itemContent:stylePopUpView |
action:nil |
menu:fontStyleMenu]; |
} |
else if ([itemIdentifier isEqualToString:kFontSizeToolbarItemID]) |
{ |
// 2) Font size toolbar item |
toolbarItem = [self toolbarItemWithIdentifier:kFontSizeToolbarItemID |
label:@"Font Size" |
paleteLabel:@"Font Size" |
toolTip:@"Grow or shrink the size of your font" |
target:self |
itemContent:fontSizeView |
action:nil |
menu:fontSizeMenu]; |
} |
else if ([itemIdentifier isEqualToString:kBlueLetterToolbarItemID]) |
{ |
// 3) Blue text toolbar item |
// often using an image will be your standard case. You'll notice that a selector is passed |
// for the action (blueText:), which will be called when the image-containing toolbar item is clicked. |
// |
toolbarItem = [self toolbarItemWithIdentifier:kBlueLetterToolbarItemID |
label:@"Blue Text" |
paleteLabel:@"Blue Text" |
toolTip:@"This toggles blue text on/off" |
target:self |
itemContent:[NSImage imageNamed:@"blueLetter.tif"] |
action:@selector(blueText:) |
menu:nil]; |
} |
|
return toolbarItem; |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This method is required of NSToolbar delegates. It returns an array holding identifiers for the default |
// set of toolbar items. It can also be called by the customization palette to display the default toolbar. |
//-------------------------------------------------------------------------------------------------- |
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar |
{ |
return [NSArray arrayWithObjects: kFontStyleToolbarItemID, |
kFontSizeToolbarItemID, |
kBlueLetterToolbarItemID, |
NSToolbarPrintItemIdentifier, |
nil]; |
// note: |
// that since our toolbar is defined from Interface Builder, an additional separator and customize |
// toolbar items will be automatically added to the "default" list of items. |
} |
|
//-------------------------------------------------------------------------------------------------- |
// This method is required of NSToolbar delegates. It returns an array holding identifiers for all allowed |
// toolbar items in this toolbar. Any not listed here will not be available in the customization palette. |
//-------------------------------------------------------------------------------------------------- |
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar |
{ |
return [NSArray arrayWithObjects: kFontStyleToolbarItemID, |
kFontSizeToolbarItemID, |
kBlueLetterToolbarItemID, |
NSToolbarSpaceItemIdentifier, |
NSToolbarFlexibleSpaceItemIdentifier, |
NSToolbarPrintItemIdentifier, |
nil]; |
// note: |
// that since our toolbar is defined from Interface Builder, an additional separator and customize |
// toolbar items will be automatically added to the "default" list of items. |
} |
|
@end |