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.
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;
34 @implementation AIMenuController
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;
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
67 [self localizeMenuTitles];
71 - (void)controllerWillClose
73 //There's no need to remove the menu items, the system will take them out for us.
77 - (void)addMenuItem:(NSMenuItem *)newItem toLocation:(AIMenuLocation)location
80 NSMenu *targetMenu = nil;
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)) {
89 menuItem = [locationArray objectAtIndex:destination];
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];
97 //If it's attached to an NSMenu (and not an NSMenuItem), insert at the top of the menu
98 targetMenu = (NSMenu *)menuItem;
102 //Insert the new item and a divider (if necessary)
103 if (location != destination) {
104 [targetMenu insertItem:[NSMenuItem separatorItem] atIndex:++targetIndex];
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];
116 - (void)removeMenuItem:(NSMenuItem *)targetItem
118 NSMenu *targetMenu = [targetItem menu];
119 int targetIndex = [targetMenu indexOfItem:targetItem];
120 unsigned loop, maxLoop;
123 NSLog(@"Warning: Attempting to remove %@ (%@, target %@), but it has no menu",targetItem, [targetItem title], [targetItem target]);
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];
138 [locationArray replaceObjectAtIndex:loop withObject:previousItem];
141 //If there are no more items, attach to the menu
142 [locationArray replaceObjectAtIndex:loop withObject:targetMenu];
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
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
164 [[adium notificationCenter] postNotificationName:AIMenuDidChnge object:targetMenu userInfo:nil];
168 - (void)delayMenuItemPostProcessing
170 menuItemProcessingDelays++;
173 - (void)endDelayMenuItemPostProcessing
175 menuItemProcessingDelays--;
178 - (void)addContextualMenuItem:(NSMenuItem *)newItem toLocation:(AIContextMenuLocation)location
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
189 itemArray = [[NSMutableArray alloc] init];
190 [contextualMenuItemDict setObject:itemArray forKey:key];
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
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
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
220 separatorItem:&separatorItem];
223 } else if ([inObject isKindOfClass:[AIListContact class]]) {
224 [self addMenuItemsForContact:(AIListContact *)inObject
226 separatorItem:&separatorItem];
232 - (NSMenu *)contextualMenuWithLocations:(NSArray *)inLocationArray forListObject:(AIListObject *)inObject inChat:(AIChat *)inChat
234 [currentContextMenuChat release];
235 currentContextMenuChat = [inChat retain];
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]];
255 enumerator = [itemArray objectEnumerator];
256 while ((menuItem = [enumerator nextObject])) {
257 [workingMenu addItem:menuItem];
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;
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;
288 if (itemsAbove && [menuItems count]) {
289 [inMenu addItem:[NSMenuItem separatorItem]];
293 //Add each menu item in the location
294 itemEnumerator = [menuItems objectEnumerator];
295 while ((menuItem = [itemEnumerator nextObject])) {
297 [inMenu addItem:menuItem];
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")];
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)];
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]];
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)];
392 [menuItem_customizeToolbar setTitle:[AILocalizedString(@"Customize Toolbar",nil) stringByAppendingEllipsis]];
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")];
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)];
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)];