* 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 "AIAppearancePreferences.h" #import "AIAppearancePreferencesPlugin.h" #import "AIDockIconSelectionSheet.h" #import "AIEmoticonPack.h" #import "AIEmoticonPreferences.h" #import "AIListLayoutWindowController.h" #import "AIListThemeWindowController.h" #import <AIUtilities/AIMenuAdditions.h> #import <AIUtilities/AIImageAdditions.h> #import <AIUtilities/AIImageDrawingAdditions.h> #import <AIUtilities/AIPopUpButtonAdditions.h> #import <AIUtilities/AIStringAdditions.h> #import <Adium/AIAbstractListController.h> #import <Adium/AIDockControllerProtocol.h> #import <Adium/AIEmoticonControllerProtocol.h> #import <Adium/AIIconState.h> #import <Adium/AIServiceIcons.h> #import <Adium/AIStatusIcons.h> #import <Adium/ESPresetManagementController.h> #import <Adium/ESPresetNameSheetController.h> #import "AIMenuBarIcons.h" @interface AIAppearancePreferences () - ( NSMenu * ) _windowStyleMenu ; - ( NSMenu * ) _emoticonPackMenu ; - ( NSMenu * ) _listLayoutMenu ; - ( NSMenu * ) _colorThemeMenu ; - ( void ) _rebuildEmoticonMenuAndSelectActivePack ; - ( void ) _addWindowStyleOption: ( NSString * ) option withTag: ( NSInteger ) tag toMenu: ( NSMenu * ) menu ; - ( void ) _updateSliderValues ; - ( void ) _editListThemeWithName: ( NSString * ) name ; - ( void ) _editListLayoutWithName: ( NSString * ) name ; - ( void ) xtrasChanged: ( NSNotification * ) notification ; - ( void ) configureDockIconMenu ; - ( void ) configureStatusIconsMenu ; - ( void ) configureServiceIconsMenu ; - ( void ) configureMenuBarIconsMenu ; @implementation AIAppearancePreferences * @brief Preference pane properties - ( NSString * ) paneIdentifier return AILocalizedString ( @"Appearance" , "Appearance preferences label" ); return @"AppearancePrefs" ; return [ NSImage imageNamed : @"pref-appearance" forClass : [ self class ]]; * @brief Configure the preference view [ slider_windowOpacity setMinValue : 0.0 ]; [ slider_windowOpacity setMaxValue : 100.0 ]; [ popUp_windowStyle setMenu : [ self _windowStyleMenu ]]; //Observe preference changes [ adium . preferenceController registerPreferenceObserver : self forGroup : PREF_GROUP_EMOTICONS ]; [ adium . preferenceController registerPreferenceObserver : self forGroup : PREF_GROUP_APPEARANCE ]; [[ NSNotificationCenter defaultCenter ] addObserver : self selector : @selector ( xtrasChanged : ) name : AIXtrasDidChangeNotification [ adium . preferenceController unregisterPreferenceObserver : self ]; [[ NSNotificationCenter defaultCenter ] removeObserver : self ]; * @brief Xtras changed, update our menus to reflect the new Xtras - ( void ) xtrasChanged: ( NSNotification * ) notification NSString * filenameExtension = [ notification object ]; //Convert our filename extension into a Uniform Type Identifier so that we can robustly determine what type of Xtra this is. CFStringRef type = ( CFStringRef )[( NSString * ) UTTypeCreatePreferredIdentifierForTag ( kUTTagClassFilenameExtension , ( CFStringRef ) filenameExtension , /*inConformingToUTI*/ NULL ) autorelease ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.emoticonset" ))) { [ self _rebuildEmoticonMenuAndSelectActivePack ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.dockicon" ))) { [ self configureDockIconMenu ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.serviceicons" ))) { [ self configureServiceIconsMenu ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.statusicons" ))) { [ self configureStatusIconsMenu ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.menubaricons" ))) { [ self configureMenuBarIconsMenu ]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.contactlisttheme" ))) { [ popUp_colorTheme setMenu : [ self _colorThemeMenu ]]; [ popUp_colorTheme selectItemWithRepresentedObject : [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]]; if ( ! type || UTTypeEqual ( type , CFSTR ( "com.adiumx.contactlistlayout" ))) { [ popUp_listLayout setMenu : [ self _listLayoutMenu ]]; [ popUp_listLayout selectItemWithRepresentedObject : [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]]; * @brief Preferences changed * Update controls in our view to reflect the changed preferences - ( void ) preferencesChangedForGroup: ( NSString * ) group key: ( NSString * ) key object: ( AIListObject * ) object preferenceDict :( NSDictionary * ) prefDict firstTime: ( BOOL ) firstTime if ([ group isEqualToString : PREF_GROUP_EMOTICONS ] && ! firstTime ) { [ self _rebuildEmoticonMenuAndSelectActivePack ]; if ([ group isEqualToString : PREF_GROUP_APPEARANCE ]) { [ popUp_windowStyle selectItemWithTag : [[ prefDict objectForKey : KEY_LIST_LAYOUT_WINDOW_STYLE ] integerValue ]]; [ checkBox_verticalAutosizing setState : [[ prefDict objectForKey : KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE ] boolValue ]]; [ checkBox_horizontalAutosizing setState : [[ prefDict objectForKey : KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE ] boolValue ]]; [ slider_windowOpacity setDoubleValue : ([[ prefDict objectForKey : KEY_LIST_LAYOUT_WINDOW_OPACITY ] doubleValue ] * 100.0 )]; [ slider_horizontalWidth setIntegerValue : [[ prefDict objectForKey : KEY_LIST_LAYOUT_HORIZONTAL_WIDTH ] integerValue ]]; [ self _updateSliderValues ]; //Horizontal resizing label [ key isEqualToString : KEY_LIST_LAYOUT_WINDOW_STYLE ] || [ key isEqualToString : KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE ]) { AIContactListWindowStyle windowStyle = [[ prefDict objectForKey : KEY_LIST_LAYOUT_WINDOW_STYLE ] intValue ]; BOOL horizontalAutosize = [[ prefDict objectForKey : KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE ] boolValue ]; if ( windowStyle == AIContactListWindowStyleStandard ) { //In standard mode, disable the horizontal autosizing slider if horiztonal autosizing is off [ textField_horizontalWidthText setLocalizedString : AILocalizedString ( @"Maximum Width:" , nil )]; [ slider_horizontalWidth setEnabled : horizontalAutosize ]; //In all the borderless transparent modes, the horizontal autosizing slider becomes the //horizontal sizing slider when autosizing is off if ( horizontalAutosize ) { [ textField_horizontalWidthText setLocalizedString : AILocalizedString ( @"Maximum Width:" , nil )]; [ textField_horizontalWidthText setLocalizedString : AILocalizedString ( @"Width:" , nil )]; [ slider_horizontalWidth setEnabled : YES ]; //Configure vertical autosizing's appearance. AIListWindowController must match this behavior for this to make sense. case AIContactListWindowStyleStandard : case AIContactListWindowStyleBorderless : case AIContactListWindowStyleGroupChat : //Standard and borderless don't have to vertically autosize [ checkBox_verticalAutosizing setEnabled : YES ]; [ checkBox_verticalAutosizing setState : [[ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE group : PREF_GROUP_APPEARANCE ] integerValue ]]; case AIContactListWindowStyleGroupBubbles : case AIContactListWindowStyleContactBubbles : case AIContactListWindowStyleContactBubbles_Fitted : //The bubbles styles don't show a window; force them to autosize [ checkBox_verticalAutosizing setEnabled : NO ]; [ checkBox_verticalAutosizing setState : YES ]; if ( firstTime || [ key isEqualToString : KEY_STATUS_ICON_PACK ]) { [ popUp_statusIcons selectItemWithTitle : [ prefDict objectForKey : KEY_STATUS_ICON_PACK ]]; //If the prefDict's item isn't present, we're using the default, so select that one if ( ! [ popUp_statusIcons selectedItem ]) { [ popUp_statusIcons selectItemWithTitle : [ adium . preferenceController defaultPreferenceForKey : KEY_STATUS_ICON_PACK group : PREF_GROUP_APPEARANCE if ( firstTime || [ key isEqualToString : KEY_SERVICE_ICON_PACK ]) { [ popUp_serviceIcons selectItemWithTitle : [ prefDict objectForKey : KEY_SERVICE_ICON_PACK ]]; //If the prefDict's item isn't present, we're using the default, so select that one if ( ! [ popUp_serviceIcons selectedItem ]) { [ popUp_serviceIcons selectItemWithTitle : [ adium . preferenceController defaultPreferenceForKey : KEY_SERVICE_ICON_PACK group : PREF_GROUP_APPEARANCE if ( firstTime || [ key isEqualToString : KEY_MENU_BAR_ICONS ]) { [ popUp_menuBarIcons selectItemWithTitle : [ prefDict objectForKey : KEY_MENU_BAR_ICONS ]]; //If the prefDict's item isn't present, we're using the default, so select that one if ( ! [ popUp_menuBarIcons selectedItem ]) { [ popUp_menuBarIcons selectItemWithTitle : [ adium . preferenceController defaultPreferenceForKey : KEY_MENU_BAR_ICONS group : PREF_GROUP_APPEARANCE if ( firstTime || [ key isEqualToString : KEY_LIST_LAYOUT_NAME ]) { [ popUp_listLayout selectItemWithRepresentedObject : [ prefDict objectForKey : KEY_LIST_LAYOUT_NAME ]]; if ( firstTime || [ key isEqualToString : KEY_LIST_THEME_NAME ]) { [ popUp_colorTheme selectItemWithRepresentedObject : [ prefDict objectForKey : KEY_LIST_THEME_NAME ]]; if ( firstTime || [ key isEqualToString : KEY_ACTIVE_DOCK_ICON ]) { /* popUp_dockIcon initially is a single-item popup menu with just the active icon; it is built * lazily in menuNeedsUpdate:. If we haven't displayed it yet, we'll need to configure again * to show just the current icon */ if ( ! [ popUp_dockIcon selectItemWithRepresentedObject : [ prefDict objectForKey : KEY_ACTIVE_DOCK_ICON ]]) [ self configureDockIconMenu ]; * @brief Rebuild the emoticon menu - ( void ) _rebuildEmoticonMenuAndSelectActivePack [ popUp_emoticons setMenu : [ self _emoticonPackMenu ]]; //Update the selected pack NSArray * activeEmoticonPacks = [ adium . emoticonController activeEmoticonPacks ]; NSInteger numActivePacks = [ activeEmoticonPacks count ]; if ( numActivePacks == 0 ) { [ popUp_emoticons selectItemWithTag : AIEmoticonMenuNone ]; } else if ( numActivePacks > 1 ) { [ popUp_emoticons selectItemWithTag : AIEmoticonMenuMultiple ]; [ popUp_emoticons selectItemWithRepresentedObject : [ activeEmoticonPacks objectAtIndex : 0 ]]; * @brief Save changed preferences - ( IBAction ) changePreference: ( id ) sender if ( sender == popUp_statusIcons ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] title ] forKey : KEY_STATUS_ICON_PACK group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_serviceIcons ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] title ] forKey : KEY_SERVICE_ICON_PACK group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_menuBarIcons ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] title ] forKey : KEY_MENU_BAR_ICONS group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_dockIcon ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] representedObject ] forKey : KEY_ACTIVE_DOCK_ICON group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_listLayout ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] title ] forKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_colorTheme ) { [ adium . preferenceController setPreference : [[ sender selectedItem ] title ] forKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]; } else if ( sender == popUp_windowStyle ) { [ adium . preferenceController setPreference : [ NSNumber numberWithInteger : [[ sender selectedItem ] tag ]] forKey : KEY_LIST_LAYOUT_WINDOW_STYLE group : PREF_GROUP_APPEARANCE ]; } else if ( sender == checkBox_verticalAutosizing ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : [ sender state ]] forKey : KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE group : PREF_GROUP_APPEARANCE ]; } else if ( sender == checkBox_horizontalAutosizing ) { [ adium . preferenceController setPreference : [ NSNumber numberWithBool : [ sender state ]] forKey : KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE group : PREF_GROUP_APPEARANCE ]; } else if ( sender == slider_windowOpacity ) { [ adium . preferenceController setPreference : [ NSNumber numberWithDouble : ([ sender doubleValue ] / 100.0 )] forKey : KEY_LIST_LAYOUT_WINDOW_OPACITY group : PREF_GROUP_APPEARANCE ]; [ self _updateSliderValues ]; } else if ( sender == slider_horizontalWidth ) { NSInteger newValue = [ sender integerValue ]; NSInteger oldValue = [[ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_HORIZONTAL_WIDTH group : PREF_GROUP_APPEARANCE ] integerValue ]; if ( newValue != oldValue ) { [ adium . preferenceController setPreference : [ NSNumber numberWithInteger : newValue ] forKey : KEY_LIST_LAYOUT_HORIZONTAL_WIDTH group : PREF_GROUP_APPEARANCE ]; [ self _updateSliderValues ]; } else if ( sender == popUp_emoticons ) { if ([[ sender selectedItem ] tag ] != AIEmoticonMenuMultiple ) { //Disable all active emoticons NSArray * activePacks = [[[ adium . emoticonController activeEmoticonPacks ] mutableCopy ] autorelease ]; AIEmoticonPack * pack , * selectedPack ; selectedPack = [[ sender selectedItem ] representedObject ]; [ adium . preferenceController delayPreferenceChangedNotifications : YES ]; for ( pack in activePacks ) { [ adium . emoticonController setEmoticonPack : pack enabled : NO ]; //Enable the selected pack if ( selectedPack ) [ adium . emoticonController setEmoticonPack : selectedPack enabled : YES ]; [ adium . preferenceController delayPreferenceChangedNotifications : NO ]; - ( void ) _updateSliderValues [ textField_windowOpacity setStringValue : [ NSString stringWithFormat : @"%ld%%" , ( NSInteger )[ slider_windowOpacity doubleValue ]]]; [ textField_horizontalWidthIndicator setStringValue : [ NSString stringWithFormat : @"%ldpx" ,[ slider_horizontalWidth integerValue ]]]; //Emoticons ------------------------------------------------------------------------------------------------------------ - ( IBAction ) customizeEmoticons: ( id ) sender AIEmoticonPreferences * emoticonPreferences = [[ AIEmoticonPreferences alloc ] init ]; [ emoticonPreferences openOnWindow : [[ self view ] window ]]; - ( NSMenu * ) _emoticonPackMenu NSMenu * menu = [[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ]; NSEnumerator * enumerator = [[ adium . emoticonController availableEmoticonPacks ] objectEnumerator ]; //Add the "No Emoticons" option menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : AILocalizedString ( @"None" , nil ) keyEquivalent : @"" ] autorelease ]; [ menuItem setImage : [ NSImage imageNamed : @"emoticonBlank" forClass : [ self class ]]]; [ menuItem setTag : AIEmoticonMenuNone ]; //Add the "Multiple packs selected" option if ([[ adium . emoticonController activeEmoticonPacks ] count ] > 1 ) { menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : AILocalizedString ( @"Multiple Packs Selected" , nil ) keyEquivalent : @"" ] autorelease ]; [ menuItem setImage : [ NSImage imageNamed : @"emoticonBlank" forClass : [ self class ]]]; [ menuItem setTag : AIEmoticonMenuMultiple ]; [ menu addItem : [ NSMenuItem separatorItem ]]; while (( pack = [ enumerator nextObject ])) { menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : [ pack name ] keyEquivalent : @"" ] autorelease ]; [ menuItem setRepresentedObject : pack ]; [ menuItem setImage : [ pack menuPreviewImage ]]; return [ menu autorelease ]; //Contact list options ------------------------------------------------------------------------------------------------- #pragma mark Contact list options - ( NSMenu * ) _windowStyleMenu NSMenu * menu = [[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ]; [ self _addWindowStyleOption : AILocalizedString ( @"Regular Window" , nil ) withTag : AIContactListWindowStyleStandard [ menu addItem : [ NSMenuItem separatorItem ]]; [ self _addWindowStyleOption : AILocalizedString ( @"Borderless Window" , nil ) withTag : AIContactListWindowStyleBorderless [ self _addWindowStyleOption : AILocalizedString ( @"Group Bubbles" , nil ) withTag : AIContactListWindowStyleGroupBubbles [ self _addWindowStyleOption : AILocalizedString ( @"Contact Bubbles" , nil ) withTag : AIContactListWindowStyleContactBubbles [ self _addWindowStyleOption : AILocalizedString ( @"Contact Bubbles (To Fit)" , nil ) withTag : AIContactListWindowStyleContactBubbles_Fitted return [ menu autorelease ]; - ( void ) _addWindowStyleOption: ( NSString * ) option withTag: ( NSInteger ) tag toMenu: ( NSMenu * ) menu { NSMenuItem * menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : option keyEquivalent : @"" ] autorelease ]; //Contact list layout & theme ---------------------------------------------------------------------------------------- #pragma mark Contact list layout & theme * @brief Create a new theme - ( IBAction ) createListTheme: ( id ) sender NSString * theme = [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]; ESPresetNameSheetController * presetNameSheetController = [[ ESPresetNameSheetController alloc ] initWithDefaultName : [[ theme stringByAppendingString : @" " ] stringByAppendingString : AILocalizedString ( @"(Copy)" , nil )] explanatoryText : AILocalizedString ( @"Enter a unique name for this new theme." , nil ) [ presetNameSheetController showOnWindow : [[ self view ] window ]]; * @brief Customize the active theme - ( IBAction ) customizeListTheme: ( id ) sender NSString * theme = [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]; AIListThemeWindowController * listThemeWindowController = [[ AIListThemeWindowController alloc ] initWithName : theme [ listThemeWindowController showOnWindow : [[ self view ] window ]]; * @brief Save (or revert) changes made when editing a theme - ( void ) listThemeEditorWillCloseWithChanges: ( BOOL ) saveChanges forThemeNamed: ( NSString * ) name //Update the modified theme if ([ plugin createSetFromPreferenceGroup : PREF_GROUP_LIST_THEME extension : LIST_THEME_EXTENSION inFolder : LIST_THEME_FOLDER ]) { [ adium . preferenceController setPreference : name forKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]; //Revert back to selected theme NSString * theme = [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]; //Reapply the selected theme [ plugin applySetWithName : theme extension : LIST_THEME_EXTENSION inFolder : LIST_THEME_FOLDER toPreferenceGroup : PREF_GROUP_LIST_THEME ]; //Revert back to the current theme name in popUp_colorTheme component [ popUp_colorTheme selectItemWithTitle : [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]]; * @brief Manage available themes - ( void ) manageListThemes: ( id ) sender _listThemes = [ plugin availableThemeSets ]; ESPresetManagementController * presetManagementController = [[ ESPresetManagementController alloc ] initWithPresets : _listThemes [ presetManagementController showOnWindow : [[ self view ] window ]]; [ popUp_colorTheme selectItemWithRepresentedObject : [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]]; * @brief Create a new layout - ( IBAction ) createListLayout: ( id ) sender NSString * layout = [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]; ESPresetNameSheetController * presetNameSheetController = [[ ESPresetNameSheetController alloc ] initWithDefaultName : [[ layout stringByAppendingString : @" " ] stringByAppendingString : AILocalizedString ( @"(Copy)" , nil )] explanatoryText : AILocalizedString ( @"Enter a unique name for this new layout." , nil ) [ presetNameSheetController showOnWindow : [[ self view ] window ]]; * @brief Customize the active layout - ( IBAction ) customizeListLayout: ( id ) sender NSString * theme = [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]; AIListLayoutWindowController * listLayoutWindowController = [[ AIListLayoutWindowController alloc ] initWithName : theme [ listLayoutWindowController showOnWindow : [[ self view ] window ]]; * @brief Save (or revert) changes made when editing a layout - ( void ) listLayoutEditorWillCloseWithChanges: ( BOOL ) saveChanges forLayoutNamed: ( NSString * ) name //Update the modified layout if ([ plugin createSetFromPreferenceGroup : PREF_GROUP_LIST_LAYOUT extension : LIST_LAYOUT_EXTENSION inFolder : LIST_LAYOUT_FOLDER ]) { [ adium . preferenceController setPreference : name forKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]; //Revert back to selected layout NSString * layout = [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]; //Reapply the selected layout [ plugin applySetWithName : layout extension : LIST_LAYOUT_EXTENSION inFolder : LIST_LAYOUT_FOLDER toPreferenceGroup : PREF_GROUP_LIST_LAYOUT ]; //Revert back to the current layout name in popUp_listLayout component [ popUp_listLayout selectItemWithTitle : [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]]; * @brief Manage available layouts - ( void ) manageListLayouts: ( id ) sender _listLayouts = [ plugin availableLayoutSets ]; ESPresetManagementController * presetManagementController = [[ ESPresetManagementController alloc ] initWithPresets : _listLayouts [ presetManagementController showOnWindow : [[ self view ] window ]]; [ popUp_listLayout selectItemWithRepresentedObject : [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]]; * @brief Validate a layout or theme name to ensure it is unique - ( BOOL ) presetNameSheetController: ( ESPresetNameSheetController * ) controller shouldAcceptNewName :( NSString * ) newName NSEnumerator * enumerator ; NSDictionary * presetDict ; //Scan the correct presets to ensure this name doesn't already exist if ([ userInfo isEqualToString : @"theme" ]) { enumerator = [[ plugin availableThemeSets ] objectEnumerator ]; enumerator = [[ plugin availableLayoutSets ] objectEnumerator ]; while (( presetDict = [ enumerator nextObject ])) { if ([ newName isEqualToString : [ presetDict objectForKey : @"name" ]]) return NO ; * @brief Create a new theme with the user supplied name, activate and edit it - ( void ) presetNameSheetControllerDidEnd: ( ESPresetNameSheetController * ) controller returnCode :( ESPresetNameSheetReturnCode ) returnCode newName :( NSString * ) newName case ESPresetNameSheetOkayReturn : //User has created a new theme/layout : show the editor if ([ userInfo isEqualToString : @"theme" ]) { [ self performSelector : @selector ( _editListThemeWithName : ) withObject : newName afterDelay : 0 ]; [ self performSelector : @selector ( _editListLayoutWithName : ) withObject : newName afterDelay : 0 ]; case ESPresetNameSheetCancelReturn : //User has canceled the operation : revert back to the current theme if ([ userInfo isEqualToString : @"theme" ]) { [ popUp_colorTheme selectItemWithTitle : [ adium . preferenceController preferenceForKey : KEY_LIST_THEME_NAME group : PREF_GROUP_APPEARANCE ]]; [ popUp_listLayout selectItemWithTitle : [ adium . preferenceController preferenceForKey : KEY_LIST_LAYOUT_NAME group : PREF_GROUP_APPEARANCE ]]; - ( void ) _editListThemeWithName: ( NSString * ) name { AIListThemeWindowController * listThemeWindowController = [[ AIListThemeWindowController alloc ] initWithName : name [ listThemeWindowController showOnWindow : [[ self view ] window ]]; - ( void ) _editListLayoutWithName: ( NSString * ) name { AIListLayoutWindowController * listLayoutWindowController = [[ AIListLayoutWindowController alloc ] initWithName : name [ listLayoutWindowController showOnWindow : [[ self view ] window ]]; - ( NSArray * ) renamePreset: ( NSDictionary * ) preset toName: ( NSString * ) newName inPresets: ( NSArray * ) presets renamedPreset: ( id * ) renamedPreset if ( presets == _listLayouts ) { [ plugin renameSetWithName : [ preset objectForKey : @"name" ] extension : LIST_LAYOUT_EXTENSION inFolder : LIST_LAYOUT_FOLDER _listLayouts = [ plugin availableLayoutSets ]; newPresets = _listLayouts ; } else if ( presets == _listThemes ) { [ plugin renameSetWithName : [ preset objectForKey : @"name" ] extension : LIST_THEME_EXTENSION inFolder : LIST_THEME_FOLDER _listThemes = [ plugin availableThemeSets ]; newPresets = _listThemes ; //Return the new duplicate by reference for the preset controller for ( aPreset in newPresets ) { if ([ newName isEqualToString : [ aPreset objectForKey : @"name" ]]) { * renamedPreset = aPreset ; - ( NSArray * ) duplicatePreset: ( NSDictionary * ) preset inPresets: ( NSArray * ) presets createdDuplicate: ( id * ) duplicatePreset NSString * newName = [ NSString stringWithFormat : @"%@ (%@)" , [ preset objectForKey : @"name" ], AILocalizedString ( @"Copy" , nil )]; NSArray * newPresets = nil ; if ( presets == _listLayouts ) { [ plugin duplicateSetWithName : [ preset objectForKey : @"name" ] extension : LIST_LAYOUT_EXTENSION inFolder : LIST_LAYOUT_FOLDER _listLayouts = [ plugin availableLayoutSets ]; newPresets = _listLayouts ; } else if ( presets == _listThemes ) { [ plugin duplicateSetWithName : [ preset objectForKey : @"name" ] extension : LIST_THEME_EXTENSION inFolder : LIST_THEME_FOLDER _listThemes = [ plugin availableThemeSets ]; newPresets = _listThemes ; //Return the new duplicate by reference for the preset controller for ( aPreset in newPresets ) { if ([ newName isEqualToString : [ aPreset objectForKey : @"name" ]]) { * duplicatePreset = aPreset ; - ( NSArray * ) deletePreset: ( NSDictionary * ) preset inPresets: ( NSArray * ) presets if ( presets == _listLayouts ) { [ plugin deleteSetWithName : [ preset objectForKey : @"name" ] extension : LIST_LAYOUT_EXTENSION inFolder : LIST_LAYOUT_FOLDER ]; _listLayouts = [ plugin availableLayoutSets ]; } else if ( presets == _listThemes ) { [ plugin deleteSetWithName : [ preset objectForKey : @"name" ] extension : LIST_THEME_EXTENSION inFolder : LIST_THEME_FOLDER ]; _listThemes = [ plugin availableThemeSets ]; - ( NSMenu * ) _listLayoutMenu NSMenu * menu = [[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ]; NSEnumerator * enumerator = [[ plugin availableLayoutSets ] objectEnumerator ]; while (( set = [ enumerator nextObject ])) { name = [ set objectForKey : @"name" ]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : name keyEquivalent : @"" ] autorelease ]; [ menuItem setRepresentedObject : name ]; [ menu addItem : [ NSMenuItem separatorItem ]]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : [ AILocalizedString ( @"Add New Layout" , nil ) stringByAppendingEllipsis ] action : @selector ( createListLayout : ) keyEquivalent : @"" ] autorelease ]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : [ AILocalizedString ( @"Edit Layouts" , nil ) stringByAppendingEllipsis ] action : @selector ( manageListLayouts : ) keyEquivalent : @"" ] autorelease ]; return [ menu autorelease ]; - ( NSMenu * ) _colorThemeMenu NSMenu * menu = [[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ]; NSEnumerator * enumerator = [[ plugin availableThemeSets ] objectEnumerator ]; while (( set = [ enumerator nextObject ])) { name = [ set objectForKey : @"name" ]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : name keyEquivalent : @"" ] autorelease ]; [ menuItem setRepresentedObject : name ]; [ menu addItem : [ NSMenuItem separatorItem ]]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : [ AILocalizedString ( @"Add New Theme" , nil ) stringByAppendingEllipsis ] action : @selector ( createListTheme : ) keyEquivalent : @"" ] autorelease ]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : [ AILocalizedString ( @"Edit Themes" , nil ) stringByAppendingEllipsis ] action : @selector ( manageListThemes : ) keyEquivalent : @"" ] autorelease ]; return [ menu autorelease ]; //Dock icons ----------------------------------------------------------------------------------------------------------- - ( IBAction ) showAllDockIcons: ( id ) sender AIDockIconSelectionSheet * dockIconSelectionSheet = [[ AIDockIconSelectionSheet alloc ] init ]; [ dockIconSelectionSheet openOnWindow : [[ self view ] window ]]; * @brief Return the menu item for a dock icon - ( NSMenuItem * ) meuItemForDockIconPackAtPath: ( NSString * ) packPath NSString * packName = [[ packPath lastPathComponent ] stringByDeletingPathExtension ]; AIIconState * preview = nil ; [ adium . dockController getName :& name forIconPackAtPath : packPath ]; menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : name keyEquivalent : @"" ] autorelease ]; [ menuItem setRepresentedObject : packName ]; [ menuItem setImage : [[ preview image ] imageByScalingForMenuItem ]]; * @brief Returns an array of menu items of all dock icon packs - ( NSArray * ) _dockIconMenuArray NSMutableArray * menuItemArray = [ NSMutableArray array ]; NSEnumerator * enumerator ; enumerator = [[ adium . dockController availableDockIconPacks ] objectEnumerator ]; while (( packPath = [ enumerator nextObject ])) { [ menuItemArray addObject : [ self meuItemForDockIconPackAtPath : packPath ]]; [ menuItemArray sortUsingSelector : @selector ( titleCompare : )]; * @brief Configure the dock icon meu initially or after the xtras change * Initially, the dock icon menu just has the currently selected icon; the others will be generated lazily if the icon is displayed, in menuNeedsUpdate: - ( void ) configureDockIconMenu NSMenu * tempMenu = [[[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ] autorelease ]; NSString * activePackName = [ adium . preferenceController preferenceForKey : KEY_ACTIVE_DOCK_ICON group : PREF_GROUP_APPEARANCE ]; iconPath = [ adium pathOfPackWithName : activePackName resourceFolderName : FOLDER_DOCK_ICONS ]; [ tempMenu addItem : [ self meuItemForDockIconPackAtPath : iconPath ]]; [ tempMenu setDelegate : self ]; [ tempMenu setTitle : @"Temporary Dock Icon Menu" ]; [ popUp_dockIcon setMenu : tempMenu ]; [ popUp_dockIcon selectItemWithRepresentedObject : activePackName ]; //Status, Service and Menu Bar icons --------------------------------------------------------------------------------------------- #pragma mark Status, service and menu bar icons - ( NSMenuItem * ) menuItemForIconPackAtPath: ( NSString * ) packPath class: ( Class ) iconClass NSString * name = [[ packPath lastPathComponent ] stringByDeletingPathExtension ]; NSMenuItem * menuItem = [[[ NSMenuItem allocWithZone : [ NSMenu menuZone ]] initWithTitle : name keyEquivalent : @"" ] autorelease ]; [ menuItem setRepresentedObject : name ]; [ menuItem setImage : [ iconClass previewMenuImageForIconPackAtPath : packPath ]]; * @brief Builds and returns an icon pack menu * @param packs NSArray of icon pack file paths * @param iconClass The controller class (AIStatusIcons, AIServiceIcons) for icon pack previews - ( NSArray * ) _iconPackMenuArrayForPacks: ( NSArray * ) packs class: ( Class ) iconClass NSMutableArray * menuItemArray = [ NSMutableArray array ]; for ( packPath in packs ) { [ menuItemArray addObject : [ self menuItemForIconPackAtPath : packPath class : iconClass ]]; [ menuItemArray sortUsingSelector : @selector ( titleCompare : )]; - ( void ) configureStatusIconsMenu NSMenu * tempMenu = [[[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ] autorelease ]; NSString * activePackName = [ adium . preferenceController preferenceForKey : KEY_STATUS_ICON_PACK group : PREF_GROUP_APPEARANCE ]; iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumStatusIcons" resourceFolderName : @"Status Icons" ]; activePackName = [ adium . preferenceController defaultPreferenceForKey : KEY_STATUS_ICON_PACK group : PREF_GROUP_APPEARANCE iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumStatusIcons" resourceFolderName : @"Status Icons" ]; [ tempMenu addItem : [ self menuItemForIconPackAtPath : iconPath class : [ AIStatusIcons class ]]]; [ tempMenu setDelegate : self ]; [ tempMenu setTitle : @"Temporary Status Icons Menu" ]; [ popUp_statusIcons setMenu : tempMenu ]; [ popUp_statusIcons selectItemWithRepresentedObject : activePackName ]; - ( void ) configureServiceIconsMenu NSMenu * tempMenu = [[[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ] autorelease ]; NSString * activePackName = [ adium . preferenceController preferenceForKey : KEY_SERVICE_ICON_PACK group : PREF_GROUP_APPEARANCE ]; iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumServiceIcons" resourceFolderName : @"Service Icons" ]; activePackName = [ adium . preferenceController defaultPreferenceForKey : KEY_SERVICE_ICON_PACK group : PREF_GROUP_APPEARANCE iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumServiceIcons" resourceFolderName : @"Service Icons" ]; [ tempMenu addItem : [ self menuItemForIconPackAtPath : iconPath class : [ AIServiceIcons class ]]]; [ tempMenu setDelegate : self ]; [ tempMenu setTitle : @"Temporary Service Icons Menu" ]; [ popUp_serviceIcons setMenu : tempMenu ]; [ popUp_serviceIcons selectItemWithRepresentedObject : activePackName ]; - ( void ) configureMenuBarIconsMenu NSMenu * tempMenu = [[[ NSMenu allocWithZone : [ NSMenu menuZone ]] init ] autorelease ]; NSString * activePackName = [ adium . preferenceController preferenceForKey : KEY_MENU_BAR_ICONS group : PREF_GROUP_APPEARANCE ]; iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumMenuBarIcons" resourceFolderName : @"Menu Bar Icons" ]; activePackName = [ adium . preferenceController defaultPreferenceForKey : KEY_MENU_BAR_ICONS group : PREF_GROUP_APPEARANCE iconPath = [ adium pathOfPackWithName : activePackName extension : @"AdiumMenuBarIcons" resourceFolderName : @"Menu Bar Icons" ]; [ tempMenu addItem : [ self menuItemForIconPackAtPath : iconPath class : [ AIMenuBarIcons class ]]]; [ tempMenu setDelegate : self ]; [ tempMenu setTitle : @"Temporary Menu Bar Icons Menu" ]; [ popUp_menuBarIcons setMenu : tempMenu ]; [ popUp_menuBarIcons selectItemWithRepresentedObject : activePackName ]; #pragma mark Menu delegate - ( void ) menuNeedsUpdate: ( NSMenu * ) menu NSString * title = [ menu title ]; NSString * repObject = nil ; NSArray * menuItemArray = nil ; NSPopUpButton * popUpButton ; if ([ title isEqualToString : @"Temporary Dock Icon Menu" ]) { //If the menu has @"Temporary Dock Icon Menu" as its title, we should update it to have all dock icons, not just our selected one menuItemArray = [ self _dockIconMenuArray ]; repObject = [ adium . preferenceController preferenceForKey : KEY_ACTIVE_DOCK_ICON group : PREF_GROUP_APPEARANCE ]; popUpButton = popUp_dockIcon ; } else if ([ title isEqualToString : @"Temporary Status Icons Menu" ]) { menuItemArray = [ self _iconPackMenuArrayForPacks : [ adium allResourcesForName : @"Status Icons" withExtensions : @"AdiumStatusIcons" ] class :[ AIStatusIcons class ]]; repObject = [ adium . preferenceController preferenceForKey : KEY_STATUS_ICON_PACK group : PREF_GROUP_APPEARANCE ]; popUpButton = popUp_statusIcons ; } else if ([ title isEqualToString : @"Temporary Service Icons Menu" ]) { menuItemArray = [ self _iconPackMenuArrayForPacks : [ adium allResourcesForName : @"Service Icons" withExtensions : @"AdiumServiceIcons" ] class :[ AIServiceIcons class ]]; repObject = [ adium . preferenceController preferenceForKey : KEY_SERVICE_ICON_PACK group : PREF_GROUP_APPEARANCE ]; popUpButton = popUp_serviceIcons ; } else if ([ title isEqualToString : @"Temporary Menu Bar Icons Menu" ]) { menuItemArray = [ self _iconPackMenuArrayForPacks : [ adium allResourcesForName : @"Menu Bar Icons" withExtensions : @"AdiumMenuBarIcons" ] class :[ AIMenuBarIcons class ]]; repObject = [ adium . preferenceController preferenceForKey : KEY_MENU_BAR_ICONS group : PREF_GROUP_APPEARANCE ]; popUpButton = popUp_menuBarIcons ; //Clear the title so we know we don't need to do this again for ( menuItem in menuItemArray ) { //Clear the title so we know we don't need to do this again //Put a checkmark by the appropriate menu item [ popUpButton selectItemWithRepresentedObject : repObject ];