// Copyright 1997-1999 Omni Development, Inc.  All rights reserved.
//
// This software may only be used and reproduced according to the
// terms in the file OmniSourceLicense.html, which should be
// distributed with this project and can also be found at
// http://www.omnigroup.com/DeveloperResources/OmniSourceLicense.html.

#import "OAPlusTableView.h"

#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <OmniBase/OmniBase.h>
#import <OmniFoundation/OmniFoundation.h>

RCS_ID("$Header: /Network/Developer/Source/CVS/OmniGroup/OmniAppKit/Widgets.subproj/OAPlusTableView.m,v 1.8 1999/03/30 21:26:11 kc Exp $")

@interface OAPlusTableView (PrivateAPI)
+ (NSImage *)_plusMinusImage;
+ (NSImage *)_checkImage;
+ (NSImage *)_noCheckImage;
- (void)_commonInit;
- (void)_addItemWithTableColumn:(NSTableColumn *)column;
- (void)_removeItemWithTableColumn:(NSTableColumn *)column;
- (void)_selectColumn:(id)sender;

@end

@implementation OAPlusTableView

//
// Overridden from superclass
//

- initWithFrame:(NSRect)frame;
{
    if (!(self = [super initWithFrame:frame]))
        return nil;

    [self _commonInit];
    
    return self;
}

- initWithCoder:(NSCoder *)coder;
{
    if (!(self = [super initWithCoder:coder]))
        return nil;

    [self _commonInit];

    return self;
}

- (void)dealloc;
{
    [inactiveTableColumns release];
    [popUpMatrix release];
    [popUpWindow release];
    [super dealloc];
}

// Turning this off since it kinda sucks -- the tableView doesn't necessarily become firstResponder when the headerView is clicked on and I'm too content with the other UI I worked out to add this less obvious way to inactivate table columns
#if 0
- (void)keyDown:(NSEvent *)event;
{
    if ([self numberOfSelectedColumns]) {
        unichar character;

        character = [[event characters] characterAtIndex:0];
        if (character == 0x7f) { // Should define this character
            NSMutableArray *selectedColumns;
            NSEnumerator *columnEnum;
            NSTableColumn *column;
            NSNumber *columnIndex;
            NSArray *tableColumns;
            
            // Getting our own array rather than using the one in the tableView
            // since we don't know if we can modified the selected set while
            // looping over that enumerator.

            selectedColumns = [NSMutableArray array];
            tableColumns = [self tableColumns];
            columnEnum = [self selectedColumnEnumerator];
            while ((columnIndex = [columnEnum nextObject]))
                [selectedColumns addObject:[tableColumns objectAtIndex:[columnIndex intValue]]];

            columnEnum = [selectedColumns objectEnumerator];
            while ((column = [columnEnum nextObject]))
                [self inactivateTableColumn:column];

            // Relayout the table columns
            [self tile];
            
            return;
        }
    }

    [super keyDown:event];
}
#endif

//
// New API
//

- (NSArray *)inactiveTableColumns;
{
    return inactiveTableColumns;
}

- (void)activateTableColumn:(NSTableColumn *)column;
{
    if ([[self tableColumns] indexOfObject:column] != NSNotFound)
        // Already active
        return;

    if ([inactiveTableColumns indexOfObject:column] == NSNotFound)
        // Cannot activate a column that is not inactive.
        // Should probably raise an exception here.
        return;

    [self addTableColumn:column];
    [inactiveTableColumns removeObject:column];
}

- (void)inactivateTableColumn:(NSTableColumn *)column;
{
    if ([inactiveTableColumns indexOfObject:column] != NSNotFound)
        // Already inactive
        return;

    if ([[self tableColumns] indexOfObject:column] == NSNotFound)
        // Cannot inactivate a column that is not active
        // Should probably raise an exception here
        return;

    [inactiveTableColumns addObject:column];
    [self removeTableColumn:column];
}

- (BOOL)isTableColumnActive:(NSTableColumn *)column;
{
    return [[self tableColumns] indexOfObject:column] != NSNotFound;
}

- (void)addInactiveTableColumn:(NSTableColumn *)column;
{
    [inactiveTableColumns addObject:column];
}

- (void)removeInactiveTableColumn:(NSTableColumn *)column;
{
    [inactiveTableColumns removeObject:column];
}

@end


@implementation OAPlusTableView (PrivateAPI)

+ (NSImage *)_plusMinusImage;
{
    static NSImage *plusMinusImage = nil;

    if (!plusMinusImage) {
        NSBundle *bundle;
        NSString *path;

        bundle = [NSBundle bundleForClass:(Class)self];
        path = [bundle pathForImageResource:@"OAPlusMinus"];

        plusMinusImage = [[NSImage alloc] initWithContentsOfFile:path];
    }

    return plusMinusImage;
}

+ (NSImage *)_checkImage;
{
    static NSImage *checkImage = nil;

    if (!checkImage) {
        NSBundle *bundle;
        NSString *path;

        bundle = [NSBundle bundleForClass:(Class)self];
        path = [bundle pathForImageResource:@"OACheck"];

        checkImage = [[NSImage alloc] initWithContentsOfFile:path];
    }

    return checkImage;
}

+ (NSImage *)_noCheckImage;
{
    static NSImage *noCheckImage = nil;

    if (!noCheckImage) {
        NSBundle *bundle;
        NSString *path;

        bundle = [NSBundle bundleForClass:(Class)self];
        path = [bundle pathForImageResource:@"OANoCheck"];

        noCheckImage = [[NSImage alloc] initWithContentsOfFile:path];
    }

    return noCheckImage;
}

// Unfortunately, NSPopUpList is private API, so we have to implement some good portion of the guts of NSPopUpButton.  Sigh.
- (void)_commonInit;
{
    NSRect cornerRect;
    NSButton *plusView;
    NSView *cornerView;
    NSEnumerator *tableColumnEnum;
    NSTableColumn *tableColumn;
    NSButtonCell *prototype;

    inactiveTableColumns = [[NSMutableArray alloc] init];

    cornerView = [self cornerView];
    cornerRect = [cornerView frame];

    plusView = [[NSButton alloc] initWithFrame:cornerRect];
    [plusView setImage:[isa _plusMinusImage]];
    [plusView setImagePosition:NSImageOnly];
    [plusView setTarget:self];
    [plusView setAction:@selector(_selectColumn:)];
    [(NSControl *)[plusView cell] sendActionOn:NSLeftMouseDownMask];

    popUpMatrix = [[NSMatrix alloc] initWithFrame:cornerRect mode:NSHighlightModeMatrix cellClass:[NSButtonCell class] numberOfRows:0 numberOfColumns:1];

    // This is a trick way of getting a prototype set up.  NSMatrix does
    // some settings to the cells it creates when NSButtonCel is the class.
    [popUpMatrix addRow];
    prototype = [popUpMatrix cellAtRow:[popUpMatrix numberOfRows] - 1 column:0];
    [popUpMatrix setPrototype:prototype];
    [popUpMatrix setCellClass:nil];
    [popUpMatrix removeRow:[popUpMatrix numberOfRows] - 1];

    [popUpMatrix setAutosizesCells:YES];
    // prototype = [[NSButtonCell alloc] init];
    [prototype setImagePosition:NSImageLeft];
    [prototype setType:NSTextCellType];
    [prototype setWraps:NO];
    [prototype setButtonType:NSMomentaryLight];
    [prototype setHighlightsBy:NSChangeBackgroundCellMask];
    [prototype setAlignment:NSLeftTextAlignment];

    popUpWindow = [[NSWindow alloc] initWithContentRect:cornerRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreRetained defer:NO];
    [popUpWindow setContentView:popUpMatrix];
    [popUpWindow setLevel:NSSubmenuWindowLevel];
    [popUpWindow setAcceptsMouseMovedEvents:YES];

    [self setCornerView:plusView];
    [plusView release];

    [self setAllowsColumnSelection:YES];

    // Add items for the initial set of columns
    tableColumnEnum = [[self tableColumns] objectEnumerator];
    while ((tableColumn = [tableColumnEnum nextObject]))
        [self _addItemWithTableColumn:tableColumn];

    // Have the matrix call back when something is actually selected
    [popUpMatrix setTarget:self];
    [popUpMatrix setAction:@selector(_matrixActed:)];
}

- (void)_addItemWithTableColumn:(NSTableColumn *)column;
{
    NSButtonCell *cell;

    [popUpMatrix addRow];
    cell = [popUpMatrix cellAtRow:[popUpMatrix numberOfRows] - 1 column:0];

    [cell setTitle:[[column headerCell] stringValue]];
    [cell setRepresentedObject:column];
}

- (void)_removeItemWithTableColumn:(NSTableColumn *)column;
{
    NSArray *cells;
    unsigned int row;
    NSButtonCell *cell;
    
    cells = [popUpMatrix cells];
    for (row = [cells count] - 1; row >= 0; row--) {
        cell = [cells objectAtIndex:row];
        if ([cell representedObject] == column) {
            [popUpMatrix removeRow:row];
            return;
        }
    }
}

static int _orderCells(id cell1, id cell2, void *context)
{
    return [[cell1 title] compare:[cell2 title]];
}

- (void)_selectColumn:(id)sender;
{
    NSButton *cornerView;
    NSRect cornerBounds, topCellRect, matrixFrame;
    NSPoint popUpPoint;
    NSEvent *currentEvent, *fakeEvent;
    NSEnumerator *cellEnum;
    NSButtonCell *cell;
    NSArray *tableColumns;
    
    
    // Sort the cells by the title
    [popUpMatrix sortUsingFunction:_orderCells context:NULL];
    
    // Set the enabled-ness based on the current activeness and delegate
    tableColumns = [self tableColumns];
    cellEnum = [[popUpMatrix cells] objectEnumerator];
    while ((cell = [cellEnum nextObject])) {
        BOOL enabled;
        NSImage *image;
        NSTableColumn *column;

        column = [cell representedObject];
        if ([tableColumns indexOfObjectIdenticalTo:column] == NSNotFound)
            image = [isa _noCheckImage];
        else
            image = [isa _checkImage];
        
        [cell setImage:image];
        [cell setImagePosition:NSImageLeft];

        // Reset the title in case the column title has changed since last time
        // we were displayed
        [cell setTitle:[[column headerCell] stringValue]];
        
        enabled = YES;
        if ([_delegate respondsToSelector:@selector(tableView:shouldAllowActivationOfColumn:)] && ![_delegate tableView:self shouldAllowActivationOfColumn:column])
            enabled = NO;
        [cell setEnabled:enabled];
    }
    
    // Update the size of the matrix and window.
    [popUpMatrix sizeToFit];
    [popUpMatrix sizeToCells];
    matrixFrame = [popUpMatrix frame];
    //matrixFrame.size.width += 10;  // give a little extra room
    //[popUpMatrix setFrame:matrixFrame];
    [popUpWindow setContentSize:matrixFrame.size];
    
    // Update the position of the window
    cornerView = (NSButton *)[self cornerView];
    cornerBounds = [cornerView bounds];
    popUpPoint = cornerBounds.origin;
    popUpPoint.y += NSHeight(cornerBounds) + 1.0; // Since we are flipped
    popUpPoint = [cornerView convertPoint:popUpPoint toView:nil];
    popUpPoint = [[self window] convertBaseToScreen:popUpPoint];

    [popUpWindow setFrameTopLeftPoint:popUpPoint];
    
    // Display the matrix of cells and let the user pick one

    currentEvent = [NSApp currentEvent];

    // We need to build an event to convine the NSMatrix that it should start
    // tracking the mouse.  If the event is not inside one of its cells, it
    // will not track correctly.  We will put the location in the top cell
    // (the closest one to the cornerView that the user clicked on).
    topCellRect = [popUpMatrix cellFrameAtRow:[popUpMatrix numberOfRows] - 1 column:0];

    fakeEvent =
        [NSEvent mouseEventWithType:[currentEvent type]
                           location:NSMakePoint(NSMinX(topCellRect) + 1.0, NSMinY(topCellRect) + 1.0)
                      modifierFlags:[currentEvent modifierFlags]
                          timestamp:[currentEvent timestamp]
                       windowNumber:[popUpWindow windowNumber]
                            context:[NSDPSContext currentContext]
                        eventNumber:[currentEvent eventNumber]
                         clickCount:[currentEvent clickCount]
                           pressure:[currentEvent pressure]];
    [popUpWindow orderFront:nil];
    [popUpMatrix mouseDown:fakeEvent];
    [popUpWindow orderOut:nil];
}

- (void)_matrixActed:(id)sender;
{
    NSCell *cell;
    NSTableColumn *column;
    
    OBPRECONDITION(sender == popUpMatrix);
    
    cell = [sender selectedCell];
    if (!cell)
        return;

    column = [cell representedObject];
    if ([self isTableColumnActive:column])
        [self inactivateTableColumn:column];
    else
        [self activateTableColumn:column];
    
    [self tile];
    [self sizeToFit];
}

@end
