Skip to content

Commit

Permalink
added LaunchAtLoginController, menu item to launch/activate iTunes
Browse files Browse the repository at this point in the history
  • Loading branch information
74hc595 committed Jan 9, 2012
1 parent f98ccc5 commit 8003356
Show file tree
Hide file tree
Showing 9 changed files with 501 additions and 259 deletions.
6 changes: 6 additions & 0 deletions Currentsong.xcodeproj/project.pbxproj
Expand Up @@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
C91F1B7114BA3E0C00C40108 /* MSDurationFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = C91F1B7014BA3E0C00C40108 /* MSDurationFormatter.m */; };
C91F1B7514BAA7DA00C40108 /* LaunchAtLoginController.m in Sources */ = {isa = PBXBuildFile; fileRef = C91F1B7414BAA7DA00C40108 /* LaunchAtLoginController.m */; };
C9E4B39514B8C440006D7B1F /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9E4B39414B8C440006D7B1F /* Cocoa.framework */; };
C9E4B39F14B8C440006D7B1F /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C9E4B39D14B8C440006D7B1F /* InfoPlist.strings */; };
C9E4B3A114B8C440006D7B1F /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = C9E4B3A014B8C440006D7B1F /* main.m */; };
Expand All @@ -23,6 +24,8 @@
/* Begin PBXFileReference section */
C91F1B6F14BA3E0B00C40108 /* MSDurationFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MSDurationFormatter.h; sourceTree = "<group>"; };
C91F1B7014BA3E0C00C40108 /* MSDurationFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MSDurationFormatter.m; sourceTree = "<group>"; };
C91F1B7314BAA7DA00C40108 /* LaunchAtLoginController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LaunchAtLoginController.h; sourceTree = "<group>"; };
C91F1B7414BAA7DA00C40108 /* LaunchAtLoginController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LaunchAtLoginController.m; sourceTree = "<group>"; };
C9E4B39014B8C440006D7B1F /* Currentsong.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Currentsong.app; sourceTree = BUILT_PRODUCTS_DIR; };
C9E4B39414B8C440006D7B1F /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
C9E4B39714B8C440006D7B1F /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -110,6 +113,8 @@
C9E4B3C114B8FBF9006D7B1F /* NSAttributedStringAdditions.m */,
C91F1B6F14BA3E0B00C40108 /* MSDurationFormatter.h */,
C91F1B7014BA3E0C00C40108 /* MSDurationFormatter.m */,
C91F1B7314BAA7DA00C40108 /* LaunchAtLoginController.h */,
C91F1B7414BAA7DA00C40108 /* LaunchAtLoginController.m */,
C9E4B3A914B8C440006D7B1F /* MainMenu.xib */,
C9E4B39B14B8C440006D7B1F /* Supporting Files */,
);
Expand Down Expand Up @@ -197,6 +202,7 @@
C9E4B3C214B8FBF9006D7B1F /* NSAttributedStringAdditions.m in Sources */,
C9E4B3C814B90138006D7B1F /* CurrentsongPreferenceKeys.m in Sources */,
C91F1B7114BA3E0C00C40108 /* MSDurationFormatter.m in Sources */,
C91F1B7514BAA7DA00C40108 /* LaunchAtLoginController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
7 changes: 7 additions & 0 deletions Currentsong/CurrentsongAppDelegate.h
Expand Up @@ -9,6 +9,7 @@
#import <Cocoa/Cocoa.h>

@class CurrentsongStatusView;
@class LaunchAtLoginController;

@interface CurrentsongAppDelegate : NSObject <NSApplicationDelegate,NSMenuDelegate>
{
Expand All @@ -23,6 +24,8 @@
IBOutlet NSMenuItem *mAlbumMenuItem;
IBOutlet NSMenuItem *mStreamTitleMenuItem;
IBOutlet NSMenuItem *mTimeMenuItem;

IBOutlet LaunchAtLoginController *mLaunchAtLoginController;
}

- (IBAction)toggleShowArtist:(id)sender;
Expand All @@ -40,4 +43,8 @@
- (IBAction)setMediumViewWidth:(id)sender;
- (IBAction)setSmallViewWidth:(id)sender;

- (IBAction)toggleLaunchAtLogin:(id)sender;

- (IBAction)launchITunes:(id)sender;

@end
37 changes: 36 additions & 1 deletion Currentsong/CurrentsongAppDelegate.m
Expand Up @@ -8,6 +8,7 @@

#import "iTunes.h"
#import "MSDurationFormatter.h"
#import "LaunchAtLoginController.h"
#import "CurrentsongStatusView.h"
#import "CurrentsongPreferenceKeys.h"
#import "CurrentsongAppDelegate.h"
Expand All @@ -28,6 +29,21 @@ @implementation CurrentsongAppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// If this is the first run, prompt the user to select launch at login
if (![[NSUserDefaults standardUserDefaults] boolForKey:kCSPrefShownLaunchAtLoginPrompt])
{
NSAlert *alert = [NSAlert alertWithMessageText:NSLocalizedString(@"Start Currentsong when you log in?", @"Launch at login prompt title")
defaultButton:NSLocalizedString(@"Yes", @"Yes")
alternateButton:NSLocalizedString(@"No", @"No")
otherButton:nil
informativeTextWithFormat:NSLocalizedString(
@"Would you like Currentsong to start automatically when you log in? "
@"This setting can also be changed from the Options sub-menu.", @"Launch at login prompt text")];
NSInteger result = [alert runModal];

[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kCSPrefShownLaunchAtLoginPrompt];
[mLaunchAtLoginController setLaunchAtLogin:(result == NSAlertDefaultReturn)];
}
}

- (void)awakeFromNib
Expand Down Expand Up @@ -181,6 +197,13 @@ - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
[menuItem setState:(mStatusView.showArtist && !mStatusView.showAlbum && (mStatusView.viewStyle == kCSStyleTwoLevel))];
} else if ([menuItem action] == @selector(setTitleArtistAlbumStacked:)) {
[menuItem setState:(mStatusView.showArtist && mStatusView.showAlbum && (mStatusView.viewStyle == kCSStyleTwoLevel))];
} else if ([menuItem action] == @selector(toggleLaunchAtLogin:)) {
[menuItem setState:[mLaunchAtLoginController launchAtLogin]];
} else if ([menuItem action] == @selector(launchITunes:)) {
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:@"com.apple.iTunes"];
//[menuItem setHidden:[iTunes isRunning]];
[menuItem setTitle:([iTunes isRunning]) ? NSLocalizedString(@"iTunes", @"iTunes")
: NSLocalizedString(@"Launch iTunes", @"Launch iTunes")];
}
return YES;
}
Expand Down Expand Up @@ -333,7 +356,6 @@ - (IBAction)setTitleArtistAlbumStacked:(id)sender;
[self writeDisplayPreference];
}


- (IBAction)toggleScrollLongText:(id)sender
{
mStatusView.shouldScroll = !mStatusView.shouldScroll;
Expand Down Expand Up @@ -361,4 +383,17 @@ - (IBAction)setSmallViewWidth:(id)sender
[self setMaxWidth:kCSViewWidthSmall];
}

- (IBAction)toggleLaunchAtLogin:(id)sender
{
[mLaunchAtLoginController setLaunchAtLogin:![mLaunchAtLoginController launchAtLogin]];
}

- (IBAction)launchITunes:(id)sender
{
[[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:@"com.apple.iTunes"
options:0
additionalEventParamDescriptor:NULL
launchIdentifier:NULL];
}

@end
3 changes: 2 additions & 1 deletion Currentsong/CurrentsongPreferenceKeys.h
Expand Up @@ -10,4 +10,5 @@ extern NSString * const kCSPrefViewStyle;
extern NSString * const kCSPrefMaxWidth;
extern NSString * const kCSPrefShowArtist;
extern NSString * const kCSPrefShowAlbum;
extern NSString * const kCSPrefScrollLongText;
extern NSString * const kCSPrefScrollLongText;
extern NSString * const kCSPrefShownLaunchAtLoginPrompt;
3 changes: 2 additions & 1 deletion Currentsong/CurrentsongPreferenceKeys.m
Expand Up @@ -10,4 +10,5 @@
NSString * const kCSPrefMaxWidth = @"maxWidth";
NSString * const kCSPrefShowArtist = @"showArtist";
NSString * const kCSPrefShowAlbum = @"showAlbum";
NSString * const kCSPrefScrollLongText = @"scroll";
NSString * const kCSPrefScrollLongText = @"scroll";
NSString * const kCSPrefShownLaunchAtLoginPrompt = @"shownLaunchAtLoginPrompt";
31 changes: 14 additions & 17 deletions Currentsong/CurrentsongStatusView.m
Expand Up @@ -121,6 +121,7 @@ - (void)generateEdgeMask
CGContextRelease(maskContext);
}

// Set the clipping mask to clip the left and right edges of text
- (void)setEdgeMask
{
NSSize viewSize = [self frame].size;
Expand Down Expand Up @@ -167,37 +168,33 @@ - (void)drawTextRow:(NSAttributedString *)text leftEdge:(CGFloat)leftEdge yPosit
}
}

- (void)drawTextInRect:(NSRect)rect
{
BOOL twoRows = ([mTopRow length] > 0 && [mBottomRow length] > 0);

CGFloat leftEdge = kCSViewSideMargin;
if (mShowPauseIcon) {
leftEdge += kCSViewPauseIconOffset;
[self drawPauseIcon];
}

[self setEdgeMask];
[self drawTextRow:mTopRow leftEdge:leftEdge yPosition:((twoRows) ? 11 : kCSDrawTextCentered) scrollOffset:mTopRowScrollOffset];
[self drawTextRow:mBottomRow leftEdge:leftEdge yPosition:((twoRows) ? 1 : kCSDrawTextCentered) scrollOffset:mBottomRowScrollOffset];
}

- (void)drawRect:(NSRect)rect
{
[mStatusItem drawStatusBarBackgroundInRect:[self bounds] withHighlight:mHighlighted];

[NSGraphicsContext saveGraphicsState];
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];

// disabling font smoothing draws text makes it look just like other menu items and the clock
// disable font smoothing; makes text look like it does in other menu items and the clock
CGContextSetShouldSmoothFonts(context, NO);

// add subtle white shadow to match other menu bar text
if (!mHighlighted) {
[[CurrentsongStatusView menuBarShadow] set];
}

[self drawTextInRect:rect];
// draw pause icon
CGFloat leftEdge = kCSViewSideMargin;
if (mShowPauseIcon) {
leftEdge += kCSViewPauseIconOffset;
[self drawPauseIcon];
}

// set clipping mask and draw text
BOOL twoRows = ([mTopRow length] > 0 && [mBottomRow length] > 0);
[self setEdgeMask];
[self drawTextRow:mTopRow leftEdge:leftEdge yPosition:((twoRows) ? 11 : kCSDrawTextCentered) scrollOffset:mTopRowScrollOffset];
[self drawTextRow:mBottomRow leftEdge:leftEdge yPosition:((twoRows) ? 1 : kCSDrawTextCentered) scrollOffset:mBottomRowScrollOffset];

[NSGraphicsContext restoreGraphicsState];
}
Expand Down
32 changes: 32 additions & 0 deletions Currentsong/LaunchAtLoginController.h
@@ -0,0 +1,32 @@
//
// LaunchAtLoginController.h
//
// Copyright 2011 Tomáš Znamenáček
// Copyright 2010 Ben Clark-Robinson
//
// 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.

@interface LaunchAtLoginController : NSObject {}

@property(assign) BOOL launchAtLogin;

- (BOOL) willLaunchAtLogin: (NSURL*) itemURL;
- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL;

@end
122 changes: 122 additions & 0 deletions Currentsong/LaunchAtLoginController.m
@@ -0,0 +1,122 @@
//
// LaunchAtLoginController.m
//
// Copyright 2011 Tomáš Znamenáček
// Copyright 2010 Ben Clark-Robinson
//
// 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.

#import "LaunchAtLoginController.h"

static NSString *const StartAtLoginKey = @"launchAtLogin";

@interface LaunchAtLoginController ()
@property(assign) LSSharedFileListRef loginItems;
@end

@implementation LaunchAtLoginController
@synthesize loginItems;

#pragma mark Change Observing

static void sharedFileListDidChange(LSSharedFileListRef inList, void *context)
{
LaunchAtLoginController *self = (id) context;
[self willChangeValueForKey:StartAtLoginKey];
[self didChangeValueForKey:StartAtLoginKey];
}

#pragma mark Initialization

- (id) init
{
[super init];
loginItems = LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL);
LSSharedFileListAddObserver(loginItems, CFRunLoopGetMain(),
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, self);
return self;
}

- (void) dealloc
{
LSSharedFileListRemoveObserver(loginItems, CFRunLoopGetMain(),
(CFStringRef)NSDefaultRunLoopMode, sharedFileListDidChange, self);
CFRelease(loginItems);
[super dealloc];
}

#pragma mark Launch List Control

- (LSSharedFileListItemRef) findItemWithURL: (NSURL*) wantedURL inFileList: (LSSharedFileListRef) fileList
{
if (wantedURL == NULL || fileList == NULL)
return NULL;

NSArray *listSnapshot = [NSMakeCollectable(LSSharedFileListCopySnapshot(fileList, NULL)) autorelease];
for (id itemObject in listSnapshot) {
LSSharedFileListItemRef item = (LSSharedFileListItemRef) itemObject;
UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
CFURLRef currentItemURL = NULL;
LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, NULL);
if (currentItemURL && CFEqual(currentItemURL, wantedURL)) {
CFRelease(currentItemURL);
return item;
}
if (currentItemURL)
CFRelease(currentItemURL);
}

return NULL;
}

- (BOOL) willLaunchAtLogin: (NSURL*) itemURL
{
return !![self findItemWithURL:itemURL inFileList:loginItems];
}

- (void) setLaunchAtLogin: (BOOL) enabled forURL: (NSURL*) itemURL
{
LSSharedFileListItemRef appItem = [self findItemWithURL:itemURL inFileList:loginItems];
if (enabled && !appItem) {
LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst,
NULL, NULL, (CFURLRef)itemURL, NULL, NULL);
} else if (!enabled && appItem)
LSSharedFileListItemRemove(loginItems, appItem);
}

#pragma mark Basic Interface

- (NSURL*) appURL
{
return [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
}

- (void) setLaunchAtLogin: (BOOL) enabled
{
[self willChangeValueForKey:StartAtLoginKey];
[self setLaunchAtLogin:enabled forURL:[self appURL]];
[self didChangeValueForKey:StartAtLoginKey];
}

- (BOOL) launchAtLogin
{
return [self willLaunchAtLogin:[self appURL]];
}

@end

0 comments on commit 8003356

Please sign in to comment.