gecko-dev/camino/PreferencePanes/MVPreferencesController.mm

381 строка
14 KiB
Plaintext

#import <PreferencePanes/PreferencePanes.h>
#import "MVPreferencesController.h"
#import "MVPreferencesMultipleIconView.h"
#import "MVPreferencesGroupedIconView.h"
#import "CHToolbarAdditions.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsIPref.h"
#include "nsCocoaBrowserService.h"
// #import "Defines.h"
static MVPreferencesController *sharedInstance = nil;
NSString *MVToolbarShowAllItemIdentifier = @"MVToolbarShowAllItem";
NSString *MVPreferencesWindowNotification = @"MVPreferencesWindowNotification";
@interface NSToolbar (NSToolbarPrivate)
- (NSView *) _toolbarView;
@end
@interface MVPreferencesController (MVPreferencesControllerPrivate)
- (void) _doUnselect:(NSNotification *) notification;
- (IBAction) _selectPreferencePane:(id) sender;
- (void) _resizeWindowForContentView:(NSView *) view;
- (NSImage *) _imageForPaneBundle:(NSBundle *) bundle;
- (NSString *) _paletteLabelForPaneBundle:(NSBundle *) bundle;
- (NSString *) _labelForPaneBundle:(NSBundle *) bundle;
@end
@implementation MVPreferencesController
+ (MVPreferencesController *) sharedInstance
{
return ( sharedInstance ? sharedInstance : [[[self alloc] init] autorelease] );
}
- (id) init
{
if ( ( self = [super init] ) ) {
unsigned i = 0;
NSBundle *bundle = nil;
NSString *bundlePath = [NSString stringWithFormat:@"%@/Contents/PreferencePanes",
[[NSBundle mainBundle] bundlePath]];
panes = [[[NSFileManager defaultManager] directoryContentsAtPath:bundlePath] mutableCopy];
for ( i = 0; i < [panes count]; i++ ) {
bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:@"%@/%@", bundlePath, [panes objectAtIndex:i]]];
[bundle load];
if( bundle ) [panes replaceObjectAtIndex:i withObject:bundle];
else {
[panes removeObjectAtIndex:i];
i--;
}
}
loadedPanes = [[NSMutableDictionary dictionary] retain];
paneInfo = [[NSMutableDictionary dictionary] retain];
[NSBundle loadNibNamed:@"MVPreferences" owner:self];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector( _doUnselect: ) name:NSPreferencePaneDoUnselectNotification object:nil];
}
return self;
}
- (void) dealloc
{
[loadedPanes autorelease];
[panes autorelease];
[paneInfo autorelease];
[[NSNotificationCenter defaultCenter] removeObserver:self];
loadedPanes = nil;
panes = nil;
paneInfo = nil;
[super dealloc];
}
- (void) awakeFromNib
{
NSToolbar *toolbar = [[[NSToolbar alloc] initWithIdentifier:@"preferences.toolbar"] autorelease];
NSArray *groups = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MVPreferencePaneGroups" ofType:@"plist"]];
if( groups ) {
[groupView setPreferencePanes:panes];
[groupView setPreferencePaneGroups:groups];
mainView = groupView;
} else {
[multiView setPreferencePanes:panes];
mainView = multiView;
}
[self showAll:nil];
[window setDelegate:self];
[toolbar setAllowsUserCustomization:YES];
[toolbar setAutosavesConfiguration:YES];
[toolbar setDelegate:self];
[toolbar setAlwaysCustomizableByDrag:YES];
[toolbar setShowsContextMenu:NO];
[window setToolbar:toolbar];
[toolbar setDisplayMode:NSToolbarDisplayModeIconAndLabel];
[toolbar setIndexOfFirstMovableItem:2];
}
- (NSWindow *) window
{
return [[window retain] autorelease];
}
- (IBAction) showPreferences:(id) sender
{
if ( ![window isVisible] ) {
// on being shown, register as a window that cares about XPCOM being active until we're done
// with it in |windowDidClose()|. Need to ensure this is exactly balanced with |BrowserClosed()|
// calls as it increments a refcount. As a result, we can only call it when we're making
// the window visible. Too bad cocoa doesn't give us any notifications of this.
nsCocoaBrowserService::InitEmbedding();
}
[self showAll:nil];
[window makeKeyAndOrderFront:nil];
}
- (IBAction) showAll:(id) sender
{
if ( [[window contentView] isEqual:mainView] ) return;
if ( currentPaneIdentifier && [[loadedPanes objectForKey:currentPaneIdentifier] shouldUnselect] != NSUnselectNow ) {
/* more to handle later */
#if DEBUG
NSLog( @"can't unselect current" );
#endif
return;
}
[window setContentView:[[[NSView alloc] initWithFrame:[mainView frame]] autorelease]];
[window setTitle:[NSString stringWithFormat:NSLocalizedString( @"%@ Preferences", nil ), [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]]];
[self _resizeWindowForContentView:mainView];
[[loadedPanes objectForKey:currentPaneIdentifier] willUnselect];
[window setContentView:mainView];
[[loadedPanes objectForKey:currentPaneIdentifier] didUnselect];
[currentPaneIdentifier autorelease];
currentPaneIdentifier = nil;
[window setInitialFirstResponder:mainView];
[window makeFirstResponder:mainView];
}
- (void) selectPreferencePaneByIdentifier:(NSString *) identifier
{
NSBundle *bundle = [NSBundle bundleWithIdentifier:identifier];
if ( bundle && ! [currentPaneIdentifier isEqualToString:identifier] ) {
NSPreferencePane *pane = nil;
NSView *prefView = nil;
if ( currentPaneIdentifier &&
[[loadedPanes objectForKey:currentPaneIdentifier] shouldUnselect] != NSUnselectNow ) {
/* more to handle later */
#if DEBUG
NSLog( @"can't unselect current" );
#endif
closeWhenPaneIsReady = NO;
[pendingPane autorelease];
pendingPane = [identifier retain];
return;
}
[pendingPane autorelease];
pendingPane = nil;
[loadingImageView setImage:[self _imageForPaneBundle:bundle]];
[loadingTextFeld setStringValue:[NSString stringWithFormat:NSLocalizedString( @"Loading %@...", nil ),
[self _labelForPaneBundle:bundle]]];
[window setTitle:[self _labelForPaneBundle:bundle]];
[window setContentView:loadingView];
[window display];
if ( ! ( pane = [loadedPanes objectForKey:identifier] ) ) {
pane = [[[[bundle principalClass] alloc] initWithBundle:bundle] autorelease];
if( pane ) [loadedPanes setObject:pane forKey:identifier];
}
if ( [pane loadMainView] ) {
[pane willSelect];
prefView = [pane mainView];
[self _resizeWindowForContentView:prefView];
[[loadedPanes objectForKey:currentPaneIdentifier] willUnselect];
[window setContentView:prefView];
[[loadedPanes objectForKey:currentPaneIdentifier] didUnselect];
[pane didSelect];
[[NSNotificationCenter defaultCenter]
postNotificationName: MVPreferencesWindowNotification
object: self
userInfo: [NSDictionary dictionaryWithObjectsAndKeys:window, @"window", nil]];
[currentPaneIdentifier autorelease];
currentPaneIdentifier = [identifier copy];
[window setInitialFirstResponder:[pane initialKeyView]];
[window makeFirstResponder:[pane initialKeyView]];
}
else {
NSRunCriticalAlertPanel( NSLocalizedString( @"Preferences Error", nil ),
[NSString stringWithFormat:NSLocalizedString( @"Could not load %@", nil ),
[self _labelForPaneBundle:bundle]], nil, nil, nil );
}
}
}
- (BOOL) windowShouldClose:(id) sender
{
if ( currentPaneIdentifier && [[loadedPanes objectForKey:currentPaneIdentifier] shouldUnselect] != NSUnselectNow ) {
#if DEBUG
NSLog( @"can't unselect current" );
#endif
closeWhenPaneIsReady = YES;
return NO;
}
return YES;
}
- (void) windowWillClose:(NSNotification *) notification
{
[[loadedPanes objectForKey:currentPaneIdentifier] willUnselect];
[[loadedPanes objectForKey:currentPaneIdentifier] didUnselect];
[currentPaneIdentifier autorelease];
currentPaneIdentifier = nil;
//[loadedPanes removeAllObjects];
// write out prefs and user defaults
nsCOMPtr<nsIPref> prefService ( do_GetService(NS_PREF_CONTRACTID) );
NS_ASSERTION(prefService, "Could not get pref service, prefs unsaved");
if ( prefService )
prefService->SavePrefFile(nsnull); // nsnull means write to prefs.js
[[NSUserDefaults standardUserDefaults] synchronize];
// tell gecko that this window no longer needs it around.
nsCocoaBrowserService::BrowserClosed();
}
- (NSToolbarItem *) toolbar:(NSToolbar *) toolbar
itemForItemIdentifier:(NSString *) itemIdentifier
willBeInsertedIntoToolbar:(BOOL) flag
{
NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier] autorelease];
if ( [itemIdentifier isEqualToString:MVToolbarShowAllItemIdentifier] ) {
[toolbarItem setLabel:NSLocalizedString( @"Show All", nil )];
[toolbarItem setImage:[NSImage imageNamed:@"NSApplicationIcon"]];
[toolbarItem setTarget:self];
[toolbarItem setAction:@selector( showAll: )];
} else {
NSBundle *bundle = [NSBundle bundleWithIdentifier:itemIdentifier];
if( bundle ) {
[toolbarItem setLabel:[self _labelForPaneBundle:bundle]];
[toolbarItem setPaletteLabel:[self _paletteLabelForPaneBundle:bundle]];
[toolbarItem setImage:[self _imageForPaneBundle:bundle]];
[toolbarItem setTarget:self];
[toolbarItem setAction:@selector( _selectPreferencePane: )];
} else toolbarItem = nil;
}
return toolbarItem;
}
- (NSArray *) toolbarDefaultItemIdentifiers:(NSToolbar *) toolbar
{
NSMutableArray *fixed = [NSMutableArray arrayWithObjects:MVToolbarShowAllItemIdentifier, NSToolbarSeparatorItemIdentifier, nil];
NSArray *defaults = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"MVPreferencePaneDefaults" ofType:@"plist"]];
[fixed addObjectsFromArray:defaults];
return fixed;
}
- (NSArray *) toolbarAllowedItemIdentifiers:(NSToolbar *) toolbar
{
NSMutableArray *items = [NSMutableArray array];
NSEnumerator *enumerator = [panes objectEnumerator];
id item = nil;
while ( ( item = [enumerator nextObject] ) )
[items addObject:[item bundleIdentifier]];
//[items addObject:MVToolbarShowAllItemIdentifier];
[items addObject:NSToolbarSeparatorItemIdentifier];
return items;
}
@end
@implementation MVPreferencesController (MVPreferencesControllerPrivate)
- (IBAction) _selectPreferencePane:(id) sender
{
[self selectPreferencePaneByIdentifier:[sender itemIdentifier]];
}
- (void) _doUnselect:(NSNotification *) notification
{
if( closeWhenPaneIsReady ) [window close];
[self selectPreferencePaneByIdentifier:pendingPane];
}
- (void) _resizeWindowForContentView:(NSView *) view
{
NSRect windowFrame, newWindowFrame;
unsigned int newWindowHeight;
windowFrame = [NSWindow contentRectForFrameRect:[window frame] styleMask:[window styleMask]];
newWindowHeight = NSHeight( [view frame] );
if ( [[window toolbar] isVisible] )
newWindowHeight += NSHeight( [[[window toolbar] _toolbarView] frame] );
newWindowFrame = [NSWindow frameRectForContentRect:NSMakeRect( NSMinX( windowFrame ), NSMaxY( windowFrame ) - newWindowHeight, NSWidth( windowFrame ), newWindowHeight ) styleMask:[window styleMask]];
[window setFrame:newWindowFrame display:YES animate:[window isVisible]];
}
- (NSImage *) _imageForPaneBundle:(NSBundle *) bundle
{
NSImage *image = nil;
NSMutableDictionary *cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
image = [[[cache objectForKey:@"MVPreferencePaneImage"] retain] autorelease];
if ( ! image ) {
NSDictionary *info = [bundle infoDictionary];
image = [[[NSImage alloc] initWithContentsOfFile:[bundle pathForImageResource:[info objectForKey:@"NSPrefPaneIconFile"]]] autorelease];
if ( ! image ) image = [[[NSImage alloc] initWithContentsOfFile:[bundle pathForImageResource:[info objectForKey:@"CFBundleIconFile"]]] autorelease];
if ( ! cache ) [paneInfo setObject:[NSMutableDictionary dictionary] forKey:[bundle bundleIdentifier]];
cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
if ( image ) [cache setObject:image forKey:@"MVPreferencePaneImage"];
}
return image;
}
- (NSString *) _paletteLabelForPaneBundle:(NSBundle *) bundle
{
NSString *label = nil;
NSMutableDictionary *cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
label = [[[cache objectForKey:@"MVPreferencePanePaletteLabel"] retain] autorelease];
if ( ! label ) {
NSDictionary *info = [bundle infoDictionary];
label = NSLocalizedStringFromTableInBundle( @"NSPrefPaneIconLabel", @"InfoPlist", bundle, nil );
if ( [label isEqualToString:@"NSPrefPaneIconLabel"] ) label = [info objectForKey:@"NSPrefPaneIconLabel"];
if ( ! label ) label = NSLocalizedStringFromTableInBundle( @"CFBundleName", @"InfoPlist", bundle, nil );
if ( [label isEqualToString:@"CFBundleName"] ) label = [info objectForKey:@"CFBundleName"];
if ( ! label ) label = [bundle bundleIdentifier];
if ( ! cache ) [paneInfo setObject:[NSMutableDictionary dictionary] forKey:[bundle bundleIdentifier]];
cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
if ( label ) [cache setObject:label forKey:@"MVPreferencePanePaletteLabel"];
}
return label;
}
- (NSString *) _labelForPaneBundle:(NSBundle *) bundle
{
NSString *label = nil;
NSMutableDictionary *cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
label = [[[cache objectForKey:@"MVPreferencePaneLabel"] retain] autorelease];
if ( ! label ) {
NSDictionary *info = [bundle infoDictionary];
label = NSLocalizedStringFromTableInBundle( @"CFBundleName", @"InfoPlist", bundle, nil );
if ( [label isEqualToString:@"CFBundleName"] ) label = [info objectForKey:@"CFBundleName"];
if ( ! label ) label = [bundle bundleIdentifier];
if ( ! cache ) [paneInfo setObject:[NSMutableDictionary dictionary] forKey:[bundle bundleIdentifier]];
cache = [paneInfo objectForKey:[bundle bundleIdentifier]];
if ( label ) [cache setObject:label forKey:@"MVPreferencePaneLabel"];
}
return label;
}
@end
// When using the Font Panel without an obvious target (like an editable text field),
// changeFont action messages propagate all the way up the responder chain to here.
// There isn't an easy way for individual prefs panes to get themselves into
// the responder chain, so we just bounce this message back to the currently
// loaded pane.
@implementation MVPreferencesController (FontChangeNotifications)
- (void)changeFont:(id)sender
{
[[loadedPanes objectForKey:currentPaneIdentifier] changeFont:sender];
}
@end