Files
2018-01-13 00:30:16 +08:00

303 lines
13 KiB
Objective-C

//
// MPToolbarController.m
// MacDown
//
// Created by Niklas Berglund on 2017-02-12.
// Copyright © 2017 Tzu-ping Chung . All rights reserved.
//
#import "MPToolbarController.h"
// Because we're creating selectors for methods which aren't in this class
#pragma GCC diagnostic ignored "-Wundeclared-selector"
#pragma clang diagnostic ignored "-Wundeclared-selector"
static CGFloat itemWidth = 37;
@implementation MPToolbarController
{
NSArray *toolbarItems;
NSArray *toolbarItemIdentifiers;
/**
* Map toolbar item identifier to it's NSToolbarItem or NSToolbarItemGroup object
*/
NSMutableDictionary *toolbarItemIdentifierObjectDictionary;
}
- (id)init
{
self = [super init];
if (!self)
{
return nil;
}
self->toolbarItemIdentifierObjectDictionary = [NSMutableDictionary new];
[self setupToolbarItems];
return self;
}
#pragma mark - Private
- (void)setupToolbarItems
{
// Set up layout drop down alternatives. title will be set in validateUserInterfaceItem:
NSMenuItem *toggleEditorMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:@selector(toggleEditorPane:) keyEquivalent:@"e"];
NSMenuItem *togglePreviewMenuItem = [[NSMenuItem alloc] initWithTitle:@"" action:@selector(togglePreviewPane:) keyEquivalent:@"p"];
// Set up all available toolbar items
self->toolbarItems = @[
[self toolbarItemGroupWithIdentifier:@"indent-group" separated:YES label:NSLocalizedString(@"Shift Left/Right", @"") items:@[
[self toolbarItemWithIdentifier:@"shift-left" label:NSLocalizedString(@"Shift Left", @"Shift text to the left toolbar button") icon:@"ToolbarIconShiftLeft" action:@selector(unindent:)],
[self toolbarItemWithIdentifier:@"shift-right" label:NSLocalizedString(@"Shift Right", @"Shift text to the right toolbar button") icon:@"ToolbarIconShiftRight" action:@selector(indent:)]
]
],
[self toolbarItemGroupWithIdentifier:@"text-formatting-group" separated:NO label:NSLocalizedString(@"Text Styles", @"") items:@[
[self toolbarItemWithIdentifier:@"bold" label:NSLocalizedString(@"Strong", @"Strong toolbar button") icon:@"ToolbarIconBold" action:@selector(toggleStrong:)],
[self toolbarItemWithIdentifier:@"italic" label:NSLocalizedString(@"Emphasize", @"Emphasize toolbar button") icon:@"ToolbarIconItalic" action:@selector(toggleEmphasis:)],
[self toolbarItemWithIdentifier:@"underline" label:NSLocalizedString(@"Underline", @"Underline toolbar button") icon:@"ToolbarIconUnderlined" action:@selector(toggleUnderline:)]
]
],
[self toolbarItemGroupWithIdentifier:@"heading-group" separated:NO label:NSLocalizedString(@"Headings", @"") items:@[
[self toolbarItemWithIdentifier:@"heading1" label:NSLocalizedString(@"Heading 1", @"Heading 1 toolbar button") icon:@"ToolbarIconHeading1" action:@selector(convertToH1:)],
[self toolbarItemWithIdentifier:@"heading2" label:NSLocalizedString(@"Heading 2", @"Heading 2 toolbar button") icon:@"ToolbarIconHeading2" action:@selector(convertToH2:)],
[self toolbarItemWithIdentifier:@"heading3" label:NSLocalizedString(@"Heading 3", @"Heading 3 toolbar button") icon:@"ToolbarIconHeading3" action:@selector(convertToH3:)]
]
],
[self toolbarItemGroupWithIdentifier:@"list-group" separated:YES label:NSLocalizedString(@"Ordered/Unordered List", @"") items:@[
[self toolbarItemWithIdentifier:@"unordered-list" label:NSLocalizedString(@"Unordered List", @"Unordered list toolbar button") icon:@"ToolbarIconUnorderedList" action:@selector(toggleUnorderedList:)],
[self toolbarItemWithIdentifier:@"ordered-list" label:NSLocalizedString(@"Ordered List", @"Ordered list toolbar button") icon:@"ToolbarIconOrderedList" action:@selector(toggleOrderedList:)]
]
],
[self toolbarItemWithIdentifier:@"blockquote" label:NSLocalizedString(@"Blockquote", @"Blockquote toolbar button") icon:@"ToolbarIconBlockquote" action:@selector(toggleBlockquote:)],
[self toolbarItemWithIdentifier:@"code" label:NSLocalizedString(@"Inline Code", @"Inline code toolbar button") icon:@"ToolbarIconInlineCode" action:@selector(toggleInlineCode:)],
[self toolbarItemWithIdentifier:@"link" label:NSLocalizedString(@"Link", @"Link toolbar button") icon:@"ToolbarIconLink" action:@selector(toggleLink:)],
[self toolbarItemWithIdentifier:@"image" label:NSLocalizedString(@"Image", @"Image toolbar button") icon:@"ToolbarIconImage" action:@selector(toggleImage:)],
[self toolbarItemWithIdentifier:@"copy-html" label:NSLocalizedString(@"Copy HTML", @"Copy HTML toolbar button") icon:@"ToolbarIconCopyHTML" action:@selector(copyHtml:)],
[self toolbarItemWithIdentifier:@"comment" label:NSLocalizedString(@"Comment", @"Comment toolbar button") icon:@"ToolbarIconComment" action:@selector(toggleComment:)],
[self toolbarItemWithIdentifier:@"highlight" label:NSLocalizedString(@"Highlight", @"Highlight toolbar button") icon:@"ToolbarIconHighlight" action:@selector(toggleHighlight:)],
[self toolbarItemWithIdentifier:@"strikethrough" label:NSLocalizedString(@"Strikethrough", @"Strikethrough toolbar button") icon:@"ToolbarIconStrikethrough" action:@selector(toggleStrikethrough:)],
[self toolbarItemDropDownWithIdentifier:@"layout" label:NSLocalizedString(@"Layout", @"Layout toolbar button") icon:@"ToolbarIconEditorAndPreview" menuItems:
@[
toggleEditorMenuItem, togglePreviewMenuItem
]
]
];
self->toolbarItemIdentifiers = [self toolbarItemIdentifiersFromItemsArray:self->toolbarItems];
}
/**
* Returns an array with all item identifiers for the toolbar items in the passed in _toolbarItemsArray_.
*/
- (NSArray *)toolbarItemIdentifiersFromItemsArray:(NSArray *)toolbarItemsArray {
NSMutableArray *orderedIdentifiers = [NSMutableArray new];
for (NSToolbarItem *item in self->toolbarItems) {
[orderedIdentifiers addObject:item.itemIdentifier];
}
return [orderedIdentifiers copy];
}
- (void)selectedToolbarItemGroupItem:(NSSegmentedControl *)sender
{
NSInteger selectedIndex = sender.selectedSegment;
NSToolbarItemGroup *selectedGroup = self->toolbarItemIdentifierObjectDictionary[sender.identifier];
NSToolbarItem *selectedItem = selectedGroup.subitems[selectedIndex];
// Invoke the toolbar item's action
// Must convert to IMP to let the compiler know about the method definition
MPDocument *document = self.document;
IMP imp = [document methodForSelector:selectedItem.action];
void (*impFunc)(id) = (void *)imp;
impFunc(document);
}
#pragma mark - NSToolbarDelegate
- (NSArray<NSString *> *)toolbarDefaultItemIdentifiers:(NSToolbar *)toolbar
{
// From toolbar item dictionary(setupToolbarItems)
//NSArray *orderedToolbarItemIdentifiers = [self orderedToolbarDefaultItemKeysForDictionary:self->toolbarItems];
NSArray *orderedToolbarItemIdentifiers = [self toolbarItemIdentifiersFromItemsArray:self->toolbarItems];
// Mixed identifiers from dictionary and space at below specified indices
NSMutableArray *defaultItemIdentifiers = [NSMutableArray new];
// Add space after the specified toolbar item indices
int spaceAfterIndices[] = {}; // No space in the default set
int flexibleSpaceAfterIndices[] = {2, 3, 5, 7, 11};
int i = 0;
int j = 0;
int k = 0;
for (NSString *itemIdentifier in orderedToolbarItemIdentifiers)
{
// exclude some toolbar items from the default toolbar
if ([itemIdentifier isEqual: @"comment"]
|| [itemIdentifier isEqual: @"highlight"]
|| [itemIdentifier isEqual: @"strikethrough"]) {
// do nothing here
}else {
[defaultItemIdentifiers addObject:itemIdentifier];
}
if (i == spaceAfterIndices[j])
{
[defaultItemIdentifiers addObject:NSToolbarSpaceItemIdentifier];
j++;
}
if (i == flexibleSpaceAfterIndices[k])
{
[defaultItemIdentifiers addObject:NSToolbarFlexibleSpaceItemIdentifier];
k++;
}
i++;
}
return [defaultItemIdentifiers copy];
}
- (NSArray<NSString *> *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
{
return self->toolbarItemIdentifiers;
}
- (NSArray<NSString *> *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
return [self toolbarAllowedItemIdentifiers:toolbar];
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
NSToolbarItem *item;
for (NSToolbarItem *currentItem in self->toolbarItems) {
if ([currentItem.itemIdentifier isEqualToString:itemIdentifier]) {
item = currentItem;
break;
}
}
return item;
}
#pragma mark - Toolbar item factory methods
/**
* Factory method for creating and configuring a NSToolbarItemGroup object.
*/
- (NSToolbarItemGroup *)toolbarItemGroupWithIdentifier:(NSString *)itemIdentifier separated:(BOOL)separated label:(NSString *)label items:(NSArray <NSToolbarItem *>*)items {
NSToolbarItemGroup *itemGroup = [[NSToolbarItemGroup alloc] initWithItemIdentifier:itemIdentifier];
itemGroup.subitems = items;
itemGroup.label = label;
itemGroup.paletteLabel = label;
CGFloat itemGroupWidth = itemWidth * items.count;
NSSegmentedControl *segmentedControl = [[NSSegmentedControl alloc] init];
segmentedControl.identifier = itemIdentifier;
segmentedControl.segmentStyle = separated ? NSSegmentStyleSeparated : NSSegmentStyleTexturedRounded;
segmentedControl.trackingMode = NSSegmentSwitchTrackingMomentary;
segmentedControl.segmentCount = items.count;
segmentedControl.target = self;
segmentedControl.action = @selector(selectedToolbarItemGroupItem:);
int segmentIndex = 0;
for (NSToolbarItem *subItem in items)
{
[segmentedControl setImage:subItem.image forSegment:segmentIndex];
[segmentedControl setImageScaling:NSImageScaleProportionallyDown forSegment:segmentIndex];
[segmentedControl setWidth:itemWidth-4 forSegment:segmentIndex];
if (@available(macOS 10.13, *)) {
[segmentedControl setToolTip:subItem.label forSegment:segmentIndex];
}
segmentIndex++;
}
itemGroup.maxSize = NSMakeSize(itemGroupWidth, 25);
itemGroup.view = segmentedControl;
[self->toolbarItemIdentifierObjectDictionary setObject:itemGroup forKey:itemIdentifier];
return itemGroup;
}
/**
* Factory method for creating and configuring a NSToolbarItem object.
*/
- (NSToolbarItem *)toolbarItemWithIdentifier:(NSString *)itemIdentifier label:(NSString *)label icon:(NSString *)iconImageName action:(SEL)action {
NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
toolbarItem.label = label;
toolbarItem.paletteLabel = label;
toolbarItem.toolTip = label;
NSImage *itemImage = [NSImage imageNamed:iconImageName];
[itemImage setTemplate:YES];
[itemImage setSize:CGSizeMake(19, 19)];
NSButton *itemButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, itemWidth, 27)];
itemButton.image = itemImage;
itemButton.imageScaling = NSImageScaleProportionallyDown;
itemButton.bezelStyle = NSBezelStyleTexturedRounded;
itemButton.focusRingType = NSFocusRingTypeDefault;
itemButton.target = self.document;
itemButton.action = action;
toolbarItem.view = itemButton;
[self->toolbarItemIdentifierObjectDictionary setObject:toolbarItem forKey:itemIdentifier];
return toolbarItem;
}
/**
* Factory method for creating and configuring a NSToolbarItem object with a NSPopupButton holding menu options as passed in the menuItems parameter.
*/
- (NSToolbarItem *)toolbarItemDropDownWithIdentifier:(NSString *)itemIdentifier label:(NSString *)label icon:(NSString *)iconImageName menuItems:(NSArray <NSMenuItem *>*)menuItems {
NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
toolbarItem.label = label;
toolbarItem.paletteLabel = label;
toolbarItem.toolTip = label;
NSImage *itemImage = [NSImage imageNamed:iconImageName];
[itemImage setTemplate:YES];
[itemImage setSize:CGSizeMake(19, 19)];
NSPopUpButton *popupButton = [[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 42, 27) pullsDown:YES];
popupButton.bezelStyle = NSBezelStyleTexturedRounded;
popupButton.focusRingType = NSFocusRingTypeDefault;
//popupButton.imageScaling = NSImageScaleProportionallyDown;
// First item's image is displayed as button image, therefor we need a dummy with the icon
[popupButton addItemWithTitle:@""];
[[popupButton lastItem] setImage:itemImage];
for (NSMenuItem *menuItem in menuItems) {
[popupButton addItemWithTitle:menuItem.title];
[[popupButton lastItem] setTarget:self.document];
[[popupButton lastItem] setAction:menuItem.action];
}
toolbarItem.view = popupButton;
[self->toolbarItemIdentifierObjectDictionary setObject:toolbarItem forKey:itemIdentifier];
return toolbarItem;
}
@end