source: trunk/ICeCoffEE/ICeCoffEE/ICeCoffEEServicePrefController.m@ 167

Last change on this file since 167 was 167, checked in by Nicholas Riley, 18 years ago

ICeCoffEE 1.4 and preliminary 1.4.1 changes. Sorry, I forgot to
commit version 1.4 when it was released, so the precise source for
that release has been lost.

See the release notes for details of what changed in these versions.
1.4 was a significant feature release; 1.4.1 is a bug fix for 10.3.9,
incorporating up-to-date Unsanity Installer and APE.

package-ICeCoffEE.sh: use xcodebuild instead of pbxbuild.

File size: 11.9 KB
Line 
1//
2// ICeCoffEEServicePrefController.m
3// ICeCoffEE APE
4//
5// Created by Nicholas Riley on Fri Jun 06 2003.
6// Copyright (c) 2003 Nicholas Riley. All rights reserved.
7//
8
9#import "ICeCoffEEShared.h"
10#import "ICeCoffEEServicePrefController.h"
11#import "ICeCoffEENonHighlightingButtonCell.h"
12#import "ICeCoffEENonHighlightingTextFieldCell.h"
13#import <objc/objc.h>
14#import <ApplicationEnhancer/ApplicationEnhancer.h>
15
16static NSNumber *ICCF_SERVICE_SHOWN, *ICCF_SERVICE_HIDDEN, *ICCF_SERVICE_MIXED;
17static NSDictionary *ICCF_SERVICE_OPTION_HIDDEN;
18
19static float ICCF_TableViewCellHeight(NSTableView *tableView) {
20 return ([tableView rowHeight] + [tableView intercellSpacing].height);
21}
22
23static inline NSCellStateValue ICCF_ServiceItemState(id <NSMenuItem> item) {
24 id state = [item representedObject];
25 return (state == nil) ? NSOnState : [state intValue];
26}
27
28static void ICCF_PropagateServiceStateChange(NSMenu *menu, id state) {
29 NSEnumerator *e = [[menu itemArray] objectEnumerator];
30 NSMenuItem *item;
31 NSMenu *submenu;
32
33 while ( (item = [e nextObject]) != nil) {
34 submenu = [item submenu];
35 if (submenu != nil) {
36 ICCF_PropagateServiceStateChange(submenu, state);
37 [item setRepresentedObject: (state == nil) ? ICCF_SERVICE_SHOWN : state];
38 }
39 [item setRepresentedObject: state];
40 }
41}
42
43static NSCellStateValue ICCF_PropagateServiceState(id <NSMenuItem> item, NSMenuItem *changedItem) {
44 NSMenu *submenu = [item submenu];
45 if (submenu == nil) return ICCF_ServiceItemState(item);
46
47 if (item == changedItem) ICCF_PropagateServiceStateChange(submenu, [item representedObject]);
48
49 BOOL areOn = NO, areOff = NO;
50 NSEnumerator *e = [[submenu itemArray] objectEnumerator];
51 NSMenuItem *subItem;
52 while ( (subItem = [e nextObject]) != nil) {
53 switch (ICCF_PropagateServiceState(subItem, changedItem)) {
54 case NSOnState: if (!areOff) { areOn = YES; continue; }
55 break;
56 case NSOffState: if (!areOn) { areOff = YES; continue; }
57 break;
58 case NSMixedState:
59 break;
60 }
61 [item setRepresentedObject: ICCF_SERVICE_MIXED];
62 return NSMixedState;
63 }
64 if (areOn) {
65 [item setRepresentedObject: ICCF_SERVICE_SHOWN];
66 return NSOnState;
67 } else {
68 [item setRepresentedObject: ICCF_SERVICE_HIDDEN];
69 return NSOffState;
70 }
71}
72
73static NSMutableDictionary *ICCF_RetainedServiceOptionsDictionary(NSMenu *menu) {
74 NSEnumerator *e = [[menu itemArray] objectEnumerator];
75 NSMenuItem *item;
76 NSMenu *submenu;
77 NSMutableDictionary *dict = nil, *subDict = nil, *submenuDict = nil;
78
79 while ( (item = [e nextObject]) != nil) {
80 submenu = [item submenu];
81 if (ICCF_ServiceItemState(item) == NSOffState) {
82 subDict = [ICCF_SERVICE_OPTION_HIDDEN retain];
83 } else if (submenu != nil) {
84 submenuDict = ICCF_RetainedServiceOptionsDictionary(submenu);
85 if (submenuDict == nil)
86 continue;
87 subDict = [[NSDictionary alloc] initWithObjectsAndKeys: submenuDict, kICServiceSubmenu, nil];
88 [submenuDict release];
89 } else continue;
90 if (dict == nil) {
91 dict = [[NSMutableDictionary alloc] init];
92 }
93 [dict setObject: subDict forKey: [item title]];
94 [subDict release];
95 }
96 return dict;
97}
98
99static void ICCF_RestoreServiceOptionsDictionary(NSMenu *menu, NSDictionary *dict) {
100 NSEnumerator *e = [dict keyEnumerator];
101 NSString *itemTitle;
102 NSDictionary *subDict, *submenuDict;
103 id <NSMenuItem> item;
104 NSMenu *submenu;
105
106 // XXX handle exceptions
107 while ( (itemTitle = [e nextObject]) != nil) {
108 item = [menu itemWithTitle: itemTitle];
109 if (item == nil) continue;
110 subDict = [dict objectForKey: itemTitle];
111 if ([[subDict objectForKey: (NSString *)kICServiceHidden] boolValue]) {
112 [item setRepresentedObject: ICCF_SERVICE_HIDDEN];
113 }
114 if ( (submenu = [item submenu]) != nil) {
115 submenuDict = [subDict objectForKey: (NSString *)kICServiceSubmenu];
116 if ([submenuDict count] == 0)
117 ICCF_PropagateServiceStateChange(submenu, ICCF_SERVICE_HIDDEN);
118 else
119 ICCF_RestoreServiceOptionsDictionary(submenu, submenuDict);
120 }
121 }
122}
123
124@implementation ICeCoffEEServicePrefController
125
126#pragma mark class initialization
127
128+ (void)initialize;
129{
130 ICCF_SERVICE_SHOWN = [[NSNumber alloc] initWithInt: NSOnState];
131 ICCF_SERVICE_HIDDEN = [[NSNumber alloc] initWithInt: NSOffState];
132 ICCF_SERVICE_MIXED = [[NSNumber alloc] initWithInt: NSMixedState];
133 ICCF_SERVICE_OPTION_HIDDEN = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithBool: YES], kICServiceHidden, nil];
134}
135
136#pragma mark initialize-release
137
138- (id)initWithParentWindow:(NSWindow *)parent;
139{
140 if ( (self = [self initWithWindowNibName: @"Select services"])) {
141 NSWindow *window = [self window]; // connect outlets
142 NSButtonCell *checkBoxCell = [[ICeCoffEENonHighlightingButtonCell alloc] init];
143 [checkBoxCell setButtonType: NSSwitchButton];
144 [checkBoxCell setImagePosition: NSImageOnly];
145 [checkBoxCell setAllowsMixedState: YES];
146 [[serviceOutline tableColumnWithIdentifier: @"show"] setDataCell: checkBoxCell];
147 [checkBoxCell release];
148
149 NSTextFieldCell *textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"service"] dataCell];
150 ((struct objc_object *)textFieldCell)->isa = [ICeCoffEENonHighlightingTextFieldCell class];
151
152 textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"key"] dataCell];
153 ((struct objc_object *)textFieldCell)->isa = [ICeCoffEENonHighlightingTextFieldCell class];
154
155 [window setResizeIncrements: NSMakeSize(1, ICCF_TableViewCellHeight(serviceOutline))];
156 if (parent != nil) {
157 [NSApp beginSheet: window modalForWindow: parent modalDelegate: self didEndSelector: nil contextInfo: nil];
158 } else {
159 [window center];
160 [window makeKeyAndOrderFront: nil];
161 }
162 }
163 return self;
164}
165
166- (void)dealloc;
167{
168 [servicesMenu release];
169 [super dealloc];
170}
171
172#pragma mark actions
173
174- (IBAction)showAll:(NSButton *)sender;
175{
176 ICCF_PropagateServiceStateChange(servicesMenu, nil);
177 [serviceOutline reloadData];
178}
179
180- (IBAction)hideAll:(NSButton *)sender;
181{
182 ICCF_PropagateServiceStateChange(servicesMenu, ICCF_SERVICE_HIDDEN);
183 [serviceOutline reloadData];
184}
185
186- (void)closeWithReturnCode:(int)returnCode;
187{
188 if ([[self window] isSheet]) {
189 [NSApp endSheet: [self window] returnCode: NSRunAbortedResponse];
190 }
191 [self close];
192}
193
194- (IBAction)cancel:(NSButton *)sender;
195{
196 [self closeWithReturnCode: NSRunAbortedResponse];
197}
198
199- (IBAction)saveChanges:(NSButton *)sender;
200{
201 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
202 NSMutableDictionary *icDefaults = [[defaults persistentDomainForName: (NSString *)kICBundleIdentifier] mutableCopy];
203 NSDictionary *serviceOptions = ICCF_RetainedServiceOptionsDictionary(servicesMenu);
204 if (serviceOptions != nil) {
205 [icDefaults setObject: serviceOptions forKey: (NSString *)kICServiceOptions];
206 [serviceOptions release];
207 } else {
208 [icDefaults removeObjectForKey: (NSString *)kICServiceOptions];
209 }
210 [defaults setPersistentDomain: icDefaults forName: (NSString *)kICBundleIdentifier];
211 [defaults synchronize];
212 APEMessageBroadcast(kICBundleIdentifier, kICPreferencesChanged, NULL);
213 [icDefaults release];
214 [self closeWithReturnCode: NSRunStoppedResponse];
215}
216
217@end
218
219@implementation ICeCoffEEServicePrefController (NSOutlineViewDataSource)
220
221- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
222{
223 if (servicesMenu == nil) {
224 servicesMenu = [[NSMenu alloc] initWithTitle: @""];
225 [[NSApplication sharedApplication] setServicesMenu: servicesMenu];
226 [servicesMenu update];
227 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
228 NSDictionary *icDefaults = [defaults persistentDomainForName: (NSString *)kICBundleIdentifier];
229 NSDictionary *serviceOptions = [icDefaults objectForKey: (NSString *)kICServiceOptions];
230 ICCF_RestoreServiceOptionsDictionary(servicesMenu, serviceOptions);
231 }
232 return [(item == nil ? servicesMenu : [item submenu]) numberOfItems];
233}
234
235- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
236{
237 return (item == nil ? YES : [item submenu] != nil);
238}
239
240- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item;
241{
242 return [(item == nil ? servicesMenu : [item submenu]) itemAtIndex: index];
243}
244
245NSAttributedString *ICCF_KeyEquivalentAttributedStringWithModifierFlags(NSString *self, unsigned int modifierFlags);
246
247- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
248{
249 if ([[tableColumn identifier] isEqualToString: @"service"]) {
250 return [item title];
251 } else if ([[tableColumn identifier] isEqualToString: @"show"]) {
252 id state = [item representedObject];
253 if ([item submenu] != nil && state == nil) {
254 return [NSNumber numberWithInt: ICCF_PropagateServiceState(item, nil)];
255 }
256 return (state == nil) ? ICCF_SERVICE_SHOWN : state;
257 } else if ([[tableColumn identifier] isEqualToString: @"key"]) {
258 [item setKeyEquivalent: @"#"];
259 NSString *equivalent = [item keyEquivalent];
260 if (equivalent == nil || [equivalent length] != 1) return nil;
261 // 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.
262 return ICCF_KeyEquivalentAttributedStringWithModifierFlags(equivalent, [item keyEquivalentModifierMask] | ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: [equivalent characterAtIndex: 0]] ? NSShiftKeyMask : 0));
263 }
264 return nil;
265}
266
267- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
268{
269 if ([[tableColumn identifier] isEqualToString: @"show"]) {
270 [item setRepresentedObject: [object boolValue] ? nil : ICCF_SERVICE_HIDDEN];
271
272 NSMenu *submenu = [item menu];
273 if (submenu == servicesMenu) {
274 ICCF_PropagateServiceState(item, item);
275 } else {
276 NSMenu *supermenu;
277 while ( (supermenu = [submenu supermenu]) != servicesMenu) {
278 submenu = supermenu;
279 }
280 ICCF_PropagateServiceState([supermenu itemAtIndex: [supermenu indexOfItemWithSubmenu: submenu]], item);
281 }
282 [outlineView reloadData];
283 }
284}
285
286@end
287
288@implementation ICeCoffEEServicePrefController (NSWindowDelegate)
289
290- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame;
291{
292 NSWindow *window = [serviceOutline window];
293 NSRect frame = [window frame];
294 NSScrollView *scrollView = [serviceOutline enclosingScrollView];
295 float displayedHeight = [[scrollView contentView] bounds].size.height;
296 float heightChange = [[scrollView documentView] bounds].size.height - displayedHeight;
297 float heightExcess;
298
299 if (heightChange >= 0 && heightChange <= 1) {
300 // either the window is already optimal size, or it's too big
301 float rowHeight = ICCF_TableViewCellHeight(serviceOutline);
302 heightChange = (rowHeight * [serviceOutline numberOfRows]) - displayedHeight;
303 }
304
305 frame.size.height += heightChange;
306
307 if ( (heightExcess = [window minSize].height - frame.size.height) > 1 ||
308 (heightExcess = [window maxSize].height - frame.size.height) < 1) {
309 heightChange += heightExcess;
310 frame.size.height += heightExcess;
311 }
312
313 frame.origin.y -= heightChange;
314
315 return frame;
316}
317
318@end
319
320@implementation ICeCoffEEServicePrefController (NSWindowNotifications)
321
322- (void)windowWillClose:(NSNotification *)notification;
323{
324 [self autorelease];
325}
326
327@end
Note: See TracBrowser for help on using the repository browser.