libgaim.framework [386] which has extensive debugging in the upnp module to try to...
[adiumx.git] / Source / AIAppearancePreferences.m
blobc1ef72240ccc3e72eaae91e8f1f33a34d1ee7f3b
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
17 #import "AIAppearancePreferences.h"
18 #import "AIAppearancePreferencesPlugin.h"
19 #import "AIDockController.h"
20 #import "AIDockIconSelectionSheet.h"
21 #import "AIEmoticonPack.h"
22 #import "AIEmoticonPreferences.h"
23 #import "AIListLayoutWindowController.h"
24 #import "AIListThemeWindowController.h"
25 #import <AIUtilities/AIMenuAdditions.h>
26 #import <AIUtilities/AIImageAdditions.h>
27 #import <AIUtilities/AIPopUpButtonAdditions.h>
28 #import <AIUtilities/AIStringAdditions.h>
29 #import <Adium/AIAbstractListController.h>
30 #import <Adium/AIEmoticonController.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>
37 #define OLD_LIST_SETTINGS_PATH [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"OldListXtras"]
39 typedef enum {
40         AIEmoticonMenuNone = 1,
41         AIEmoticonMenuMultiple
42 } AIEmoticonMenuTag;
44 @interface AIAppearancePreferences (PRIVATE)
45 - (NSMenu *)_windowStyleMenu;
46 - (NSMenu *)_dockIconMenu;
47 - (NSMenu *)_emoticonPackMenu;
48 - (NSMenu *)_statusIconsMenu;
49 - (NSMenu *)_serviceIconsMenu;
50 - (NSMenu *)_listLayoutMenu;
51 - (NSMenu *)_colorThemeMenu;
52 - (void)_rebuildEmoticonMenuAndSelectActivePack;
53 - (NSMenu *)_iconPackMenuForPacks:(NSArray *)packs class:(Class)iconClass;
54 - (void)_addWindowStyleOption:(NSString *)option withTag:(int)tag toMenu:(NSMenu *)menu;
55 - (void)_updateSliderValues;
56 - (void)xtrasChanged:(NSNotification *)notification;
58 - (void)configureDockIconMenu;
59 - (void)configureStatusIconsMenu;
60 - (void)configureServiceIconsMenu;
61 @end
63 @implementation AIAppearancePreferences
65 /*!
66  * @brief Preference pane properties
67  */
68 - (AIPreferenceCategory)category{
69     return AIPref_Appearance;
71 - (NSString *)label{
72     return AILocalizedString(@"Appearance","Appearance preferences label");
74 - (NSString *)nibName{
75     return @"AppearancePrefs";
78 /*!
79  * @brief Configure the preference view
80  */
81 - (void)viewDidLoad
82 {       
83         //Other list options
84         [popUp_windowStyle setMenu:[self _windowStyleMenu]];
85                 
86         //Observe preference changes
87         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_EMOTICONS];
88         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
90         //Observe xtras changes
91         [[adium notificationCenter] addObserver:self
92                                                                    selector:@selector(xtrasChanged:)
93                                                                            name:Adium_Xtras_Changed
94                                                                          object:nil];   
95         [self xtrasChanged:nil];
98 /*!
99  * @brief View will close
100  */
101 - (void)viewWillClose
103         [[adium preferenceController] unregisterPreferenceObserver:self];
104         
105         [[adium notificationCenter] removeObserver:self];
109  * @brief Xtras changed, update our menus to reflect the new Xtras
110  */
111 - (void)xtrasChanged:(NSNotification *)notification
113         NSString                *type = [[notification object] lowercaseString];
115         if (!type || [type isEqualToString:@"adiumemoticonset"]) {
116                 [self _rebuildEmoticonMenuAndSelectActivePack];
117         }
118         
119         if (!type || [type isEqualToString:@"adiumicon"]) {
120                 [self configureDockIconMenu];
121         }
122         
123         if (!type || [type isEqualToString:@"adiumserviceicons"]) {
124                 [self configureServiceIconsMenu];
125         }
126         
127         if (!type || [type isEqualToString:@"adiumstatusicons"]) {
128                 [self configureStatusIconsMenu];
129         }
130         
131         if (!type || [type isEqualToString:@"listtheme"]) {
132                 [popUp_colorTheme setMenu:[self _colorThemeMenu]];
133                 [popUp_colorTheme selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME
134                                                                                                                                                                                                    group:PREF_GROUP_APPEARANCE]];
135         }
137         if (!type || [type isEqualToString:@"listlayout"]) {
138                 [popUp_listLayout setMenu:[self _listLayoutMenu]];
139                 [popUp_listLayout selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME
140                                                                                                                                                                                                    group:PREF_GROUP_APPEARANCE]];
141         }
145  * @brief Preferences changed
147  * Update controls in our view to reflect the changed preferences
148  */
149 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key object:(AIListObject *)object
150                                         preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
152         //Emoticons
153         if ([group isEqualToString:PREF_GROUP_EMOTICONS] && !firstTime) {
154                 [self _rebuildEmoticonMenuAndSelectActivePack];
155         }
156         
157         //Appearance
158         if ([group isEqualToString:PREF_GROUP_APPEARANCE]) {
159                 
160                 //Horizontal resizing label
161                 if (firstTime || 
162                    [key isEqualToString:KEY_LIST_LAYOUT_WINDOW_STYLE] ||
163                    [key isEqualToString:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE]) {
164                         
165                         int windowMode = [[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_STYLE] intValue];
166                         BOOL horizontalAutosize = [[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE] boolValue];
167                         
168                         if (windowMode == AIContactListWindowStyleStandard) {
169                                 //In standard mode, disable the horizontal autosizing slider if horiztonal autosizing is off
170                                 [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Maximum Width:",nil)];
171                                 [slider_horizontalWidth setEnabled:horizontalAutosize];
172                                 
173                         } else {
174                                 //In all the borderless transparent modes, the horizontal autosizing slider becomes the
175                                 //horizontal sizing slider when autosizing is off
176                                 if (horizontalAutosize) {
177                                         [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Maximum Width:",nil)];
178                                 } else {
179                                         [textField_horizontalWidthText setLocalizedString:AILocalizedString(@"Width:",nil)];                    
180                                 }
181                                 [slider_horizontalWidth setEnabled:YES];
182                         }
183                         
184                 }
186                 //Selected menu items
187                 if (firstTime || [key isEqualToString:KEY_STATUS_ICON_PACK]) {
188                         [popUp_statusIcons selectItemWithTitle:[prefDict objectForKey:KEY_STATUS_ICON_PACK]];
189                         
190                         //If the prefDict's item isn't present, we're using the default, so select that one
191                         if (![popUp_serviceIcons selectedItem]) {
192                                 [popUp_serviceIcons selectItemWithTitle:[[adium preferenceController] defaultPreferenceForKey:KEY_STATUS_ICON_PACK
193                                                                                                                                                                                                                 group:PREF_GROUP_APPEARANCE
194                                                                                                                                                                                                            object:nil]];
195                         }                       
196                 }
197                 if (firstTime || [key isEqualToString:KEY_SERVICE_ICON_PACK]) {
198                         [popUp_serviceIcons selectItemWithTitle:[prefDict objectForKey:KEY_SERVICE_ICON_PACK]];
199                         
200                         //If the prefDict's item isn't present, we're using the default, so select that one
201                         if (![popUp_serviceIcons selectedItem]) {
202                                 [popUp_serviceIcons selectItemWithTitle:[[adium preferenceController] defaultPreferenceForKey:KEY_SERVICE_ICON_PACK
203                                                                                                                                                                                                                 group:PREF_GROUP_APPEARANCE
204                                                                                                                                                                                                            object:nil]];
205                         }
206                 }               
207                 if (firstTime || [key isEqualToString:KEY_LIST_LAYOUT_NAME]) {
208                         [popUp_listLayout selectItemWithRepresentedObject:[prefDict objectForKey:KEY_LIST_LAYOUT_NAME]];
209                 }
210                 if (firstTime || [key isEqualToString:KEY_LIST_THEME_NAME]) {
211                         [popUp_colorTheme selectItemWithRepresentedObject:[prefDict objectForKey:KEY_LIST_THEME_NAME]]; 
212                 }       
213                 if (firstTime || [key isEqualToString:KEY_ACTIVE_DOCK_ICON]) {
214                         [popUp_dockIcon selectItemWithRepresentedObject:[prefDict objectForKey:KEY_ACTIVE_DOCK_ICON]];
215                 }
216                 if (firstTime) {
217                         [popUp_windowStyle compatibleSelectItemWithTag:[[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_STYLE] intValue]];        
218                         [checkBox_verticalAutosizing setState:[[prefDict objectForKey:KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE] boolValue]];
219                         [checkBox_horizontalAutosizing setState:[[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE] boolValue]];
220                         [slider_windowOpacity setFloatValue:([[prefDict objectForKey:KEY_LIST_LAYOUT_WINDOW_OPACITY] floatValue] * 100.0)];
221                         [slider_horizontalWidth setIntValue:[[prefDict objectForKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH] intValue]];
222                         [self _updateSliderValues];
223                 }
224         }
228  * @brief Rebuild the emoticon menu
229  */
230 - (void)_rebuildEmoticonMenuAndSelectActivePack
232         [popUp_emoticons setMenu:[self _emoticonPackMenu]];
233         
234         //Update the selected pack
235         NSArray *activeEmoticonPacks = [[adium emoticonController] activeEmoticonPacks];
236         int             numActivePacks = [activeEmoticonPacks count];
237         
238         if (numActivePacks == 0) {
239                 [popUp_emoticons compatibleSelectItemWithTag:AIEmoticonMenuNone];
240         } else if (numActivePacks > 1) {
241                 [popUp_emoticons compatibleSelectItemWithTag:AIEmoticonMenuMultiple];
242         } else {
243                 [popUp_emoticons selectItemWithRepresentedObject:[activeEmoticonPacks objectAtIndex:0]];
244         }
248  * @brief Save changed preferences
249  */
250 - (IBAction)changePreference:(id)sender
252         if (sender == popUp_statusIcons) {
253         [[adium preferenceController] setPreference:[[sender selectedItem] title]
254                                              forKey:KEY_STATUS_ICON_PACK
255                                               group:PREF_GROUP_APPEARANCE];
256                 
257         } else if (sender == popUp_serviceIcons) {
258         [[adium preferenceController] setPreference:[[sender selectedItem] title]
259                                              forKey:KEY_SERVICE_ICON_PACK
260                                               group:PREF_GROUP_APPEARANCE];
261                 
262         } else if (sender == popUp_dockIcon) {
263         [[adium preferenceController] setPreference:[[sender selectedItem] representedObject]
264                                              forKey:KEY_ACTIVE_DOCK_ICON
265                                               group:PREF_GROUP_APPEARANCE];
266                 
267         } else if (sender == popUp_listLayout) {
268         [[adium preferenceController] setPreference:[[sender selectedItem] title]
269                                              forKey:KEY_LIST_LAYOUT_NAME
270                                               group:PREF_GROUP_APPEARANCE];             
271                 
272         } else if (sender == popUp_colorTheme) {
273                 [[adium preferenceController] setPreference:[[sender selectedItem] title]
274                                                                                          forKey:KEY_LIST_THEME_NAME
275                                                                                           group:PREF_GROUP_APPEARANCE];
277         } else if (sender == popUp_windowStyle) {
278                 [[adium preferenceController] setPreference:[NSNumber numberWithInt:[[sender selectedItem] tag]]
279                                                                                          forKey:KEY_LIST_LAYOUT_WINDOW_STYLE
280                                                                                           group:PREF_GROUP_APPEARANCE];
281                 
282     } else if (sender == checkBox_verticalAutosizing) {
283         [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
284                                              forKey:KEY_LIST_LAYOUT_VERTICAL_AUTOSIZE
285                                               group:PREF_GROUP_APPEARANCE];
286                 
287     } else if (sender == checkBox_horizontalAutosizing) {
288         [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
289                                              forKey:KEY_LIST_LAYOUT_HORIZONTAL_AUTOSIZE
290                                               group:PREF_GROUP_APPEARANCE];
292     } else if (sender == slider_windowOpacity) {
293         [[adium preferenceController] setPreference:[NSNumber numberWithFloat:([sender floatValue] / 100.0)]
294                                              forKey:KEY_LIST_LAYOUT_WINDOW_OPACITY
295                                               group:PREF_GROUP_APPEARANCE];
296                 [self _updateSliderValues];
297                 
298         } else if (sender == slider_horizontalWidth) {
299                 int newValue = [sender intValue];
300                 int oldValue = [[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH
301                                                                                                                                  group:PREF_GROUP_APPEARANCE] intValue];
302                 if (newValue != oldValue) { 
303                         [[adium preferenceController] setPreference:[NSNumber numberWithInt:newValue]
304                                                                                                  forKey:KEY_LIST_LAYOUT_HORIZONTAL_WIDTH
305                                                                                                   group:PREF_GROUP_APPEARANCE];
306                         [self _updateSliderValues];
307                 }
308                 
309         } else if (sender == popUp_emoticons) {
310                 if ([[sender selectedItem] tag] != AIEmoticonMenuMultiple) {
311                         //Disable all active emoticons
312                         NSArray                 *activePacks = [[[[adium emoticonController] activeEmoticonPacks] mutableCopy] autorelease];
313                         NSEnumerator    *enumerator = [activePacks objectEnumerator];
314                         AIEmoticonPack  *pack, *selectedPack;
315                         
316                         selectedPack = [[sender selectedItem] representedObject];
317                         
318                         [[adium preferenceController] delayPreferenceChangedNotifications:YES];
320                         while ((pack = [enumerator nextObject])) {
321                                 [[adium emoticonController] setEmoticonPack:pack enabled:NO];
322                         }
323                         
324                         //Enable the selected pack
325                         if (selectedPack) [[adium emoticonController] setEmoticonPack:selectedPack enabled:YES];
327                         [[adium preferenceController] delayPreferenceChangedNotifications:NO];
328                 }
329         }
334  */
335 - (void)_updateSliderValues
337         [textField_windowOpacity setStringValue:[NSString stringWithFormat:@"%i%%", (int)[slider_windowOpacity floatValue]]];
338         [textField_horizontalWidthIndicator setStringValue:[NSString stringWithFormat:@"%ipx",[slider_horizontalWidth intValue]]];
342 //Emoticons ------------------------------------------------------------------------------------------------------------
343 #pragma mark Emoticons
346  */
347 - (IBAction)customizeEmoticons:(id)sender
349         [AIEmoticonPreferences showEmoticionCustomizationOnWindow:[[self view] window]];
354  */
355 - (NSMenu *)_emoticonPackMenu
357         NSMenu                  *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
358         NSEnumerator    *enumerator = [[[adium emoticonController] availableEmoticonPacks] objectEnumerator];
359         AIEmoticonPack  *pack;
360         NSMenuItem              *menuItem;
361                 
362         //Add the "No Emoticons" option
363         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"None",nil)
364                                                                                                                                          target:nil
365                                                                                                                                          action:nil
366                                                                                                                           keyEquivalent:@""] autorelease];
367         [menuItem setImage:[NSImage imageNamed:@"emoticonBlank" forClass:[self class]]];
368         [menuItem setTag:AIEmoticonMenuNone];
369         [menu addItem:menuItem];
370         
371         //Add the "Multiple packs selected" option
372         if ([[[adium emoticonController] activeEmoticonPacks] count] > 1) {
373                 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Multiple Packs Selected",nil)
374                                                                                                                                                  target:nil
375                                                                                                                                                  action:nil
376                                                                                                                                   keyEquivalent:@""] autorelease];
377                 [menuItem setImage:[NSImage imageNamed:@"emoticonBlank" forClass:[self class]]];
378                 [menuItem setTag:AIEmoticonMenuMultiple];
379                 [menu addItem:menuItem];
380         }
382         //Divider
383         [menu addItem:[NSMenuItem separatorItem]];
385         //Emoticon Packs
386         while ((pack = [enumerator nextObject])) {
387                 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[pack name]
388                                                                                                                                                  target:nil
389                                                                                                                                                  action:nil
390                                                                                                                                   keyEquivalent:@""] autorelease];
391                 [menuItem setRepresentedObject:pack];
392                 [menuItem setImage:[pack menuPreviewImage]];
393                 [menu addItem:menuItem];
394         }
396         return [menu autorelease];
400 //Contact list options -------------------------------------------------------------------------------------------------
401 #pragma mark Contact list options
404  */
405 - (NSMenu *)_windowStyleMenu
407         NSMenu  *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
409         [self _addWindowStyleOption:AILocalizedString(@"Regular Window",nil)
410                                                 withTag:AIContactListWindowStyleStandard
411                                                  toMenu:menu];
412         [menu addItem:[NSMenuItem separatorItem]];
413         [self _addWindowStyleOption:AILocalizedString(@"Borderless Window",nil)
414                                                 withTag:AIContactListWindowStyleBorderless
415                                                  toMenu:menu];
416         [self _addWindowStyleOption:AILocalizedString(@"Group Bubbles",nil)
417                                                 withTag:AIContactListWindowStyleGroupBubbles
418                                                  toMenu:menu];
419         [self _addWindowStyleOption:AILocalizedString(@"Contact Bubbles",nil)
420                                                 withTag:AIContactListWindowStyleContactBubbles
421                                                  toMenu:menu];
422         [self _addWindowStyleOption:AILocalizedString(@"Contact Bubbles (To Fit)",nil)
423                                                 withTag:AIContactListWindowStyleContactBubbles_Fitted
424                                                  toMenu:menu];
426         return [menu autorelease];
428 - (void)_addWindowStyleOption:(NSString *)option withTag:(int)tag toMenu:(NSMenu *)menu{
429     NSMenuItem  *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:option
430                                                                                                                                                                   target:nil
431                                                                                                                                                                   action:nil
432                                                                                                                                                    keyEquivalent:@""] autorelease];
433         [menuItem setTag:tag];
434         [menu addItem:menuItem];
438 //Contact list layout & theme ----------------------------------------------------------------------------------------
439 #pragma mark Contact list layout & theme
441 + (void) migrateOldListSettingsIfNeeded
443         id<AIPreferenceController> prefController = [[AIObject sharedAdiumInstance] preferenceController];
444         NSFileManager *manager = [NSFileManager defaultManager];
445         NSString *theme = [NSString stringWithFormat:@"%@/%@/%@.%@", 
446                                                         [[NSBundle mainBundle] resourcePath], 
447                                                         LIST_THEME_FOLDER,
448                                                         [prefController preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE],
449                                                         LIST_THEME_EXTENSION];
450         if(![manager fileExistsAtPath:theme])
451         {
452                 NSString *oldTheme = [OLD_LIST_SETTINGS_PATH stringByAppendingPathComponent:[theme lastPathComponent]];
453                 if([manager fileExistsAtPath:oldTheme])
454                         [manager movePath:oldTheme toPath:theme handler:nil];
455         }
456         NSString *layout = [NSString stringWithFormat:@"%@/%@/%@.%@", 
457                                                         [[NSBundle mainBundle] resourcePath], 
458                                                         LIST_THEME_FOLDER, 
459                                                         [prefController preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE],
460                                                         @"ListLayout"];
461         if(![manager fileExistsAtPath:layout])
462         {
463                 NSString *oldLayout = [OLD_LIST_SETTINGS_PATH stringByAppendingPathComponent:[layout lastPathComponent]];
464                 if([manager fileExistsAtPath:oldLayout])
465                         [manager movePath:oldLayout toPath:layout handler:nil];
466         }
470  * @brief Create a new theme
471  */
472 - (IBAction)createListTheme:(id)sender
474         NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];      
475         
476         [ESPresetNameSheetController showPresetNameSheetWithDefaultName:[theme stringByAppendingString:@" Copy"]
477                                                                                                         explanatoryText:AILocalizedString(@"Enter a unique name for this new theme.",nil)
478                                                                                                                    onWindow:[[self view] window]
479                                                                                                         notifyingTarget:self
480                                                                                                                    userInfo:@"theme"];
484  * @brief Customize the active theme
485  */
486 - (IBAction)customizeListTheme:(id)sender
488         NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];      
489         
490         //Allow alpha in our color pickers
491         [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];
493         [AIListThemeWindowController editListThemeWithName:theme
494                                                                                           onWindow:[[self view] window]
495                                                                            notifyingTarget:self];
499  * @brief Save (or revert) changes made when editing a theme
500  */
501 - (void)listThemeEditorWillCloseWithChanges:(BOOL)saveChanges forThemeNamed:(NSString *)name
503         if (saveChanges) {
504                 //Update the modified theme
505                 if ([plugin createSetFromPreferenceGroup:PREF_GROUP_LIST_THEME
506                                                                                 withName:name
507                                                                            extension:LIST_THEME_EXTENSION
508                                                                                 inFolder:LIST_THEME_FOLDER]) {
509                         
510                         [[adium preferenceController] setPreference:name
511                                                                                                  forKey:KEY_LIST_THEME_NAME
512                                                                                                   group:PREF_GROUP_APPEARANCE];
513                 }
514                 
515         } else {
516                 //Revert back to selected theme
517                 NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME group:PREF_GROUP_APPEARANCE];      
518                 
519                 [plugin applySetWithName:theme
520                                            extension:LIST_THEME_EXTENSION
521                                                 inFolder:LIST_THEME_FOLDER
522                            toPreferenceGroup:PREF_GROUP_LIST_THEME];
523         }
524         
526         //No longer allow alpha in our color pickers
527         [[NSColorPanel sharedColorPanel] setShowsAlpha:NO];
531  * @brief Manage available themes
532  */
533 - (void)manageListThemes:(id)sender
535         _listThemes = [plugin availableThemeSets];
536         [ESPresetManagementController managePresets:_listThemes
537                                                                          namedByKey:@"name"
538                                                                            onWindow:[[self view] window]
539                                                                    withDelegate:self];
540         
541         [popUp_colorTheme selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_THEME_NAME
542                                                                                                                                                                                            group:PREF_GROUP_APPEARANCE]];               
546  * @brief Create a new layout
547  */
548 - (IBAction)createListLayout:(id)sender
550         NSString *layout = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];
551         
552         [ESPresetNameSheetController showPresetNameSheetWithDefaultName:[layout stringByAppendingString:@" Copy"]
553                                                                                                         explanatoryText:AILocalizedString(@"Enter a unique name for this new layout.",nil)
554                                                                                                                    onWindow:[[self view] window]
555                                                                                                         notifyingTarget:self
556                                                                                                                    userInfo:@"layout"];
560  * @brief Customize the active layout
561  */
562 - (IBAction)customizeListLayout:(id)sender
564         NSString *theme = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];     
565         
566         //Allow alpha in our color pickers
567         [[NSColorPanel sharedColorPanel] setShowsAlpha:YES];    
569         [AIListLayoutWindowController editListLayoutWithName:theme
570                                                                                           onWindow:[[self view] window]
571                                                                            notifyingTarget:self];
575  * @brief Save (or revert) changes made when editing a layout
576  */
577 - (void)listLayoutEditorWillCloseWithChanges:(BOOL)saveChanges forLayoutNamed:(NSString *)name
579         if (saveChanges) {
580                 //Update the modified layout
581                 if ([plugin createSetFromPreferenceGroup:PREF_GROUP_LIST_LAYOUT
582                                                                                 withName:name
583                                                                            extension:LIST_LAYOUT_EXTENSION
584                                                                                 inFolder:LIST_LAYOUT_FOLDER]) {
585                         
586                         [[adium preferenceController] setPreference:name
587                                                                                                  forKey:KEY_LIST_LAYOUT_NAME
588                                                                                                   group:PREF_GROUP_APPEARANCE];
589                 }
590                 
591         } else {
592                 //Revert back to selected layout
593                 NSString *layout = [[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME group:PREF_GROUP_APPEARANCE];    
594                 
595                 [plugin applySetWithName:layout
596                                            extension:LIST_LAYOUT_EXTENSION
597                                                 inFolder:LIST_LAYOUT_FOLDER
598                            toPreferenceGroup:PREF_GROUP_LIST_LAYOUT];
599         }
600         
601         //No longer allow alpha in our color pickers
602         [[NSColorPanel sharedColorPanel] setShowsAlpha:NO];
606  * @brief Manage available layouts
607  */
608 - (void)manageListLayouts:(id)sender
610         _listLayouts = [plugin availableLayoutSets];
611         [ESPresetManagementController managePresets:_listLayouts
612                                                                          namedByKey:@"name"
613                                                                            onWindow:[[self view] window]
614                                                                    withDelegate:self];
616         [popUp_listLayout selectItemWithRepresentedObject:[[adium preferenceController] preferenceForKey:KEY_LIST_LAYOUT_NAME
617                                                                                                                                                                                            group:PREF_GROUP_APPEARANCE]];               
621  * @brief Validate a layout or theme name to ensure it is unique
622  */
623 - (BOOL)presetNameSheetController:(ESPresetNameSheetController *)controller
624                           shouldAcceptNewName:(NSString *)newName
625                                                  userInfo:(id)userInfo
627         NSEnumerator    *enumerator;
628         NSDictionary    *presetDict;
630         //Scan the correct presets to ensure this name doesn't already exist
631         if ([userInfo isEqualToString:@"theme"]) {
632                 enumerator = [[plugin availableThemeSets] objectEnumerator];
633         } else {
634                 enumerator = [[plugin availableLayoutSets] objectEnumerator];
635         }
636         
637         while ((presetDict = [enumerator nextObject])) {
638                 if ([newName isEqualToString:[presetDict objectForKey:@"name"]]) return NO;
639         }
640         
641         return YES;
645  * @brief Create a new theme with the user supplied name, activate and edit it
646  */
647 - (void)presetNameSheetControllerDidEnd:(ESPresetNameSheetController *)controller 
648                                                          returnCode:(ESPresetNameSheetReturnCode)returnCode
649                                                                 newName:(NSString *)newName
650                                                            userInfo:(id)userInfo
652         switch (returnCode) {
653                 case ESPresetNameSheetOkayReturn:
654                         if ([userInfo isEqualToString:@"theme"]) {
655                                 [self performSelector:@selector(_editListThemeWithName:) withObject:newName afterDelay:0.00001];
656                         } else {
657                                 [self performSelector:@selector(_editListLayoutWithName:) withObject:newName afterDelay:0.00001];
658                         }
659                 break;
660                         
661                 case ESPresetNameSheetCancelReturn:
662                         //Do nothing
663                 break;
664         }
666 - (void)_editListThemeWithName:(NSString *)name{
667         [AIListThemeWindowController editListThemeWithName:name
668                                                                                           onWindow:[[self view] window]
669                                                                            notifyingTarget:self];
671 - (void)_editListLayoutWithName:(NSString *)name{
672         [AIListLayoutWindowController editListLayoutWithName:name
673                                                                                                 onWindow:[[self view] window]
674                                                                                  notifyingTarget:self];
678  * 
679  */
680 - (NSArray *)renamePreset:(NSDictionary *)preset toName:(NSString *)newName inPresets:(NSArray *)presets renamedPreset:(id *)renamedPreset
682         NSArray         *newPresets;
683         
684         if (presets == _listLayouts) {
685                 [plugin renameSetWithName:[preset objectForKey:@"name"]
686                                                 extension:LIST_LAYOUT_EXTENSION
687                                                  inFolder:LIST_LAYOUT_FOLDER
688                                                    toName:newName];             
689                 _listLayouts = [plugin availableLayoutSets];
690                 newPresets = _listLayouts;
691                 
692         } else if (presets == _listThemes) {
693                 [plugin renameSetWithName:[preset objectForKey:@"name"]
694                                                 extension:LIST_THEME_EXTENSION
695                                                  inFolder:LIST_THEME_FOLDER
696                                                    toName:newName];             
697                 _listThemes = [plugin availableThemeSets];
698                 newPresets = _listThemes;
699                 
700         } else {
701                 newPresets = nil;
702         }
703         
704         //Return the new duplicate by reference for the preset controller
705         if (renamedPreset) {
706                 NSEnumerator    *enumerator = [newPresets objectEnumerator];
707                 NSDictionary    *aPreset;
708                 
709                 while ((aPreset = [enumerator nextObject])) {
710                         if ([newName isEqualToString:[aPreset objectForKey:@"name"]]) {
711                                 *renamedPreset = aPreset;
712                                 break;
713                         }
714                 }
715         }
716         
717         return newPresets;
721  * 
722  */
723 - (NSArray *)duplicatePreset:(NSDictionary *)preset inPresets:(NSArray *)presets createdDuplicate:(id *)duplicatePreset
725         NSString        *newName = [NSString stringWithFormat:@"%@ (%@)", [preset objectForKey:@"name"], AILocalizedString(@"Copy",nil)];
726         NSArray         *newPresets = nil;
727         
728         if (presets == _listLayouts) {
729                 [plugin duplicateSetWithName:[preset objectForKey:@"name"]
730                                                    extension:LIST_LAYOUT_EXTENSION
731                                                         inFolder:LIST_LAYOUT_FOLDER
732                                                          newName:newName];              
733                 _listLayouts = [plugin availableLayoutSets];
734                 newPresets = _listLayouts;
735                 
736         } else if (presets == _listThemes) {
737                 [plugin duplicateSetWithName:[preset objectForKey:@"name"]
738                                                    extension:LIST_THEME_EXTENSION
739                                                         inFolder:LIST_THEME_FOLDER
740                                                          newName:newName];
741                 _listThemes = [plugin availableThemeSets];
742                 newPresets = _listThemes;
743         }
744         
745         //Return the new duplicate by reference for the preset controller
746         if (duplicatePreset) {
747                 NSEnumerator    *enumerator = [newPresets objectEnumerator];
748                 NSDictionary    *aPreset;
749                 
750                 while ((aPreset = [enumerator nextObject])) {
751                         if ([newName isEqualToString:[aPreset objectForKey:@"name"]]) {
752                                 *duplicatePreset = aPreset;
753                                 break;
754                         }
755                 }
756         }
758         return newPresets;
762  * 
763  */
764 - (NSArray *)deletePreset:(NSDictionary *)preset inPresets:(NSArray *)presets
766         if (presets == _listLayouts) {
767                 [plugin deleteSetWithName:[preset objectForKey:@"name"]
768                                                 extension:LIST_LAYOUT_EXTENSION
769                                                  inFolder:LIST_LAYOUT_FOLDER];          
770                 _listLayouts = [plugin availableLayoutSets];
771                 
772                 return _listLayouts;
773                 
774         } else if (presets == _listThemes) {
775                 [plugin deleteSetWithName:[preset objectForKey:@"name"]
776                                                 extension:LIST_THEME_EXTENSION
777                                                  inFolder:LIST_THEME_FOLDER];           
778                 _listThemes = [plugin availableThemeSets];
779                 
780                 return _listThemes;
781                 
782         } else {
783                 return nil;
784         }
789  */
790 - (NSMenu *)_listLayoutMenu
792         NSMenu                  *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
793         NSEnumerator    *enumerator = [[plugin availableLayoutSets] objectEnumerator];
794         NSDictionary    *set;
795         NSMenuItem              *menuItem;
796         NSString                *name;
797         
798         //Available Layouts
799         while ((set = [enumerator nextObject])) {
800                 name = [set objectForKey:@"name"];
801                 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
802                                                                                                                                                  target:nil
803                                                                                                                                                  action:nil
804                                                                                                                                   keyEquivalent:@""] autorelease];
805                 [menuItem setRepresentedObject:name];
806                 [menu addItem:menuItem];
807         }
808         
809         //Divider
810         [menu addItem:[NSMenuItem separatorItem]];
812         //Preset management     
813         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Add New Layout",nil) stringByAppendingEllipsis]
814                                                                                                                                          target:self
815                                                                                                                                          action:@selector(createListLayout:)
816                                                                                                                           keyEquivalent:@""] autorelease];
817         [menu addItem:menuItem];
818         
819         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Edit Layouts",nil) stringByAppendingEllipsis]
820                                                                                                                                          target:self
821                                                                                                                                          action:@selector(manageListLayouts:)
822                                                                                                                           keyEquivalent:@""] autorelease];
823         [menu addItem:menuItem];
824         
825         return menu;    
830  */
831 - (NSMenu *)_colorThemeMenu
833         NSMenu                  *menu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
834         NSEnumerator    *enumerator = [[plugin availableThemeSets] objectEnumerator];
835         NSDictionary    *set;
836         NSMenuItem              *menuItem;
837         NSString                *name;
838         
839         //Available themes
840         while ((set = [enumerator nextObject])) {
841                 name = [set objectForKey:@"name"];
842                 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
843                                                                                                                                                  target:nil
844                                                                                                                                                  action:nil
845                                                                                                                                   keyEquivalent:@""] autorelease];
846                 [menuItem setRepresentedObject:name];
847                 [menu addItem:menuItem];
848         }
850         //Divider
851         [menu addItem:[NSMenuItem separatorItem]];
852         
853         //Preset management     
854         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Add New Theme",nil) stringByAppendingEllipsis]
855                                                                                                                                          target:self
856                                                                                                                                          action:@selector(createListTheme:)
857                                                                                                                           keyEquivalent:@""] autorelease];
858         [menu addItem:menuItem];
859         
860         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Edit Themes",nil) stringByAppendingEllipsis]
861                                                                                                                                          target:self
862                                                                                                                                          action:@selector(manageListThemes:)
863                                                                                                                           keyEquivalent:@""] autorelease];
864         [menu addItem:menuItem];
865         
866         return menu;    
870 //Dock icons -----------------------------------------------------------------------------------------------------------
871 #pragma mark Dock icons
874  */
875 - (IBAction)showAllDockIcons:(id)sender
877         [AIDockIconSelectionSheet showDockIconSelectorOnWindow:[[self view] window]];
881  * @brief Return the menu item for a dock icon
882  */
883 - (NSMenuItem *)meuItemForDockIconPackAtPath:(NSString *)packPath
885         NSMenuItem      *menuItem;
886         NSString        *name = nil;
887         NSString        *packName = [[packPath lastPathComponent] stringByDeletingPathExtension];
888         AIIconState     *preview = nil;
889         
890         [[adium dockController] getName:&name
891                                            previewState:&preview
892                                   forIconPackAtPath:packPath];
893         
894         if (!name) {
895                 name = packName;
896         }
897         
898         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
899                                                                                                                                          target:nil
900                                                                                                                                          action:nil
901                                                                                                                           keyEquivalent:@""] autorelease];
902         [menuItem setRepresentedObject:packName];
903         [menuItem setImage:[[preview image] imageByScalingForMenuItem]];
904         
905         return menuItem;
909  * @brief Returns an array of menu items of all dock icon packs
910  */
911 - (NSArray *)_dockIconMenuArray
913         NSMutableArray          *menuItemArray = [NSMutableArray array];
914         NSEnumerator            *enumerator;
915         NSString                        *packPath;
917         enumerator = [[[adium dockController] availableDockIconPacks] objectEnumerator];
918         while ((packPath = [enumerator nextObject])) {
919                 [menuItemArray addObject:[self meuItemForDockIconPackAtPath:packPath]];
920         }
922         [menuItemArray sortUsingSelector:@selector(titleCompare:)];
924         return menuItemArray;
928  * @brief Configure the dock icon meu initially or after the xtras change
930  * Initially, the dock icon menu just has the currently selected icon; the others will be generated lazily if the icon is displayed, in menuNeedsUpdate:
931  */
932 - (void)configureDockIconMenu
934         NSMenu          *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
935         NSString        *iconPath;
936         NSString        *activePackName = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_DOCK_ICON
937                                                                                                                                                    group:PREF_GROUP_APPEARANCE];
938         iconPath = [adium pathOfPackWithName:activePackName
939                                                            extension:@"AdiumIcon"
940                                           resourceFolderName:FOLDER_DOCK_ICONS];
941         
942         [tempMenu addItem:[self meuItemForDockIconPackAtPath:iconPath]];
943         [tempMenu setDelegate:self];
944         [tempMenu setTitle:@"Temporary Dock Icon Menu"];
946         [popUp_dockIcon setMenu:tempMenu];
947         [popUp_dockIcon selectItemWithRepresentedObject:activePackName];
950 //Status and Service icons ---------------------------------------------------------------------------------------------
951 #pragma mark Status and service icons
952 - (NSMenuItem *)meuItemForIconPackAtPath:(NSString *)packPath class:(Class)iconClass
954         NSString        *name = [[packPath lastPathComponent] stringByDeletingPathExtension];
955         NSMenuItem      *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
956                                                                                                                                                                   target:nil
957                                                                                                                                                                   action:nil
958                                                                                                                                                    keyEquivalent:@""] autorelease];
959         [menuItem setRepresentedObject:name];
960         [menuItem setImage:[iconClass previewMenuImageForIconPackAtPath:packPath]];     
962         return menuItem;
966  * @brief Builds and returns an icon pack menu
968  * @param packs NSArray of icon pack file paths
969  * @param iconClass The controller class (AIStatusIcons, AIServiceIcons) for icon pack previews
970  */
971 - (NSArray *)_iconPackMenuArrayForPacks:(NSArray *)packs class:(Class)iconClass
973         NSMutableArray  *menuItemArray = [NSMutableArray array];
974         NSEnumerator    *enumerator = [packs objectEnumerator];
975         NSString                *packPath;
977         while ((packPath = [enumerator nextObject])) {
978                 [menuItemArray addObject:[self meuItemForIconPackAtPath:packPath class:iconClass]];
979         }
980         
981         [menuItemArray sortUsingSelector:@selector(titleCompare:)];
983         return menuItemArray;   
986 - (void)configureStatusIconsMenu
988         NSMenu          *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
989         NSString        *iconPath;
990         NSString        *activePackName = [[adium preferenceController] preferenceForKey:KEY_STATUS_ICON_PACK
991                                                                                                                                                    group:PREF_GROUP_APPEARANCE];
992         iconPath = [adium pathOfPackWithName:activePackName
993                                                            extension:@"AdiumStatusIcons"
994                                           resourceFolderName:@"Status Icons"];
995         if (!iconPath) {
996                 activePackName = [[adium preferenceController] defaultPreferenceForKey:KEY_STATUS_ICON_PACK
997                                                                                                                                                  group:PREF_GROUP_APPEARANCE
998                                                                                                                                                 object:nil];
999                 
1000                 iconPath = [adium pathOfPackWithName:activePackName
1001                                                                    extension:@"AdiumStatusIcons"
1002                                                   resourceFolderName:@"Status Icons"];          
1003         }
1004         [tempMenu addItem:[self meuItemForIconPackAtPath:iconPath class:[AIStatusIcons class]]];
1005         [tempMenu setDelegate:self];
1006         [tempMenu setTitle:@"Temporary Status Icons Menu"];
1007         
1008         [popUp_statusIcons setMenu:tempMenu];
1009         [popUp_statusIcons selectItemWithRepresentedObject:activePackName];
1012 - (void)configureServiceIconsMenu
1014         NSMenu          *tempMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
1015         NSString        *iconPath;
1016         NSString        *activePackName = [[adium preferenceController] preferenceForKey:KEY_SERVICE_ICON_PACK
1017                                                                                                                                                    group:PREF_GROUP_APPEARANCE];
1018         iconPath = [adium pathOfPackWithName:activePackName
1019                                                            extension:@"AdiumServiceIcons"
1020                                           resourceFolderName:@"Service Icons"];
1021         
1022         if (!iconPath) {
1023                 activePackName = [[adium preferenceController] defaultPreferenceForKey:KEY_SERVICE_ICON_PACK
1024                                                                                                                                                  group:PREF_GROUP_APPEARANCE
1025                                                                                                                                                 object:nil];
1026                 
1027                 iconPath = [adium pathOfPackWithName:activePackName
1028                                                                    extension:@"AdiumServiceIcons"
1029                                                   resourceFolderName:@"Service Icons"];         
1030         }
1031         [tempMenu addItem:[self meuItemForIconPackAtPath:iconPath class:[AIServiceIcons class]]];
1032         [tempMenu setDelegate:self];
1033         [tempMenu setTitle:@"Temporary Service Icons Menu"];
1034         
1035         [popUp_serviceIcons setMenu:tempMenu];
1036         [popUp_serviceIcons selectItemWithRepresentedObject:activePackName];
1039 #pragma mark Menu delegate
1040 - (void)menuNeedsUpdate:(NSMenu *)menu
1042         NSString                *title =[menu title];
1043         NSString                *repObject = nil;
1044         NSArray                 *menuItemArray = nil;
1045         NSPopUpButton   *popUpButton;
1046         
1047         if ([title isEqualToString:@"Temporary Dock Icon Menu"]) {
1048                 //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
1049                 menuItemArray = [self _dockIconMenuArray];
1050                 repObject = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_DOCK_ICON
1051                                                                                                                          group:PREF_GROUP_APPEARANCE];
1052                 popUpButton = popUp_dockIcon;
1053                 
1054         } else if ([title isEqualToString:@"Temporary Status Icons Menu"]) {            
1055                 menuItemArray = [self _iconPackMenuArrayForPacks:[adium allResourcesForName:@"Status Icons" 
1056                                                                                                                                          withExtensions:@"AdiumStatusIcons"] 
1057                                                                                                    class:[AIStatusIcons class]];
1058                 repObject = [[adium preferenceController] preferenceForKey:KEY_STATUS_ICON_PACK
1059                                                                                                                          group:PREF_GROUP_APPEARANCE];
1060                 popUpButton = popUp_statusIcons;
1061                 
1062         } else if ([title isEqualToString:@"Temporary Service Icons Menu"]) {           
1063                 menuItemArray = [self _iconPackMenuArrayForPacks:[adium allResourcesForName:@"Service Icons" 
1064                                                                                                                                          withExtensions:@"AdiumServiceIcons"] 
1065                                                                                                    class:[AIServiceIcons class]];
1066                 repObject = [[adium preferenceController] preferenceForKey:KEY_SERVICE_ICON_PACK
1067                                                                                                                          group:PREF_GROUP_APPEARANCE];
1068                 popUpButton = popUp_serviceIcons;
1069                 
1070         }
1071         
1072         if (menuItemArray) {
1073                 NSEnumerator    *enumerator;
1074                 NSMenuItem              *menuItem;
1075                 
1076                 //Remove existing items
1077                 [menu removeAllItems];
1078                 
1079                 //Clear the title so we know we don't need to do this again
1080                 [menu setTitle:@""];
1081                 
1082                 //Add the items
1083                 enumerator = [menuItemArray objectEnumerator];
1084                 while ((menuItem = [enumerator nextObject])) {
1085                         [menu addItem:menuItem];
1086                 }
1087                 
1088                 //Clear the title so we know we don't need to do this again
1089                 [menu setTitle:@""];
1090                 
1091                 //Put a checkmark by the appropriate menu item
1092                 [popUpButton selectItemWithRepresentedObject:repObject];
1093         }       
1096 @end