Change NSStatusItem's title (statusitem app)

Hey there!
I have a menulet working but i’m trying to dynamically refresh the status item’s title (as well as some menu items) from applescript.
I have tried calling cocoa methods from AS but with no success and can never get the title to change. I know my cocoa call works because if I place it right in the (void)awakeFromNib part of my script (where the NSStatusItem is being constructed) it works just fine.
Can someone help me figure this one out, I’m at a loss here… :slight_smile:

Thanks!

Here is my code:



//MyMenu.m

@implementation MyMenu

- (void)awakeFromNib
{
 //Create the NSStatusBar and set its length
 statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain];
[statusItem setHighlightMode:YES];
[statusItem setEnabled:YES];

 //Sets the tooltip of our NSStatusItem
[statusItem setToolTip:@"My Menu!"];
//[statusItem setTarget:self];

//Sets the title of our NSStatusItem
NSUserDefaults * defaults;
defaults = [NSUserDefaults standardUserDefaults];
NSString *currScene =[defaults stringForKey:@"currTest"] ;
[statusItem setTitle:[NSString stringWithFormat:@" %@", currTest]];

 //Used to detect where our files are
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@"IPMenuIcon" ofType:@"tif"];
menuIcon= [[NSImage alloc] initWithContentsOfFile:path];

//Sets the images in our NSStatusItem
[statusItem setImage];
[menuIcon release];

 //Tells the NSStatusItem what menu to load
[statusItem setMenu:theMenu];

}

-(void)dealloc
{
	[[NSUserDefaults standardUserDefaults] synchronize];
	[statusItem release];
	[super dealloc];
}

-(void)updateMenu
{

//Sets the title of our NSStatusItem
NSUserDefaults *defaults;
defaults = [NSUserDefaults standardUserDefaults];
NSString *currScene =[defaults stringForKey:@"currTest"] ;
[statusItem setTitle:[NSString stringWithFormat:@" %@", currTest]];
//[defaults synchronize];
//NSLog(@"Hello there!");
}
@end


//MyMenu.h

@interface MatteMenu :  NSObject {
// Our outlets which allow us to access the interface
IBOutlet NSMenu *theMenu;

// The other stuff
NSStatusItem *statusItem;
NSImage *menuIcon;
}


// Our IBAction which will call the updateMenu2 method when our connected Menu Item is pressed
- (void)updateMenu;


@end



-- The actions for the menu item that would refresh the Menu and NSStatusItem's title

on choose menu item theObject
set object_name to name of theObject as string
if object_name = "menuItem1" then
		call method "updateMenu:" of class "MyMenu"
end if


HI,

your method has no parameter, so omit the trailing colon

call method "updateMenu" of class "MyMenu"

Thanks, I just tried omitting the colon but no dice… Any other ideas?
It’s weird because if I create a blank menu item and convert the updateMenu method to an IBAction and connect it to the menu item, it works. But calling it from applescript doesn’t.
I have seen an example from apple that worked but the method was declared under @implementation NSApplication (ASKAMultiLanguage)
But i can’t use that since my statusItem is being built in “MyMenu”…

two possible reasons:

you define a NSString variable currScene, but you use a variable currTest in the next line.

The second reason is a bit more complex: You can’t access instance variables directly from outside the class.

Try to implement an accessor method to retrieve the reference to the StatusItem

- (NSStatusItem *)statusItem { return statusItem; }
and a new class method to set the title

+ (void)setTitleForStatusBar:(NSStatusItem *)bar { [bar setTitle:[NSString stringWithFormat:@" %@", [[NSUserDefaults standardUserDefaults] stringForKey:@"currTest"]]]; }
maybe it works also with an instance method (-)

the AppleScript code is


set statusItem to call method "statusItem" of class "MyMenu"
call method "setTitleForStatusBar:" of class "MyMenu" with parameter statusItem

Hey StefanK, thanks for your help!
I tried what you suggested, which made a lot of sense to me, but I’m getting the following error “The variable statusItem is not defined. (-2753)”.
It looks like something is not getting passed to the applescript side. Not sure what.
Any help would be greatly appreciated!

have you declared the accessor methods in the .h file?

PS: the names of the interface and implementation parts are different, they should be the same

@implementation MyMenu
@interface MatteMenu

Here is the code as i have it in the project:

MatteMenu.m:

[code]#import “MatteMenu.h”
@implementation MatteMenu

  • (void)awakeFromNib
    {
    //Create the NSStatusBar and set its length
    statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain];
    [statusItem setHighlightMode:YES];
    [statusItem setEnabled:YES];

//Sets the tooltip of our NSStatusItem
[statusItem setToolTip:@“RH Matte Menu”];
//[statusItem setTarget:self];

//Sets the title of our NSStatusItem
NSUserDefaults * defaults;
defaults = [NSUserDefaults standardUserDefaults];
NSString *currScene =[defaults stringForKey:@“currScene”] ;
[statusItem setTitle:[NSString stringWithFormat:@" %@",currScene]];

//Used to detect where our files are
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSString *path = [bundle pathForResource:@“IPMenuIcon” ofType:@“tif”];
menuIcon= [[NSImage alloc] initWithContentsOfFile:path];

//Sets the images in our NSStatusItem
[statusItem setImage];
[menuIcon release];

//Tells the NSStatusItem what menu to load
[statusItem setMenu:theMenu];

}

  • (void)dealloc
    {
    [[NSUserDefaults standardUserDefaults] synchronize];
    [statusItem release];
    [super dealloc];
    }

-(IBAction)updateMenu:(id)sender
{

//Sets the title of our NSStatusItem
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *currScene =[defaults stringForKey:@“currScene”] ;
[statusItem setTitle:[NSString stringWithFormat:@" %@“, currScene]];
//[statusItem setTitle:[NSString stringWithString:@” test"]];
[defaults synchronize];
NSAppleScript *closeOutput = [[NSAppleScript alloc] initWithSource:@“close window "outputDlg"”];
[closeOutput executeAndReturnError:nil];
NSLog(@“done”);
}

  • (NSStatusItem *)getStatusItem
    {
    return statusItem;
    }

  • (void)setTitleForStatusBar:(NSStatusItem *)bar
    {
    [bar setTitle:[NSString stringWithFormat:@" %@", [[NSUserDefaults standardUserDefaults] stringForKey:@“currScene”]]];
    }

@end[/code]
MatteMenu.h:

[code]#import <Cocoa/Cocoa.h>

@interface MatteMenu : NSObject {
// Our outlets which allow us to access the interface
IBOutlet NSMenu *theMenu;

// The other stuff
NSStatusItem *statusItem;
NSImage *menuIcon;
}

// Our IBAction which will call the updateMenu2 method when our connected Menu Item is pressed

  • (IBAction)updateMenu:(id)sender;

  • (NSStatusItem *)getStatusItem;

  • (void)setTitleForStatusBar:(NSStatusItem *)bar;

@end[/code]
Applescript call:


on clicked theObject
    set objname to name of theObject as string
    if objname = "setBt" then
        set statusItem to call method "getStatusItem" of class "MatteMenu"
        call method "setTitleForStatusBar:" of class "MatteMenu" with parameter statusItem
    end if
end clicked

I have the IBAction there because it is the only way I found to call on the updateMenu method, since I can’t get it to work from applescript.

it might be much easier to do the whole thing in ObjC

add this in the .h file

[code]@interface MatteMenu : NSObject {
IBOutlet NSButton *button;
}

  • (IBAction)setStatusBarTitle:(id)sender;[/code]
    and this in the .m file

- (IBAction)setStatusBarTitle:(id)sender { [statusItem setTitle:[NSString stringWithFormat:@" %@", [[NSUserDefaults standardUserDefaults] stringForKey:@"currScene"]]]; }
connect the button in Interface Builder (action and outlet)

Thanks, this is what I had going but I need to be able to use an applescript call to update the menu because sometimes the menu updates need to occur without user input…
I wonder why the NSStatusItem isn’t getting passed to applescript…

In my first post I suggested a class method (consider the + sign) because AppleScript cannot call instance methods without creating an instance

  • (void)setTitleForStatusBar:(NSStatusItem *)bar

Just tried it and still getting “The variable statusItem is not defined. (-2753)”
Thanks for all your help, btw Stefan. =)

You can do only if you create an instance of the MatteMenu class.
This should work

MatteMenu.h

[code]#import <Cocoa/Cocoa.h>

@interface MatteMenu : NSObject {
// Our outlets which allow us to access the interface
IBOutlet NSMenu *theMenu;

// The other stuff
NSStatusItem *statusItem;
NSImage *menuIcon;

}

  • sharedInstance;
  • (id)init;

  • (void)updateMenu;
    @end[/code]
    MatteMenu.m

[code]#import “MatteMenu.h”

@implementation MatteMenu

  • sharedInstance
    {
    static id sharedTask = nil;
    if(sharedTask == nil) {
    sharedTask = [[[self alloc] init] autorelease];
    }
    return sharedTask;
    }
  • (id)init {
    self = [super init];
    if (self) {
    NSLog(@“Init”);
    //Create the NSStatusBar and set its length
    statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength] retain];
    [statusItem setHighlightMode:YES];
    [statusItem setEnabled:YES];

      //Sets the tooltip of our NSStatusItem
      [statusItem setToolTip:@"RH Matte Menu"];
      //[statusItem setTarget:self];
      
      //Sets the title of our NSStatusItem
      NSUserDefaults * defaults;
      defaults = [NSUserDefaults standardUserDefaults];
      NSString *currScene = [defaults stringForKey:@"currScene"] ;
      [statusItem setTitle:[NSString stringWithFormat:@" %@",currScene]];
      
      //Used to detect where our files are
      NSBundle *bundle = [NSBundle bundleForClass:[self class]];
      NSString *path = [bundle pathForResource:@"IPMenuIcon" ofType:@"tif"];
      menuIcon = [[NSImage alloc] initWithContentsOfFile:path];
      
      //Sets the images in our NSStatusItem
      [statusItem setImage];
      [menuIcon release];
      
      //Tells the NSStatusItem what menu to load
      [statusItem setMenu:theMenu];
    

    }
    return self;
    }

  • (void)dealloc
    {
    [[NSUserDefaults standardUserDefaults] synchronize];
    [statusItem release];
    [super dealloc];
    }

-(void)updateMenu
{

//Sets the title of our NSStatusItem
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *currScene = [defaults stringForKey:@"currScene"] ;
[statusItem setTitle:[NSString stringWithFormat:@" %@", currScene]];
//[statusItem setTitle:[NSString stringWithString:@" test"]];
[defaults synchronize];
NSAppleScript *closeOutput = [[NSAppleScript alloc] initWithSource:@"close window \"outputDlg\""];
[closeOutput executeAndReturnError:nil];
NSLog(@"done");

}

@end[/code]
AppleScript part (connect the window to the awake from nib handler)


property sharedInstance : missing value

on awake from nib theObject
    set sharedInstance to call method "sharedInstance" of class "MatteMenu"
end awake from nib

on clicked theObject
    call method "updateMenu" of sharedInstance
end clicked

Just tried it, I get two Status Items now and none of them seem to be connected to the menu now. :confused:
I get this in the Debugger console:

Init Could not connect the action updateMenu: to target of class MatteMenu Init done done done

In my solution (I’ve tested it successfully) the method is called updateMenu (without colon) and is not an IBAction

PS: For the second init call there is probably a delegate set in Interface Builder

I managed to get only one StatusItem spawned by renaming the init method to “initMenu” (also renamed in the call in sharedInstance).
But the StatusItem is still not connected to the main menu (no menu shown when I click on the StatusItem), it’s weird. I haven’t changed anything in IB and it works in my old code…

which main menu? There is no ObjC code which specifies any menu

[statusItem setMenu:theMenu];

This is linked to a “MatteMenu” NSObject controller in IB that connects theMenu to the main menu.

got it.

You can connect only a single (custom) menu to the status menu, not a menu bar like the default main menu

That’s what I currently have in IB: a custom NSMenu called “mainMenu” linked to the “MatteMenu” NSObject that connects “theMenu” to “mainMenu”.
It worked fine with my previous code but something in the code you provided happened to break that connection, not sure what.
It otherwise seems to work exactly how I’d want it, though. =)