How to enable main menu item "copy"? - objective-c

My main menu item "copy" is not clickable:
But I enable it in Xcode:
I haven't any Outlets of Main menu items in code.
What I can do?

“Enabling Menu Items” in Application Menu and Pop-up List Programming Topics says this:
By default, every time a user event occurs, NSMenu automatically enables and disables each visible menu item. You can also force a menu to update using NSMenu’s update method.
and this:
If the menu item’s target is not set (that is, if it is nil—typically if the menu item is connected to First Responder) and the NSMenu object is not a contextual menu, then NSMenu uses the responder chain (described in “The Responder Chain” in Cocoa Event Handling Guide) to determine the target. If there is no object in the responder chain that implements the item’s action, the item is disabled.
If there is an object in the responder chain that implements the item’s action, NSMenu then checks to see if that object implements the validateMenuItem: or validateUserInterfaceItem: method. If it does not, then the menu item is enabled. If it does, then the enabled status of the menu item is determined by the return value of the method.
By default (when you create a project using the “Cocoa Application” template), the Copy menu item's target is First Responder (nil) and the action is copy:. So you need to implement the copy: method on some item in your responder chain. That is sufficient to enable the menu item. If you want more precise control of when the menu item is enabled, you can also implement validateMenuItem: to check which menu item is being validated and return YES or NO as appropriate.
For example, the application delegate is in the responder chain. So you can add this method to CMAppDelegate:
- (IBAction)copy:(id)sender {
NSLog(#"%# %s", self, __func__);
}
That should be sufficient to enable the Copy menu item. Of course, choosing Edit > Copy will just log a message to the console. It's up to you to actually write the code that copies whatever the user has selected.
If you want more granular control, try giving the app delegate an outlet connected to the Copy menu item:
#interface AppDelegate : NSObject <NSApplicationDelegate>
#property (assign) IBOutlet NSWindow *window;
#property (strong) IBOutlet NSMenuItem *copyMenuItem;
#end
Hook up the outlet in MainMenu.xib. Then you can implement validateMenuItem: like this:
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
if (menuItem == self.copyMenuItem) {
NSLog(#"%# %s %#", self, __func__, menuItem);
return [self shouldEnableCopyMenuItem];
}
return NO;
}

Related

Access Property from Another Class, separate XIB

This might be an easy question, but bear with me I am very new and just experimenting. MacOS, Not iOS, let's say I have two separate XIB files (MainMenu.xib and AnotherWindow.xib). I am using the File Menu in MainMenu.xib to open AnotherWindow.xib, and disable the file menu when it opens with:
- (IBAction)OpenAnotherWindow:(id)sender {
if (!anotherWindow) {
anotherWindow = [[AnotherWindow alloc] initWithWindowNibName:#"AnotherWindow"];
}
[anotherWindow showWindow:self];
[self.MenuItem setEnabled:NO];
In the AnotherWindow.xib, I want to re-enable the file menu when it closes using:
- (void)windowWillClose:(NSNotification *)aNotification {
[self.MenuItem setEnabled:YES];
}
The problem I have is I am not able to access the MenuItem from the second class because it is part of MainMenu.xib - so I just get error: Property not found on object of type with the [self.MenuItem setEnabled:YES]; in the AnotherWindow.xib
So I guess my question is: How can I access a property like
#property (weak) IBOutlet NSMenuItem *MenuItem;
That is in my MainMenu.xib from AnotherWindow.xib.
Instead of manually enabling and disabling the menu item, override the function validateUserInterfaceItem in the class that contains the OpenAnotherWindow IBAction.
The validateUserInterfaceItem function takes an item of type NSValidatedUserInterfaceItem as an argument. Check if the item's action is OpenAnotherWindow. If it is, check if anotherWindow is open. If it's open, return false, which will disable the menu item. If the window isn't open, return true, which will enable the menu item. My Objective-C is rusty so I don't have a code listing for you.

NSComboBox close popup pogrammatically

Is there a way to close the NSComboBox popup programmatically?
There seem to be a bug with NSComboBox when the popup is open, and the control get's removed and then released. For some reason, the dealloc of the NSComboBox doesn't clear the popup before destructing.
Edit: Just to clarify - this bug only happens when the popup list is opened. Otherwise the control is released properly and cleanly.
Edit Edit:
This is how to reproduce it.
Create a new Cocoa application.
Turn off automatic reference counting.
In #(applicationDidFinishLaunching) create a new NSComboBox and store it to a member.
Add the combo box to the window's contentView.
Create a new menu item with a keyboard shortcut and bind to a selector.
In the menu item's selector: remove the combo box from the view and release the member. Don't forget to set it back to nil.
Run the application.
Click on the combo box to show the popup up.
Use the shortcut to remove the combo box.
You can perform a check in your code or grey out menu items by using the NSComboBox delegate methods -comboBoxWillPopUp: and -comboBoxWillDismiss: to control a BOOL.
The BOOL property can be used to control enabling of the menu item.
Set the delegate of the combo box.
To the interface of the delegate add
#property BOOL itemEnabled;
and to the implementation add
- (void)comboBoxWillPopUp:(NSNotification *)notification {
self.itemEnabled = NO;
}
- (void)comboBoxWillDismiss:(NSNotification *)notification {
self.itemEnabled = YES; //re-enabled when dismissed
}
Set initial value of itemEnabled to YES.
In the xib bind the Enabled attribute of the menu item to the delegate and the Model Key Path set to self.itemEnabled

Window called with "showWindow" not focused

i have a simple cocoa coredata statusbar application with Xcode 4.6.2. This is the situation:
Renamed MainMenu.xib to PreferencesWindow.xib, deleted the mainmenu, created a simple and working coredata function with arraycontrollers and bindings in the window.
I have created a new file->User Interface->Main Menu and named it StatusBarMenu.xib. Added a simple menu to it and removed the main menu.
Created new file->objective-c class->subclass of NSObject and named it StatusBarController.
Here's the code for the interface:
#property IBOutlet NSMenu *statusMenu;
#property NSStatusItem *statusItem;
#property [some items for statusbar image]
implementation:
#synthesize [everything]
-(void)awakeFromNib{
statusItem = [[NSStatusBar systemStatusBar]statusItemWithLength:NSVariableStatusItemLength];
[some string, path and stuff for the images]
statusItem.menu = statusMenu;
statusItem.toolTip = #"";
statusItem.highlightMode = YES;
}
Then I've created another new file->objective-c class->subclass of NSWindowController, named it PreferencesWindowController and leave it as it is.
Then a new file->objective-c class->subclass of NSObjects named PreferencesAppController. Here's the code for .h:
#property (assign) IBOutlet NSWindow *mainWindow;
#property (retain) PreferencesWindowController *prefController;
-(IBAction)showPreferences:(id)sender;
.m code:
#synthesize [everything];
-(IBAction)showPreferences:(id)sender{
if(!self.prefController)
self.prefController = [[PreferencesWindowController alloc] initWithWindowNibName:#"PreferencesWindow"];
[self.prefController showWindow:sender];
}
In the AppDelegate files there's only code for coredata, nothing added.
Then in the PreferencesWindow.xib I've added NSObject (the blue cube) for PreferencesAppController with some bindings: Outlets-> mainWindow binded to the window with the simple coredata function. AppDelegate has the window outlet binded to the same window, then Referencing Outlets->File's Owner delegate, some saveaction and managedobjectcontext.
In the StatusBarMenu.xib i've created a StatusBarController object and binded it to the menu (outlets->statusMenu), created another blue object called PreferencesAppController with Received Actions->showPreferences binded to a menu item.
Then i run the program and everything goes fine: an icon appears in the status bar, the dropdown menu works, if i click on "preferences..." the preferences window appears but... it isn't focused! It's on top of the other windows but i have to click to make it focused.
The coredata saving functions works fine except that i have to manually save with a button, quitting the application from the statusbar menu does not save, but this is a marginal issue.
Why isn't the window focused?
I'm assuming from your description of your app as a “statusbar application” that it is meant to run in the background and not show up in the Dock.
This means that your application is not the active application. The user clicking on your status item and choosing an item from its menu does not change that.
When an application that is not the active application opens a window, that window does not take focus (since this ordinarily would amount to stealing focus from whatever the user has been doing in the application that is active).
So, you need to activate your application.

menu item is enabled, but still grayed out

I have a menu with several items created in interface builder. It looks fine there and 'enabled' is checked. But when I run the application, all menu items are grayed out.
I've checked isEnabled, it returns true.
Also, menu items created programmatically (with initWithTitle and without interface builder) work just fine.
Am I missing something here? I'm really quite new to OS X development.
Remember to set your menu item's target and ensure that said target implements the menu item's action method.
menuItem.target = self;
If the menu item’s target is set, then NSMenu first checks to see if that object implements the item’s action method. If it does not, then the item is disabled. If the target does implement the item’s action method, NSMenu first checks to see if that object implements validateMenuItem: or validateUserInterfaceItem: method. If it does not, then the menu item is enabled. If it does, then the enabled status of the menu item is determined by the return value of the method.
If the menu item’s target is not set and the NSMenu object is not a contextual menu, then NSMenu uses the responder chain to determine the target. If there is no object in the responder chain that implements the item’s action, the item is disabled.
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MenuList/Articles/EnablingMenuItems.html
In case somebody might google this out and benefit, 'Action' method was declared without :(id)sender parameter:
-(IBAction) quit;
Strangely, setAction method in NSMenuItem ate it and didn't complain. Oh well.
Ah, the plague of using NSMenu...
Check out <NSMenuValidation>.
Usually the implementation will be as simple as:
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem {
return [menuItem isEnabled];
}

Getting NSArrayController item for right click in NSCollectionView

I'm trying to create a file explorer using nscollectionview and am currently implementing a right click menu for each item (i.e. copy/delete/rename/etc). I currently have:
An NSCollectionView linked with an NSArrayController which holds a custom object
A subclass of NSBox as the view for each item, this also tracks mouse events and passes them to the controller
The controller has an NSMenu outlet (rcMenu) and also an NSView outlet (itemView) for the NSBox subclass that should be where the menu popup
The code for calling the menu is:
[NSMenu popUpContextMenu:rcMenu withEvent:event forView:itemView];
Once run, this works in that the menu pops up when right clicking the item in the collection view, but on inspecting the event that's passed to the controller, there's not really anything I could use to find out which item was right clicked other than the x,y coordinates (which seem to be for the NSWindow rather than the item or NSCollectionView). What I really want is the object in the NSArrayController that had it's view right clicked.
Is this down to me setting it up incorrectly, is there an easy way to figure it out, or is it just that tough to work it out?
You might try setting the menu of each collection view item's view. Most likely, you'll do this by overriding +defaultMenu in your item view class. Once you do that, comment out the popUpContextMenu:withEvent:forView: message and see whether you can get away without it.
Furthermore, it would then not be too hard to serve up different menus for different kinds of items (e.g., folders vs. packages vs. files, and different types of files at that). You'd probably have to override -menuForEvent: instead of +defaultMenu.
I found an other solution that might help.
For this solution I made a subclass of NSCollectionViewItem and NSView, respectively (and for the ease of explaining) ItemViewController and ItemView.
I'm assuming you work with IB where you have already bound your NSCollectionView to the ContentArray of your NSArrayController (also bind the selectionIndexes).
Next add an ViewController object to the NIB and make sure its custom class is set to the ItemViewController. Now connect it to the itemPrototype outlet of your NSCollectionView.
Next add a Custom View object to the NIB and set its custom class to ItemView. Connect its outlet to the view property of your ItemViewController.
In the interface file of ItemView create a representedObject-like property. With this I mean something like:
#property (nonatomic, assign) id someRepresentedObjectPropertyName
This will be the property which will represent the item in your NSArrayController.
Now go to the implementation file of ItemViewController and override the -setRepresentedObject: method. In here we will first let the ItemViewController handle setting its representedObject, afterwards we assign the same representedObject to the property we made in ItemView. The override would look like:
-(void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
//Do some appropiate checking on the representedObject...
if (self.view != nil) {
[(ItemView *)self.view setSomeRepresentedObjectPropertyName:self.representedObject];
}
}
Now if you go back to the implementation of ItemView you can override the method -rightMouseUp: and build/set-up a NSMenu there and use the -popUpMenuPositioning...: method. The someRepresentedObjectPropertyName property of ItemView should be set to the correct item in your NSArrayController.
EDIT:
Instead of overriding -setRepresentedObject you could also bind the ItemView's someRepresentedObjectPropertyName to representedObject.someRepresentedObjectPropertyName