adium/adium

6194bf1c1f66
Update version number in Info.plist. Add a ticket number to Changes.txt.
/*
* Adium is the legal property of its developers, whose names are listed in the copyright file included
* with this source distribution.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This program 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 General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <Adium/AIAccountControllerProtocol.h>
#import <Adium/AIChatControllerProtocol.h>
#import <Adium/AIInterfaceControllerProtocol.h>
#import <Adium/AIStatusControllerProtocol.h>
#import <Adium/AIContactControllerProtocol.h>
#import <Adium/AIListObject.h>
#import "CBStatusMenuItemPlugin.h"
#import "CBStatusMenuItemController.h"
#import "AIMenuBarIcons.h"
#import <AIUtilities/AIMenuAdditions.h>
#import <AIUtilities/AIEventAdditions.h>
#import <AIUtilities/AIArrayAdditions.h>
#import <AIUtilities/AIImageAdditions.h>
#import <Adium/AIAccount.h>
#import <Adium/AIChat.h>
#import <Adium/AIListContact.h>
#import <Adium/AIListBookmark.h>
#import <Adium/AIStatusIcons.h>
#import <Adium/AIContactHidingController.h>
#import <AIUtilities/AIColorAdditions.h>
#import <AIUtilities/AIStringAdditions.h>
// For the KEY_SHOW_OFFLINE_CONTACTS and PREF_GROUP_CONTACT_LIST_DISPLAY
#import "AIContactController.h"
#import "AIInterfaceController.h"
#define STATUS_ITEM_MARGIN 8
@interface CBStatusMenuItemController ()
- (void)activateAdium;
- (NSImage *)badgeDuck:(NSImage *)duckImage withImage:(NSImage *)inImage;
- (void)updateMenuIcons;
- (void)updateMenuIconsBundle;
- (void)updateUnreadCount;
- (void)updateOpenChats;
- (void)updateStatusItemLength;
- (void)switchToChat:(id)sender;
- (void)activateAccountList:(id)sender;
- (void)disableStatusItem:(id)sender;
@property (nonatomic, retain) NSMenuItem *contactsMenuItem;
@end
@implementation CBStatusMenuItemController
@synthesize contactsMenuItem;
+ (CBStatusMenuItemController *)statusMenuItemController
{
return [[[self alloc] init] autorelease];
}
- (id)init
{
if ((self = [super init])) {
//Create and set up the status item
statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:25] retain];
statusItemView = [[AIStatusItemView alloc] initWithFrame:NSMakeRect(0,0,25,22)];
statusItemView.statusItem = statusItem;
[statusItem setView:statusItemView];
unviewedContent = NO;
[self updateMenuIconsBundle];
// Create our menus
mainMenu = [[NSMenu alloc] init];
[mainMenu setDelegate:self];
mainAccountsMenu = [[NSMenu alloc] init];
[mainAccountsMenu setDelegate:self];
mainOptionsMenu = [[NSMenu alloc] init];
[mainOptionsMenu setDelegate:self];
// Set the main menu as the status item's menu
statusItemView.menu = mainMenu;
// Flag all the menus as needing updates
mainMenuNeedsUpdate = YES;
contactsMenuNeedsUpdate = YES;
accountsMenuNeedsUpdate = YES;
optionsMenuNeedsUpdate = YES;
self.contactsMenuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Contacts",nil)
target:self
action:nil
keyEquivalent:@""] autorelease];
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
//Register to recieve chat opened and chat closed notifications
[notificationCenter addObserver:self
selector:@selector(updateOpenChats)
name:Chat_DidOpen
object:nil];
[notificationCenter addObserver:self
selector:@selector(updateOpenChats)
name:Chat_WillClose
object:nil];
[notificationCenter addObserver:self
selector:@selector(updateOpenChats)
name:Chat_OrderDidChange
object:nil];
[notificationCenter addObserver:self
selector:@selector(updateMenuIcons)
name:AIStatusIconSetDidChangeNotification
object:nil];
// Register for our menu bar icon set changing
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(updateMenuIconsBundle)
name:AIMenuBarIconsDidChangeNotification
object:nil];
// Register as a chat observer so we can know the status of unread messages
[adium.chatController registerChatObserver:self];
// Register as a list object observer so we can know when accounts need to show reconnecting
[[AIContactObserverManager sharedManager] registerListObjectObserver:self];
// Register as an observer of the preference group so we can update our "show groups contacts" option
[adium.preferenceController registerPreferenceObserver:self
forGroup:PREF_GROUP_CONTACT_LIST_DISPLAY];
// Register as an observer of the status preferences for unread conversation count
[adium.preferenceController registerPreferenceObserver:self
forGroup:PREF_GROUP_STATUS_PREFERENCES];
// Register as an observer of our own preference group
[adium.preferenceController registerPreferenceObserver:self
forGroup:PREF_GROUP_STATUS_MENU_ITEM];
//Register to recieve active state changed notifications
[notificationCenter addObserver:self
selector:@selector(updateMenuIcons)
name:AIStatusActiveStateChangedNotification
object:nil];
//Register ourself for the status menu items
statusMenu = [[AIStatusMenu statusMenuWithDelegate:self] retain];
//Account menu
accountMenu = [[AIAccountMenu accountMenuWithDelegate:self
submenuType:AIAccountStatusSubmenu
showTitleVerbs:YES] retain];
//Contact menu
contactMenu = [[AIContactMenu contactMenuWithDelegate:self
forContactsInObject:nil] retain];
}
return self;
}
- (void)dealloc
{
// Invalidate and release our timers
[self invalidateTimers];
//Unregister ourself
[[AIContactObserverManager sharedManager] unregisterListObjectObserver:self];
[adium.chatController unregisterChatObserver:self];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[adium.preferenceController unregisterPreferenceObserver:self];
//Release our objects
[[statusItem statusBar] removeStatusItem:statusItem];
[statusItemView release];
// All the temporary NSMutableArrays we store
[accountMenuItemsArray release];
[stateMenuItemsArray release];
[openChatsArray release];
// The menus
[mainMenu release];
[mainAccountsMenu release];
[mainOptionsMenu release];
// Release our various menus.
[accountMenu setDelegate:nil]; [accountMenu release];
[contactMenu setDelegate:nil]; [contactMenu release];
[statusMenu setDelegate:nil]; [statusMenu release];
// Release our AIMenuBarIcons bundle
[menuIcons release];
// Can't release this because it causes a crash on quit. rdar://4139755, rdar://4160625, and #743. --boredzo
// [statusItem release];
//To the superclass, Robin!
[super dealloc];
}
#pragma mark Icon State
#define PREF_GROUP_APPEARANCE @"Appearance"
#define KEY_MENU_BAR_ICONS @"Menu Bar Icons"
#define EXTENSION_MENU_BAR_ICONS @"AdiumMenuBarIcons"
#define RESOURCE_MENU_BAR_ICONS @"Menu Bar Icons"
/*!
* @brief Update the Xtra bundle
*
* Updates the stored information we have on an \c AdiumMenuBarIcons bundle.
*/
- (void)updateMenuIconsBundle
{
NSString *menuIconPath = nil, *menuIconName;
menuIconName = [adium.preferenceController preferenceForKey:KEY_MENU_BAR_ICONS
group:PREF_GROUP_APPEARANCE
object:nil];
// Get the path of the pack if found.
if (menuIconName) {
menuIconPath = [adium pathOfPackWithName:menuIconName
extension:EXTENSION_MENU_BAR_ICONS
resourceFolderName:RESOURCE_MENU_BAR_ICONS];
}
// If the pack is not found, get the default one.
if (!menuIconPath || !menuIconName) {
menuIconName = [adium.preferenceController defaultPreferenceForKey:KEY_MENU_BAR_ICONS
group:PREF_GROUP_APPEARANCE
object:nil];
menuIconPath = [adium pathOfPackWithName:menuIconName
extension:EXTENSION_MENU_BAR_ICONS
resourceFolderName:RESOURCE_MENU_BAR_ICONS];
}
[menuIcons release];
menuIcons = [[AIMenuBarIcons alloc] initWithURL:[NSURL fileURLWithPath:menuIconPath]];
[self updateMenuIcons];
}
/*!
* @brief Update the unread count
*
* Updates the string text found next to the status item's icon.
*/
- (void)updateUnreadCount
{
NSUInteger unreadCount = (showConversationCount ?
[adium.chatController unviewedConversationCount] : [adium.chatController unviewedContentCount]);
// Only show if enabled and greater-than zero; otherwise, set to nil.
if (showUnreadCount && unreadCount > 0) {
[statusItemView setStringValue:[NSString stringWithFormat:@"%lu", unreadCount]];
} else {
[statusItemView setStringValue:nil];
}
}
/*!
* @brief Update the unviewed content flash
* @arg timer The NSTimer calling this method
*
* Toggles state between having unread content and not every time the timer ends.
*/
- (void)updateUnviewedContentFlash:(NSTimer *)timer
{
// Invert our current setting
currentlyIgnoringUnviewed = !currentlyIgnoringUnviewed;
// Update our current menu icon
[self updateMenuIcons];
}
/*!
* @brief Invalidate running timers
*
* Since an NSTimer instance retains its targets, this method is used to prevent
* \c autoreleased objects from being stuck around indefinitely.
*/
- (void)invalidateTimers
{
currentlyIgnoringUnviewed = NO;
[unviewedContentFlash invalidate];
[unviewedContentFlash release]; unviewedContentFlash = nil;
}
#define IMAGE_TYPE_CONTENT @"Content"
#define IMAGE_TYPE_AWAY @"Away"
#define IMAGE_TYPE_IDLE @"Idle"
#define IMAGE_TYPE_INVISIBLE @"Invisible"
#define IMAGE_TYPE_OFFLINE @"Offline"
#define IMAGE_TYPE_ONLINE @"Online"
/*!
* @brief Update the menu icons
*
* Updates the menu icon with the appropriate icon and badge icon.
*/
- (void)updateMenuIcons
{
NSImage *badge = nil;
NSString *imageName;
// If there's content, set our badge to the "content" icon.
if (unviewedContent && !currentlyIgnoringUnviewed) {
if (showBadge) {
badge = [AIStatusIcons statusIconForStatusName:@"content"
statusType:AIAvailableStatusType
iconType:AIStatusIconList
direction:AIIconNormal];
}
imageName = IMAGE_TYPE_CONTENT;
} else {
// Get the correct icon for our current state.
switch([adium.statusController.activeStatusState statusType]) {
case AIAwayStatusType:
if (showBadge) {
badge = [adium.statusController.activeStatusState icon];
}
imageName = IMAGE_TYPE_AWAY;
break;
case AIInvisibleStatusType:
if (showBadge) {
badge = [adium.statusController.activeStatusState icon];
}
imageName = IMAGE_TYPE_INVISIBLE;
break;
case AIOfflineStatusType:
imageName = IMAGE_TYPE_OFFLINE;
break;
default:
// Assuming we're using an online image unless proven otherwise
imageName = IMAGE_TYPE_ONLINE;
// Check idle here, since it has less precedence than offline, invisible, or away.
for (AIAccount *account in adium.accountController.accounts) {
if (account.online && [account valueForProperty:@"idleSince"]) {
if (showBadge) {
badge = [AIStatusIcons statusIconForStatusName:@"Idle"
statusType:AIAvailableStatusType
iconType:AIStatusIconList
direction:AIIconNormal];
}
imageName = IMAGE_TYPE_IDLE;
break;
}
}
break;
}
}
NSImage *menuIcon = [menuIcons imageOfType:imageName alternate:NO];
NSImage *alternateMenuIcon = [menuIcons imageOfType:imageName alternate:YES];
// Set our icon.
statusItemView.regularImage = [self badgeDuck:menuIcon withImage:badge];
// Badge the highlight image and set it.
statusItemView.alternateImage = [self badgeDuck:alternateMenuIcon withImage:badge];
// Update our unread count.
if (showUnreadCount) {
[self updateUnreadCount];
}
// Update the status item length
[self updateStatusItemLength];
}
/*!
* @brief Update the status item's width
*/
- (void)updateStatusItemLength
{
[statusItem setLength:statusItemView.desiredWidth + STATUS_ITEM_MARGIN];
[statusItemView setFrame:NSMakeRect(0, 0, statusItemView.desiredWidth + STATUS_ITEM_MARGIN, 22)];
[statusItemView setNeedsDisplay:YES];
}
/*!
* @brief Badge the given image with the given badge
* @arg duckImage The base image
* @arg badgeImage The badge which will be draw on the base image
*
* Draws the \c badgeImage in the bottom right quadrant of the \c duckImage.
*/
- (NSImage *)badgeDuck:(NSImage *)duckImage withImage:(NSImage *)badgeImage
{
NSImage *image = duckImage;
if (badgeImage) {
image = [[duckImage copy] autorelease];
[image lockFocus];
NSRect srcRect = { NSZeroPoint, [badgeImage size] };
//Draw in the lower-right quadrant.
NSRect destRect = {
{ .x = srcRect.size.width, .y = 0.0f },
[duckImage size]
};
destRect.size.width *= 0.5f;
destRect.size.height *= 0.5f;
//If the badge is bigger than that portion, resize proportionally. Otherwise, leave it alone and adjust the destination origin appropriately.
if ((srcRect.size.width > destRect.size.width) || (srcRect.size.height > destRect.size.height)) {
//Resize the dest rect.
CGFloat scale;
if (srcRect.size.width > srcRect.size.height) {
scale = destRect.size.width / srcRect.size.width;
} else {
scale = destRect.size.height / srcRect.size.height;
}
destRect.size.width = srcRect.size.width * scale;
destRect.size.height = srcRect.size.height * scale;
//Make sure we scale in a pretty manner.
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
}
//Move the drawing origin.
destRect.origin.x = [duckImage size].width - destRect.size.width;
[badgeImage drawInRect:destRect
fromRect:srcRect
operation:NSCompositeSourceOver
fraction:1.0f];
[image unlockFocus];
}
return image;
}
#pragma mark Account Menu
/*!
* @brief AIAccountMenu delegate method
*/
- (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems {
// Going from or to 1 account requires a main menu update
if ([accountMenuItemsArray count] == 1 || [menuItems count] == 1)
mainMenuNeedsUpdate = YES;
[accountMenuItemsArray release];
accountMenuItemsArray = [menuItems retain];
//We need to update next time we're clicked
accountsMenuNeedsUpdate = YES;
}
/*!
* @brief AIAccountMenu delegate method
*/
- (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount {
[inAccount toggleOnline];
}
#pragma mark Status Menu
/*!
* @brief AIStatusMenu delegate method
*/
- (void)statusMenu:(AIStatusMenu *)inStatusMenu didRebuildStatusMenuItems:(NSArray *)menuItemArray
{
[stateMenuItemsArray release];
stateMenuItemsArray = [menuItemArray retain];
//We need to update next time we're clicked
mainMenuNeedsUpdate = YES;
}
#pragma mark Contact Menu
/*!
* @brief AIContactMenu delegate method
*/
- (void)contactMenuDidRebuild:(AIContactMenu *)inContactMenu
{
NSMenu *menu = inContactMenu.menu;
NSInteger newNumberOfMenuItems = menu.numberOfItems;
// Going from or to 0 contacts requires a main menu update
if (currentContactMenuItemsCount == 0 || newNumberOfMenuItems == 0)
mainMenuNeedsUpdate = YES;
currentContactMenuItemsCount = menu.numberOfItems;
/* The alternate menu is what shows if you option-click the menu item */
statusItemView.alternateMenu = menu;
[self.contactsMenuItem setSubmenu:menu];
}
/*!
* @brief AIContactMenu delegate method
*/
- (void)contactMenu:(AIContactMenu *)inContactMenu didSelectContact:(AIListContact *)inContact
{
[adium.interfaceController setActiveChat:[adium.chatController openChatWithContact:inContact
onPreferredAccount:YES]];
[self activateAdium];
}
/*!
* @brief AIContactMenu delegate method
*
* Shows the given contact if it is visible in the contact list.
*/
- (BOOL)contactMenu:(AIContactMenu *)inContactMenu shouldIncludeContact:(AIListContact *)inContact
{
// Show this contact if we're showing offline contacts or if this contact is online.
for (id<AIContainingObject> container in inContact.containingObjects)
{
if ([[AIContactHidingController sharedController] visibilityOfListObject:inContact inContainer:container])
return YES;
}
return NO;
}
/*!
* @brief AIContactMenu delegate method
*/
- (BOOL)contactMenuShouldDisplayGroupHeaders:(AIContactMenu *)inContactMenu
{
return showContactGroups;
}
/*!
* @brief AIContactMenu delegate method
*/
- (BOOL)contactMenuShouldUseDisplayName:(AIContactMenu *)inContactMenu
{
return YES;
}
/*!
* @brief AIContactMenu delegate method
*/
- (BOOL)contactMenuShouldUseUserIcon:(AIContactMenu *)inContactMenu
{
return YES;
}
/*!
* @brief AIContactMenu delegate method
*/
- (BOOL)contactMenuShouldSetTooltip:(AIContactMenu *)inContactMenu
{
return YES;
}
- (BOOL) contactMenuShouldIncludeContactListMenuItem:(AIContactMenu *)inContactMenu
{
return YES;
}
- (BOOL)contactMenuShouldPopulateMenuLazily:(AIContactMenu *)inContactMenu
{
return YES;
}
#pragma mark List Object Observer
/*!
* @brief List Observer delegate method
*
* Updates the menu icon if our accounts change connecting state.
*/
- (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
{
if ([inObject isKindOfClass:[AIAccount class]]) {
if ([inModifiedKeys containsObject:@"isConnecting"] ||
[inModifiedKeys containsObject:@"waitingToReconnect"]) {
[self updateMenuIcons];
}
}
return nil;
}
#pragma mark Chat Observer
/*!
* @brief Chat observer delegate method
*
* Updates our opened chats when called.
*/
- (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
{
[self updateOpenChats];
// We didn't modify anything; return nil.
return nil;
}
/*!
* @brief Updates open chats menu
*
* Update our content image if necessary, creating an NSTimer instance to flash the badge if the
* user has the preference enabled to do so.
*/
- (void)updateOpenChats
{
[self retain];
NSUInteger unviewedContentCount = [adium.chatController unviewedContentCount];
// Update our open chats
[openChatsArray release];
openChatsArray = [[adium.interfaceController openChats] retain];
// We think there's unviewed content, but there's not.
if (unviewedContent && unviewedContentCount == 0) {
// Invalidate and release the unviewed content flash timer
[unviewedContentFlash invalidate];
[unviewedContentFlash release]; unviewedContentFlash = nil;
currentlyIgnoringUnviewed = NO;
// Update unviewed content
unviewedContent = NO;
// Update our menu icons
[self updateMenuIcons];
// We think there's no unviewed content, and there is.
} else if (!unviewedContent && unviewedContentCount > 0) {
// If this particular Xtra wants us to flash unviewed content, start the timer up
if (flashUnviewed) {
currentlyIgnoringUnviewed = NO;
unviewedContentFlash = [[NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(updateUnviewedContentFlash:)
userInfo:nil
repeats:YES] retain];
}
// Update unviewed content
unviewedContent = YES;
// Update our menu icons
[self updateMenuIcons];
// If we already know there's unviewed content, just update the count.
} else if (unviewedContent && unviewedContentCount > 0) {
[self updateUnreadCount];
}
mainMenuNeedsUpdate = YES;
[self release];
}
#pragma mark Menu Delegates/Actions
/*!
* @brief NSMenu delegate method
*
* This method updates all of the given menus which we control if we've deteremined an update to be necessary.
*/
- (void)menuNeedsUpdate:(NSMenu *)menu
{
// Main menu if it needs an update
if (menu == mainMenu && mainMenuNeedsUpdate) {
NSMenuItem *menuItem;
//Clear out all the items, start from scratch
[menu removeAllItems];
// Show the contacts menu if we have any contacts to display
if ([contactMenu.menu numberOfItems] > 0) {
// Add contacts
[menu addItem:self.contactsMenuItem];
} else {
[menu addItemWithTitle:[AILocalizedString(@"Contact List", nil) stringByAppendingEllipsis]
target:adium.interfaceController
action:@selector(toggleContactList:)
keyEquivalent:@""];
}
// If there's more than one account, show the accounts menu
if ([accountMenuItemsArray count] > 1) {
menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Accounts",nil)
target:self
action:nil
keyEquivalent:@""];
[menuItem setSubmenu:mainAccountsMenu];
[menu addItem:menuItem];
[menuItem release];
}
menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Options",nil)
target:self
action:nil
keyEquivalent:@""];
[menuItem setSubmenu:mainOptionsMenu];
[menu addItem:menuItem];
[menuItem release];
[menu addItem:[NSMenuItem separatorItem]];
//Add the state menu items
menuItem = nil;
for (menuItem in stateMenuItemsArray) {
[menu addItem:menuItem];
//Validate the menu items as they are added since they weren't previously validated when the menu was clicked
if ([[menuItem target] respondsToSelector:@selector(validateMenuItem:)]) {
[[menuItem target] validateMenuItem:menuItem];
}
}
//If there exist any open chats, add them
if ([openChatsArray count] > 0) {
//Add a seperator
[menu addItem:[NSMenuItem separatorItem]];
//Create and add the menu items
for (AIChat *chat in openChatsArray) {
NSImage *image = nil;
//Create a menu item from the chat
menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:chat.displayName
target:self
action:@selector(switchToChat:)
keyEquivalent:@""];
//Set the represented object
[menuItem setRepresentedObject:chat];
//Set the image
//If there is a chat status image, use that
image = [AIStatusIcons statusIconForChat:chat type:AIStatusIconMenu direction:AIIconNormal];
//Otherwise use the chat's -chatMenuImage
if (!image) {
image = [chat chatMenuImage];
}
[menuItem setImage:image];
//Add it to the menu
[menu addItem:menuItem];
[menuItem release];
}
}
//Only update next time if we need to
mainMenuNeedsUpdate = NO;
// Accounts menu
} else if (menu == mainAccountsMenu && accountsMenuNeedsUpdate) {
NSMenuItem *menuItem;
[menu removeAllItems];
[menu addItemWithTitle:[AILocalizedString(@"Account List", nil) stringByAppendingEllipsis]
target:self
action:@selector(activateAccountList:)
keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
//Add the account menu items
for (menuItem in accountMenuItemsArray) {
NSMenu *submenu;
[menu addItem:menuItem];
//Validate the menu items as they are added since they weren't previously validated when the menu was clicked
if ([[menuItem target] respondsToSelector:@selector(validateMenuItem:)]) {
[[menuItem target] validateMenuItem:menuItem];
}
if ((submenu = [menuItem submenu])) {
for (NSMenuItem *submenuItem in submenu.itemArray) {
//Validate the submenu items as they are added since they weren't previously validated when the menu was clicked
if ([[submenuItem target] respondsToSelector:@selector(validateMenuItem:)]) {
[[submenuItem target] validateMenuItem:submenuItem];
}
}
}
}
accountsMenuNeedsUpdate = NO;
} else if (menu == mainOptionsMenu && optionsMenuNeedsUpdate) {
[menu removeAllItems];
[menu addItemWithTitle:[AILocalizedString(@"Adium Preferences", nil) stringByAppendingEllipsis]
target:self
action:@selector(showPreferenceWindow:)
keyEquivalent:@""];
[menu addItemWithTitle:AILocalizedString(@"Toggle Contact List", nil)
target:adium.interfaceController
action:@selector(toggleContactList:)
keyEquivalent:@""];
[menu addItem:[NSMenuItem separatorItem]];
[menu addItemWithTitle:AILocalizedString(@"Hide Status Item", nil)
target:self
action:@selector(disableStatusItem:)
keyEquivalent:@""];
[menu addItemWithTitle:AILocalizedString(@"Quit Adium", nil)
target:NSApp
action:@selector(terminate:)
keyEquivalent:@""];
optionsMenuNeedsUpdate = NO;
}
}
/*!
* @brief Switch to a chat
* @arg An NSMenuItem instance whose \c representedObject is an AIChat.
*/
- (void)switchToChat:(id)sender
{
[adium.interfaceController setActiveChat:[sender representedObject]];
[self activateAdium];
}
/*!
* @brief Open the account list
*/
- (void)activateAccountList:(id)sender
{
[adium.preferenceController openPreferencesToCategoryWithIdentifier:@"Accounts"];
[self activateAdium];
}
/*!
* @brief Disable the status item
*
* Updates the preference for displaying the status item to be NO.
*/
- (void)disableStatusItem:(id)sender
{
[adium.preferenceController setPreference:[NSNumber numberWithBool:NO]
forKey:KEY_STATUS_MENU_ITEM_ENABLED
group:PREF_GROUP_STATUS_MENU_ITEM];
}
/*!
* @brief Show the preference window
*/
- (void)showPreferenceWindow:(id)sender
{
[adium.preferenceController showPreferenceWindow:nil];
[self activateAdium];
}
/*!
* @brief Activate Adium
*
* Brings Adium to front.
*/
- (void)activateAdium
{
if (![NSApp isActive]) {
[NSApp activateIgnoringOtherApps:YES];
[NSApp arrangeInFront:nil];
}
}
#pragma mark Preferences Observer
/*!
* @brief Preferences observer
*
* Updates our display based on preference changes, such as the display of badges or the unread count being displayed.
*/
- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
{
if ([group isEqualToString:PREF_GROUP_CONTACT_LIST_DISPLAY]) {
showContactGroups = ![[prefDict objectForKey:KEY_HIDE_CONTACT_LIST_GROUPS] boolValue];
[contactMenu rebuildMenu];
}
if ([group isEqualToString:PREF_GROUP_STATUS_MENU_ITEM]) {
showUnreadCount = [[prefDict objectForKey:KEY_STATUS_MENU_ITEM_COUNT] boolValue];
showBadge = [[prefDict objectForKey:KEY_STATUS_MENU_ITEM_BADGE] boolValue];
flashUnviewed = [[prefDict objectForKey:KEY_STATUS_MENU_ITEM_FLASH] boolValue];
[self updateMenuIcons];
[self updateUnreadCount];
[self updateStatusItemLength];
}
if ([group isEqualToString:PREF_GROUP_STATUS_PREFERENCES]) {
showConversationCount = [[prefDict objectForKey:KEY_STATUS_CONVERSATION_COUNT] boolValue];
[self updateUnreadCount];
}
}
@end