Airr
6/1/2018 - 9:56 PM

"Menulet Demo": A Single Source CONSOLE OSX Objective C StatusBar App (NO IB/NIB/XIB/XCODE)

"Menulet Demo": A Single Source CONSOLE OSX Objective C StatusBar App (NO IB/NIB/XIB/XCODE)

// "Menulet Demo": A Single Source CONSOLE OSX Objective C StatusBar App (NO IB/NIB/XIB)

// Copyright (c) 2018 Armando Rivera

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.


// REQUIRED IMPORTS
#import <Cocoa/Cocoa.h>
#import <mach-o/getsect.h>
#import  <mach-o/ldsyms.h>

// TYPEDEF FOR FUNCTION CALLBACKS
typedef void(*ACTION)(id);


// SUBCLASSED NSMENUITEM ENABLING C-STYLE CALLBACKS
@interface CocoaMenuItem: NSMenuItem{
    ACTION menuAction;
}
    @property ACTION menuAction;
    

    - (void) click:(id)sender;
@end

@implementation CocoaMenuItem

	@synthesize menuAction;


	- (void) click:(id)sender {
		menuAction(sender);
	}
@end


// FUNCTION DECLARATIONS
NSData *getResource(NSString *sectionName, NSString *itemName);
void Cocoa_Init();
void Cocoa_Run(id mainWin);
void menuCallback(CocoaMenuItem *widget);
CocoaMenuItem *createMenuItem(id parent, NSString *title, ACTION func);
NSMenu *createMenu();

// FUNCTION TO RETRIEVE EMBEDDED PNG FILE
NSData *getResource(NSString *sectionName, NSString *itemName) {
    unsigned long size;
    void *ptr = getsectiondata(&_mh_execute_header, sectionName.UTF8String, itemName.UTF8String, &size);
    NSData *resourceData = [NSData dataWithBytesNoCopy:ptr length:size freeWhenDone:NO];
    return resourceData;
}

// FUNCTION TO INITIALIZE COCOA SUBSYSTEM
void Cocoa_Init() {
    [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
}

// FUNCTION THAT PASSES CONTROL TO COCOA SUBSYSTEM
void Cocoa_Run(id mainWin) {
    [NSApp setDelegate:mainWin]; 
    [NSApp run];    
}

// FUNCTION TO CREATE STATUSBAR ITEM, WITH EMBEDDED ICON EXTRACTED FROM THE BINARY
id createStatusBarItem() {
	NSStatusItem *self;
  	NSStatusBar *statusBar = [NSStatusBar systemStatusBar];
  	self = [statusBar statusItemWithLength:NSSquareStatusItemLength];

  	NSImage *icon = [[NSImage alloc] initWithData: getResource(@"__PNG",@"__icon")];
  	icon.template = YES;
 
  	self.button.image = icon;
  	return self;
}

// FUNCTION TO CREATE MENU ITEMS/ENTRIES
CocoaMenuItem *createMenuItem(id parent, NSString *title, ACTION func) {
	CocoaMenuItem *self = [CocoaMenuItem new];
	self.title = title;
	self.target = self;
	if (func)
	{
		self.menuAction = func;
		self.action = @selector(click:);

	}

	[parent addItem: self];

	return self;
}

// FUNCTION THAT RETURNS A MENU OBJECT
NSMenu *createMenu() {
	return NSMenu.new;
}

// FUNCTION TO ADD A SEPARATOR TO A MENU
void addSeparator(id parent) {
  [parent addItem: [NSMenuItem separatorItem]];
}

// CALLBACK USED BY MENU CLICKS
void menuCallback(CocoaMenuItem *widget) {
	switch (widget.tag) {
		case 1001:
			[NSApp terminate:nil];
			break;
		default:
			printf("%s\n",widget.title.UTF8String);
			break;
	}
	
}

// MAIN PROGRAM BEGINS HERE
int main(int argc, char const *argv[]) {
	@autoreleasepool {
		NSStatusItem *statusItem;
		NSMenu *menu;
		CocoaMenuItem *item1,*item2,*item3,*quitItem;

		// Initialize GUI
		Cocoa_Init();

		// Create Menu
		menu = createMenu();

		// Create StatusBar Widget
		statusItem = createStatusBarItem();

		// Create Several Menu Items
		item1 = createMenuItem(menu, @"Item 1",menuCallback);
		item2 = createMenuItem(menu, @"Item 2",menuCallback);
		item3 = createMenuItem(menu, @"Item 3",menuCallback);
		addSeparator(menu);
		quitItem = createMenuItem(menu, @"Quit",menuCallback);

		// We can set numeric Identifiers on Menu Items
		quitItem.tag = 1001;

		// Add the Menu to the StatusItem
		statusItem.menu = menu;

		// Run the GUI!!!
		Cocoa_Run(menu);
	}
	
	return 0;
}

/* Makefile MAKE SURE YOU CHANGE THE PATH IN THE 'RESOURCE' VARIABLE TO A VALID PNG FILE!!!
CC := gcc
LDFLAGS := -fobjc-arc -framework Cocoa
EMBED := -sectcreate __PNG __icon
RESOURCE := img/icon.png
SOURCE := demo.m
MODULES := 
PROGRAM := demo


all: 
	@echo Building demo....
	@${CC} ${LDFLAGS} ${EMBED} ${RESOURCE} ${SOURCE} ${MODULES} -o ${PROGRAM}
	@echo Build Complete.

clean:
	@echo Removing Demo...
	@rm -f ${PROGRAM}
*/