COCOA: NSStatusitem icon disappearing on launching external code - cocoa

I am a .Net developer that needs to port a small program over to Mac OS X. I have this mostly done (partly thanks to people on this site, thanks!) but have a bug that maybe people I can get help with.
I am creating a tool that sits in the status bar, that when clicked opens a window with several links or buttons. When the links or buttons are clicked they either open a website or external program. The problem is that the icon in the status bar disappears as I launch one of these external commands. Even more interesting is that the space on the status bar where the icon should be still responds; meaning that if I click on the area (even without the visible icon) it still runs the code and opens the window.
Here is the current code:
tray.m
#import "tray.h"
#import "MyView.h"
#implementation Tray
-(void) awakeFromNib{
NSBundle *bundle = [NSBundle mainBundle];
statusItem = [[NSImage alloc] initWithContentsofFile:[bundle pathForResource:"#icon" ofType:#"png"]];
MyView *view = [MyView new];
[statusItem setImage:statusImage];
view.image = statusImage;
[statusitem setView:view];
[statusitem setToolTip:#"Tray App"];
[view setTarget:self];
[view setAction:#selector(openWindow)];
}
-(IBAction)openWindow:(id)sender{
[trayWin makeKeyAndOrderFront:nil];
}
-(IBAction)openActMon:(id)sender {
(void)system("open '\/Applications/Utilities/Activity Monitor.app'");
}
tray.h
#import "MyView.h"
#interface Tray : NSObject {
NSStatusItem *statusItem;
NSImage *statusImage;
IBOutlet NSWindow * trayWin;
IBOutlet NSButton *ActMon;
void *openWindow;
}
#property (retain,nonatomic) NSStatusItem *statusItem;
-(IBAction)ActMon:(id)sender;
#end
MyView.h
#interface MyView : NSControl {
NSImage *image;
id target;
SEL action;
}
#property (retain)NSImage *image;
#property (assign) id target;
#property (assign) SEL action;
#end
MyView.m
#import "MyView.h"
#implementation MyView;
#synthethize image, target, action;
-(void)mousemouseUP:(NSEvent *)event{
[NSApp sendAction:selfself.action to:self.target from:self];
}
-(void)dealloc {
self.image = nil;
[super dealloc];
}
-(void)drawRect:(NSRect)rect {
[self.image drawInRect:CGRectMake(0,0,18,18) fromRect:NSZeroRect operation:NSCompositeSourceOver];
}
#end
}
The openActMon is run when the image/button is clicked, the image is located in the trayWin Window that is opened when the icon is clicked. At this point, Activity monitor successfully launches, but the icon in the StatusBar disappears.
I have tried putting a [super setNeedsDisplay:YES] in the openActMon, but that didn't help. And I added [view setNeedsDisplay:YES] in the openActMon and it responded undeclared.
I have given all of this code because, as I said, I am not a Objective-C coder, but .Net who just needs to port something small over. Hoping that this will be helpful to others in the future. Alot of this I have hodgepodged together from different forums and sites or have gotten from some help on StackOverflow. I am hoping someone can help.
Thanks in advance!

In awakeFromNib add:
[statusitem retain];

In awakeFromNib, you are allocating the NSImage into statusItem. I think you mean to allocate it into statusImage.

Related

Menu items don't appear in nibless StatusMenu app?

I've been working on an application port that must show a status menu and some dynamic items within. It's behaviour is similar to the apple WIFI menu which has an icon, some fixed items and some dynamic items in the center (the available WIFI networks).
For various reasons I decided to go nibless. I've managed to get the menu icon to appear, but I can't seem to get the items to show in the menu when I click the icon.
This is what I have so far:
AppDelegate.h
#import <Cocoa/Cocoa.h>
#interface AppDelegate : NSObject <NSApplicationDelegate>
// Strange, if these are not properties and not declared strong,
// the menu flashes momentarily and disappears. Could the dynamic menu items be related to object lifetime?
#property (strong, nonatomic) IBOutlet NSMenu *statusMenu;
#property (strong, nonatomic) NSStatusItem *statusItem;
#end
AppDelegate.m
import "AppDelegate.h"
#implementation AppDelegate
- (IBAction)loginClicked:(id)sender
{
NSLog(#"LoginClicked");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
NSLog(#"AppDidFinishLaunching!");
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setMenu:self.statusMenu];
NSImage *statusImage = [[NSImage alloc] initWithContentsOfFile:#"/tmp/applogo.png"];
//menuImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:#"applogo" ofType:#"png"]];
[self.statusItem setImage:statusImage];
[self.statusItem setAlternateImage:statusImage];
//[self.statusItem setTitle:#"MyApp"];
[self.statusItem setHighlightMode:YES];
// Add login
NSMenuItem *login = [[NSMenuItem alloc] initWithTitle:#"Login" action:loginClicked keyEquivalent:#""];
[self.statusMenu addItem:login];
NSMenuItem *quit = [[NSMenuItem alloc] initWithTitle:#"Quit" action:nil keyEquivalent:#""];
[self.statusMenu addItem:quit];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}
main.m
#import <Cocoa/Cocoa.h>
#import "AppDelegate.h"
int main(int argc, const char* argv[])
{
#autoreleasepool { // Do I need this or is it on by default?
// make sure the application singleton has been instantiated
NSApplication * application = [NSApplication sharedApplication];
// instantiate our application delegate
AppDelegate * applicationDelegate = [[AppDelegate alloc] init];
// assign our delegate to the NSApplication
[application setDelegate:applicationDelegate];
// call the run method of our application
[application run];
}
// execution never gets here...
return 0;
}
After much mucking around, I tried something that now seems obvious.
I don't know why, but in examples of StatusBar Apps that use nib, IBMenu is not alloc'ed and inited. It appears to be done automatically somehow.
Adding the following seemed to fix it.
self.statusMenu = [[NSMenu alloc] init];
Also, noting my comment on lifetime. If I don't use properties, I have to set retain on statusItem when creating it. Then it doesn't get destroyed. I'm still learning about how objective-C managages object lifetime and still a bit confused about ARC here. But it least it now seems to show my menu items.

Why is this Cocoa layout ambiguous?

I want to use layout constraints and create my UI programmatically. Here is a simple program that I'm hoping you can help me understand. In Interface Builder, I simply took the defaults -- there is an NSWindow with its default contentView. Below is all the code, and a screenshot.
I create a single button, and place it in the content view. Then I try to use constraints to make it fill the window. As you can see, it claims the layout is ambiguous. But when I click that button to "Exercise Ambiguity", nothing changes. The docs say it should choose a different possible layout.
I also think the content view is tightly surrounding the button and not filling the window, but I don't know how to force that with constraints.
// In AppDelegate.h
#interface AppDelegate : NSObject <NSApplicationDelegate> {
NSButton *_button;
}
#property (assign) IBOutlet NSWindow *window;
#end
// In AppDelegate.m
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSView *contentView = (NSView*)_window.contentView;
_button = [[NSButton alloc] init];
_button.title = #"Test";
_button.translatesAutoresizingMaskIntoConstraints = NO;
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:_button];
NSDictionary *viewsDict = NSDictionaryOfVariableBindings(_button, contentView);
NSMutableArray *constraints = [[NSMutableArray alloc] init];
[constraints addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat:#"|[_button]|" options:0 metrics:0 views:viewsDict]];
[constraints addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat:#"V:|[_button]|" options:0 metrics:0 views:viewsDict]];
[contentView addConstraints:constraints];
[_window visualizeConstraints:constraints];
printf("Is layout ambiguous? %d\n", contentView.hasAmbiguousLayout);
}
#end
What if you visualize constraints on a subsequent iteration of the run loop, for example with a timer or by clicking a button, after the layout engine has had a pass at it? It may just be ambiguous because the layout engine hasn’t solved the system yet.
Edit: I ran your code, and am seeing the same issue. I’m also stumped now.

NSTableView not Populating

I've been trying to get this NSTableView to populate for the last 7 hours. I am trying to get a list of all the currently running application and put them into an NSTableView. Eventually I would like to parse the resultes and organize the PID in one column and the Application Bundle in the other. I am getting an EXC_BAD_ACCESS error on " return [listOfWindows objectAtIndex:row];" I am currently using Xcode 4.3.2 and running OS X Lion 10.7.4. Thanks in advance everyone!
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet NSMenu *statusMenu;
IBOutlet NSButton *button;
IBOutlet NSWindow *menuWindow;
IBOutlet NSTableView *proTable;
NSArray *listOfWindows;
IBOutlet NSArrayController *arrayController;
AppDelegate *mainMenu;
NSWorkspace *workSpace;
NSStatusItem *statusItem;
}
#property (assign) IBOutlet NSWindow *window;
-(IBAction)loadConfig:(id)sender;
#end
#import "AppDelegate.h"
#implementation AppDelegate
#synthesize window = _window;
- (void) awakeFromNib
{
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:#selector(loadMenu:)
name:#"WhiteBox"
object:nil];
[self addStatusItem];
//[proTable setDataSource:self];
listOfWindows = [[NSWorkspace sharedWorkspace] runningApplications];
NSLog(#"index %#", listOfWindows);
int y = 0;
y = [listOfWindows count];
NSLog(#"y = %d", y);
[proTable setAllowsMultipleSelection:YES];
}
-(void)applicationWillTerminate
{
NSLog(#"Will Terminate");
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
}
-(void)applicationDidResignActive:(NSNotification *)notification
{
NSLog(#"Resign Active");
}
-(void) addStatusItem
{
//Create a variable length status item from the system statusBar
statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
[statusItem retain];
//Set a Title for it
[statusItem setTitle:#"Status Item"];
//Set an Image and an alternate image
//[statusItem setImage:[NSImage imageNamed:#"lnc"]];
//[statusItem setAlternateImage: [NSImage imageNamed:#"status"]];
//Add a Tool Tip
[statusItem setToolTip:#"Status Item Tooltip"];
//Choose to highlight the item when clicked
[statusItem setHighlightMode:YES];
//To Trigger a method on click use the following two lines of code
[statusItem setMenu:statusMenu];
//[statusItem setAction:#selector(loadMenu:)];
}
-(IBAction)loadConfig:(id)sender
{
if(! [menuWindow isVisible] )
{
[menuWindow makeKeyAndOrderFront:sender];
} else {
[menuWindow performClose:sender];
}
}
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [listOfWindows count];
}
- (id)tableView:(NSTableView *)tableView
objectValueForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row
{
return [listOfWindows objectAtIndex:row];
}
#end
What object is the table view's data source? I don't see any object in the source you posted as implementing the NSTableViewDataSource protocol.
Further, have you tried putting breakpoints in the various data source methods to see if the debugger stops in them? If not, it's usually a good sign that your data source isn't connected to your table view.
I got: -[NSRunningApplication copyWithZone:]: unrecognized selector error when I ran your code. This could be fixed by changing your return line in tableView:objectValueForTableColumn:row: to
return [[listOfWindows objectAtIndex:row]localizedName];
NSRunningApplication doesn't conform to NSCopying, so I don't know if you can put instances of that class in a table view. However, you can get its properties like localizedName, processIdentifier, and bundleIdentifier.
I've run into this problem before with classes that don't implement NSCopying, I'd be happy to know if anyone knows a way to use these classes in table views or outline views.

Change WebView position Cocoa

I'm quite a newbie to Cocoa & Mac programming. I have a WebView and I want to move it. Is there a way to change the coordinates of the WebView?
EDIT: Some relevant code:
NistractAppDelegate.m
#import "NistractAppDelegate.h"
#implementation NistractAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[window setAlphaValue:0];
[window setBackgroundColor:[NSColor colorWithCalibratedRed:0.125490196078431 green:0.125490196078431 blue:0.125490196078431 alpha:1]];
[mainWebView setBackgroundColor:[NSColor colorWithCalibratedRed:0.125490196078431 green:0.125490196078431 blue:0.125490196078431 alpha:1]];
[[mainWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.google.com"]]];
mainWebView.frame = NSMakeRect(mainWebView.frame.origin.x + 10, mainWebView.frame.origin.y, mainWebView.frame.size.width, mainWebView.frame.size.height);
[window makeKeyAndOrderFront:self];
[[window animator] setAlphaValue:1];
}
#end
NistractAppDelegate.h
#import <Cocoa/Cocoa.h>
#interface NistractAppDelegate : NSObject <NSApplicationDelegate> {
NSWindow *window;
IBOutlet NSView *mainView;
IBOutlet id mainWebView;
}
#property (assign) IBOutlet NSWindow *window;
#end
If you just want to move the entire webview you need set it's frame.
WebView.frame = NSMakeRect(x,y,width,height);
Eg if you want to just shift it left by ten pixels:
WebView.frame = NSMakeRect(WebView.frame.origin.x + 10, WebView.frame.origin.y, WebView.frame.size.width, WebView.frame.size.height);
When in doubt always you look in the documentation, which you can do from within side XCode, by selecting some text and right clicking then selecting Find Text In Documentation
Or use Google by searching "{Class name} class reference" Eg "nsview class reference"

How to make a program load an web image and show it on a Custom View?

Well, I'm learning how to program on a mac, and I'm tired of searching the for answers, can you guys explain me what I did wrong on my program.
First, I dragged 2 buttons on my window (Load Image, and Unload). Then, I dragged a custom view item and here`s what I did with the Test_Loading_ImagesAppDelegate.h:
#import <Cocoa/Cocoa.h>
#interface Test_Loading_ImagesAppDelegate : NSView {
NSWindow *window;
IBOutlet NSView *mypicture;
}
- (IBAction)LoadImage: (id)sender;
- (IBAction)Unload: (id)sender;
#property (assign) IBOutlet NSWindow *window;
#end
After that, I linked up things on Interface Builder normally and made the Test_Loading_ImagesAppDelegate.m:
#import "Test_Loading_ImagesAppDelegate.h"
#implementation Test_Loading_ImagesAppDelegate
#synthesize window;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
}
-(IBAction)LoadImage: (id) sender {
NSURL *myurl;
NSImage *image;
NSData *myurldata;
myurl = [NSURL URLWithString:#"http://....jpg"];
myurldata = [NSData dataWithContentsOfURL:myurl];
image = [NSImage initWithData:myurldata];
[mypicture setImage: image];
}
-(IBAction)Unload: (id) sender {
//Well, still thinking how I'm going to do this, and I would like to make another question: If I dealloc the View or something else the image will disappear?
}
#end
Thanks in advance.
Well, after coming here again I've found this http://www.markj.net/iphone-asynchronous-table-image/ it's for iPhone but can be adapted to OS X easily. Thanks.

Resources