2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "AIAppearancePreferences.h"
18 #import "AIAppearancePreferencesPlugin.h"
19 #import "AIDockIconSelectionSheet.h"
20 #import "AIEmoticonPack.h"
21 #import "AIEmoticonPreferences.h"
22 #import "AIListLayoutWindowController.h"
23 #import "AIListThemeWindowController.h"
24 #import <AIUtilities/AIMenuAdditions.h>
25 #import <AIUtilities/AIImageAdditions.h>
26 #import <AIUtilities/AIPopUpButtonAdditions.h>
27 #import <AIUtilities/AIStringAdditions.h>
28 #import <Adium/AIAbstractListController.h>
29 #import <Adium/AIDockControllerProtocol.h>
30 #import <Adium/AIEmoticonControllerProtocol.h>
31 #import <Adium/AIIconState.h>
32 #import <Adium/AIServiceIcons.h>
33 #import <Adium/AIStatusIcons.h>
34 #import <Adium/ESPresetManagementController.h>
35 #import <Adium/ESPresetNameSheetController.h>
36 #import <AIMenuBarIcons.h>
38 #define OLD_LIST_SETTINGS_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"OldListXtras"]
41 AIEmoticonMenuNone = 1,
42 AIEmoticonMenuMultiple
45 @interface AIAppearancePreferences (PRIVATE)
46 - (NSMenu *)_windowStyleMenu;
47 - (NSMenu *)_dockIconMenu;
48 - (NSMenu *)_emoticonPackMenu;
49 - (NSMenu *)_statusIconsMenu;
50 - (NSMenu *)_serviceIconsMenu;
51 - (NSMenu *)_listLayoutMenu;
52 - (NSMenu *)_colorThemeMenu;
53 - (void)_rebuildEmoticonMenuAndSelectActivePack;
54 - (NSMenu *)_iconPackMenuForPacks:(NSArray *)packs class:(Class)iconClass;
55 - (void)_addWindowStyleOption:(NSString *)option withTag:(int)tag toMenu:(NSMenu *)menu;
56 - (void)_updateSliderValues;
57 - (void)xtrasChanged:(NSNotification *)notification;
59 - (void)configureDockIconMenu;
60 - (void)configureStatusIconsMenu;
61 - (void)configureServiceIconsMenu;
62 - (void)configureMenuBarIconsMenu;
65 @implementation AIAppearancePreferences
68 * @brief Preference pane properties
70 - (NSString *)paneIdentifier
74 - (NSString *)paneName{
75 return AILocalizedString(@"Appearance","Appearance preferences label");
77 - (NSString *)nibName{
78 return @"AppearancePrefs";
82 return [NSImage imageNamed:@"pref-appearance" forClass:[self class]];
86 * @brief Configure the preference view
91 [popUp_windowStyle setMenu:[self _windowStyleMenu]];
93 //Observe preference changes
94 [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_EMOTICONS];
95 [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
97 //Observe xtras changes
98 [[adium notificationCenter] addObserver:self
99 selector:@selector(xtrasChanged:)
100 name:AIXtrasDidChangeNotification
102 [self xtrasChanged:nil];
106 * @brief View will close
108 - (void)viewWillClose
110 [[adium preferenceController] unregisterPreferenceObserver:self];
112 [[adium notificationCenter] removeObserver:self];
116 * @brief Xtras changed, update our menus to reflect the new Xtras
118 - (void)xtrasChanged:(NSNotification *)notification
120 NSString *type = [[notification object] lowercaseString];
122 if (!type || [type isEqualToString:@"adiumemoticonset"]) {
123 [self _rebuildEmoticonMenuAndSelectActivePack];
126 if (!type || [type isEqualToString:@"adiumicon"]) {
127 [self configureDockIconMenu];
130 if (!type || [type isEqualToString:@"adiumserviceicons"]) {
131 [self configureServiceIconsMenu];
134 if (!type || [type isEqualToString:@"adiumstatusicons"]) {
135 [self configureStatusIconsMenu];
138 if (!type || [type isEqualToString:@"adiummenubaricons"]) {
139 [self configureMenuBarIconsMenu];
142 if (!type || [type isEqualToString:@"listtheme"]) {
143 [popUp_colorTheme setMenu:[self _colorThemeMenu]];
144 [popUp_colorTheme selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME
145 group:PREF_GROUP_APPEARANCE]];
148 if (!type || [type isEqualToString:@"listlayout"]) {
149 [popUp_listLayout setMenu:[self _listLayoutMenu]];
150 [popUp_listLayout selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME
151 group:PREF_GROUP_APPEARANCE]];
156 * @brief Preferences changed
158 * Update controls in our view to reflect the changed preferences
160 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key object:(AIListObject *)object
161 preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
164 if ([group isEqualToString:PREF_GROUP_EMOTICONS] && !firstTime) {
165 [self _rebuildEmoticonMenuAndSelectActivePack];
169 if ([group isEqualToString:PREF_GROUP_APPEARANCE]) {
171 //Horizontal resizing label
173 [key isEqualToString:KEY_LIST_LAYOUT_WINDOW_STYLE] ||
174 [key isEqualToString:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE]) {
176 int windowMode = [[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_STYLE] intValue];
177 BOOL horizontalAutosize = [[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE] boolValue];
179 if (windowMode == AIContactListWindowStyleStandard) {
180 //In standard mode, disable the horizontal autosizing slider if horiztonal autosizing is off
181 [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Maximum Width:",nil)];
182 [slider_horizontalWidth setEnabled:horizontalAutosize];
185 //In all the borderless transparent modes, the horizontal autosizing slider becomes the
186 //horizontal sizing slider when autosizing is off
187 if (horizontalAutosize) {
188 [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Maximum Width:",nil)];
190 [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Width:",nil)];
192 [slider_horizontalWidth setEnabled:YES];
197 //Selected menu items
198 if (firstTime || [key isEqualToString:KEY_STATUS_ICON_PACK]) {
199 [popUp_statusIcons selectItemWithTitle:[prefDict objectForKey:KEY_STATUS_ICON_PACK]];
201 //If the prefDict's item isn't present, we're using the default, so select that one
202 if (![popUp_statusIcons selectedItem]) {
203 [popUp_statusIcons selectItemWithTitle:[[adium preferenceController] defaultPreferenceForKey:KEY_STATUS_ICON_PACK
204 group:PREF_GROUP_APPEARANCE
208 if (firstTime || [key isEqualToString:KEY_SERVICE_ICON_PACK]) {
209 [popUp_serviceIcons selectItemWithTitle:[prefDict objectForKey:KEY_SERVICE_ICON_PACK]];
211 //If the prefDict's item isn't present, we're using the default, so select that one
212 if (![popUp_serviceIcons selectedItem]) {
213 [popUp_serviceIcons selectItemWithTitle:[[adium preferenceController] defaultPreferenceForKey:KEY_SERVICE_ICON_PACK
214 group:PREF_GROUP_APPEARANCE
218 if (firstTime || [key isEqualToString:KEY_MENU_BAR_ICONS]) {
219 [popUp_menuBarIcons selectItemWithTitle:[prefDict objectForKey:KEY_MENU_BAR_ICONS]];
221 //If the prefDict's item isn't present, we're using the default, so select that one
222 if (![popUp_menuBarIcons selectedItem]) {
223 [popUp_menuBarIcons selectItemWithTitle:[[adium preferenceController] defaultPreferenceForKey:KEY_MENU_BAR_ICONS
224 group:PREF_GROUP_APPEARANCE
228 if (firstTime || [key isEqualToString:KEY_LIST_LAYOUT_NAME]) {
229 [popUp_listLayout selectItemWithRepresentedObject:[prefDict objectForKey:KEY_LIST_LAYOUT_NAME]];
231 if (firstTime || [key isEqualToString:KEY_LIST_THEME_NAME]) {
232 [popUp_colorTheme selectItemWithRepresentedObject:[prefDict objectForKey:KEY_LIST_THEME_NAME]];
234 if (firstTime || [key isEqualToString:KEY_ACTIVE_DOCK_ICON]) {
235 [popUp_dockIcon selectItemWithRepresentedObject:[prefDict objectForKey:KEY_ACTIVE_DOCK_ICON]];
238 [popUp_windowStyle compatibleSelectItemWithTag:[[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_STYLE] intValue]];
239 [checkBox_verticalAutosizing setState:[[prefDict objectForKey:KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE] boolValue]];
240 [checkBox_horizontalAutosizing setState:[[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE] boolValue]];
241 [slider_windowOpacity setFloatValue:([[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_OPACITY] floatValue] * 100.0)];
242 [slider_horizontalWidth setIntValue:[[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH] intValue]];
243 [self _updateSliderValues];
249 * @brief Rebuild the emoticon menu
251 - (void)_rebuildEmoticonMenuAndSelectActivePack
253 [popUp_emoticons setMenu:[self _emoticonPackMenu]];
255 //Update the selected pack
256 NSArray *activeEmoticonPacks = [[adium emoticonController] activeEmoticonPacks];
257 int numActivePacks = [activeEmoticonPacks count];
259 if (numActivePacks == 0) {
260 [popUp_emoticons compatibleSelectItemWithTag:AIEmoticonMenuNone];
261 } else if (numActivePacks > 1) {
262 [popUp_emoticons compatibleSelectItemWithTag:AIEmoticonMenuMultiple];
264 [popUp_emoticons selectItemWithRepresentedObject:[activeEmoticonPacks objectAtIndex:0]];
269 * @brief Save changed preferences
271 - (IBAction)changePreference:(id)sender
273 if (sender == popUp_statusIcons) {
274 [[adium preferenceController] setPreference:[[sender selectedItem] title]
275 forKey:KEY_STATUS_ICON_PACK
276 group:PREF_GROUP_APPEARANCE];
278 } else if (sender == popUp_serviceIcons) {
279 [[adium preferenceController] setPreference:[[sender selectedItem] title]
280 forKey:KEY_SERVICE_ICON_PACK
281 group:PREF_GROUP_APPEARANCE];
282 } else if (sender == popUp_menuBarIcons) {
283 [[adium preferenceController] setPreference:[[sender selectedItem] title]
284 forKey:KEY_MENU_BAR_ICONS
285 group:PREF_GROUP_APPEARANCE];
286 } else if (sender == popUp_dockIcon) {
287 [[adium preferenceController] setPreference:[[sender selectedItem] representedObject]
288 forKey:KEY_ACTIVE_DOCK_ICON
289 group:PREF_GROUP_APPEARANCE];
291 } else if (sender == popUp_listLayout) {
292 [[adium preferenceController] setPreference:[[sender selectedItem] title]
293 forKey:KEY_LIST_LAYOUT_NAME
294 group:PREF_GROUP_APPEARANCE];
296 } else if (sender == popUp_colorTheme) {
297 [[adium preferenceController] setPreference:[[sender selectedItem] title]
298 forKey:KEY_LIST_THEME_NAME
299 group:PREF_GROUP_APPEARANCE];
301 } else if (sender == popUp_windowStyle) {
302 [[adium preferenceController] setPreference:[NSNumber numberWithInt:[[sender selectedItem] tag]]
303 forKey:KEY_LIST_LAYOUT_WINDOW_STYLE
304 group:PREF_GROUP_APPEARANCE];
306 } else if (sender == checkBox_verticalAutosizing) {
307 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
308 forKey:KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE
309 group:PREF_GROUP_APPEARANCE];
311 } else if (sender == checkBox_horizontalAutosizing) {
312 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
313 forKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE
314 group:PREF_GROUP_APPEARANCE];
316 } else if (sender == slider_windowOpacity) {
317 [[adium preferenceController] setPreference:[NSNumber numberWithFloat:([sender floatValue] / 100.0)]
318 forKey:KEY_LIST_LAYOUT_WINDOW_OPACITY
319 group:PREF_GROUP_APPEARANCE];
320 [self _updateSliderValues];
322 } else if (sender == slider_horizontalWidth) {
323 int newValue = [sender intValue];
324 int oldValue = [[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH
325 group:PREF_GROUP_APPEARANCE] intValue];
326 if (newValue != oldValue) {
327 [[adium preferenceController] setPreference:[NSNumber numberWithInt:newValue]
328 forKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH
329 group:PREF_GROUP_APPEARANCE];
330 [self _updateSliderValues];
333 } else if (sender == popUp_emoticons) {
334 if ([[sender selectedItem] tag] != AIEmoticonMenuMultiple) {
335 //Disable all active emoticons
336 NSArray *activePacks = [[[[adium emoticonController] activeEmoticonPacks] mutableCopy] autorelease];
337 NSEnumerator *enumerator = [activePacks objectEnumerator];
338 AIEmoticonPack *pack, *selectedPack;
340 selectedPack = [[sender selectedItem] representedObject];
342 [[adium preferenceController] delayPreferenceChangedNotifications:YES];
344 while ((pack = [enumerator nextObject])) {
345 [[adium emoticonController] setEmoticonPack:pack enabled:NO];
348 //Enable the selected pack
349 if (selectedPack) [[adium emoticonController] setEmoticonPack:selectedPack enabled:YES];
351 [[adium preferenceController] delayPreferenceChangedNotifications:NO];
359 - (void)_updateSliderValues
361 [textField_windowOpacity setStringValue:[NSString stringWithFormat:@"%i%%", (int)[slider_windowOpacity floatValue]]];
362 [textField_horizontalWidthIndicator setStringValue:[NSString stringWithFormat:@"%ipx",[slider_horizontalWidth intValue]]];
366 //Emoticons ------------------------------------------------------------------------------------------------------------
367 #pragma mark Emoticons
371 - (IBAction)customizeEmoticons:(id)sender
373 [AIEmoticonPreferences showEmoticionCustomizationOnWindow:[[self view] window]];
379 - (NSMenu *)_emoticonPackMenu
381 NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
382 NSEnumerator *enumerator = [[[adium emoticonController] availableEmoticonPacks] objectEnumerator];
383 AIEmoticonPack *pack;
384 NSMenuItem *menuItem;
386 //Add the "No Emoticons" option
387 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"None",nil)
390 keyEquivalent:@""] autorelease];
391 [menuItem setImage:[NSImage imageNamed:@"emoticonBlank" forClass:[self class]]];
392 [menuItem setTag:AIEmoticonMenuNone];
393 [menu addItem:menuItem];
395 //Add the "Multiple packs selected" option
396 if ([[[adium emoticonController] activeEmoticonPacks] count] > 1) {
397 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Multiple Packs Selected",nil)
400 keyEquivalent:@""] autorelease];
401 [menuItem setImage:[NSImage imageNamed:@"emoticonBlank" forClass:[self class]]];
402 [menuItem setTag:AIEmoticonMenuMultiple];
403 [menu addItem:menuItem];
407 [menu addItem:[NSMenuItem separatorItem]];
410 while ((pack = [enumerator nextObject])) {
411 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[pack name]
414 keyEquivalent:@""] autorelease];
415 [menuItem setRepresentedObject:pack];
416 [menuItem setImage:[pack menuPreviewImage]];
417 [menu addItem:menuItem];
420 return [menu autorelease];
424 //Contact list options -------------------------------------------------------------------------------------------------
425 #pragma mark Contact list options
429 - (NSMenu *)_windowStyleMenu
431 NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
433 [self _addWindowStyleOption:AILocalizedString(@"Regular Window",nil)
434 withTag:AIContactListWindowStyleStandard
436 [menu addItem:[NSMenuItem separatorItem]];
437 [self _addWindowStyleOption:AILocalizedString(@"Borderless Window",nil)
438 withTag:AIContactListWindowStyleBorderless
440 [self _addWindowStyleOption:AILocalizedString(@"Group Bubbles",nil)
441 withTag:AIContactListWindowStyleGroupBubbles
443 [self _addWindowStyleOption:AILocalizedString(@"Contact Bubbles",nil)
444 withTag:AIContactListWindowStyleContactBubbles
446 [self _addWindowStyleOption:AILocalizedString(@"Contact Bubbles (To Fit)",nil)
447 withTag:AIContactListWindowStyleContactBubbles_Fitted
450 return [menu autorelease];
452 - (void)_addWindowStyleOption:(NSString *)option withTag:(int)tag toMenu:(NSMenu *)menu{
453 NSMenuItem *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:option
456 keyEquivalent:@""] autorelease];
457 [menuItem setTag:tag];
458 [menu addItem:menuItem];
462 //Contact list layout & theme ----------------------------------------------------------------------------------------
463 #pragma mark Contact list layout & theme
465 + (void) migrateOldListSettingsIfNeeded
467 id<AIPreferenceController> prefController = [[AIObject sharedAdiumInstance] preferenceController];
468 NSFileManager *manager = [NSFileManager defaultManager];
469 NSString *theme = [NSString stringWithFormat:@"%@/%@/%@.%@",
470 [[NSBundle mainBundle] resourcePath],
472 [prefController preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE],
473 LIST_THEME_EXTENSION];
474 if(![manager fileExistsAtPath:theme])
476 NSString *oldTheme = [OLD_LIST_SETTINGS_PATH stringByAppendingPathComponent:[theme lastPathComponent]];
477 if([manager fileExistsAtPath:oldTheme])
478 [manager movePath:oldTheme toPath:theme handler:nil];
480 NSString *layout = [NSString stringWithFormat:@"%@/%@/%@.%@",
481 [[NSBundle mainBundle] resourcePath],
483 [prefController preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE],
485 if(![manager fileExistsAtPath:layout])
487 NSString *oldLayout = [OLD_LIST_SETTINGS_PATH stringByAppendingPathComponent:[layout lastPathComponent]];
488 if([manager fileExistsAtPath:oldLayout])
489 [manager movePath:oldLayout toPath:layout handler:nil];
494 * @brief Create a new theme
496 - (IBAction)createListTheme:(id)sender
498 NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];
500 [ESPresetNameSheetController showPresetNameSheetWithDefaultName:[[theme stringByAppendingString:@" "] stringByAppendingString:AILocalizedString(@"(Copy)", nil)]
501 explanatoryText:AILocalizedString(@"Enter a unique name for this new theme.",nil)
502 onWindow:[[self view] window]
508 * @brief Customize the active theme
510 - (IBAction)customizeListTheme:(id)sender
512 NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];
514 //Allow alpha in our color pickers
515 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
517 [AIListThemeWindowController editListThemeWithName:theme
518 onWindow:[[self view] window]
519 notifyingTarget:self];
523 * @brief Save (or revert) changes made when editing a theme
525 - (void)listThemeEditorWillCloseWithChanges:(BOOL)saveChanges forThemeNamed:(NSString *)name
528 //Update the modified theme
529 if ([plugin createSetFromPreferenceGroup:PREF_GROUP_LIST_THEME
531 extension:LIST_THEME_EXTENSION
532 inFolder:LIST_THEME_FOLDER]) {
534 [[adium preferenceController] setPreference:name
535 forKey:KEY_LIST_THEME_NAME
536 group:PREF_GROUP_APPEARANCE];
540 //Revert back to selected theme
541 NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];
543 //Reapply the selected theme
544 [plugin applySetWithName:theme
545 extension:LIST_THEME_EXTENSION
546 inFolder:LIST_THEME_FOLDER
547 toPreferenceGroup:PREF_GROUP_LIST_THEME];
549 //Revert back to the current theme name in popUp_colorTheme component
550 [popUp_colorTheme selectItemWithTitle:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE]];
554 //No longer allow alpha in our color pickers
555 [[NSColorPanel sharedColorPanel] setShowsAlpha:NO];
559 * @brief Manage available themes
561 - (void)manageListThemes:(id)sender
563 _listThemes = [plugin availableThemeSets];
564 [ESPresetManagementController managePresets:_listThemes
566 onWindow:[[self view] window]
569 [popUp_colorTheme selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME
570 group:PREF_GROUP_APPEARANCE]];
574 * @brief Create a new layout
576 - (IBAction)createListLayout:(id)sender
578 NSString *layout = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];
580 [ESPresetNameSheetController showPresetNameSheetWithDefaultName:[[layout stringByAppendingString:@" "] stringByAppendingString:AILocalizedString(@"(Copy)",nil)]
581 explanatoryText:AILocalizedString(@"Enter a unique name for this new layout.",nil)
582 onWindow:[[self view] window]
588 * @brief Customize the active layout
590 - (IBAction)customizeListLayout:(id)sender
592 NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];
594 //Allow alpha in our color pickers
595 [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
597 [AIListLayoutWindowController editListLayoutWithName:theme
598 onWindow:[[self view] window]
599 notifyingTarget:self];
603 * @brief Save (or revert) changes made when editing a layout
605 - (void)listLayoutEditorWillCloseWithChanges:(BOOL)saveChanges forLayoutNamed:(NSString *)name
608 //Update the modified layout
609 if ([plugin createSetFromPreferenceGroup:PREF_GROUP_LIST_LAYOUT
611 extension:LIST_LAYOUT_EXTENSION
612 inFolder:LIST_LAYOUT_FOLDER]) {
614 [[adium preferenceController] setPreference:name
615 forKey:KEY_LIST_LAYOUT_NAME
616 group:PREF_GROUP_APPEARANCE];
620 //Revert back to selected layout
621 NSString *layout = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];
623 //Reapply the selected layout
624 [plugin applySetWithName:layout
625 extension:LIST_LAYOUT_EXTENSION
626 inFolder:LIST_LAYOUT_FOLDER
627 toPreferenceGroup:PREF_GROUP_LIST_LAYOUT];
629 //Revert back to the current layout name in popUp_listLayout component
630 [popUp_listLayout selectItemWithTitle:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE]];
633 //No longer allow alpha in our color pickers
634 [[NSColorPanel sharedColorPanel] setShowsAlpha:NO];
638 * @brief Manage available layouts
640 - (void)manageListLayouts:(id)sender
642 _listLayouts = [plugin availableLayoutSets];
643 [ESPresetManagementController managePresets:_listLayouts
645 onWindow:[[self view] window]
648 [popUp_listLayout selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME
649 group:PREF_GROUP_APPEARANCE]];
653 * @brief Validate a layout or theme name to ensure it is unique
655 - (BOOL)presetNameSheetController:(ESPresetNameSheetController *)controller
656 shouldAcceptNewName:(NSString *)newName
657 userInfo:(id)userInfo
659 NSEnumerator *enumerator;
660 NSDictionary *presetDict;
662 //Scan the correct presets to ensure this name doesn't already exist
663 if ([userInfo isEqualToString:@"theme"]) {
664 enumerator = [[plugin availableThemeSets] objectEnumerator];
666 enumerator = [[plugin availableLayoutSets] objectEnumerator];
669 while ((presetDict = [enumerator nextObject])) {
670 if ([newName isEqualToString:[presetDict objectForKey:@"name"]]) return NO;
677 * @brief Create a new theme with the user supplied name, activate and edit it
679 - (void)presetNameSheetControllerDidEnd:(ESPresetNameSheetController *)controller
680 returnCode:(ESPresetNameSheetReturnCode)returnCode
681 newName:(NSString *)newName
682 userInfo:(id)userInfo
684 switch (returnCode) {
685 case ESPresetNameSheetOkayReturn:
686 //User has created a new theme/layout : show the editor
687 if ([userInfo isEqualToString:@"theme"]) {
688 [self performSelector:@selector(_editListThemeWithName:) withObject:newName afterDelay:0.00001];
690 [self performSelector:@selector(_editListLayoutWithName:) withObject:newName afterDelay:0.00001];
694 case ESPresetNameSheetCancelReturn:
695 //User has canceled the operation : revert back to the current theme
696 if ([userInfo isEqualToString:@"theme"]) {
697 [popUp_colorTheme selectItemWithTitle:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE]];
699 [popUp_listLayout selectItemWithTitle:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE]];
704 - (void)_editListThemeWithName:(NSString *)name{
705 [AIListThemeWindowController editListThemeWithName:name
706 onWindow:[[self view] window]
707 notifyingTarget:self];
709 - (void)_editListLayoutWithName:(NSString *)name{
710 [AIListLayoutWindowController editListLayoutWithName:name
711 onWindow:[[self view] window]
712 notifyingTarget:self];
718 - (NSArray *)renamePreset:(NSDictionary *)preset toName:(NSString *)newName inPresets:(NSArray *)presets renamedPreset:(id *)renamedPreset
722 if (presets == _listLayouts) {
723 [plugin renameSetWithName:[preset objectForKey:@"name"]
724 extension:LIST_LAYOUT_EXTENSION
725 inFolder:LIST_LAYOUT_FOLDER
727 _listLayouts = [plugin availableLayoutSets];
728 newPresets = _listLayouts;
730 } else if (presets == _listThemes) {
731 [plugin renameSetWithName:[preset objectForKey:@"name"]
732 extension:LIST_THEME_EXTENSION
733 inFolder:LIST_THEME_FOLDER
735 _listThemes = [plugin availableThemeSets];
736 newPresets = _listThemes;
742 //Return the new duplicate by reference for the preset controller
744 NSEnumerator *enumerator = [newPresets objectEnumerator];
745 NSDictionary *aPreset;
747 while ((aPreset = [enumerator nextObject])) {
748 if ([newName isEqualToString:[aPreset objectForKey:@"name"]]) {
749 *renamedPreset = aPreset;
761 - (NSArray *)duplicatePreset:(NSDictionary *)preset inPresets:(NSArray *)presets createdDuplicate:(id *)duplicatePreset
763 NSString *newName = [NSString stringWithFormat:@"%@ (%@)", [preset objectForKey:@"name"], AILocalizedString(@"Copy",nil)];
764 NSArray *newPresets = nil;
766 if (presets == _listLayouts) {
767 [plugin duplicateSetWithName:[preset objectForKey:@"name"]
768 extension:LIST_LAYOUT_EXTENSION
769 inFolder:LIST_LAYOUT_FOLDER
771 _listLayouts = [plugin availableLayoutSets];
772 newPresets = _listLayouts;
774 } else if (presets == _listThemes) {
775 [plugin duplicateSetWithName:[preset objectForKey:@"name"]
776 extension:LIST_THEME_EXTENSION
777 inFolder:LIST_THEME_FOLDER
779 _listThemes = [plugin availableThemeSets];
780 newPresets = _listThemes;
783 //Return the new duplicate by reference for the preset controller
784 if (duplicatePreset) {
785 NSEnumerator *enumerator = [newPresets objectEnumerator];
786 NSDictionary *aPreset;
788 while ((aPreset = [enumerator nextObject])) {
789 if ([newName isEqualToString:[aPreset objectForKey:@"name"]]) {
790 *duplicatePreset = aPreset;
802 - (NSArray *)deletePreset:(NSDictionary *)preset inPresets:(NSArray *)presets
804 if (presets == _listLayouts) {
805 [plugin deleteSetWithName:[preset objectForKey:@"name"]
806 extension:LIST_LAYOUT_EXTENSION
807 inFolder:LIST_LAYOUT_FOLDER];
808 _listLayouts = [plugin availableLayoutSets];
812 } else if (presets == _listThemes) {
813 [plugin deleteSetWithName:[preset objectForKey:@"name"]
814 extension:LIST_THEME_EXTENSION
815 inFolder:LIST_THEME_FOLDER];
816 _listThemes = [plugin availableThemeSets];
828 - (NSMenu *)_listLayoutMenu
830 NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
831 NSEnumerator *enumerator = [[plugin availableLayoutSets] objectEnumerator];
833 NSMenuItem *menuItem;
837 while ((set = [enumerator nextObject])) {
838 name = [set objectForKey:@"name"];
839 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
842 keyEquivalent:@""] autorelease];
843 [menuItem setRepresentedObject:name];
844 [menu addItem:menuItem];
848 [menu addItem:[NSMenuItem separatorItem]];
851 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Add New Layout",nil) stringByAppendingEllipsis]
853 action:@selector(createListLayout:)
854 keyEquivalent:@""] autorelease];
855 [menu addItem:menuItem];
857 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Edit Layouts",nil) stringByAppendingEllipsis]
859 action:@selector(manageListLayouts:)
860 keyEquivalent:@""] autorelease];
861 [menu addItem:menuItem];
869 - (NSMenu *)_colorThemeMenu
871 NSMenu *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
872 NSEnumerator *enumerator = [[plugin availableThemeSets] objectEnumerator];
874 NSMenuItem *menuItem;
878 while ((set = [enumerator nextObject])) {
879 name = [set objectForKey:@"name"];
880 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
883 keyEquivalent:@""] autorelease];
884 [menuItem setRepresentedObject:name];
885 [menu addItem:menuItem];
889 [menu addItem:[NSMenuItem separatorItem]];
892 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Add New Theme",nil) stringByAppendingEllipsis]
894 action:@selector(createListTheme:)
895 keyEquivalent:@""] autorelease];
896 [menu addItem:menuItem];
898 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Edit Themes",nil) stringByAppendingEllipsis]
900 action:@selector(manageListThemes:)
901 keyEquivalent:@""] autorelease];
902 [menu addItem:menuItem];
908 //Dock icons -----------------------------------------------------------------------------------------------------------
909 #pragma mark Dock icons
913 - (IBAction)showAllDockIcons:(id)sender
915 [AIDockIconSelectionSheet showDockIconSelectorOnWindow:[[self view] window]];
919 * @brief Return the menu item for a dock icon
921 - (NSMenuItem *)meuItemForDockIconPackAtPath:(NSString *)packPath
923 NSMenuItem *menuItem;
924 NSString *name = nil;
925 NSString *packName = [[packPath lastPathComponent] stringByDeletingPathExtension];
926 AIIconState *preview = nil;
928 [[adium dockController] getName:&name
929 previewState:&preview
930 forIconPackAtPath:packPath];
936 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
939 keyEquivalent:@""] autorelease];
940 [menuItem setRepresentedObject:packName];
941 [menuItem setImage:[[preview image] imageByScalingForMenuItem]];
947 * @brief Returns an array of menu items of all dock icon packs
949 - (NSArray *)_dockIconMenuArray
951 NSMutableArray *menuItemArray = [NSMutableArray array];
952 NSEnumerator *enumerator;
955 enumerator = [[[adium dockController] availableDockIconPacks] objectEnumerator];
956 while ((packPath = [enumerator nextObject])) {
957 [menuItemArray addObject:[self meuItemForDockIconPackAtPath:packPath]];
960 [menuItemArray sortUsingSelector:@selector(titleCompare:)];
962 return menuItemArray;
966 * @brief Configure the dock icon meu initially or after the xtras change
968 * Initially, the dock icon menu just has the currently selected icon; the others will be generated lazily if the icon is displayed, in menuNeedsUpdate:
970 - (void)configureDockIconMenu
972 NSMenu *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
974 NSString *activePackName = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_DOCK_ICON
975 group:PREF_GROUP_APPEARANCE];
976 iconPath = [adium pathOfPackWithName:activePackName
977 extension:@"AdiumIcon"
978 resourceFolderName:FOLDER_DOCK_ICONS];
980 [tempMenu addItem:[self meuItemForDockIconPackAtPath:iconPath]];
981 [tempMenu setDelegate:self];
982 [tempMenu setTitle:@"Temporary Dock Icon Menu"];
984 [popUp_dockIcon setMenu:tempMenu];
985 [popUp_dockIcon selectItemWithRepresentedObject:activePackName];
988 //Status, Service and Menu Bar icons ---------------------------------------------------------------------------------------------
989 #pragma mark Status, service and menu bar icons
990 - (NSMenuItem *)menuItemForIconPackAtPath:(NSString *)packPath class:(Class)iconClass
992 NSString *name = [[packPath lastPathComponent] stringByDeletingPathExtension];
993 NSMenuItem *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
996 keyEquivalent:@""] autorelease];
997 [menuItem setRepresentedObject:name];
998 [menuItem setImage:[iconClass previewMenuImageForIconPackAtPath:packPath]];
1004 * @brief Builds and returns an icon pack menu
1006 * @param packs NSArray of icon pack file paths
1007 * @param iconClass The controller class (AIStatusIcons, AIServiceIcons) for icon pack previews
1009 - (NSArray *)_iconPackMenuArrayForPacks:(NSArray *)packs class:(Class)iconClass
1011 NSMutableArray *menuItemArray = [NSMutableArray array];
1012 NSEnumerator *enumerator = [packs objectEnumerator];
1015 while ((packPath = [enumerator nextObject])) {
1016 [menuItemArray addObject:[self menuItemForIconPackAtPath:packPath class:iconClass]];
1019 [menuItemArray sortUsingSelector:@selector(titleCompare:)];
1021 return menuItemArray;
1024 - (void)configureStatusIconsMenu
1026 NSMenu *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
1028 NSString *activePackName = [[adium preferenceController] preferenceForKey:KEY_STATUS_ICON_PACK
1029 group:PREF_GROUP_APPEARANCE];
1030 iconPath = [adium pathOfPackWithName:activePackName
1031 extension:@"AdiumStatusIcons"
1032 resourceFolderName:@"Status Icons"];
1035 activePackName = [[adium preferenceController] defaultPreferenceForKey:KEY_STATUS_ICON_PACK
1036 group:PREF_GROUP_APPEARANCE
1039 iconPath = [adium pathOfPackWithName:activePackName
1040 extension:@"AdiumStatusIcons"
1041 resourceFolderName:@"Status Icons"];
1043 [tempMenu addItem:[self menuItemForIconPackAtPath:iconPath class:[AIStatusIcons class]]];
1044 [tempMenu setDelegate:self];
1045 [tempMenu setTitle:@"Temporary Status Icons Menu"];
1047 [popUp_statusIcons setMenu:tempMenu];
1048 [popUp_statusIcons selectItemWithRepresentedObject:activePackName];
1051 - (void)configureServiceIconsMenu
1053 NSMenu *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
1055 NSString *activePackName = [[adium preferenceController] preferenceForKey:KEY_SERVICE_ICON_PACK
1056 group:PREF_GROUP_APPEARANCE];
1057 iconPath = [adium pathOfPackWithName:activePackName
1058 extension:@"AdiumServiceIcons"
1059 resourceFolderName:@"Service Icons"];
1062 activePackName = [[adium preferenceController] defaultPreferenceForKey:KEY_SERVICE_ICON_PACK
1063 group:PREF_GROUP_APPEARANCE
1066 iconPath = [adium pathOfPackWithName:activePackName
1067 extension:@"AdiumServiceIcons"
1068 resourceFolderName:@"Service Icons"];
1070 [tempMenu addItem:[self menuItemForIconPackAtPath:iconPath class:[AIServiceIcons class]]];
1071 [tempMenu setDelegate:self];
1072 [tempMenu setTitle:@"Temporary Service Icons Menu"];
1074 [popUp_serviceIcons setMenu:tempMenu];
1075 [popUp_serviceIcons selectItemWithRepresentedObject:activePackName];
1078 - (void)configureMenuBarIconsMenu
1080 NSMenu *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
1082 NSString *activePackName = [[adium preferenceController] preferenceForKey:KEY_MENU_BAR_ICONS
1083 group:PREF_GROUP_APPEARANCE];
1084 iconPath = [adium pathOfPackWithName:activePackName
1085 extension:@"AdiumMenuBarIcons"
1086 resourceFolderName:@"Menu Bar Icons"];
1089 activePackName = [[adium preferenceController] defaultPreferenceForKey:KEY_MENU_BAR_ICONS
1090 group:PREF_GROUP_APPEARANCE
1093 iconPath = [adium pathOfPackWithName:activePackName
1094 extension:@"AdiumMenuBarIcons"
1095 resourceFolderName:@"Menu Bar Icons"];
1097 [tempMenu addItem:[self menuItemForIconPackAtPath:iconPath class:[AIMenuBarIcons class]]];
1098 [tempMenu setDelegate:self];
1099 [tempMenu setTitle:@"Temporary Menu Bar Icons Menu"];
1101 [popUp_menuBarIcons setMenu:tempMenu];
1102 [popUp_menuBarIcons selectItemWithRepresentedObject:activePackName];
1105 #pragma mark Menu delegate
1106 - (void)menuNeedsUpdate:(NSMenu *)menu
1108 NSString *title =[menu title];
1109 NSString *repObject = nil;
1110 NSArray *menuItemArray = nil;
1111 NSPopUpButton *popUpButton;
1113 if ([title isEqualToString:@"Temporary Dock Icon Menu"]) {
1114 //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
1115 menuItemArray = [self _dockIconMenuArray];
1116 repObject = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_DOCK_ICON
1117 group:PREF_GROUP_APPEARANCE];
1118 popUpButton = popUp_dockIcon;
1120 } else if ([title isEqualToString:@"Temporary Status Icons Menu"]) {
1121 menuItemArray = [self _iconPackMenuArrayForPacks:[adium allResourcesForName:@"Status Icons"
1122 withExtensions:@"AdiumStatusIcons"]
1123 class:[AIStatusIcons class]];
1124 repObject = [[adium preferenceController] preferenceForKey:KEY_STATUS_ICON_PACK
1125 group:PREF_GROUP_APPEARANCE];
1126 popUpButton = popUp_statusIcons;
1128 } else if ([title isEqualToString:@"Temporary Service Icons Menu"]) {
1129 menuItemArray = [self _iconPackMenuArrayForPacks:[adium allResourcesForName:@"Service Icons"
1130 withExtensions:@"AdiumServiceIcons"]
1131 class:[AIServiceIcons class]];
1132 repObject = [[adium preferenceController] preferenceForKey:KEY_SERVICE_ICON_PACK
1133 group:PREF_GROUP_APPEARANCE];
1134 popUpButton = popUp_serviceIcons;
1136 } else if ([title isEqualToString:@"Temporary Menu Bar Icons Menu"]) {
1137 menuItemArray = [self _iconPackMenuArrayForPacks:[adium allResourcesForName:@"Menu Bar Icons"
1138 withExtensions:@"AdiumMenuBarIcons"]
1139 class:[AIMenuBarIcons class]];
1140 repObject = [[adium preferenceController] preferenceForKey:KEY_MENU_BAR_ICONS
1141 group:PREF_GROUP_APPEARANCE];
1142 popUpButton = popUp_menuBarIcons;
1145 if (menuItemArray) {
1146 NSEnumerator *enumerator;
1147 NSMenuItem *menuItem;
1149 //Remove existing items
1150 [menu removeAllItems];
1152 //Clear the title so we know we don't need to do this again
1153 [menu setTitle:@""];
1156 enumerator = [menuItemArray objectEnumerator];
1157 while ((menuItem = [enumerator nextObject])) {
1158 [menu addItem:menuItem];
1161 //Clear the title so we know we don't need to do this again
1162 [menu setTitle:@""];
1164 //Put a checkmark by the appropriate menu item
1165 [popUpButton selectItemWithRepresentedObject:repObject];