/* NSToolbarItem.m The Toolbar item class. Copyright (C) 2002 Free Software Foundation, Inc. Author: Gregory John Casamento , Fabien Vallon , Quentin Mathe Date: May 2002 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "AppKit/NSApplication.h" #include "AppKit/NSButton.h" #include "AppKit/NSButtonCell.h" #include "AppKit/NSDragging.h" #include "AppKit/NSEvent.h" #include "AppKit/NSFont.h" #include "AppKit/NSImage.h" #include "AppKit/NSMenu.h" #include "AppKit/NSMenuItem.h" #include "AppKit/NSParagraphStyle.h" #include "AppKit/NSPasteboard.h" #include "AppKit/NSView.h" #include "GNUstepGUI/GSToolbar.h" #include "GNUstepGUI/GSToolbarView.h" #include "AppKit/NSToolbarItem.h" /* * Each NSToolbarItem object are coupled with a backView which is their * representation on the screen. * backView for the standard toolbar item (without custom view) are NSButton * subclass called GSToolbarButton. * backView for the toolbar item with a custom view are NSView subclass called * GSToolbarBackView. * GSToolbarButton and GSToolbarBackView are adjusted according to their content * and their title when the method layout is called. * The predefined GNUstep toolbar items are implemented with a class cluster * pattern: initWithToolbarItemIdentifier: returns differents concrete subclass * in accordance with the item identifier. */ typedef enum { ItemBackViewDefaultHeight = 60, ItemBackViewRegularHeight = 60, ItemBackViewSmallHeight = 50 } ItemBackViewHeight; typedef enum { ItemBackViewDefaultWidth = 60, ItemBackViewRegularWidth = 60, ItemBackViewSmallWidth = 50 } ItemBackViewWidth; static const int ItemBackViewX = 0; static const int ItemBackViewY = 0; static const int InsetItemViewX = 10; static const int InsetItemViewY = 26; static const int InsetItemTextX = 3; static const int InsetItemTextY = 4; static NSFont *NormalFont = nil; // See NSToolbarItem -initialize method // [NSFont smallSystemFontSize] or better should be NSControlContentFontSize static NSFont *SmallFont = nil; NSString *GSMovableToolbarItemPboardType = @"GSMovableToolbarItemPboardType"; @interface GSToolbar (GNUstepPrivate) - (GSToolbarView *) _toolbarView; - (int) _indexOfItem: (NSToolbarItem *)item; // Used by drag setup - (void) _performRemoveItem: (NSToolbarItem *)item; // Used by drag setup @end @interface NSToolbarItem (GNUstepPrivate) - (void) _layout; // --- - (NSView *) _backView; - (NSMenuItem *) _defaultMenuFormRepresentation; - (BOOL) _isFlexibleSpace; - (BOOL) _isModified; - (BOOL) _selectable; - (GSToolbar *) _toolbar; - (void) _setSelectable: (BOOL)selectable; - (BOOL) _selected; - (void) _setSelected: (BOOL)selected; - (void) _setToolbar: (GSToolbar *)toolbar; @end @interface GSToolbarView (GNUstepPrivate) - (void) _reload; @end /* * NSButton subclass is the toolbar buttons _backView */ @interface GSToolbarButton : NSButton { NSToolbarItem *_toolbarItem; SEL _toolbarItemAction; } - (id) initWithToolbarItem: (NSToolbarItem *)toolbarItem; - (void) layout; // Accessors - (NSToolbarItem *) toolbarItem; - (SEL) toolbarItemAction; - (void) setToolbarItemAction: (SEL)action; @end @interface GSToolbarButtonCell : NSButtonCell { NSRect titleRect; NSRect imageRect; } @end // --- @implementation GSToolbarButton + (Class) cellClass { return [GSToolbarButtonCell class]; } - (id) initWithToolbarItem: (NSToolbarItem *)toolbarItem { self = [super initWithFrame: NSMakeRect(ItemBackViewX, ItemBackViewY, ItemBackViewDefaultWidth, ItemBackViewDefaultHeight)]; // Frame will be reset by the layout method if (self != nil) { // Don't do an ASSIGN here, the toolbar item itself retains us. _toolbarItem = toolbarItem; //[self setCell: [[GSToolbarButtonCell alloc] init]]; } return self; } - (void) dealloc { // Nothing to do currently [super dealloc]; } - (void) layout { float textWidth, layoutedWidth = -1, layoutedHeight = -1; NSAttributedString *attrStr; NSDictionary *attr; NSFont *font; unsigned int borderMask = [[[_toolbarItem toolbar] _toolbarView] borderMask]; NSString *label = [_toolbarItem label]; // Adjust the layout in accordance with NSToolbarSizeMode font = NormalFont; switch ([[_toolbarItem toolbar] sizeMode]) { case NSToolbarSizeModeDefault: layoutedWidth = ItemBackViewDefaultWidth; layoutedHeight = ItemBackViewDefaultHeight; [[_toolbarItem image] setSize: NSMakeSize(32, 32)]; break; case NSToolbarSizeModeRegular: layoutedWidth = ItemBackViewRegularWidth; layoutedHeight = ItemBackViewRegularHeight; [[_toolbarItem image] setSize: NSMakeSize(32, 32)]; break; case NSToolbarSizeModeSmall: layoutedWidth = ItemBackViewSmallWidth; layoutedHeight = ItemBackViewSmallHeight; [[_toolbarItem image] setSize: NSMakeSize(24, 24)]; /* Not use [self image] here because it can return nil, when image position is set to NSNoImage. Even if NSToolbarDisplayModeTextOnly is not true anymore -setImagePosition: is only called below, then [self image] can still returns nil. */ font = SmallFont; break; default: ; // Invalid } [[self cell] setFont: font]; // Adjust the layout in accordance with the border if (!(borderMask & GSToolbarViewBottomBorder)) { layoutedHeight++; layoutedWidth++; } if (!(borderMask & GSToolbarViewTopBorder)) { layoutedHeight++; layoutedWidth++; } // Adjust the layout in accordance with the label attr = [NSDictionary dictionaryWithObject: font forKey: NSFontAttributeName]; if (label == nil || [label isEqualToString: @""]) label = @"Dummy"; attrStr = [[NSAttributedString alloc] initWithString: label attributes: attr]; textWidth = [attrStr size].width + 2 * InsetItemTextX; if ([[_toolbarItem toolbar] displayMode] != NSToolbarDisplayModeIconOnly && layoutedWidth != -1 && textWidth > layoutedWidth) layoutedWidth = textWidth; // Adjust the layout in accordance with NSToolbarDisplayMode switch ([[_toolbarItem toolbar] displayMode]) { case NSToolbarDisplayModeDefault: [self setImagePosition: NSImageAbove]; break; case NSToolbarDisplayModeIconAndLabel: [self setImagePosition: NSImageAbove]; break; case NSToolbarDisplayModeIconOnly: [self setImagePosition: NSImageOnly]; layoutedHeight -= [attrStr size].height + InsetItemTextY; break; case NSToolbarDisplayModeLabelOnly: [self setImagePosition: NSNoImage]; layoutedHeight = [attrStr size].height + InsetItemTextY * 2; break; default: ; // Invalid } DESTROY(attrStr); // Set the frame size to use the new layout [self setFrameSize: NSMakeSize(layoutedWidth, layoutedHeight)]; } /* * The code below should be kept in sync with GSToolbarBackView methods which * have identical names. */ - (void) mouseDown: (NSEvent *)event { GSToolbar *toolbar = [_toolbarItem toolbar]; if ([event modifierFlags] == NSCommandKeyMask && [toolbar allowsUserCustomization]) { NSSize viewSize = [self frame].size; NSImage *image = [[NSImage alloc] initWithSize: viewSize]; NSCell *cell = [self cell]; NSPasteboard *pboard; int index; AUTORELEASE(image); // Prepare the drag RETAIN(self); /* We need to keep this view (aka self) to be able to draw the drag image. */ // Draw the drag content in an image /* The code below is only partially supported by GNUstep, then NSImage needs to be improved. */ [image lockFocus]; [cell setShowsFirstResponder: NO]; // To remove the dotted rect [cell drawWithFrame: NSMakeRect(0, 0, viewSize.width, viewSize.height) inView: nil]; [cell setShowsFirstResponder: YES]; [image unlockFocus]; pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; [pboard declareTypes: [NSArray arrayWithObject: GSMovableToolbarItemPboardType] owner: nil]; index = [toolbar _indexOfItem: _toolbarItem]; [pboard setString: [NSString stringWithFormat:@"%d", index] forType: GSMovableToolbarItemPboardType]; [self dragImage: image at: NSMakePoint(0.0, 0.0) offset: NSMakeSize(0.0, 0.0) event: event pasteboard: pboard source: self slideBack: NO]; } else if ([event modifierFlags] != NSCommandKeyMask) { [super mouseDown: event]; } } - (void) draggedImage: (NSImage *)dragImage beganAt: (NSPoint)location { GSToolbar *toolbar = [_toolbarItem toolbar]; RETAIN(_toolbarItem); /* We retain the toolbar item to be able to have have it reinsered later by the dragging destination. */ [toolbar _performRemoveItem: _toolbarItem]; } - (void) draggedImage: (NSImage *)dragImage endedAt: (NSPoint)location operation: (NSDragOperation)operation { RELEASE(self); // The view is no more needed : no more drawing to do with it. } - (unsigned int) draggingSourceOperationMaskForLocal: (BOOL)isLocal { return isLocal ? NSDragOperationGeneric : NSDragOperationNone; } /* * End of the code to keep in sync */ - (BOOL) sendAction: (SEL)action to: (id)target { if ([_toolbarItem _selectable]) { [[_toolbarItem toolbar] setSelectedItemIdentifier: [_toolbarItem itemIdentifier]]; } if (_toolbarItemAction) { return [NSApp sendAction: _toolbarItemAction to: target from: _toolbarItem]; } else { return NO; } } - (NSToolbarItem *) toolbarItem { return _toolbarItem; } - (void) setToolbarItemAction: (SEL) action { _toolbarItemAction = action; } - (SEL) toolbarItemAction { return _toolbarItemAction; } @end @implementation GSToolbarButtonCell /* Overriden NSButtonCell method to handle cell type in a basic way which avoids to lose image or empty title on new image position (when this involves a cell type switch) and the need to reset it. That would happen in GSToolbarButton -layout method (on toolbar display mode switch). Note that empty title are used with space or separator toolbar items. */ - (void) setImagePosition: (NSCellImagePosition)aPosition { _cell.image_position = aPosition; if (_cell.image_position == NSNoImage) { _cell.type = NSTextCellType; } else { _cell.type = NSImageCellType; } } // Overriden NSButtonCell method - (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView { NSSize titleSize = [[self attributedTitle] size]; // We ignore alternateAttributedTitle, it is not needed /* We store the values we need to customize the drawing into titleRect and imageRect. */ titleRect.origin.x = cellFrame.origin.x; titleRect.origin.y = cellFrame.origin.y + InsetItemTextY; titleRect.size.width = cellFrame.size.width; titleRect.size.height = titleSize.height; imageRect.origin.x = cellFrame.origin.x; imageRect.origin.y = cellFrame.origin.y; if ([self imagePosition] != NSImageOnly) imageRect.origin.y += titleRect.size.height; imageRect.size.width = cellFrame.size.width; imageRect.size.height = cellFrame.size.height; if ([self imagePosition] != NSImageOnly) imageRect.size.height -= titleRect.size.height; [super drawInteriorWithFrame: cellFrame inView: controlView]; } // Overriden NSCell method - (void) _drawAttributedText: (NSAttributedString*)aString inFrame: (NSRect)aRect { if (aString == nil) return; /* Important: text should always be vertically centered without considering descender (as if descender did not exist). This is particularly important for single line texts.Please make sure the output remains always correct. */ [aString drawInRect: titleRect]; // We ignore aRect value } // Overriden NSButtonCell method - (void) _drawImage: (NSImage *)anImage inFrame: (NSRect)aRect isFlipped: (BOOL)flipped { NSSize size; NSPoint position; // We ignore aRect value size = [anImage size]; position.x = MAX(NSMidX(imageRect) - (size.width / 2.), 0.); position.y = MAX(NSMidY(imageRect) - (size.height / 2.), 0.); /* Images are always drawn with their bottom-left corner at the origin so we must adjust the position to take account of a flipped view. */ if (flipped) { position.y += size.height; } if (_cell.is_disabled && _image_dims_when_disabled) { [anImage dissolveToPoint: position fraction: 0.5]; } else { [anImage compositeToPoint: position operation: NSCompositeSourceOver]; } } @end /* * Back view used to enclose toolbar item's custom view */ @interface GSToolbarBackView : NSView { NSToolbarItem *_toolbarItem; NSFont *_font; BOOL _enabled; BOOL _showLabel; } - (id) initWithToolbarItem: (NSToolbarItem *)toolbarItem; - (NSToolbarItem *) toolbarItem; - (BOOL) enabled; - (void) setEnabled: (BOOL)enabled; @end @implementation GSToolbarBackView - (id)initWithToolbarItem: (NSToolbarItem *)toolbarItem { self = [super initWithFrame: NSMakeRect(ItemBackViewX, ItemBackViewY, ItemBackViewDefaultWidth, ItemBackViewDefaultHeight)]; // Frame will be reset by the layout method if (self != nil) { // Don't do an ASSIGN here, the toolbar item itself retains us. _toolbarItem = toolbarItem; } return self; } - (void) dealloc { /* _font is pointing on a static variable then we do own it and don't need to release it. */ [super dealloc]; } - (void) drawRect: (NSRect)rect { [super drawRect: rect]; // We draw _view which is a subview if (_showLabel) { NSAttributedString *attrString; NSDictionary *attr; NSColor *color; NSMutableParagraphStyle *pStyle; NSRect titleRect; NSRect viewBounds = [self bounds]; if (_enabled) { color = [NSColor blackColor]; } else { color = [NSColor disabledControlTextColor]; } pStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy]; [pStyle setAlignment: NSCenterTextAlignment]; // We draw the label attr = [NSDictionary dictionaryWithObjectsAndKeys: _font, NSFontAttributeName, color, NSForegroundColorAttributeName, pStyle, NSParagraphStyleAttributeName, nil]; RELEASE(pStyle); attrString = [[NSAttributedString alloc] initWithString: [_toolbarItem label] attributes: attr]; titleRect.origin.x = viewBounds.origin.x; titleRect.origin.y = viewBounds.origin.y + InsetItemTextY; titleRect.size.width = viewBounds.size.width; titleRect.size.height = [attrString size].height; [attrString drawInRect: titleRect]; DESTROY(attrString); } } - (void) layout { NSView *view = [_toolbarItem view]; float insetItemViewY; float textWidth, layoutedWidth = -1, layoutedHeight = -1; NSAttributedString *attrStr; NSDictionary *attr; unsigned int borderMask = [[[_toolbarItem toolbar] _toolbarView] borderMask]; NSString *label = [_toolbarItem label]; _font = NormalFont; if ([view superview] == nil) // Show the view to eventually hide it later [self addSubview: view]; // Adjust the layout in accordance with NSToolbarSizeMode switch ([[_toolbarItem toolbar] sizeMode]) { case NSToolbarSizeModeDefault: layoutedWidth = ItemBackViewDefaultWidth; layoutedHeight = ItemBackViewDefaultHeight; if ([view frame].size.height > 32) [view removeFromSuperview]; break; case NSToolbarSizeModeRegular: layoutedWidth = ItemBackViewRegularWidth; layoutedHeight = ItemBackViewRegularHeight; if ([view frame].size.height > 32) [view removeFromSuperview]; break; case NSToolbarSizeModeSmall: layoutedWidth = ItemBackViewSmallWidth; layoutedHeight = ItemBackViewSmallHeight; _font = SmallFont; if ([view frame].size.height > 24) [view removeFromSuperview]; break; default: NSLog(@"Invalid NSToolbarSizeMode"); // Invalid } // Adjust the layout in accordance with the border if (!(borderMask & GSToolbarViewBottomBorder)) { layoutedHeight++; layoutedWidth++; } if (!(borderMask & GSToolbarViewTopBorder)) { layoutedHeight++; layoutedWidth++; } // Adjust the layout in accordance with the label attr = [NSDictionary dictionaryWithObject: _font forKey: NSFontAttributeName]; if (label == nil || [label isEqualToString: @""]) label = @"Dummy"; attrStr = [[NSAttributedString alloc] initWithString: label attributes: attr]; textWidth = [attrStr size].width + 2 * InsetItemTextX; if (textWidth > layoutedWidth) layoutedWidth = textWidth; // Adjust the layout in accordance with NSToolbarDisplayMode _enabled = YES; _showLabel = YES; /* This boolean variable is used to known when it's needed to draw the label in the -drawRect: method. */ switch ([[_toolbarItem toolbar] displayMode]) { case NSToolbarDisplayModeDefault: break; // Nothing to do case NSToolbarDisplayModeIconAndLabel: break; // Nothing to do case NSToolbarDisplayModeIconOnly: _showLabel = NO; layoutedHeight -= [attrStr size].height + InsetItemTextY; break; case NSToolbarDisplayModeLabelOnly: _enabled = NO; layoutedHeight = [attrStr size].height + InsetItemTextY * 2; if ([view superview] != nil) [view removeFromSuperview]; break; default: ; // Invalid } /* If the view is visible... Adjust the layout in accordance with the view width in the case it is needed. */ if ([view superview] != nil) { if (layoutedWidth < [view frame].size.width + 2 * InsetItemViewX) layoutedWidth = [view frame].size.width + 2 * InsetItemViewX; } // Set the frame size to use the new layout [self setFrameSize: NSMakeSize(layoutedWidth, layoutedHeight)]; /* If the view is visible... Adjust the view position in accordance with the new layout. */ if ([view superview] != nil) { if (_showLabel) { insetItemViewY = ([self frame].size.height - [view frame].size.height - [attrStr size].height - InsetItemTextX) / 2 + [attrStr size].height + InsetItemTextX; } else { insetItemViewY = ([self frame].size.height - [view frame].size.height) / 2; } [view setFrameOrigin: NSMakePoint((layoutedWidth - [view frame].size.width) / 2, insetItemViewY)]; } DESTROY(attrStr); } - (NSView *) hitTest: (NSPoint)point { NSEvent *event = [NSApp currentEvent]; if ([self mouse: point inRect: [self frame]] && [event modifierFlags] == NSCommandKeyMask && [event type] == NSLeftMouseDown) { return self; } else { return [super hitTest: point]; } } /* * The code below should be kept in sync with GSToolbarButton methods which * have identical names. */ - (void) mouseDown: (NSEvent *)event { GSToolbar *toolbar = [_toolbarItem toolbar]; if ([event modifierFlags] == NSCommandKeyMask && [toolbar allowsUserCustomization]) { NSSize viewSize = [self frame].size; NSImage *image = [[NSImage alloc] initWithSize: viewSize]; NSPasteboard *pboard; int index; AUTORELEASE(image); // Prepare the drag RETAIN(self); /* We need to keep this view (aka self) to be able to draw the drag image. */ // Draw the drag content in an image /* The code below is only partially supported by GNUstep, then NSImage needs to be improved. */ [image lockFocus]; [self drawRect: NSMakeRect(0, 0, viewSize.width, viewSize.height)]; [image unlockFocus]; pboard = [NSPasteboard pasteboardWithName: NSDragPboard]; [pboard declareTypes: [NSArray arrayWithObject: GSMovableToolbarItemPboardType] owner: nil]; index = [toolbar _indexOfItem: _toolbarItem]; [pboard setString: [NSString stringWithFormat:@"%d", index] forType: GSMovableToolbarItemPboardType]; [self dragImage: image at: NSMakePoint(0.0, 0.0) offset: NSMakeSize(0.0, 0.0) event: event pasteboard: pboard source: self slideBack: NO]; } else if ([event modifierFlags] != NSCommandKeyMask) { [super mouseDown: event]; } } - (void) draggedImage: (NSImage *)dragImage beganAt: (NSPoint)location { GSToolbar *toolbar = [_toolbarItem toolbar]; RETAIN(_toolbarItem); /* We retain the toolbar item to be able to have have it reinsered later by the dragging destination. */ [toolbar _performRemoveItem: _toolbarItem]; } - (void) draggedImage: (NSImage *)dragImage endedAt: (NSPoint)location operation: (NSDragOperation)operation { RELEASE(self); // The view is no more needed : no more drawing to do with it. } - (unsigned int) draggingSourceOperationMaskForLocal: (BOOL)isLocal { return isLocal ? NSDragOperationGeneric : NSDragOperationNone; } /* * End of the code to keep in sync */ - (NSToolbarItem *)toolbarItem { return _toolbarItem; } - (BOOL) enabled { id view = [_toolbarItem view]; if ([view respondsToSelector: @selector(setEnabled:)]) { return [view enabled]; } return _enabled; } - (void) setEnabled: (BOOL)enabled { id view = [_toolbarItem view]; _enabled = enabled; if ([view respondsToSelector: @selector(setEnabled:)]) { [view setEnabled: enabled]; } } @end /* * Standard toolbar items. */ // ---- NSToolbarSeparatorItemIdentifier @interface GSToolbarSeparatorItem : NSToolbarItem { } @end @implementation GSToolbarSeparatorItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { NSImage *image = [NSImage imageNamed: @"common_ToolbarSeparatorItem"]; self = [super initWithItemIdentifier: itemIdentifier]; [(NSButton *)[self _backView] setImagePosition: NSImageOnly]; [(NSButton *)[self _backView] setImage: image]; /* We bypass the toolbar item accessor to set the image in order to have it (48 * 48) not resized. */ [[self _backView] setFrameSize: NSMakeSize(30, ItemBackViewDefaultHeight)]; return self; } - (NSMenuItem *) _defaultMenuFormRepresentation { return nil; // Override the default implementation in order to do nothing } - (void) _layout { NSView *backView = [self _backView]; // Override the default implementation [(id)backView layout]; [backView setFrameSize: NSMakeSize(30, [backView frame].size.height)]; } @end // ---- NSToolbarSpaceItemIdentifier @interface GSToolbarSpaceItem : NSToolbarItem { } @end @implementation GSToolbarSpaceItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { self = [super initWithItemIdentifier: itemIdentifier]; [self setLabel: @""]; return self; } - (NSMenuItem *) _defaultMenuFormRepresentation { return nil; // Override the default implementation in order to do nothing } @end // ---- NSToolbarFlexibleSpaceItemIdentifier @interface GSToolbarFlexibleSpaceItem : NSToolbarItem { } @end @implementation GSToolbarFlexibleSpaceItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { self = [super initWithItemIdentifier: itemIdentifier]; [self setLabel: @""]; [self _layout]; return self; } - (NSMenuItem *) _defaultMenuFormRepresentation { return nil; // Override the default implementation in order to do nothing } - (void) _layout { NSView *backView = [self _backView]; [(id)backView layout]; [backView setFrameSize: NSMakeSize(0, [backView frame].size.height)]; // Override the default implementation in order to reset the _backView to a zero width } @end // ---- NSToolbarShowColorsItemIdentifier @interface GSToolbarShowColorsItem : NSToolbarItem { } @end @implementation GSToolbarShowColorsItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { NSImage *image = [NSImage imageNamed: @"common_ToolbarShowColorsItem"]; self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; [self setLabel: @"Colors"]; // FIX ME: localize // Set action... [self setTarget: nil]; // Goes to first responder.. [self setAction: @selector(orderFrontColorPanel:)]; return self; } @end // ---- NSToolbarShowFontsItemIdentifier @interface GSToolbarShowFontsItem : NSToolbarItem { } @end @implementation GSToolbarShowFontsItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { NSImage *image = [NSImage imageNamed: @"common_ToolbarShowFontsItem"]; self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; [self setLabel: @"Fonts"]; // FIX ME: localize // Set action... [self setTarget: nil]; // Goes to first responder.. [self setAction: @selector(orderFrontFontPanel:)]; return self; } @end // ---- NSToolbarCustomizeToolbarItemIdentifier @interface GSToolbarCustomizeToolbarItem : NSToolbarItem { } @end @implementation GSToolbarCustomizeToolbarItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { NSImage *image = [NSImage imageNamed: @"common_ToolbarCustomizeToolbarItem"]; self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; [self setLabel: @"Customize"]; // FIX ME: localize // Set action... [self setTarget: nil]; // Goes to first responder.. [self setAction: @selector(runCustomizationPalette:)]; return self; } @end // ---- NSToolbarPrintItemIdentifier @interface GSToolbarPrintItem : NSToolbarItem { } @end @implementation GSToolbarPrintItem - (id) initWithItemIdentifier: (NSString *)itemIdentifier { NSImage *image = [NSImage imageNamed: @"common_Printer"]; self = [super initWithItemIdentifier: itemIdentifier]; [self setImage: image]; [self setLabel: @"Print..."]; // FIX ME: localize // Set action... [self setTarget: nil]; // goes to first responder.. [self setAction: @selector(print:)]; return self; } @end @implementation NSToolbarItem + (void) initialize { NormalFont = RETAIN([NSFont systemFontOfSize: 11]); // [NSFont smallSystemFontSize] or better should be NSControlContentFontSize SmallFont = RETAIN([NSFont systemFontOfSize: 9]); } - (id) initWithItemIdentifier: (NSString *)itemIdentifier { GSToolbarButton *button; NSButtonCell *cell; if ((self = [super init]) != nil) { // GNUstep predefined toolbar items if ([itemIdentifier isEqualToString: @"NSToolbarSeparatorItemIdentifier"] && [self isKindOfClass:[GSToolbarSeparatorItem class]] == NO) { [self release]; self = [[GSToolbarSeparatorItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarSpaceItemIdentifier"] && [self isKindOfClass:[GSToolbarSpaceItem class]] == NO) { [self release]; self = [[GSToolbarSpaceItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarFlexibleSpaceItemIdentifier"] && [self isKindOfClass:[GSToolbarFlexibleSpaceItem class]] == NO) { [self release]; self = [[GSToolbarFlexibleSpaceItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarShowColorsItemIdentifier"] && [self isKindOfClass:[GSToolbarShowColorsItem class]] == NO) { [self release]; self = [[GSToolbarShowColorsItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarShowFontsItemIdentifier"] && [self isKindOfClass:[GSToolbarShowFontsItem class]] == NO) { [self release]; self = [[GSToolbarShowFontsItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarCustomizeToolbarItemIdentifier"] && [self isKindOfClass:[GSToolbarCustomizeToolbarItem class]] == NO) { [self release]; self = [[GSToolbarCustomizeToolbarItem alloc] initWithItemIdentifier: itemIdentifier]; } else if ([itemIdentifier isEqualToString: @"NSToolbarPrintItemIdentifier"] && [self isKindOfClass:[GSToolbarPrintItem class]] == NO) { [self release]; self = [[GSToolbarPrintItem alloc] initWithItemIdentifier: itemIdentifier]; } // Normal toolbar items else { ASSIGN(_itemIdentifier, itemIdentifier); button = [[GSToolbarButton alloc] initWithToolbarItem: self]; cell = [button cell]; [button setTitle: @""]; [button setEnabled: NO]; [button setBordered: NO]; [button setImagePosition: NSImageAbove]; [cell setBezeled: YES]; [cell setHighlightsBy: NSChangeGrayCellMask | NSChangeBackgroundCellMask]; [cell setFont: [NSFont systemFontOfSize: 11]]; /* [NSFont smallSystemFontSize] or better should be controlContentFontSize. */ [_backView release]; _backView = button; } // gets _flags._isEnabled = [_backView respondsToSelector: @selector(isEnabled)]; _flags._tag = YES; _flags._action = [_backView respondsToSelector: @selector(toolbarItemAction)]; _flags._target = [_backView respondsToSelector: @selector(target)]; _flags._image = [_backView respondsToSelector: @selector(image)]; // sets _flags._setEnabled = [_backView respondsToSelector: @selector(setEnabled:)]; _flags._setTag = YES; _flags._setAction = [_backView respondsToSelector: @selector(setToolbarItemAction:)]; _flags._setTarget = [_backView respondsToSelector: @selector(setTarget:)]; _flags._setImage = [_backView respondsToSelector: @selector(setImage:)]; } return self; } - (void) dealloc { RELEASE(_itemIdentifier); RELEASE(_label); RELEASE(_image); RELEASE(_menuFormRepresentation); RELEASE(_paletteLabel); RELEASE(_toolTip); RELEASE(_view); RELEASE(_backView); [super dealloc]; } - (BOOL) allowsDuplicatesInToolbar { return _allowsDuplicatesInToolbar; } - (BOOL) isEnabled { if (_flags._isEnabled) { return [(id)_backView isEnabled]; } return NO; } - (NSImage *)image { if (_flags._image) { return _image; } return nil; } - (NSString *) itemIdentifier { return _itemIdentifier; } - (NSString *) label { NSMenuItem *menuItem = [self menuFormRepresentation]; if ([[self toolbar] displayMode] == NSToolbarDisplayModeLabelOnly && menuItem != nil) { return [menuItem title]; } else { return _label; } } - (NSSize) maxSize { return _maxSize; } - (NSMenuItem *) menuFormRepresentation { return _menuFormRepresentation; } - (NSSize) minSize { return _minSize; } - (NSString *) paletteLabel { return _paletteLabel; } - (void) setAction: (SEL)action { if (_flags._setAction) { if ([_backView isKindOfClass: [GSToolbarButton class]]) [(GSToolbarButton *)_backView setToolbarItemAction: action]; if (action != NULL) { [self setEnabled: YES]; } else { [self setEnabled: NO]; } } } - (void) setEnabled: (BOOL)enabled { if (_flags._setEnabled) [(id)_backView setEnabled: enabled]; } - (void) setImage: (NSImage *)image { if (_flags._setImage) { ASSIGN(_image, image); [_image setScalesWhenResized: YES]; //[_image setSize: NSMakeSize(32, 32)]; if ([_backView isKindOfClass: [NSButton class]]) [(NSButton *)_backView setImage: _image]; } } - (void) setLabel: (NSString *)label { ASSIGN(_label, label); if ([_backView isKindOfClass: [NSButton class]]) [(NSButton *)_backView setTitle:_label]; _modified = YES; if (_toolbar != nil) [[_toolbar _toolbarView] _reload]; } - (void) setMaxSize: (NSSize)maxSize { _maxSize = maxSize; } - (void) setMenuFormRepresentation: (NSMenuItem *)menuItem { ASSIGN(_menuFormRepresentation, menuItem); } - (void) setMinSize: (NSSize)minSize { _minSize = minSize; } - (void) setPaletteLabel: (NSString *)paletteLabel { ASSIGN(_paletteLabel, paletteLabel); } - (void) setTag: (int)tag { if (_flags._tag) _tag = tag; } - (void) setTarget: (id)target { if (_flags._target) { if ([_backView isKindOfClass: [NSButton class]]) [(NSButton *)_backView setTarget: target]; } } - (void) setToolTip: (NSString *)toolTip { ASSIGN(_toolTip, toolTip); [_view setToolTip: _toolTip]; } - (void) setView: (NSView *)view { ASSIGN(_view, view); if (_view == nil) { // gets _flags._isEnabled = [_backView respondsToSelector: @selector(isEnabled)]; _flags._action = [_backView respondsToSelector: @selector(toolbarItemAction)]; _flags._target = [_backView respondsToSelector: @selector(target)]; _flags._image = [_backView respondsToSelector: @selector(image)]; // sets _flags._setEnabled = [_backView respondsToSelector: @selector(setEnabled:)]; _flags._setAction = [_backView respondsToSelector: @selector(setToolbarItemAction:)]; _flags._setTarget = [_backView respondsToSelector: @selector(setTarget:)]; _flags._setImage = [_backView respondsToSelector: @selector(setImage:)]; } else { // gets _flags._isEnabled = [_view respondsToSelector: @selector(isEnabled)]; _flags._action = [_view respondsToSelector: @selector(action)]; _flags._target = [_view respondsToSelector: @selector(target)]; _flags._image = [_backView respondsToSelector: @selector(image)]; // sets _flags._setEnabled = [_view respondsToSelector: @selector(setEnabled:)]; _flags._setAction = [_view respondsToSelector: @selector(setAction:)]; _flags._setTarget = [_view respondsToSelector: @selector(setTarget:)]; _flags._setImage = [_backView respondsToSelector: @selector(setImage:)]; [_view setToolTip: _toolTip]; } [_backView release]; _backView = [[GSToolbarBackView alloc] initWithToolbarItem: self]; } - (int) tag { if (_flags._tag) return _tag; return 0; } - (NSString *) toolTip { return _toolTip; } - (GSToolbar *) toolbar { return _toolbar; } - (void) validate { /* Validate by default, we know that all of the "standard" items are correct. */ NSMenuItem *menuItem = [self menuFormRepresentation]; id target = [self target]; if ([[self toolbar] displayMode] == NSToolbarDisplayModeLabelOnly && menuItem != nil) { if ([target respondsToSelector: @selector(validateMenuItem:)]) [self setEnabled: [target validateMenuItem: menuItem]]; } else { if ([target respondsToSelector: @selector(validateToolbarItem:)]) [self setEnabled: [target validateToolbarItem: self]]; } // We can get a crash here when the target is pointing garbage memory... } - (NSView *) view { return _view; } // Private or package like visibility methods - (NSView *) _backView { return _backView; } - (NSMenuItem *) _defaultMenuFormRepresentation { NSMenuItem *menuItem; menuItem = [[NSMenuItem alloc] initWithTitle: [self label] action: [self action] keyEquivalent: @""]; [menuItem setTarget: [self target]]; AUTORELEASE(menuItem); return menuItem; } - (void) _layout { [(id)_backView layout]; } - (BOOL) _isModified { return _modified; } - (BOOL) _isFlexibleSpace { return [self isKindOfClass: [GSToolbarFlexibleSpaceItem class]]; } - (BOOL) _selectable { return _selectable; } - (BOOL) _selected { return [(GSToolbarButton *)_backView state]; } - (GSToolbar *) _toolbar { return _toolbar; } - (void) _setSelected: (BOOL)selected { if (_selectable && [self _selected] == NO && selected) { [(GSToolbarButton *)_backView performClick:self]; } else if (selected == NO) { [(GSToolbarButton *)_backView setState: NO]; } else if (_selectable == NO) { NSLog(@"The toolbar item %@ is not selectable", self); } } - (void) _setSelectable: (BOOL)selectable { if ([_backView isKindOfClass: [GSToolbarButton class]]) { _selectable = selectable; [(GSToolbarButton *)_backView setButtonType: NSOnOffButton]; } else { NSLog(@"The toolbar item %@ is not selectable", self); } } - (void) _setToolbar: (GSToolbar *)toolbar { // Don't do an ASSIGN here, the toolbar itself retains us. _toolbar = toolbar; } // NSValidatedUserInterfaceItem protocol - (SEL) action { if (_flags._action) { if ([_backView isKindOfClass: [GSToolbarButton class]]) return [(GSToolbarButton *)_backView toolbarItemAction]; } return 0; } - (id) target { if (_flags._target) { if ([_backView isKindOfClass: [NSButton class]]) return [(NSButton *)_backView target]; } return nil; } // NSCopying protocol - (id) copyWithZone: (NSZone *)zone { NSToolbarItem *new = [[NSToolbarItem allocWithZone: zone] initWithItemIdentifier: _itemIdentifier]; // Copy all items individually... [new setTarget: [self target]]; [new setAction: [self action]]; [new setView: [self view]]; [new setToolTip: [[self toolTip] copyWithZone: zone]]; [new setTag: [self tag]]; [new setImage: [[self image] copyWithZone: zone]]; [new setEnabled: [self isEnabled]]; [new setPaletteLabel: [[self paletteLabel] copyWithZone: zone]]; [new setMinSize: NSMakeSize(_minSize.width, _minSize.height)]; [new setMaxSize: NSMakeSize(_maxSize.width, _maxSize.height)]; return new; } @end