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

Last change on this file since 431 was 431, checked in by Nicholas Riley, 12 years ago

Handle NSUserKeyEquivalents; disable conflict resolution; fix keyEquivalents invalidation crasher

File size: 19.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 "ICeCoffEEServices.h"
11#import "ICeCoffEEServicePrefController.h"
12#import "ICeCoffEENonHighlightingButtonCell.h"
13#import "ICeCoffEEInvertingTextFieldCell.h"
14#import "ICeCoffEELabeledIconCell.h"
15#import <objc/objc.h>
16#import <ApplicationEnhancer/ApplicationEnhancer.h>
17#import <QuartzCore/QuartzCore.h>
18
19const int ICCF_SERVICE_UNKNOWN = 0;
20const int ICCF_SERVICE_SHOWN = 1;
21const int ICCF_SERVICE_HIDDEN = 2;
22const int ICCF_SERVICE_MIXED = 3;
23
24static NSDictionary *ICCF_SERVICE_OPTION_HIDDEN;
25
26static float ICCF_TableViewCellHeight(NSTableView *tableView) {
27    return ([tableView rowHeight] + [tableView intercellSpacing].height);
28}
29
30static NSMutableDictionary *keyEquivalents;
31static NSDictionary *userKeyEquivalents;
32
33static void ICCF_RemoveSingleKeyEquivalents() {
34    NSMutableArray *singleKeys = [[NSMutableArray alloc] init];
35    NSEnumerator *e = [[keyEquivalents allKeys] objectEnumerator];
36    NSString *keyEquivalent;
37    ICLog(@"before ICCF_RemoveSingleKeyEquivalents: %@", keyEquivalents);
38    while ( (keyEquivalent = [e nextObject]) != nil) {
39        if ([[keyEquivalents objectForKey: keyEquivalent] count] == 1)
40            [singleKeys addObject: keyEquivalent];
41    }
42    [keyEquivalents removeObjectsForKeys: singleKeys];
43    [singleKeys release];
44    ICLog(@"after ICCF_RemoveSingleKeyEquivalents: %@", keyEquivalents);
45}
46
47static inline unsigned ICCF_CountForKeyEquivalent(NSString *keyEquivalent) {
48    if (keyEquivalent == nil) return 0;
49    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
50    return (setOrNil == nil) ? 0 : [setOrNil count];
51}
52
53static inline void ICCF_AddKeyEquivalentForItem(NSMenuItem *item) {
54    NSString *keyEquivalent = [item toolTip];
55    if (keyEquivalent == nil) return;
56    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
57    if (setOrNil == nil) return;
58    [setOrNil addObject: item];
59}
60
61static inline void ICCF_RemoveKeyEquivalentForItem(NSMenuItem *item) {
62    NSString *keyEquivalent = [item toolTip];
63    if (keyEquivalent == nil) return;
64    NSMutableSet *setOrNil = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
65    if (setOrNil == nil) return;
66    [setOrNil removeObject: item];
67}
68
69static int ICCF_GetServiceState(NSMenuItem *item) {
70    return [item tag];
71}
72
73static void ICCF_SetServiceState(NSMenuItem *item, int state) {
74    [item setTag: state];
75}
76
77static inline void ICCF_UpdateKeyEquivalentForItem(NSMenuItem *item, int state) {
78    return; // XXX disabled until we can affect menubar Services menu(s)
79    int oldState = ICCF_GetServiceState(item);
80    if ((oldState == ICCF_SERVICE_UNKNOWN || oldState == ICCF_SERVICE_SHOWN) && state == ICCF_SERVICE_HIDDEN)
81        ICCF_RemoveKeyEquivalentForItem(item);
82    else if (oldState == ICCF_SERVICE_HIDDEN && (state == ICCF_SERVICE_SHOWN || state == ICCF_SERVICE_UNKNOWN))
83        ICCF_AddKeyEquivalentForItem(item);
84}
85
86
87static inline NSCellStateValue ICCF_ServiceItemState(NSMenuItem *item) {
88    int state = ICCF_GetServiceState(item);
89    if (state == ICCF_SERVICE_HIDDEN)
90        return NSOffState;
91    if (state == ICCF_SERVICE_MIXED)
92            return NSMixedState;
93    return NSOnState;
94}
95
96static void ICCF_PropagateServiceStateChange(NSMenu *menu, int state) {
97    NSEnumerator *e = [[menu itemArray] objectEnumerator];
98    NSMenuItem *item;
99    NSMenu *submenu;
100
101    while ( (item = [e nextObject]) != nil) {
102        submenu = [item submenu];
103        if (submenu != nil)
104            ICCF_PropagateServiceStateChange(submenu, state);
105        else
106            ICCF_UpdateKeyEquivalentForItem(item, state);
107       
108        ICCF_SetServiceState(item, state);
109    }
110}
111
112static NSCellStateValue ICCF_PropagateServiceState(NSMenuItem *item, NSMenuItem *changedItem) {
113    NSMenu *submenu = [item submenu];
114    if (submenu == nil) return ICCF_ServiceItemState(item);
115
116    if (item == changedItem) ICCF_PropagateServiceStateChange(submenu, [item tag]);
117
118    BOOL areOn = NO, areOff = NO;
119    NSEnumerator *e = [[submenu itemArray] objectEnumerator];
120    NSMenuItem *subItem;
121    while ( (subItem = [e nextObject]) != nil) {
122        switch (ICCF_PropagateServiceState(subItem, changedItem)) {
123            case NSOnState: if (!areOff) { areOn = YES; continue; }
124                break;
125            case NSOffState: if (!areOn) { areOff = YES; continue; }
126                break;
127            case NSMixedState:
128                break;
129        }
130        ICCF_SetServiceState(item, ICCF_SERVICE_MIXED);
131        return NSMixedState;
132    }
133    if (areOn) {
134        ICCF_SetServiceState(item, ICCF_SERVICE_SHOWN);
135        return NSOnState;
136    } else {
137        ICCF_SetServiceState(item, ICCF_SERVICE_HIDDEN);
138        return NSOffState;
139    }
140}
141
142static NSMutableDictionary *ICCF_RetainedServiceOptionsDictionary(NSMenu *menu) {
143    NSEnumerator *e = [[menu itemArray] objectEnumerator];
144    NSMenuItem *item;
145    NSMenu *submenu;
146    NSMutableDictionary *dict = nil, *subDict = nil, *submenuDict = nil;
147
148    while ( (item = [e nextObject]) != nil) {
149        submenu = [item submenu];
150        if (ICCF_ServiceItemState(item) == NSOffState) {
151            subDict = [ICCF_SERVICE_OPTION_HIDDEN retain];
152        } else if (submenu != nil) {
153            submenuDict = ICCF_RetainedServiceOptionsDictionary(submenu);
154            if (submenuDict == nil)
155                continue;
156            subDict = [[NSDictionary alloc] initWithObjectsAndKeys: submenuDict, kICServiceSubmenu, nil];
157            [submenuDict release];
158        } else continue;
159        if (dict == nil) {
160            dict = [[NSMutableDictionary alloc] init];
161        }
162        [dict setObject: subDict forKey: [item title]];
163        [subDict release];
164    }
165    return dict;
166}
167
168static void ICCF_RestoreServiceOptionsDictionary(NSMenu *menu, NSDictionary *dict) {
169    NSEnumerator *e = [dict keyEnumerator];
170    NSString *itemTitle;
171    NSDictionary *subDict, *submenuDict;
172    NSMenuItem *item;
173    NSMenu *submenu;
174
175    // XXX handle exceptions
176    while ( (itemTitle = [e nextObject]) != nil) {
177        item = (NSMenuItem *)[menu itemWithTitle: itemTitle];
178        if (item == nil) continue;
179        subDict = [dict objectForKey: itemTitle];
180        if ([[subDict objectForKey: (NSString *)kICServiceHidden] boolValue]) {
181            ICCF_SetServiceState(item, ICCF_SERVICE_HIDDEN);
182            ICCF_RemoveKeyEquivalentForItem(item);
183        }
184        if ( (submenu = [item submenu]) != nil) {
185            submenuDict = [subDict objectForKey: (NSString *)kICServiceSubmenu];
186            if ([submenuDict count] == 0)
187                ICCF_PropagateServiceStateChange(submenu, ICCF_SERVICE_HIDDEN);
188            else
189                ICCF_RestoreServiceOptionsDictionary(submenu, submenuDict);
190        }
191    }
192}
193
194static void ICCF_AddServiceKeyEquivalentsAndIcons(NSMenu *menu, NSDictionary *serviceInfo) {
195    if (serviceInfo == nil) return;
196    NSEnumerator *enumerator = [[menu itemArray] objectEnumerator];
197    NSMenuItem *menuItem;
198    NSMenu *submenu;
199    NSDictionary *itemInfo = nil;
200    while ( (menuItem = [enumerator nextObject]) != nil) {
201        itemInfo = [serviceInfo objectForKey: [menuItem title]];
202        if (itemInfo == nil) continue;
203       
204        if ( (submenu = [menuItem submenu]) != nil) {
205            ICCF_AddServiceKeyEquivalentsAndIcons(submenu, [itemInfo objectForKey: (NSString *)kICServiceSubmenu]);
206        } else {
207            NSString *keyEquivalent = (NSString *)[itemInfo objectForKey: (NSString *)kICServiceShortcut];
208            if (keyEquivalent == nil) {
209                keyEquivalent = [userKeyEquivalents objectForKey: [menuItem title]];
210            } else if ([keyEquivalent length] != 1) {
211                keyEquivalent = nil;
212            } else {
213                // 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.
214                unichar key = [keyEquivalent characterAtIndex: 0];
215                if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember: key])
216                    keyEquivalent = [NSString stringWithFormat: @"$@%c", key];
217                else
218                    keyEquivalent = [NSString stringWithFormat: @"@%c", key];
219            }
220            if (keyEquivalent != nil) {
221                [menuItem setToolTip: keyEquivalent];
222                NSMutableSet *equivalentItems = (NSMutableSet *)[keyEquivalents objectForKey: keyEquivalent];
223                if (equivalentItems == nil) {
224                    equivalentItems = [[NSMutableSet alloc] initWithObjects: menuItem, nil];
225                    [keyEquivalents setObject: equivalentItems forKey: keyEquivalent];
226                    [equivalentItems release];
227                } else {
228                    [equivalentItems addObject: menuItem];
229                }
230            }
231        }
232
233        NSString *bundlePath = (NSString *)[itemInfo objectForKey: (NSString *)kICServiceBundlePath];
234        if (bundlePath == NULL) continue;
235        IconRef serviceIcon = ICCF_CopyIconRefForPath(bundlePath);
236        if (serviceIcon == NULL) continue;
237        [menuItem _setIconRef: serviceIcon];
238        ReleaseIconRef(serviceIcon);
239    }
240}
241
242@implementation ICeCoffEEServicePrefController
243
244#pragma mark class initialization
245
246+ (void)initialize;
247{
248    ICCF_SERVICE_OPTION_HIDDEN = [[NSDictionary alloc] initWithObjectsAndKeys: [NSNumber numberWithBool: YES], kICServiceHidden, nil];
249}
250
251#pragma mark initialize-release
252
253- (id)initWithParentWindow:(NSWindow *)parent;
254{
255    if ( (self = [self initWithWindowNibName: @"Select services"])) {
256        NSWindow *window = [self window]; // connect outlets
257        [serviceOutline setAutoresizesOutlineColumn: NO];
258       
259        NSButtonCell *checkBoxCell = [[ICeCoffEENonHighlightingButtonCell alloc] init];
260        [checkBoxCell setButtonType: NSSwitchButton];
261        [checkBoxCell setImagePosition: NSImageOnly];
262        [checkBoxCell setAllowsMixedState: YES];
263       
264        [[serviceOutline tableColumnWithIdentifier: @"show"] setDataCell: checkBoxCell];
265        [checkBoxCell release];
266
267        NSTextFieldCell *textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"service"] dataCell];
268        [textFieldCell setWraps: YES];
269        [[serviceOutline tableColumnWithIdentifier: @"service"] setDataCell:
270            [ICeCoffEELabeledIconCell copyFromTextFieldCell: textFieldCell]];
271       
272        textFieldCell = [[serviceOutline tableColumnWithIdentifier: @"key"] dataCell];
273        ((struct objc_object *)textFieldCell)->isa = [ICeCoffEEInvertingTextFieldCell class];
274       
275        [serviceOutline noteNumberOfRowsChanged]; // or we get no active scroll bar or initial selection
276
277        [window setResizeIncrements: NSMakeSize(1, ICCF_TableViewCellHeight(serviceOutline))];
278        if (parent != nil) {
279            [NSApp beginSheet: window modalForWindow: parent modalDelegate: self didEndSelector: nil contextInfo: nil];
280        } else {
281            [window center];
282            [window makeKeyAndOrderFront: nil];
283        }
284    }
285    return self;
286}
287
288- (void)dealloc;
289{
290    [keyEquivalents release]; keyEquivalents = nil;
291    [userKeyEquivalents release]; userKeyEquivalents = nil;
292    [servicesMenu release]; servicesMenu = nil;
293    [closedTriangle release]; closedTriangle = nil;
294    [selectedClosedTriangle release]; selectedClosedTriangle = nil;
295    [openTriangle release]; openTriangle = nil;
296    [selectedOpenTriangle release]; selectedOpenTriangle = nil;
297    [super dealloc];
298}
299
300#pragma mark actions
301
302- (IBAction)showAll:(NSButton *)sender;
303{
304    ICCF_PropagateServiceStateChange(servicesMenu, 0);
305    [serviceOutline reloadData];
306}
307
308- (IBAction)hideAll:(NSButton *)sender;
309{
310    ICCF_PropagateServiceStateChange(servicesMenu, ICCF_SERVICE_HIDDEN);
311    [serviceOutline reloadData];
312}
313
314- (void)closeWithReturnCode:(int)returnCode;
315{
316    if ([[self window] isSheet]) {
317        [NSApp endSheet: [self window] returnCode: NSRunAbortedResponse];
318    }
319    [self close];
320}
321
322- (IBAction)cancel:(NSButton *)sender;
323{
324    [self closeWithReturnCode: NSRunAbortedResponse];
325}
326
327- (IBAction)saveChanges:(NSButton *)sender;
328{
329    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
330    NSMutableDictionary *icDefaults = [[defaults persistentDomainForName: (NSString *)kICBundleIdentifier] mutableCopy];
331    NSDictionary *serviceOptions = ICCF_RetainedServiceOptionsDictionary(servicesMenu);
332    if (serviceOptions != nil) {
333        [icDefaults setObject: serviceOptions forKey: (NSString *)kICServiceOptions];
334        [serviceOptions release];
335    } else {
336        [icDefaults removeObjectForKey: (NSString *)kICServiceOptions];
337    }
338    [defaults setPersistentDomain: icDefaults forName: (NSString *)kICBundleIdentifier];
339    [defaults synchronize];
340    APEMessageBroadcast(kICBundleIdentifier, kICPreferencesChanged, NULL);
341    [icDefaults release];
342    [self closeWithReturnCode: NSRunStoppedResponse];
343}
344
345@end
346
347@implementation ICeCoffEEServicePrefController (NSOutlineViewDataSource)
348
349- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item;
350{
351    if (servicesMenu == nil) {
352        keyEquivalents = [[NSMutableDictionary alloc] init];
353        userKeyEquivalents = [[[NSUserDefaults standardUserDefaults] dictionaryForKey: @"NSUserKeyEquivalents"] retain];
354        servicesMenu = [[NSMenu alloc] initWithTitle: @""];
355        ICCF_SetServicesMenu(servicesMenu);
356        ICCF_AddServiceKeyEquivalentsAndIcons(servicesMenu, ICCF_GetServicesInfo());
357        ICCF_RemoveSingleKeyEquivalents();
358       
359        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
360        NSDictionary *icDefaults = [defaults persistentDomainForName: (NSString *)kICBundleIdentifier];
361        NSDictionary *serviceOptions = [icDefaults objectForKey: (NSString *)kICServiceOptions];
362        ICCF_RestoreServiceOptionsDictionary(servicesMenu, serviceOptions);
363    }
364    return [(item == nil ? servicesMenu : [item submenu]) numberOfItems];
365}
366
367- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item;
368{
369    return (item == nil ? YES : [item submenu] != nil);
370}
371
372- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item;
373{
374    return [(item == nil ? servicesMenu : [item submenu]) itemAtIndex: index];
375}
376
377NSAttributedString *ICCF_KeyEquivalentAttributedString(NSString *self, unsigned count);
378
379- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
380{
381    if ([[tableColumn identifier] isEqualToString: @"service"]) {
382        static NSDictionary *attrDict = nil;
383        if (attrDict == nil) { // XXX we leak this, but so does the Apple sample code...
384            NSMutableParagraphStyle *style = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
385            [style setLineBreakMode: NSLineBreakByTruncatingMiddle];
386            attrDict = [[NSDictionary alloc] initWithObjectsAndKeys: style, NSParagraphStyleAttributeName, nil];
387            [style release];
388        }
389        return [[[NSAttributedString alloc] initWithString: [item title] attributes: attrDict] autorelease];
390    } else if ([[tableColumn identifier] isEqualToString: @"show"]) {
391        int state = ICCF_GetServiceState(item);
392        if ([item submenu] != nil && state == ICCF_SERVICE_UNKNOWN) {
393            return [NSNumber numberWithInt: ICCF_PropagateServiceState(item, nil)];
394        }
395        return [NSNumber numberWithInt: ICCF_ServiceItemState(item)];
396    } else if ([[tableColumn identifier] isEqualToString: @"key"]) {
397        NSString *equivalent = [item toolTip];
398        if (equivalent == nil)
399            return nil;
400        return ICCF_KeyEquivalentAttributedString(equivalent, ICCF_CountForKeyEquivalent(equivalent));
401    }
402    return nil;
403}
404
405- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item;
406{
407    if ([[tableColumn identifier] isEqualToString: @"show"]) {
408        int newState = [object boolValue] ? ICCF_SERVICE_SHOWN : ICCF_SERVICE_HIDDEN;
409        ICCF_UpdateKeyEquivalentForItem(item, newState);
410        ICCF_SetServiceState(item, newState);
411       
412        NSMenu *submenu = [item menu];
413        if (submenu == servicesMenu) {
414            ICCF_PropagateServiceState(item, item);
415        } else {
416            NSMenu *supermenu;
417            while ( (supermenu = [submenu supermenu]) != servicesMenu) {
418                submenu = supermenu;
419            }
420            ICCF_PropagateServiceState((NSMenuItem *)[supermenu itemAtIndex: [supermenu indexOfItemWithSubmenu: submenu]], item);
421        }
422        [outlineView reloadData];
423    }
424}
425
426@end
427
428@implementation ICeCoffEEServicePrefController (NSOutlineViewDelegate)
429
430- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
431{
432    if (![[tableColumn identifier] isEqualToString: @"service"])
433        return;
434    [(ICeCoffEELabeledIconCell *)cell setIconRef: [item _iconRef]];
435}
436
437static NSImage *ICCF_InvertedImage(NSImage *image) {
438    NSImage *invertedImage = [[NSImage alloc] initWithSize: [image size]];
439    NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData: [image TIFFRepresentation]];
440    CIImage *source = [[CIImage alloc] initWithBitmapImageRep: imageRep];
441    CIColor *black = [[CIColor alloc] initWithColor: [NSColor blackColor]];
442                                                             
443    CIFilter *monochromeFilter = [CIFilter filterWithName: @"CIColorMonochrome"];
444    [monochromeFilter setValue: source forKey: @"inputImage"];
445    [monochromeFilter setValue: [NSNumber numberWithFloat: 1.0] forKey: @"inputIntensity"];
446    [monochromeFilter setValue: black forKey: @"inputColor"];
447   
448    CIFilter *invertFilter = [CIFilter filterWithName: @"CIColorInvert"];
449    [invertFilter setValue: [monochromeFilter valueForKey: @"outputImage"] forKey: @"inputImage"];
450   
451    CIFilter *maskToAlphaFilter = [CIFilter filterWithName: @"CIMaskToAlpha"];
452    [maskToAlphaFilter setValue: [invertFilter valueForKey: @"outputImage"] forKey: @"inputImage"];
453   
454    [invertedImage lockFocus];
455    CIContext *context = [CIContext contextWithCGContext: [[NSGraphicsContext currentContext] graphicsPort]
456                                                 options: nil];
457    CIImage *result = [maskToAlphaFilter valueForKey: @"outputImage"];
458    [context drawImage: result atPoint: CGPointZero fromRect: [result extent]];
459    [invertedImage unlockFocus];
460
461    [source release];
462    [black release];
463
464    return invertedImage;
465}
466
467- (void)outlineView:(NSOutlineView *)outlineView willDisplayOutlineCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item;
468{
469    static BOOL isInverted = NO;
470
471    if (closedTriangle == nil) {
472        closedTriangle = [[cell image] retain];
473        openTriangle = [[cell alternateImage] retain];
474        selectedClosedTriangle = ICCF_InvertedImage(closedTriangle);
475        selectedOpenTriangle = ICCF_InvertedImage(openTriangle);
476    }
477   
478    if (![outlineView isRowSelected: [outlineView rowForItem: item]] ||
479        [[outlineView window] firstResponder] != outlineView || ![[outlineView window] isKeyWindow]) {
480        if (!isInverted)
481            return;
482       
483        [cell setImage: closedTriangle];
484        [cell setAlternateImage: openTriangle];
485        isInverted = NO;
486        return;
487    }
488   
489    // not checking for isInverted is intentional - images reset when triangle is flipped
490    [cell setImage: selectedClosedTriangle];
491    [cell setAlternateImage: selectedOpenTriangle];
492    isInverted = YES;
493}
494
495@end
496
497@implementation ICeCoffEEServicePrefController (NSWindowDelegate)
498
499- (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame;
500{
501    NSWindow *window = [serviceOutline window];
502    NSRect frame = [window frame];
503    NSScrollView *scrollView = [serviceOutline enclosingScrollView];
504    float displayedHeight = [[scrollView contentView] bounds].size.height;
505    float heightChange = [[scrollView documentView] bounds].size.height - displayedHeight;
506    float heightExcess;
507
508    if (heightChange >= 0 && heightChange <= 1) {
509        // either the window is already optimal size, or it's too big
510        float rowHeight = ICCF_TableViewCellHeight(serviceOutline);
511        heightChange = (rowHeight * [serviceOutline numberOfRows]) - displayedHeight;
512    }
513
514    frame.size.height += heightChange;
515
516    if ( (heightExcess = [window minSize].height - frame.size.height) > 1 ||
517         (heightExcess = [window maxSize].height - frame.size.height) < 1) {
518        heightChange += heightExcess;
519        frame.size.height += heightExcess;
520    }
521
522    frame.origin.y -= heightChange;
523
524    return frame;
525}
526
527@end
528
529@implementation ICeCoffEEServicePrefController (NSWindowNotifications)
530
531- (void)windowWillClose:(NSNotification *)notification;
532{
533    [self autorelease];
534}
535
536@end
Note: See TracBrowser for help on using the repository browser.