
/*
    MiscWindowManager_Protected.m
*/

// RCS identification information
static char *rcsID = "$Id:$";
static void __AvoidCompilerWarning(void) {if(!rcsID)__AvoidCompilerWarning();}

// NeXT Headers
#import <AppKit/AppKit.h>

// Other Headers
#import <MiscControllerKit/MiscNibManager.h>
#import "MiscWindowManager.h"


@implementation MiscWindowManager (Protected)

/*"
    This protected category of MiscWindowManager should only be
    used by this class or subclasses. 
"*/


//-------------------------------------------------------------------
// 	Customizing the Window close
//-------------------------------------------------------------------

- (NSString*) _closeAlertMessage
/*"
    Subclasses that allow editing should implement this method to return 
    the title of the Alert panel that that appears when a dirty main Window 
    is closed, or nil to indicate that no close Panel should be displayed. 
    The default is nil. 
"*/
{
    return @"Close Window?";
}

- (SEL) _closeAction
/*"
    Subclasses that allow editing should implement this method to return 
    the method that will be called when the user closes the main Window, 
    or NULL to indicate that no selector should be invoked.  The default 
    is NULL. This message is only dispatched when the user chooses the
    default button (rightmost button, usually "Save" or "OK") when 
	presented with the close panel.
"*/
{
    return (SEL)NULL;
}

- (NSArray*) _closePanelButtonTitles
/*"
   Subclasses that allow editing should implement this method to return
   an NSArray of NSStrings containing the titles of the buttons in
   the warning Panel that appears when our window is closed. By
   default they are "OK" and "Cancel". This is the reverse
   they will appear in the buttons. You can have either two or three 		
   button titles. Whatever title you assign the buttons
   makes no difference on their action. The rightmost button is always the
   "the window's going to close and our #_closeAction will be sent". The 
   leftmost button is always cancel the window close. The middle button 
   (if there is one) means close but don't do the #_closeAction.
"*/
{
    return [NSArray arrayWithObjects:@"OK", @"Cancel", nil];
}

- (BOOL) _alertUserOfWindowClose:(NSWindow*)ourWindow
/*"
    Querys the user with an alert panel, asking if the window should really
    be closed. Returns YES if it should, or NO if they clicked Cancel. This
    is called from our #windowWillClose: method.
"*/
{
    BOOL	shouldClose = YES;
    int 	result = NSAlertDefaultReturn;
    SEL		myCloseAction = [self _closeAction];

    if (shouldClose && [self _closeAlertMessage] == nil) {
        shouldClose = NO;
        NSLog (@"MiscWindowManager _alertUserOfWindowClose: We normally "
               @"would have alerted the user to see if they wanted the "
               @"window closed, but we don't have a message to display "
               @"on the panel.");
    }

    // Only bring up alert panel is we are edited and visible.
    if (shouldClose && [self isDocumentEdited] &&
        [[self window] isVisible]) {
        NSArray* 	titles = [self _closePanelButtonTitles];
        int 		titlesCount = [titles count];

        // Run the alert panel.
        result = NSRunAlertPanel(@"Window Close", [self _closeAlertMessage],
                                 titlesCount > 0 ? [titles objectAtIndex:0] : @"OK",
                                 titlesCount > 1 ? [titles objectAtIndex:1] : nil,
                                 titlesCount > 2 ? [titles objectAtIndex:2] : nil);

        // We shouldn't close if they clicked on the Cancel button. Whether
		// there are two or three buttons we'll assume the last one is 
		// Cancel.
        if (titlesCount == 3) {
            shouldClose = (result == NSAlertOtherReturn) ? NO : YES;
        }
        else if (titlesCount == 2) {
            shouldClose = (result == NSAlertAlternateReturn) ? NO : YES;
        }
        
        // As long as we have a closeAction, perform it. It can return nil
        // to stop the whole close window operation.
        if (shouldClose && result == NSAlertDefaultReturn &&
            [self _shouldSendCloseAction]) {
            shouldClose = ([self performSelector:myCloseAction] == nil);
        }
    }

    return shouldClose;
}

- (BOOL) _shouldSendCloseAction
/*"
    Returns YES if we have a close action and also respond to it.
"*/
{
    BOOL 	sendCloseAction = YES;
    SEL		ourCloseAction = [self _closeAction];

    if (ourCloseAction == (SEL)NULL ||
        [self respondsToSelector:ourCloseAction]) {
        sendCloseAction = NO;
    }

    return sendCloseAction;
}

- (BOOL) _windowShouldClose:(NSWindow*)ourWindow
/*"
    This method gives our class one last chance to stop the window from
    closing. Overridding this method is preferrabled to extending 
    #windowWillClose:. This implementation just returns YES.
"*/
{
    return YES;
}

- (void) _windowClosed
/*"
    A hook for subclasses to clean up knowing the window is going to
    close (it will right after this call). This implemenation does nothing.
"*/
{
}

//-------------------------------------------------------------------
// 	Accessing the miniwindow icon
//-------------------------------------------------------------------

- (NSString*) _miniwindowIcon
/*"
**  Subclasses that allow the main Window to be miniaturized should implement 
**  this method to return the name of an icon to use in the miniwindow. 
**  This implementation returns nil. 
"*/
{
    return nil;
}


//-------------------------------------------------------------------
// 	Window frame autosave support
//-------------------------------------------------------------------

- (void) _possiblySetupWindowFrameAutosaving
/*"
**  Currently called from our #setAutosavesWindowFrame and 
**  #setWindowFrameName: to setup or disable window frame autosaving
**  as necessary.
"*/
{
    NSString*	windowFrameName = [self windowFrameName];

    // As long as we have a windowFrameName and we autosave then setup
    // the window to take care of it, otherwise we should stop autosaving
    // (if it happened to be working already or not).
    if ([self autosavesWindowFrame] && windowFrameName != nil) {
        [[self window] setFrameAutosaveName:windowFrameName];
    }
    else {
        [[self window] setFrameAutosaveName:@""];
    }
}

- (void) _tryToCascadeWindow
/*"
**  Attempts to cascade our window. We first look at the main window's 
**  controllerMgr to see if it is the same as ours. If it is then we'll
**  cascade from that. If not we look backwards through the window list
**  for the same controllerMgr and cascade from that window if it was
**  found. If no matching window can be found then we don't move at all.
"*/
{
    BOOL 	error = NO;
    NSWindow*	baseWindow = nil;

    // If we are already visible then we shouldn't cascade ourselves,
    // or it'll look silly when the window jumps. This also stops us from
    // cascading over and over if this is called while the window is onscreen.
    if (!error) {
        error = [self isVisible];
    }
	
    // First check if the main window has the same controller manager
    // class as we do. If it does then we'll use it's window position.
    if (!error) {
        id	mainWinDelegate;

        mainWinDelegate = [[NSApp mainWindow] delegate];
        if ([mainWinDelegate isKindOfClass:[self class]]) {
            baseWindow = [NSApp mainWindow];
        }
    }

    // If the main window wasn't what we wanted then look through
    // our other windows and pick the last one that has the same type
    // of controller manager.
    if (!error && baseWindow == nil) {
        NSArray* ourInstances;
        int instanceCount;
        
        ourInstances = [[self class] windowManagersOfClass:[self class]];
        instanceCount = [ourInstances count];
        if (instanceCount > 0) {
            baseWindow = [[ourInstances lastObject] window];

            // We may have already been added to the end of the windowList so
            // make sure we won't cascade from ourselves.
            if (baseWindow == [self window] && instanceCount > 1) {
                baseWindow = [[ourInstances objectAtIndex:instanceCount - 2]
                    window];
            }
        }
    }

    // As long as we have a base window to cascade from then do the
    // actual moving of our window.
    if (!error && baseWindow != nil) {
        NSRect	baseFrame;
        float	newX;
        float	newY;

        baseFrame = [baseWindow frame];
        newX = NSMinX(baseFrame) + _xOffset;
        newY = NSMinY(baseFrame) - _yOffset;

        [[self window] setFrameOrigin:NSMakePoint(newX, newY)];
    }
}

- (void) _initializeUI
/*"
	A place to initialize the UI. We cascade our window if we can find 
	a window of our own kind (really a Window with the same WindowManager)
	to cascade from.
"*/
{
	// Check our window outlet. If it's nil then log a warning.
	if ([self window] == nil) {
		NSLog(@"An instance of MiscWindowManager doesn't have a "
				@"window to take care of. Why not?");
	}

	// This is probably done in the nib but just in case
	[[self window] setDelegate:self];

	// Set up the window for autosaving if we autosave and
	// if we have a frame name.
	[self _possiblySetupWindowFrameAutosaving];

    if ([self autoCascadesWindow]) {
        // We will only try to cascade the window if we can find
        // the same kind (same type of controllerMgr) as ours.
        [self _tryToCascadeWindow];
    }
}

@end


@implementation MiscWindowManager (MiscNibManagerNotification)

- (void) _notifyNibManagerOfRelease
/*"
**  We are going to close so notify our nib manager so they can remove 
**  us from their list.
"*/
{
    [[self nibManager] windowManagerWillClose:self];
}

@end


@implementation MiscWindowManager (NibNotification)

- (void) awakeFromNib
{
	[self _initializeUI];
}

@end

