boinc/clientgui/mac/SystemMenu.m

252 lines
6.9 KiB
Objective-C

/* SystemMenu.m */
// Berkeley Open Infrastructure for Network Computing
// http://boinc.berkeley.edu
// Copyright (C) 2005 University of California
//
// This is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
//
// This software is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Lesser General Public License for more details.
//
// To view the GNU Lesser General Public License visit
// http://www.gnu.org/copyleft/lesser.html
// or write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/* Cocoa routines for BOINC Manager OS X System Icon Menu.
Written by Charlie Fenton for UC Berkeley Space Sciences Laboratory 2005
Adapted from Apple Developer Tech Support sample code, including:
SpellingChecker-CarbonCocoa
MenuMadness
*/
#include <Carbon/Carbon.h>
#include <Cocoa/Cocoa.h>
#define _DEBUG 0
@interface SystemMenu : NSObject {
}
- (void)BuildSysMenu:(MenuRef)menuToCopy;
@end
SystemMenu *gSystemMenu = NULL;
NSStatusItem *gStatusItem = NULL;
void SetSystemMenuIcon(PicHandle theIcon);
static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr);
/*
Needed to make sure the Cocoa framework has a chance to initialize things in a Carbon app.
NSApplicationLoad() is an API introduced in 10.2 which is a startup function to call when
running Cocoa code from a Carbon application.
Because NSApplicationLoad() is not available pre 10.2, we load the function pointer through
CFBundle. If we encounter an error during this process, (i.e. it's not there), we fall back
to calling NSApplication *NSApp=[NSApplication sharedApplication];
The fallback method does have bugs with regard to window activation seen when hiding and showing
the main application.
*/
typedef BOOL (*NSApplicationLoadFuncPtr)( void );
void InitializeCocoa() {
CFBundleRef appKitBundleRef;
NSApplicationLoadFuncPtr myNSApplicationLoad;
OSStatus err;
// Load the "AppKit.framework" bundl to locate NSApplicationLoad
err = LoadFrameworkBundle( CFSTR("AppKit.framework"), &appKitBundleRef );
if (err != noErr) goto FallbackMethod;
// Manually load the Mach-O function pointers for the routines we will be using.
myNSApplicationLoad = (NSApplicationLoadFuncPtr) CFBundleGetFunctionPointerForName( appKitBundleRef, CFSTR("NSApplicationLoad") );
if ( myNSApplicationLoad == NULL ) goto FallbackMethod;
(void) myNSApplicationLoad();
return;
FallbackMethod:
{ /* NSApplication *NSApp = */ [NSApplication sharedApplication]; }
}
/*
*/
void SetUpSystemMenu(MenuRef menuToCopy, PicHandle theIcon) {
NSAutoreleasePool* pool;
if (gSystemMenu == NULL)
InitializeCocoa();
pool = [[NSAutoreleasePool alloc] init];
if (gSystemMenu)
[gSystemMenu release];
gSystemMenu = [[SystemMenu alloc] init];
[gSystemMenu retain];
[gSystemMenu BuildSysMenu:menuToCopy];
// [gStatusItem setImage: [NSImage imageNamed:@"Icon0"]];
SetSystemMenuIcon(theIcon);
[pool release];
}
@implementation SystemMenu
- (void)BuildSysMenu:(MenuRef)menuToCopy {
NSStatusBar *bar;
NSMenuItem *newItem;
NSMenu *sysMenu;
int i, n;
Str255 s;
CFStringRef CFText;
// Add the submenu
newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:@"BOINC!" action:NULL keyEquivalent:@""];
sysMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@"BOINC!"];
[newItem setSubmenu:sysMenu];
if (gStatusItem == NULL) {
bar = [NSStatusBar systemStatusBar];
gStatusItem = [bar statusItemWithLength:NSSquareStatusItemLength];
[gStatusItem retain];
}
[gStatusItem setTitle: NSLocalizedString(@"",@"")];
[gStatusItem setHighlightMode:YES];
[gStatusItem setMenu:sysMenu];
[sysMenu release];
[newItem release];
n = CountMenuItems(menuToCopy);
// Add the items
for (i=1; i<=n; i++)
{
GetMenuItemText(menuToCopy, i, s);
if (PLstrcmp(s, "\p-") == 0)
{
[sysMenu addItem:[NSMenuItem separatorItem]];
continue;
}
CFText = CFStringCreateWithPascalString(kCFAllocatorDefault, s, kCFStringEncodingMacRoman);
if (CFText != NULL)
{
newItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:(NSString*)CFText action:NULL keyEquivalent:@""];
[newItem setTarget:self];
[sysMenu addItem:newItem];
if( IsMenuItemEnabled(menuToCopy, i) )
[newItem setEnabled:YES];
else
[newItem setEnabled:NO];
[newItem release];
CFRelease(CFText);
}
}
[sysMenu setAutoenablesItems:NO];
return;
}
void SetSystemMenuIcon(PicHandle theIcon)
{
if (theIcon == NULL)
{
// A NULL icon handle is a request to remove the status item from the menu bar
[[gStatusItem statusBar] removeStatusItem:gStatusItem];
[gStatusItem release];
[gSystemMenu release];
gStatusItem = NULL;
gSystemMenu = NULL;
return;
}
unsigned theLength = GetHandleSize((Handle)theIcon);
NSData* theData = [[NSData alloc] initWithBytes:*theIcon length:theLength];
NSImage* theImage = [[NSImage alloc] initWithData:theData];
[gStatusItem setImage:theImage];
[theImage release];
[theData release];
return;
}
static OSStatus LoadFrameworkBundle(CFStringRef framework, CFBundleRef *bundlePtr)
{
OSStatus err;
FSRef frameworksFolderRef;
CFURLRef baseURL;
CFURLRef bundleURL;
if ( bundlePtr == nil ) return( -1 );
*bundlePtr = nil;
baseURL = nil;
bundleURL = nil;
err = FSFindFolder(kOnAppropriateDisk, kFrameworksFolderType, true, &frameworksFolderRef);
if (err == noErr) {
baseURL = CFURLCreateFromFSRef(kCFAllocatorSystemDefault, &frameworksFolderRef);
if (baseURL == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
bundleURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorSystemDefault, baseURL, framework, false);
if (bundleURL == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
*bundlePtr = CFBundleCreate(kCFAllocatorSystemDefault, bundleURL);
if (*bundlePtr == nil) {
err = coreFoundationUnknownErr;
}
}
if (err == noErr) {
if ( ! CFBundleLoadExecutable( *bundlePtr ) ) {
err = coreFoundationUnknownErr;
}
}
// Clean up.
if (err != noErr && *bundlePtr != nil) {
CFRelease(*bundlePtr);
*bundlePtr = nil;
}
if (bundleURL != nil) {
CFRelease(bundleURL);
}
if (baseURL != nil) {
CFRelease(baseURL);
}
return err;
}
@end