How to customize the context menu of NSToolbar? - macos

I need to remove some items from right-click context menu, but the toolbar does not contain a public API to access 'toolbarView'.
Is it possible to customize the menu without using private API?

You can access and modify a toolbar contextual menu when the toolbar is created, i.e. in -[awakeFromNib]:
- (NSMenu *)toolbarMenuInWindow:(NSWindow *)window
{
NSView *contentView = window.contentView;
NSView *toolbarView = contentView.superview.subviews.lastObject;
NSMenu *toolbarMenu = toolbarView.menu;
return toolbarMenu;
}
Now you can directly edit menu items and hide or disable them.

Related

Remove 'Layout Orientation' from NSTextView context menu

I have a NSTextView which has a ruler attached showing line numbers. If the user uses the 'Layout Orientation' -> 'Vertical' context menu, things go wonky. The applications intended purpose does not support a Vertical orientation anyway, so I would like to remove this context menu.
So far I have subclassed an NSTextView and overwrote the defaultMenu action:
+ (NSMenu *) defaultMenu
{
// Get our default menu
NSMenu * contextMenu =
[NSTextView defaultMenu];
for(NSInteger menuItemIndex = contextMenu.itemArray.count - 1;
menuItemIndex != -1;
--menuItemIndex)
{
NSMenuItem * menuItem =
[contextMenu itemAtIndex: menuItemIndex];
NSLog(#"%ld %#, %#",
menuItemIndex,
NSStringFromSelector(menuItem.action),
menuItem.title);
} // End of menuItem loop
return contextMenu;
} // End of defaultMenu
My original thought was that I could remove the menu item with a specific selector, but unfortunately the 'Layout Orientation' is a submenu, so it has the submenuAction: selector.
I could still remove the menu by comparing the title, but that seems like a poor way to do this and would probably break in a localized environment.
Any suggestions as to PROPERLY go about removing menu items from the NSTextView context menu? (Removing by index also seems hacky, as that could possible break on different versions of the OS).
Each submenu menuitem has a menu. Scan the submenus for action changeLayoutOrientation:.
You can disable the layout orientation menu items by implementing validateUserInterfaceItem:.
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem {
if ([anItem action] == #selector(changeLayoutOrientation:))
return NO;
return [super validateUserInterfaceItem:anItem];
}

Mouse Event in Outline View

I have a window which contains a split view. One of the "splits" contains an outline view. I have a window controller (which is the file owner for the window's XIB). The window controller is the delegate and data source of the outline view.
When I call the -(void)mouseDown:(NSEvent *)e method in the window controller only the toolbar responds to the method - the outline view does not.
How do I get the mouse events, e.g. the mouseDown, of the outline view?
To get the mouse event of the outline view:
Subclass the outline view.
In Interface Builder (IB) > Library panel > Classes tab select NSOutlineView
Right-click NSOutlineView and select "New Subclass..."
Complete the following pop-up windows selecting "Generate Source Files" and add the fils to your project
Select the NSOutlineView
In Inspector Panel > Identity tab > Class Identity > Class select your new class
Implement your mouse event method
In Xcode > your new subclass of your outline view > the implementation (.m) file type your method e.g.
(void)mouseDown:(NSEvent *)theEvent {
/* CODE YOU WANT EXECUTED WHEN MOUSE IS CLICKED */
NSLog(#"Mouse down occurred");
// call this to get the usual behaviour of your outline
// view in addition to your custom code
[super mouseDown:theEvent];
}
It may be useful to know that one can get mouse events by using [NSEvent modifierFlags]. This is will work not just for the outline view but for views throughout the app. For example, in the window controller (referred to in the question) I could include code like:
if ([NSEvent modifierFlags] == NSAlternateKeyMask) { // if the option key is being pressed
/*SOME CODE*/
}

NSView custom context menu and keys

i have an NSCollectionView in my application's main window that manages a collection of custom NSView items. Each custom view has a context menu assigned to it. I want to add shortcut keys to some of the items, for example to associate a "delete" key with "remove item from collection" action. I've added key equivalents to context menu items through IB but the question is how do i make the collection items respond to the pressed keys?
I know that i can achieve this by adding this menu to the NSApp's main menu and keep track of the selected item. Is there any other way besides that?
You could add something like this to your NSCollectionView subclass:
- (BOOL)performKeyEquivalent:(NSEvent *)theEvent
{
BOOL rv = NO;
id firstResponder = self.window.firstResponder;
if ([firstResponder isKindOfClass:[NSView class]] && [firstResponder isDescendantOf:self]) {
// Note: performKeyEquivalent: messages come DOWN the view hierarchy, not UP the responder chain.
// Perform the key equivalent
}
if (!rv) {
rv = [super performKeyEquivalent:theEvent];
}
return rv;
}

Changing NSMenuItem depending on user selection

I am working on a Cocoa document based application. I have a menu item in the file menu named "Export Things…". This menu item is connected to the first responder, and calls a selector in MyDocument. So far so good.
I would like to change the title of this menu item depending on user selection. If the user has nothing selected, it should read "Export All Things…", when the user has some things selected it should read "Export Selected Things…". When no document is open, it should just read "Export Things…".
Where and when should I change this menu item? I figure I can just change the menu item using setTitle:, but how do I get a reference to the NSMenuItem?
You can implement -validateMenuItem: in the responder class that handles the menu item's action. It should return a BOOL (which indicates whether the item is enabled), but you also get a reference to the NSMenuItem as a parameter that you can use to change the title.
To decide which menu item you're dealing with, you should inspect its action, e.g.
- (BOOL)validateMenuItem:(NSMenuItem *)item {
if ([item action] == #selector(export:)) {
if (hasSelection) {
[item setTitle:NSLocalizedString(#"Export Selected Things",nil)];
} else {
[item setTitle:NSLocalizedString(#"Export All Things",nil)];
}
}
return YES;
}

Cocoa webView - Disable all interaction

I have a webView in my cocoa application (macosx not iphone), it displays some javascript to the user but I don't want the user to be able to select any of the text or right click and select the reload option.
Is there a simple way to disable all interaction with the webView?
I know on the iPhone its easy to disable user interaction but I have never needed to do this on the desktop before and can't find a solution.
Set both editing and UI delegations:
[view setUIDelegate:self];
[view setEditingDelegate:self];
Then add the methods above to disable text selection and context menu.
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element
defaultMenuItems:(NSArray *)defaultMenuItems
{
// disable right-click context menu
return nil;
}
- (BOOL)webView:(WebView *)webView shouldChangeSelectedDOMRange:(DOMRange *)currentRange
toDOMRange:(DOMRange *)proposedRange
affinity:(NSSelectionAffinity)selectionAffinity
stillSelecting:(BOOL)flag
{
// disable text selection
return NO;
}
via the UI delegation it might be useful also to disable drag and drop operations, add
- (NSUInteger)webView:(WebView *)sender dragSourceActionMaskForPoint:(NSPoint)point
{
return WebDragSourceActionNone; // Disable any WebView content drag
}
- (NSUInteger)webView:(WebView *)sender dragDestinationActionMaskForDraggingInfo:(id <NSDraggingInfo>)draggingInfo
{
return WebDragDestinationActionNone; // Disable any WebView content drop
}

Resources