Index: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServicePrefController.m
===================================================================
--- trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServicePrefController.m	(revision 182)
+++ trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServicePrefController.m	(revision 319)
@@ -12,4 +12,5 @@
 #import "ICeCoffEENonHighlightingButtonCell.h"
 #import "ICeCoffEENonHighlightingTextFieldCell.h"
+#import "ICeCoffEELabeledIconCell.h"
 #import <objc/objc.h>
 #import <ApplicationEnhancer/ApplicationEnhancer.h>
@@ -22,5 +23,51 @@
 }
 
-static inline NSCellStateValue ICCF_ServiceItemState(id <NSMenuItem> item) {
+static NSMutableDictionary *keyEquivalents;
+
+static void ICCF_RemoveSingleKeyEquivalents() {
+    NSMutableArray *singleKeys = [[NSMutableArray alloc] init];
+    NSEnumerator *e = [[keyEquivalents allKeys] objectEnumerator];
+    NSString *keyEquivalent;
+    ICLog(@"before ICCF_RemoveSingleKeyEquivalents: %@", keyEquivalents);
+    while ( (keyEquivalent = [e nextObject]) != nil) {
+	if ([[keyEquivalents objectForKey: keyEquivalent] count] == 1)
+	    [singleKeys addObject: keyEquivalent];
+    }
+    [keyEquivalents removeObjectsForKeys: singleKeys];
+    [singleKeys release];
+    ICLog(@"after ICCF_RemoveSingleKeyEquivalents: %@", keyEquivalents);
+}
+
+static inline unsigned ICCF_CountForKeyEquivalent(NSString *keyEquivalent) {
+    if (keyEquivalent == nil) return 0;
+    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
+    return (setOrNil == nil) ? nil : [setOrNil count];
+}
+
+static inline void ICCF_AddKeyEquivalentForItem(NSMenuItem *item) {
+    NSString *keyEquivalent = [item toolTip];
+    if (keyEquivalent == nil) return;
+    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
+    if (setOrNil == nil) return;
+    [setOrNil addObject: item];
+}
+
+static inline void ICCF_RemoveKeyEquivalentForItem(NSMenuItem *item) {
+    NSString *keyEquivalent = [item toolTip];
+    if (keyEquivalent == nil) return;
+    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
+    if (setOrNil == nil) return;
+    [setOrNil removeObject: item];
+}
+
+static inline void ICCF_UpdateKeyEquivalentForItem(NSMenuItem *item, id state) {
+    NSNumber *oldState = [item representedObject];
+    if ((oldState == nil || oldState == ICCF_SERVICE_SHOWN) && state == ICCF_SERVICE_HIDDEN)
+	ICCF_RemoveKeyEquivalentForItem(item);
+    else if (oldState == ICCF_SERVICE_HIDDEN && (state == ICCF_SERVICE_SHOWN || state == nil))
+	ICCF_AddKeyEquivalentForItem(item);
+}
+
+static inline NSCellStateValue ICCF_ServiceItemState(NSMenuItem *item) {
     id state = [item representedObject];
     return (state == nil) ? NSOnState : [state intValue];
@@ -34,13 +81,14 @@
     while ( (item = [e nextObject]) != nil) {
         submenu = [item submenu];
-        if (submenu != nil) {
-            ICCF_PropagateServiceStateChange(submenu, state);
-            [item setRepresentedObject: (state == nil) ? ICCF_SERVICE_SHOWN : state];
-        }
-        [item setRepresentedObject: state];
-    }
-}
-
-static NSCellStateValue ICCF_PropagateServiceState(id <NSMenuItem> item, NSMenuItem *changedItem) {
+        if (submenu != nil)
+	    ICCF_PropagateServiceStateChange(submenu, state);
+	else
+	    ICCF_UpdateKeyEquivalentForItem(item, state);
+	
+	[item setRepresentedObject: state];
+    }
+}
+
+static NSCellStateValue ICCF_PropagateServiceState(NSMenuItem *item, NSMenuItem *changedItem) {
     NSMenu *submenu = [item submenu];
     if (submenu == nil) return ICCF_ServiceItemState(item);
@@ -102,14 +150,15 @@
     NSString *itemTitle;
     NSDictionary *subDict, *submenuDict;
-    id <NSMenuItem> item;
+    NSMenuItem *item;
     NSMenu *submenu;
 
     // XXX handle exceptions
     while ( (itemTitle = [e nextObject]) != nil) {
-        item = [menu itemWithTitle: itemTitle];
+        item = (NSMenuItem *)[menu itemWithTitle: itemTitle];
         if (item == nil) continue;
         subDict = [dict objectForKey: itemTitle];
         if ([[subDict objectForKey: (NSString *)kICServiceHidden] boolValue]) {
             [item setRepresentedObject: ICCF_SERVICE_HIDDEN];
+	    ICCF_RemoveKeyEquivalentForItem(item);
         }
         if ( (submenu = [item submenu]) != nil) {
@@ -123,4 +172,40 @@
 }
 
+static void ICCF_AddServiceKeyEquivalentsAndIcons(NSMenu *menu, NSDictionary *serviceInfo) {
+    if (serviceInfo == nil) return;
+    NSEnumerator *enumerator = [[menu itemArray] objectEnumerator];
+    NSMenuItem *menuItem;
+    NSMenu *submenu;
+    NSDictionary *itemInfo = nil;
+    while ( (menuItem = [enumerator nextObject]) != nil) {
+        itemInfo = [serviceInfo objectForKey: [menuItem title]];
+	if (itemInfo == nil) continue;
+	
+	if ( (submenu = [menuItem submenu]) != nil) {
+	    ICCF_AddServiceKeyEquivalentsAndIcons(submenu, itemInfo);
+	} else {
+	    NSString *keyEquivalent = (NSString *)[itemInfo objectForKey: (NSString *)kICServiceShortcut];
+	    if (keyEquivalent != nil) {
+		[menuItem setToolTip: keyEquivalent];
+		NSMutableSet *equivalentItems = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
+		if (equivalentItems == nil) {
+		    equivalentItems = [[NSMutableSet alloc] initWithObjects: menuItem, nil];
+		    [keyEquivalents setObject: equivalentItems forKey: keyEquivalent];
+		    [equivalentItems release];
+		} else {
+		    [equivalentItems addObject: menuItem];
+		}
+	    }
+	}
+
+        NSString *bundlePath = (NSString *)[itemInfo objectForKey: (NSString *)kICServiceBundlePath];
+        if (bundlePath == NULL) continue;
+        IconRef serviceIcon = ICCF_CopyIconRefForPath(bundlePath);
+        if (serviceIcon == NULL) continue;
+        [menuItem _setIconRef: serviceIcon];
+        ReleaseIconRef(serviceIcon);
+    }
+}
+
 @implementation ICeCoffEEServicePrefController
 
@@ -141,4 +226,6 @@
     if ( (self = [self initWithWindowNibName: @"Select services"])) {
         NSWindow *window = [self window]; // connect outlets
+	[serviceOutline setAutoresizesOutlineColumn: NO];
+	
         NSButtonCell *checkBoxCell = [[ICeCoffEENonHighlightingButtonCell alloc] init];
         [checkBoxCell setButtonType: NSSwitchButton];
@@ -149,5 +236,7 @@
 
         NSTextFieldCell *textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"service"] dataCell];
-        ((struct objc_object *)textFieldCell)->isa = [ICeCoffEENonHighlightingTextFieldCell class];
+	[textFieldCell setWraps: YES];
+	[[serviceOutline tableColumnWithIdentifier: @"service"] setDataCell:
+	    [ICeCoffEELabeledIconCell copyFromTextFieldCell: textFieldCell]];
 
         textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"key"] dataCell];
@@ -167,4 +256,5 @@
 - (void)dealloc;
 {
+    [keyEquivalents release];
     [servicesMenu release];
     [super dealloc];
@@ -223,7 +313,10 @@
 {
     if (servicesMenu == nil) {
+	keyEquivalents = [[NSMutableDictionary alloc] init];
 	servicesMenu = [[NSMenu alloc] initWithTitle: @""];
 	ICCF_SetServicesMenu(servicesMenu);
-        [servicesMenu update]; // XXX necessary on 10.3? or anywhere?
+	ICCF_AddServiceKeyEquivalentsAndIcons(servicesMenu, ICCF_GetServicesInfo());
+	ICCF_RemoveSingleKeyEquivalents();
+	
         NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
         NSDictionary *icDefaults = [defaults persistentDomainForName: (NSString *)kICBundleIdentifier];
@@ -244,10 +337,17 @@
 }
 
-NSAttributedString *ICCF_KeyEquivalentAttributedStringWithModifierFlags(NSString *self, unsigned int modifierFlags);
+NSAttributedString *ICCF_KeyEquivalentAttributedStringWithModifierFlags(NSString *self, unsigned int modifierFlags, unsigned count);
 
 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
 {
     if ([[tableColumn identifier] isEqualToString: @"service"]) {
-        return [item title];
+	static NSDictionary *attrDict = nil;
+	if (attrDict == nil) { // XXX we leak this, but so does the Apple sample code...
+	    NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
+	    [style setLineBreakMode: NSLineBreakByTruncatingMiddle];
+	    attrDict = [[NSDictionary alloc] initWithObjectsAndKeys: style, NSParagraphStyleAttributeName, nil];
+	    [style release];
+	}
+        return [[[NSAttributedString alloc] initWithString: [item title] attributes: attrDict] autorelease];
     } else if ([[tableColumn identifier] isEqualToString: @"show"]) {
         id state = [item representedObject];
@@ -257,9 +357,8 @@
         return (state == nil) ? ICCF_SERVICE_SHOWN : state;
     } else if ([[tableColumn identifier] isEqualToString: @"key"]) {
-        [item setKeyEquivalent: @"#"];
-        NSString *equivalent = [item keyEquivalent];
+        NSString *equivalent = [item toolTip];
         if (equivalent == nil || [equivalent length] != 1) return nil;
         // XXX Inconsistency between Cocoa and Carbon: always command-shift in Carbon, not in Cocoa.  Since we only patch Cocoa for the moment, keep as is.
-        return ICCF_KeyEquivalentAttributedStringWithModifierFlags(equivalent, [item keyEquivalentModifierMask] | ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: [equivalent characterAtIndex: 0]] ? NSShiftKeyMask : 0));
+        return ICCF_KeyEquivalentAttributedStringWithModifierFlags(equivalent, [item keyEquivalentModifierMask] | ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: [equivalent characterAtIndex: 0]] ? NSShiftKeyMask : 0), ICCF_CountForKeyEquivalent(equivalent));
     }
     return nil;
@@ -269,5 +368,7 @@
 {
     if ([[tableColumn identifier] isEqualToString: @"show"]) {
-        [item setRepresentedObject: [object boolValue] ? nil : ICCF_SERVICE_HIDDEN];
+	NSNumber *newState = [object boolValue] ? nil : ICCF_SERVICE_HIDDEN;
+	ICCF_UpdateKeyEquivalentForItem(item, newState);
+        [item setRepresentedObject: newState];
         
         NSMenu *submenu = [item menu];
@@ -279,8 +380,19 @@
                 submenu = supermenu;
             }
-            ICCF_PropagateServiceState([supermenu itemAtIndex: [supermenu indexOfItemWithSubmenu: submenu]], item);
+            ICCF_PropagateServiceState((NSMenuItem *)[supermenu itemAtIndex: [supermenu indexOfItemWithSubmenu: submenu]], item);
         }
         [outlineView reloadData];
     }
+}
+
+@end
+
+@implementation ICeCoffEEServicePrefController (NSOutlineViewDelegate)
+
+- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
+{
+    if (![[tableColumn identifier] isEqualToString: @"service"])
+	return;
+    [(ICeCoffEELabeledIconCell *)cell setIconRef: [item _iconRef]];
 }
 
