/* ProjectWindowController.m Implementation of the ProjectWindowController class for the ProjectManager application. Copyright (C) 2005, 2006 Saso Kiselkov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #import "ProjectWindowController.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import "ProjectDocument.h" #import "ProjectModule.h" #import "ProjectType.h" @implementation ProjectWindowController static NSArray * standardToolbarItems = nil; static NSString * const ProjectModuleSwitcherToolbarItemIdentifier = @"PMProjectModuleSwitcherToolbarItemIdentifier"; + (void) initialize { if (standardToolbarItems == nil) { standardToolbarItems = [[NSArray alloc] initWithObjects: ProjectModuleSwitcherToolbarItemIdentifier, NSToolbarSeparatorItemIdentifier, NSToolbarSpaceItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, NSToolbarShowColorsItemIdentifier, NSToolbarShowFontsItemIdentifier, NSToolbarCustomizeToolbarItemIdentifier, NSToolbarPrintItemIdentifier, nil]; } } - initWithWindowNibName: (NSString *) nibName ownerDocument: (ProjectDocument *) anOwner { // we must do this before we load the nib owner = anOwner; if ((self = [self initWithWindowNibName: nibName]) != nil) { [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(projectNameChanged:) name: ProjectNameDidChangeNotification object: owner]; } return self; } - (void) dealloc { [[NSNotificationCenter defaultCenter] removeObserver: self name: ProjectNameDidChangeNotification object: owner]; TEST_RELEASE (currentModule); TEST_RELEASE (switcherItem); TEST_RELEASE (log); TEST_RELEASE (moduleSwitcher); /* N.b. we can't say [myWindow setToolbar: nil] here, because would * cause all the views inside the window to redraw, including some * views which use data sources (e.g. table views). However, since * these data sources are likely to have been the project modules * (which have been dealloc'ed earlier), they are no longer valid * and would cause segfaults. Therefore we just rely on the window * being so kind as the destroy the toolbar. */ [super dealloc]; } - (void) awakeFromNib { NSUserDefaults * df = [NSUserDefaults standardUserDefaults]; [myWindow setMiniwindowImage: [NSImage imageNamed: @"pmproj"]]; toolbar = [[[NSToolbar alloc] initWithIdentifier: [owner projectDirectory]] autorelease]; [toolbar setAutosavesConfiguration: NO]; [toolbar setAllowsUserCustomization: NO]; [toolbar setDelegate: self]; [toolbar setVisible: [df boolForKey: @"DontShowProjectWindowToolbar"] == NO]; [myWindow setToolbar: toolbar]; if ([[owner projectModules] count] > 0) { [self selectModule: 0]; } } /** * Action invoked when a module-switch has been requested, either from * the project window's switcher popup, or from a menu item (unused so far). */ - (void) switchView: sender { // invoked by the module-switcher popup button if (sender == moduleSwitcher) { // bug in GNUstep - we have to select the proper item manually, // even though the popup button already knows which it has selected, // but it won't redraw unless we really force it to [moduleSwitcher selectItemAtIndex: [moduleSwitcher indexOfSelectedItem]]; [self selectModule: [moduleSwitcher indexOfSelectedItem]]; } // invoked by a menu item else { int tag = [sender tag]; [moduleSwitcher selectItemAtIndex: tag]; [self selectModule: tag]; } } /** * Sets the current module displayed in the receiver's project window. * * @param aModule The module which to display. It must be one of the * project's modules, otherwise an exception is thrown. */ - (void) setCurrentModule: (id ) aModule { NSString * moduleName; int i; NSArray * itemIdentifiers; NSEnumerator * e; NSString * identifier; NSAssert ([[owner projectModules] containsObject: aModule], _(@"Invalid module %@ passed to -setCurrentModule:")); if (currentModule == aModule) { // nothing to do return; } ASSIGN (currentModule, aModule); moduleName = [[currentModule class] moduleName]; [box setContentView: [currentModule view]]; // remove extra toolbar items for (i = [[toolbar items] count] - 1; i > 1; i--) { [toolbar removeItemAtIndex: i]; } itemIdentifiers = [currentModule toolbarItemIdentifiers]; e = [itemIdentifiers objectEnumerator]; for (i = 2; (identifier = [e nextObject]) != nil; i++) { [toolbar insertItemWithItemIdentifier: identifier atIndex: i]; } // and revalidate the toolbar's visible items [toolbar validateVisibleItems]; [[NSNotificationCenter defaultCenter] postNotificationName: CurrentProjectModuleDidChangeNotification object: self userInfo: [NSDictionary dictionaryWithObject: currentModule forKey: @"Module"]]; } /** * Returns the currently visible project module. To some stuff (e.g. the * debugger's inspectors) this information is better than the current tab. */ - (id ) currentModule { return currentModule; } /** * Puts the project module in the project module list at index `moduleNumber' * into the receiver's window. */ - (void) selectModule: (unsigned int) moduleNumber { [self setCurrentModule: [[owner projectModules] objectAtIndex: moduleNumber]]; } - (NSToolbarItem*)toolbar: (NSToolbar*)toolbar itemForItemIdentifier: (NSString*)itemIdentifier willBeInsertedIntoToolbar: (BOOL)flag { // if it's the project module switcher, set it up correctly if ([itemIdentifier isEqualToString: ProjectModuleSwitcherToolbarItemIdentifier]) { if (switcherItem == nil) { int i, n; NSArray * projectModules; NSMenu * modulesMenu; NSMenuItem * menuItem; switcherItem = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier]; moduleSwitcher = [[NSPopUpButton alloc] initWithFrame: NSMakeRect(0, 0, 100, 22)]; modulesMenu = [[[NSMenu alloc] initWithTitle: _(@"Project Modules")] autorelease]; modulesMenu = nil; projectModules = [owner projectModules]; for (i = 0, n = [projectModules count]; i < n; i++) { id module = [projectModules objectAtIndex: i]; NSString * name = [[module class] humanReadableModuleName]; NSMenuItem * item; [moduleSwitcher addItemWithTitle: name]; [[moduleSwitcher lastItem] setKeyEquivalent: [NSString stringWithFormat: @"%i", i + 1]]; item = [[[NSMenuItem alloc] initWithTitle: name action: @selector(switchView:) keyEquivalent: nil] autorelease]; [item setTarget: self]; [item setTag: i]; [modulesMenu addItem: item]; } [modulesMenu sizeToFit]; menuItem = [[[NSMenuItem alloc] initWithTitle: _(@"Project Modules") action: NULL keyEquivalent: nil] autorelease]; [menuItem setSubmenu: modulesMenu]; [moduleSwitcher setTarget: self]; [moduleSwitcher setAction: @selector(switchView:)]; [moduleSwitcher selectItemAtIndex: 0]; [switcherItem setLabel: _(@"Switch Module")]; [switcherItem setPaletteLabel: _(@"Switch Module")]; [switcherItem setToolTip: _(@"Switches between project modules")]; [switcherItem setMenuFormRepresentation: menuItem]; [switcherItem setView: moduleSwitcher]; [switcherItem setMinSize: NSMakeSize(100, NSHeight([moduleSwitcher frame]))]; [switcherItem setMaxSize: NSMakeSize(200, NSHeight([moduleSwitcher frame]))]; } return switcherItem; } // otherwise ask the module responsible for the toolbar to create it else { return [currentModule toolbarItemForItemIdentifier: itemIdentifier]; } } /** * Returns the allowed item identifiers for a project window toolbar. * The allowed item identifiers are first our project module switcher, * then any of the standard toolbar items (NSToolbarSeparatorItemIdentifier * etc.) and then any item identifiers returned when sending the responsible * module a allowedToolbarItemIdentifiers message. */ - (NSArray*) toolbarAllowedItemIdentifiers: (NSToolbar*)toolbar { NSMutableArray * array = [NSMutableArray array]; NSEnumerator * e = [[owner projectModules] objectEnumerator]; id module; [array addObjectsFromArray: standardToolbarItems]; while ((module = [e nextObject]) != nil) { [array addObjectsFromArray: [module toolbarItemIdentifiers]]; } return array; } /** * Returns the default item identifiers for a project window toolbar. * The default item identifiers are first our project module switcher, * then a separator and then any item identifiers returned when sending * the responsible module a defaultToolbarItemIdentifiers message. */ - (NSArray*) toolbarDefaultItemIdentifiers: (NSToolbar*)toolbar { return [NSArray arrayWithObjects: ProjectModuleSwitcherToolbarItemIdentifier, NSToolbarSeparatorItemIdentifier, nil]; } - (BOOL) validateToolbarItem: (NSToolbarItem *) toolbarItem { NSString * identifier = [toolbarItem itemIdentifier]; // these two are always valid if ([identifier isEqualToString: ProjectModuleSwitcherToolbarItemIdentifier]) { return YES; } else { return [currentModule validateToolbarItem: toolbarItem]; } } /** * Notification method invoked when the project name changes - we then * update our window's title to hold it. */ - (void) projectNameChanged: (NSNotification *) notif { [myWindow setTitle: [owner projectName]]; } /** * Appends a message to the project log. * * @param aMessage The message which to append. It does not have * to end with a newline character - one is automatically * appended if necessary. */ - (void) logMessage: (NSString *) aMessage { NSCalendarDate * calendarDate = [NSCalendarDate calendarDate]; // append a trailing newline character if necessary if ([aMessage characterAtIndex: [aMessage length] - 1] != '\n') { aMessage = [aMessage stringByAppendingString: @"\n"]; } // prepend the current time to the message aMessage = [NSString stringWithFormat: @"%.2i:%.2i:%.2i %@", [calendarDate hourOfDay], [calendarDate minuteOfHour], [calendarDate secondOfMinute], aMessage]; [log replaceCharactersInRange: NSMakeRange([[log textStorage] length], 0) withString: aMessage]; [log scrollRangeToVisible: NSMakeRange([[log textStorage] length], 0)]; } @end