Updating svn:mergeinfo
[adiumx.git] / Source / AIMenuController.m
blob8000f7d83baa10ebb5cce5df35077ad22bf7f7ec
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 // $Id$
19 #import "AIMenuController.h"
20 #import <Adium/AIAccount.h>
21 #import <Adium/AIChat.h>
22 #import <Adium/AIListContact.h>
23 #import <Adium/AIListObject.h>
24 #import <Adium/AIMetaContact.h>
25 #import <AIUtilities/AIMenuAdditions.h>
26 #import <AIUtilities/AIStringAdditions.h>
28 @interface AIMenuController (PRIVATE)
29 - (void)localizeMenuTitles;
30 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray usingMenu:(NSMenu *)inMenu;
31 - (void)addMenuItemsForContact:(AIListContact *)inContact toMenu:(NSMenu *)workingMenu separatorItem:(BOOL *)separatorItem;
32 @end
34 @implementation AIMenuController
36 - (id)init
38         if ((self = [super init])) {
39                 //Set up our contextual menu stuff
40                 contextualMenu = [[NSMenu alloc] init];
41                 contextualMenuItemDict = [[NSMutableDictionary alloc] init];
42                 currentContextMenuObject = nil;
43                 textViewContextualMenu = [[NSMenu alloc] init];
44                 contextualMenu_TextView = nil;
45         }
46         
47         return self;
50 - (void)awakeFromNib
52         //Build the array of menu locations
53         locationArray = [[NSMutableArray alloc] initWithObjects:menu_Adium_About, menu_Adium_Preferences, menu_Adium_Other,
54                 menu_File_New, menu_File_Close, menu_File_Save, menu_File_Accounts, menu_File_Additions,        
55                 menu_Edit_Bottom, menu_Edit_Links, menu_Edit_Additions,
56                 menu_View_General, menu_View_Sorting, menu_View_Toggles, menu_View_Appearance_Toggles, menu_View_Additions, 
57                 menu_Contact_Manage, menu_Contact_Info, menu_Contact_Action, menu_Contact_NegativeAction, menu_Contact_Additions,
58                 menu_Status_State, menu_Status_Accounts, menu_Status_Additions,
59                 menu_Format_Styles, menu_Format_Palettes, menu_Format_Additions,
60                 menu_Window_Top, menu_Window_Commands, menu_Window_Auxiliary, menu_Window_Fixed,
61                 menu_Help_Local, menu_Help_Web, menu_Help_Additions,
62                 menu_Dock_Status, nil];
65 - (void)controllerDidLoad
66 {       
67         [self localizeMenuTitles];      
70 //Close
71 - (void)controllerWillClose
73         //There's no need to remove the menu items, the system will take them out for us.
76 //Add a menu item
77 - (void)addMenuItem:(NSMenuItem *)newItem toLocation:(AIMenuLocation)location
79         NSMenuItem  *menuItem;
80         NSMenu          *targetMenu = nil;
81         int                     targetIndex;
82         int                     destination;
84         //Find the menu item (or the closest one above it)
85         destination = location;
86         menuItem = [locationArray objectAtIndex:destination];
87         while ((menuItem == nilMenuItem) && (destination > 0)) {
88                 destination--;
89                 menuItem = [locationArray objectAtIndex:destination];
90         }
92         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
93                 //If attached to a menu item, insert below that item
94                 targetMenu = [menuItem menu];
95                 targetIndex = [targetMenu indexOfItem:menuItem];
96         } else {
97                 //If it's attached to an NSMenu (and not an NSMenuItem), insert at the top of the menu
98                 targetMenu = (NSMenu *)menuItem;
99                 targetIndex = -1;
100         }
102         //Insert the new item and a divider (if necessary)
103         if (location != destination) {
104                 [targetMenu insertItem:[NSMenuItem separatorItem] atIndex:++targetIndex];
105         }
107         [targetMenu insertItem:newItem atIndex:targetIndex+1];
109         //update the location array
110         [locationArray replaceObjectAtIndex:location withObject:newItem];
112         [[adium notificationCenter] postNotificationName:AIMenuDidChnge object:[newItem menu] userInfo:nil];
115 //Remove a menu item
116 - (void)removeMenuItem:(NSMenuItem *)targetItem
118         NSMenu          *targetMenu = [targetItem menu];
119         int                     targetIndex = [targetMenu indexOfItem:targetItem];
120         unsigned        loop, maxLoop;
122         if (!targetMenu) {
123                 NSLog(@"Warning: Attempting to remove %@ (%@, target %@), but it has no menu",targetItem, [targetItem title], [targetItem target]);
124         }
125         
126         //Fix the pointer if this is one
127         for (loop = 0, maxLoop = [locationArray count]; loop < maxLoop; loop++) {
128                 NSMenuItem      *menuItem = [locationArray objectAtIndex:loop];
130                 //Move to the item above it, nil if a divider
131                 if (menuItem == targetItem) {
132                         if (targetIndex != 0) {
133                                 NSMenuItem      *previousItem = [targetMenu itemAtIndex:(targetIndex - 1)];
135                                 if ([previousItem isSeparatorItem]) {
136                                         [locationArray replaceObjectAtIndex:loop withObject:nilMenuItem];
137                                 } else {
138                                         [locationArray replaceObjectAtIndex:loop withObject:previousItem];
139                                 }
140                         } else {
141                                 //If there are no more items, attach to the menu
142                                 [locationArray replaceObjectAtIndex:loop withObject:targetMenu];
143                         }
144                 }
145         }
147         //Remove the item
148         [targetMenu removeItem:targetItem];
150         if (!menuItemProcessingDelays) {
151                 //Remove any double dividers by removing the upper divier. Also, remove dividers at the top or bottom of the menu
152                 for (loop = 0; loop < [targetMenu numberOfItems]; loop++) {
153                         if (([[targetMenu itemAtIndex:loop] isSeparatorItem]) && 
154                                 ((loop == [targetMenu numberOfItems] - 1) || (loop == 0) || ([[targetMenu itemAtIndex:loop-1] isSeparatorItem]))) {
155                                 [targetMenu removeItemAtIndex:loop];
156                                 loop--;//re-search the location
157                         }
158                 }
159                 
160                 /* XXX Note that this notification isn't being posted if triggerred while in menuItemProcessingDelays.
161                  * It's not currently needed in that situation so this is a very small performance hack... it could move outside the
162                  * conditional if necessary. -evands
163                  */
164                 [[adium notificationCenter] postNotificationName:AIMenuDidChnge object:targetMenu userInfo:nil];
165         }
168 - (void)delayMenuItemPostProcessing
170         menuItemProcessingDelays++;
173 - (void)endDelayMenuItemPostProcessing
175         menuItemProcessingDelays--;     
178 - (void)addContextualMenuItem:(NSMenuItem *)newItem toLocation:(AIContextMenuLocation)location
180         NSNumber                        *key;
181         NSMutableArray          *itemArray;
183         //Search for an existing item array for menu items in this location
184         key = [NSNumber numberWithInt:location];
185         itemArray = [contextualMenuItemDict objectForKey:key];
187         //If one is not found, create it
188         if (!itemArray) {
189                 itemArray = [[NSMutableArray alloc] init];
190                 [contextualMenuItemDict setObject:itemArray forKey:key];
191         }
193         //Add the passed menu item to the array
194         [itemArray addObject:newItem];
197 //Pass an array of NSNumbers corresponding to the desired contextual menu locations
198 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray forListObject:(AIListObject *)inObject
200         NSMenu          *workingMenu;
201         BOOL            separatorItem;
203         //Remember what our menu is configured for
204         [currentContextMenuObject release];
205         currentContextMenuObject = [inObject retain];
207         //Get the pre-created contextual menu items
208         workingMenu = [self contextualMenuWithLocations:inLocationArray usingMenu:contextualMenu];
210         //Add any account-specific menu items
211         separatorItem = YES;
212         if ([inObject isKindOfClass:[AIMetaContact class]]) {
213                 NSEnumerator    *enumerator;
214                 AIListContact   *aListContact;
215                 enumerator = [[(AIMetaContact *)inObject listContacts] objectEnumerator];
217                 while ((aListContact = [enumerator nextObject])) {
218                         [self addMenuItemsForContact:aListContact
219                                                                   toMenu:workingMenu
220                                                    separatorItem:&separatorItem];
221                 }
223         } else  if ([inObject isKindOfClass:[AIListContact class]]) {
224                 [self addMenuItemsForContact:(AIListContact *)inObject
225                                                           toMenu:workingMenu
226                                            separatorItem:&separatorItem];
227         }
229         return workingMenu;
232 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray forListObject:(AIListObject *)inObject inChat:(AIChat *)inChat
234         [currentContextMenuChat release];
235         currentContextMenuChat = [inChat retain];
236         
237         return [self contextualMenuWithLocations:inLocationArray forListObject:inObject];
240 //Add menuItems for a passed contact to a specified menu.  *seperatorItem can be YES to indicate that a 
241 //separator item should be inserted before the menu items if desired. It will then be set to NO.
242 - (void)addMenuItemsForContact:(AIListContact *)inContact toMenu:(NSMenu *)workingMenu separatorItem:(BOOL *)separatorItem
244         NSArray                 *itemArray = [[inContact account] menuItemsForContact:inContact];
246         if (itemArray && [itemArray count]) {
247                 NSEnumerator    *enumerator;
248                 NSMenuItem              *menuItem;
250                 if (*separatorItem == YES) {
251                         [workingMenu addItem:[NSMenuItem separatorItem]];
252                         *separatorItem = NO;
253                 }
255                 enumerator = [itemArray objectEnumerator];
256                 while ((menuItem = [enumerator nextObject])) {
257                         [workingMenu addItem:menuItem];
258                 }
259         }
262 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray forTextView:(NSTextView *)inTextView
264         //remember menu config
265         [contextualMenu_TextView release];
266         contextualMenu_TextView = [inTextView retain];
268         return [self contextualMenuWithLocations:inLocationArray usingMenu:textViewContextualMenu];
271 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray usingMenu:(NSMenu *)inMenu
273         NSEnumerator    *enumerator;
274         NSNumber                *location;
275         NSMenuItem              *menuItem;
276         BOOL                    itemsAbove = NO;
278         //Remove all items from the existing menu
279         [inMenu removeAllItems];
281         //Process each specified location
282         enumerator = [inLocationArray objectEnumerator];
283         while ((location = [enumerator nextObject])) {
284                 NSArray                 *menuItems = [contextualMenuItemDict objectForKey:location];
285                 NSEnumerator    *itemEnumerator;
287                 //Add a seperator
288                 if (itemsAbove && [menuItems count]) {
289                         [inMenu addItem:[NSMenuItem separatorItem]];
290                         itemsAbove = NO;
291                 }
293                 //Add each menu item in the location
294                 itemEnumerator = [menuItems objectEnumerator];
295                 while ((menuItem = [itemEnumerator nextObject])) {
296                         //Add the menu item
297                         [inMenu addItem:menuItem];
298                         itemsAbove = YES;
299                 }
300         }
302         return inMenu;
305 - (AIListObject *)currentContextMenuObject
307         return currentContextMenuObject;
310 - (AIChat *)currentContextMenuChat
312         return currentContextMenuChat;
315 - (NSTextView *)contextualMenuTextView
317         return contextualMenu_TextView;
320 - (void)removeItalicsKeyEquivalent
322         [menuItem_Format_Italics setKeyEquivalent:@""];
325 - (void)restoreItalicsKeyEquivalent
327         [menuItem_Format_Italics setKeyEquivalent:@"i"];
330 - (void)localizeMenuTitles
332         //Menu items in MainMenu.nib for localization purposes
333         [menuItem_file setTitle:AILocalizedString(@"File","Title of the File menu")];
334         [menuItem_edit setTitle:AILocalizedString(@"Edit","Title of the Edit menu")];
335         [menuItem_view setTitle:AILocalizedString(@"View","Title of the View menu")];
336         [menuItem_status setTitle:AILocalizedString(@"Status","Title of the Status menu")];
337         [menuItem_contact setTitle:AILocalizedString(@"Contact","Title of the Contact menu")];
338         [menuItem_format setTitle:AILocalizedString(@"Format","Title of the Format menu")];
339         [menuItem_window setTitle:AILocalizedString(@"Window","Title of the Window menu")];
340         [menuItem_help setTitle:AILocalizedString(@"Help","Title of the Help menu")];
342         //Adium menu
343         [menuItem_aboutAdium setTitle:AILocalizedString(@"About Adium",nil)];
344         [menuItem_adiumXtras setTitle:AILocalizedString(@"Xtras Manager",nil)];
345         [menuItem_checkForUpdates setTitle:[AILocalizedString(@"Check For Updates",nil) stringByAppendingEllipsis]];
346         [menuItem_preferences setTitle:[AILocalizedString(@"Preferences",nil) stringByAppendingEllipsis]];
347         [menuItem_donate setTitle:AILocalizedString(@"Donate",nil)];
348         [menuItem_helpOut setTitle:AILocalizedString(@"Contributing to Adium",nil)];
350         [menuItem_services setTitle:AILocalizedString(@"Services","Services menu item in the Adium menu")];
351         [menuItem_hideAdium setTitle:AILocalizedString(@"Hide Adium",nil)];
352         [menuItem_hideOthers setTitle:AILocalizedString(@"Hide Others",nil)];
353         [menuItem_showAll setTitle:AILocalizedString(@"Show All",nil)];
354         [menuItem_quitAdium setTitle:AILocalizedString(@"Quit Adium",nil)];
356         //File menu     
357         [menuItem_close setTitle:AILocalizedString(@"Close","Title for the close menu item")];
358         [menuItem_closeChat setTitle:AILocalizedString(@"Close Chat","Title for the close chat menu item")];
359         [menuItem_closeAllChats setTitle:AILocalizedString(@"Close All Chats","Title for the close all chats menu item")];
360         [menuItem_saveAs setTitle:[AILocalizedString(@"Save As",nil) stringByAppendingEllipsis]];
361         [menuItem_pageSetup setTitle:[AILocalizedString(@"Page Setup",nil) stringByAppendingEllipsis]];
362         [menuItem_print setTitle:[AILocalizedString(@"Print",nil) stringByAppendingEllipsis]];
364         //Edit menu
365         [menuItem_cut setTitle:AILocalizedString(@"Cut",nil)];
366         [menuItem_copy setTitle:AILocalizedString(@"Copy",nil)];
367         [menuItem_paste setTitle:AILocalizedString(@"Paste",nil)];
368         [menuItem_pasteWithImagesAndColors setTitle:AILocalizedString(@"Paste with Images and Colors",nil)];
369         [menuItem_pasteAndMatchStyle setTitle:AILocalizedString(@"Paste and Match Style",nil)];
370         [menuItem_clear setTitle:AILocalizedString(@"Clear",nil)];
371         [menuItem_selectAll setTitle:AILocalizedString(@"Select All",nil)];
373 #define TITLE_FIND AILocalizedString(@"Find",nil)
374         [menuItem_find setTitle:TITLE_FIND];
375         [menuItem_findCommand setTitle:[TITLE_FIND stringByAppendingEllipsis]];
376         [menuItem_findNext setTitle:AILocalizedString(@"Find Next",nil)];
377         [menuItem_findPrevious setTitle:AILocalizedString(@"Find Previous",nil)];
378         [menuItem_findUseSelectionForFind setTitle:AILocalizedString(@"Use Selection for Find",nil)];
379         [menuItem_findJumpToSelection setTitle:AILocalizedString(@"Jump to Selection",nil)];
381 #define TITLE_SPELLING AILocalizedString(@"Spelling",nil)
382         [menuItem_spelling setTitle:TITLE_SPELLING];
383         [menuItem_spellingCommand setTitle:[TITLE_SPELLING stringByAppendingEllipsis]];
384         [menuItem_spellingCheckSpelling setTitle:AILocalizedString(@"Check Spelling",nil)];
385         [menuItem_spellingCheckSpellingAsYouType setTitle:AILocalizedString(@"Check Spelling As You Type",nil)];
387         [menuItem_speech setTitle:AILocalizedString(@"Speech",nil)];
388         [menuItem_startSpeaking setTitle:AILocalizedString(@"Start Speaking",nil)];
389         [menuItem_stopSpeaking setTitle:AILocalizedString(@"Stop Speaking",nil)];
390         
391         //View menu
392         [menuItem_customizeToolbar setTitle:[AILocalizedString(@"Customize Toolbar",nil) stringByAppendingEllipsis]];
394         //Format menu
395         [menuItem_bold setTitle:AILocalizedString(@"Bold",nil)];
396         [menuItem_italic setTitle:AILocalizedString(@"Italic",nil)];
397         [menuItem_underline setTitle:AILocalizedString(@"Underline",nil)];
398         [menuItem_showFonts setTitle:AILocalizedString(@"Show Fonts",nil)];
399         [menuItem_showColors setTitle:AILocalizedString(@"Show Colors",nil)];
400         [menuItem_bigger setTitle:AILocalizedString(@"Bigger", "Menu item title for making the font size bigger")];
401         [menuItem_smaller setTitle:AILocalizedString(@"Smaller", "Menu item title for making the font size smaller")];
402         [menuItem_copyStyle setTitle:AILocalizedString(@"Copy Style",nil)];
403         [menuItem_pasteStyle setTitle:AILocalizedString(@"Paste Style",nil)];
404         [menuItem_writingDirection setTitle:AILocalizedString(@"Writing Direction",nil)];
405         [menuItem_rightToLeft setTitle:AILocalizedString(@"Right to Left", "Menu item in a submenu under 'writing direction' for writing which goes from right to left")];
406         
407         //Window menu
408         [menuItem_minimize setTitle:AILocalizedString(@"Minimize", "Minimize menu item title int he Wndow menu")];
409         [menuItem_zoom setTitle:AILocalizedString(@"Zoom", "Zoom menu item title in the Window menu")];
410         [menuItem_bringAllToFront setTitle:AILocalizedString(@"Bring All to Front",nil)];
412         //Help menu
413         [menuItem_adiumHelp setTitle:AILocalizedString(@"Adium Help",nil)];
414         [menuItem_reportABug setTitle:AILocalizedString(@"Report a Bug",nil)];
415         [menuItem_sendFeedback setTitle:AILocalizedString(@"Send Feedback",nil)];
416         [menuItem_adiumForums setTitle:AILocalizedString(@"Adium Forums",nil)];
419 @end