Menu of app in Status Bar wont work on Snow Leopard - macos

I have an application that sits in the status bar on the Mac. It has a menu that lets you look at the app version, change some settings and quit the app.
I built it on Leopard and it works fine on Leopard.
On Snow Leopard, I can see the app in the status bar and clicking on it shows the menu items. But clicking items in the menu doesn't do anything. The app isn't hung - it's doing stuff that it's expected to do. The UI thread isn't hung either - I can see the menu obviously, and and dialogs that the app causes to show on the UI thread (outside of the ones when the menu is clicked) show up fine. Compiling it on Snow Leopard targeted for 10.6 doesn't seem to change this behavior.
So I know that the app is able to create dialogs, isn't hung and is functioning.
I suspect this has something to do with the menu, and handling menu clicks either in my app or in Snow Leopard.
Anyone else have suggestions on what this might be or where I should look? Any suggestions on where I can place a BP to trap whether the menu item click is actually happening?
EDIT: I've uploaded a simple repro 10.6 XCode project. I'll send a virtual beer your way if you can take a look and let me know if you see what's wrong. The app is simple - create a status bar app with 1 menu item "Quit" which calls terminate on the app. The status bar app show, the menu shows but clicking does nothing. Here's the project. It runs fine on Leopard (Clean All before you build/run) but not on Snow Leopard.

It sounds likely that your menu item's action outlet isn't "hooked up" to anything in your code. I don't know from your description whether or not you created the menu in interface builder or are creating it programmatically, but the first thing to do would be to check there. Make sure that if you're creating the menu programmatically, that you've spelled the method name correctly in the #selector() (if you've got warnings that say "[class] may not respond to -Foo" that's a big tip off of a typo).
If the connections are looking good, I'd break point the method set as your action and verify that you're actually calling the method you think you are.
If you're setting the connections programmatically, breakpoint the addItemWithTitle: (or equivalent call) call and ensure that your NSMenuItem setup correctly immediately after stepping over that line.
---EDIT---
After looking over the posted code, there are several fundamental flaws with it, any of which could be causing the breakage under 10.6.
First, you're confusing the role of a delegate in relation to the menu. The delegate should not be your NSMenu object, your delegate should be an object that is invoked as a "call back" by the NSMenuItem to provide custom logic. You need not be extending NSMenu at all, and your MenuTestAppDelegate should only be extended from NSObject.
The class of your Menu object in your MainMenu.xib file is of type MenuTest, but you don't have a MenuTest class declared in your project. Hopefully this is merely a copy/paste error while you were setting up your test project, but you should check it out.
I find it far easier to set up the NSStatusItem's menu programmatically. Try this:
// create the menu
- (void)createMenu
{
NSMenu* aMenu = [NSMenu new];
[aMenu setDelegate:self];
NSMenuItem* quitItem = [[NSMenuItem alloc] initWithTitle:#"quitItem"
action:#selector(quitSelected:)
keyEquivalent:#""];
[aMenu addItem:quitItem];
[quitItem release];
NSStatusItem* statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
NSImage* itemImage = [[NSImage alloc] initWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"image" ofType:#"png"]];
NSImage* itemAlternate = [[NSImage alloc] initWithContentsOfFile:
[[NSBundle mainBundle] pathForResource:#"alternate" ofType:#"png"]];
[statusItem setImage:itemImage];
[statusItem setAlternateImage:itemAlternate];
[statusItem setHighlightMode:YES];
[statusItem setMenu:aMenu];
}

Related

Window not showing up in Window menu

I am creating an OS X application with multiple windows. The main window appears in the Window menu as expected.
I'm creating the 2nd NSWindow using initWithWindowNibName. It appears as expected when I call showWindow. However the window doesn't show up in the Window menu (under "Bring all to front" and the main window's name).
I tried:
[[NSApplication sharedApplication] addWindowsItem:[winController window]
title:#"The Window"
filename:NO]; }
I also tried:
[[winController window] setExcludedFromWindowsMenu:NO];
Neither of these seemed to help.
If you can't tell, I'm new to OS X (but I know Obj-C relatively well).
I figured it out. I was creating the window in a NSResponder's init function. Somehow it was happening too early and the sharedApplication wasn't ready yet. When I moved the window creation into an awakeFromNib everything worked as expected.
The Window Programming Guide says that "This menu automatically lists windows that have a title bar and are resizable and that can become the main window". Is there a reason that this window cannot become main, e.g., is it an NSPanel?

How can I prevent Lion to display the Do you want to restore your windows after a crash

On lion after each crash when I start the app Lion asks if I want it to restore the application windows since it did not terminate properly last time, how can I disable this?
BACKGROUND: I am developing a Cocoa app for Lion (and Snow Leopard), it is a scientific app (not a consumer app, and is used only in house, no distribution outside).
It interfaces with a couple of hardware and it crashes a lot! (I know I should make it not crash but there are lots of legacy C code involved and not well written drivers and ...).
QUESTION: On lion after each crash when I start the app Lion asks if I want it to restore the application windows, how can I disable this?
This seems to work but there isn't anything documented about it, so it might not work in a future OS update:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"ApplePersistenceIgnoreState"];
Preventing Lion from State Save
You can subclass NSApplication and implement restoreWindowWithIdentifier:state:completionHandler: (see also Windows Are Restored Automatically in the Document-Based App Programming Guide for Mac; unlike its sibling method on NSWindows this one returns a BOOL). For example, add a property preventWindowRestoration to your NSApplication subclass, so you can do this:
- (BOOL)restoreWindowWithIdentifier:(NSString *)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler
{
if ([self preventWindowRestoration]) return NO;
return [super restoreWindowWithIdentifier:identifier state:state completionHandler:completionHandler];
}
You need to set your property in applicationWillFinishLaunching: at latest since the restoration is happening right between applicationWillFinishLaunching: and applicationDidFinishLaunching:. And don't forget to specify your NSApplication subclass as principal class in Info.plist.
None of the above answers worked for me (I didn't try the defaults key trick but it doesn't seem to be intended for production use). Here's what worked for me:
In Xcode, open the window xib of storyboard and select the NSWindow.
Show the Utilities panel (right side pop-in) and select the Attribute Inspector (looks like a slider knob) and uncheck '[ ] Restorable' and '[ ] Visible At Launch'.
The restorable property can be set for windows you create:
NSWindow* window = ...
window.restorable = NO;
I can't find the best time to set the window.visible property, but setting it in the xib work for me.

Why isn't applicationShouldOpenUntitledFile being called?

I added a applicationShouldOpenUntitledFile method to my application delegate, returning NO as Apple's documentation specifies. However, I'm still getting a new document on startup. What's wrong?
#implementation AppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSLog( #"This is being called" );
}
- (BOOL)applicationShouldOpenUntitledFile:(NSApplication *)sender
{
NSLog( #"This never is" );
return NO;
}
#end
You're running Lion. When you ran before adding the applicationShouldOpenUntitledFile handler, a new document was created. Now, with 10.7's "Restore windows when quitting and re-opening apps", your application is restoring that untitled window, and not creating a new one as you suppose.
Close that window and re-run your application, and applicationShouldOpenUntitledFile will be called and will suppress the creation of a new untitled file.
-(void)applicationDidFinishLaunching:(NSNotification *)notification
{
// Schedule "Checking whether document exists." into next UI Loop.
// Because document is not restored yet.
// So we don't know what do we have to create new one.
// Opened document can be identified here. (double click document file)
NSInvocationOperation* op = [[NSInvocationOperation alloc]initWithTarget:self selector:#selector(openNewDocumentIfNeeded) object:nil];
[[NSOperationQueue mainQueue] addOperation: op];
}
-(void)openNewDocumentIfNeeded
{
NSUInteger documentCount = [[[NSDocumentController sharedDocumentController] documents]count];
// Open an untitled document what if there is no document. (restored, opened).
if(documentCount == 0){
[[NSDocumentController sharedDocumentController]openUntitledDocumentAndDisplay:YES error: nil];
}
}
I'm using Xcode 8.3.2 and compiling for Os X 10.11 using a storyboard for a document based app.
I noted that, if you set the window controller as initial controller, a window is created without any document and without calling applicationShouldOpenUntitledFile.
I solved removing the "is initial controller" checkbox in the storyboard.
If you're not running Lion / 10.7 or later, this can still happen if you have some other window open (even a non-Document window) when applicationShouldOpenUntitledFileshould be called.
I have a Document-based app where the AppDelegate class opens a global logging window, both for debugging purposes and for user status messages. If I have the program display that window on startup while running on OS X 10.6, applicationShouldOpenUntitledFile never gets called, even with no document windows displayed. If I turn that window off, the call is made.
Since OSX Lion, the app's state restoration may interfere with your custom preferences for this exercise.
Citing an update to Aaron Hillegass and Adam Preble's book Cocoa Programming for MacOSX:
Note that Mac OS X Lion's state-restoration features may make it tricky to observe the new document preference. You can disable state restoration by editing the Run scheme in Xcode. Open the product menu and select Edit Scheme. Select the Run RaiseMan.app scheme, change to the Options pane, and check Disable state restoration.

Cocoa send background window to front

At the bottom of this post I included an example project that has my code in it. I have a Cocoa Application that uses a main default window, and another window nib I created. When the program starts, I want it to load the window nib and show it in front of the default 'MainMenu' nib window. How can I do this? I've tried the following code, but the window is still displayed behind the default window:
InfoWindowController *winInfo = [InfoWindowController new];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo.window setOrderedIndex:0];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo showWindow:self];
This is being called from AppController, which is a class I added to the MainMenu nib. InfoWindowController subclasses NSWindowController. I have included an example project here.
So, I know i'm reviving an old thread, but I was having a similar issue. Try putting [NSApp activateIgnoringOtherApps:YES]; in there.

Snow Leopard & LSUIElement -> application not activating properly, window not "active" despite being "key"

I'm running into a few problem with a background application that uses LSUIElement=1 to hide its dock item, menu bar and prevent it from appearing in the Command-Tab application switcher.
It seems to be a Snow Leopard only problem.
The application places an NSStatusItem in the menu bar and pops up a menu when clicked on. Selecting "Preferences..." should bring up an NSWindow with the preferences.
The first thing that doesn't seem to work is that the Window does not get ordered in at the front, but appears behind all other application windows.
I tried to fix this by calling
[[NSApplication sharedApplication] activateIgnoringOtherApps: YES]
but that didn't work.
After a while I figured out that the menu is blocking the message to the run loop from being sent, so I wrote another method on the MainController and sent the message with a delay:
[self performSelector:#selector(setFront:)
withObject: [preferencesController window] afterDelay:1.0];
-(void)setFront: (id) theWindow {
[[NSApplication sharedApplication]activateIgnoringOtherApps:YES];
[theWindow orderFrontRegardless];
[theWindow makeKeyWindow];
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
Note the send-every-possible-message-to-make-it-do-what-it-should-be-doing-approach.
This works, kind-of, the window is brought to the front on top of all other windows from all apps, BUT most of the time it isn't active, meaning it's title bar is greyed out. Clicking on the title bar won't make the window active either. Clicking INSIDE of the window will make it active!?
This all didn't seem to be a problem in Leopard; just calling activateIgnoringOtherApps and making the window key seemed to work just fine.
In Snow Leopard there is a new API designed to replace LSUIElement that is supposed to emulate its behaviour:
http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html
I've played around with that, but it's SL-only and I haven't been able to get LSUIElement being set.
That's strange - I'm writing a LSUIElement application under Snow Leopard, and I didn't have such problems as you've described... I did have the problem that the newly created window didn't appear at the front, but I fixed it by calling activateIgnoringOtherApps. This was all I had to do to make it work as it should:
[NSApp activateIgnoringOtherApps: YES];
[preferencesWindow makeKeyAndOrderFront: self];
I didn't even touch anything that had 'policy' in the name.
After posting the question in desperation, I did continue looking and did eventually find the solution. Since this stumped me for a few days and there seems to be no other answer out there that google can find, I'll explain the solution for "future generations".
Snow Leopard adds a new NSApplication presentationOptions API:
http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html
This is supposed to simulate the way that LSUIElement works, but provide more developer control. Unfortunately, the simulation isn't perfect so there is a change of behaviour between 10.5 and 10.6.
In particular, if your application has the LSUIElement = 1 line in its info.plist, Snow Leopard will initialize "the application’s presentationOptions .. to an equivalent combination of NSApplicationPresentationOptions flags instead".
Only it doesn't really. It sets the new NSApplication setActivationPolicy to NSApplicationActivationPolicyAccessory:
"The application does not appear in the Dock and does not have a menu bar, but it may be activated programmatically or by clicking on one of its windows. This corresponds to value of the LSUIElement key in the application’s Info.plist being 1."
Despite the mention of being activated programatically, activateIgnoringOtherApps: is simply ignored completely.
The solution is to set the activation policy to "regular":
[[NSApplication sharedApplication] setActivationPolicy: NSApplicationActivationPolicyRegular];
Of course, you can only do this if you use the 10.6 SDK as Base SDK, something few people want to do at the moment, so below is a 10.5-safe way of doing this:
NSApplication* app = [NSApplication sharedApplication];
if( [app respondsToSelector: #selector(setActivationPolicy:)] ) {
NSMethodSignature* method = [[app class] instanceMethodSignatureForSelector: #selector(setActivationPolicy:)];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: method];
[invocation setTarget: app];
[invocation setSelector: #selector(setActivationPolicy:)];
NSInteger myNSApplicationActivationPolicyAccessory = 0;
[invocation setArgument: &myNSApplicationActivationPolicyAccessory atIndex: 2];
[invocation invoke];
}
I hope somebody will find this useful.

Resources