/*
SRSourceWindowController.m

Author: Makoto Kinoshita

Copyright 2004 The Shiira Project. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted 
provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions 
  and the following disclaimer.

  2. Redistributions in binary form must reproduce the above copyright notice, this list of 
  conditions and the following disclaimer in the documentation and/or other materials provided 
  with the distribution.

THIS SOFTWARE IS PROVIDED BY THE SHIIRA PROJECT ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE SHIIRA PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
POSSIBILITY OF SUCH DAMAGE.
*/

#import "SRDefaultsKey.h"

#import "SRAppDelegate.h"
#import "SRSourceWindowController.h"

#import "AppKitEx.h"
#import "CGSPrivate.h"

static NSMutableArray* _SRSourceWindows;

enum {
    _SRHTTPHeaderInitialRequestTag = 0, 
    _SRHTTPHeaderFinalRequestTag, 
    _SRHTTPHeaderRequestTag, 
    _SRHTTPHeaderResposneTag, 
};

@implementation SRSourceWindowController

//--------------------------------------------------------------//
#pragma mark -- Initialize --
//--------------------------------------------------------------//

+ (NSMutableArray*)sourceWindows
{
    if(_SRSourceWindows==nil){
        _SRSourceWindows=[[NSMutableArray alloc]initWithCapacity:8];
    }
    return _SRSourceWindows;
}

+ (SRSourceWindowController*)sourceWindowForURL:(NSURL*)URL
{
    NSMutableArray* sourceWindows=[SRSourceWindowController sourceWindows];
    int i, cnt=[sourceWindows count];
    for(i=0;i<cnt;i++){
        SRSourceWindowController*   srcCtl=[sourceWindows objectAtIndex:i];
        NSURL*  aURL=[srcCtl URL];
        if([URL isEqual:aURL]) return srcCtl;
    }
    return nil;
}

+ (SRSourceWindowController*)sourceWindowWithWebDataSource:(WebDataSource*)dataSource
{
    SRSourceWindowController*   sourceWindowController;
    NSURL*  URL=[[dataSource request]URL];
    
    //check
    if(![[dataSource representation]canProvideDocumentSource]){
        return nil;
    }
    
    sourceWindowController=[SRSourceWindowController sourceWindowForURL:URL];
    
    if (!sourceWindowController) {
        sourceWindowController = [[SRSourceWindowController alloc] 
                initWithWindowNibName:@"SourceCode"];
        if (!sourceWindowController) {
            // Fatal error
            SR_FATAL(@"Could not load SourceCode.nib");
        }
        [sourceWindowController autorelease];
        [[SRSourceWindowController sourceWindows]addObject:sourceWindowController];
        [sourceWindowController loadWindow];
    }
    [sourceWindowController setDataSource:dataSource];
    
    return sourceWindowController;
}

+ (SRSourceWindowController*)sourceWindowWithXMLDocument:(SRXMLDocument*)XMLDocument
{
    SRSourceWindowController*   sourceWindowController;
    sourceWindowController = [[SRSourceWindowController alloc] initWithWindowNibName:@"SourceCode"];
    [sourceWindowController autorelease];
    [[SRSourceWindowController sourceWindows]addObject:sourceWindowController];
    [sourceWindowController loadWindow];
    
    [sourceWindowController setXMLDocument:XMLDocument];
    
    return sourceWindowController;
}

- (void)awakeFromNib
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    _copiedDOMNodes = [[NSMutableArray array] retain];
    [_disclosureBtn setState:NSOnState];
    
    // Update transparancy
    [self updateTransparency];
    
    // Register key value observation
    [defaults addObserver:self 
            forKeyPath:SRSourceFontName 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceFontSize 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceDefaultColor 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceBackgroundColor 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceTagColor 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceCommentColor 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceTransparentWindow 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    [defaults addObserver:self 
            forKeyPath:SRSourceAlpha 
            options:NSKeyValueObservingOptionNew 
            context:NULL];
    
    [[self window]setFrameAutosaveName:@"SRSourceWindow"];
    [[self window]setFrameUsingName:@"SRSourceWindow"];
}

- (void)windowDidLoad
{
    // Update transparancy
    [self updateTransparency];
}

- (void)dealloc
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    [defaults removeObserver:self forKeyPath:SRSourceFontName];
    [defaults removeObserver:self forKeyPath:SRSourceFontSize];
    [defaults removeObserver:self forKeyPath:SRSourceDefaultColor];
    [defaults removeObserver:self forKeyPath:SRSourceBackgroundColor];
    [defaults removeObserver:self forKeyPath:SRSourceTagColor];
    [defaults removeObserver:self forKeyPath:SRSourceCommentColor];
    [defaults removeObserver:self forKeyPath:SRSourceTransparentWindow];
    [defaults removeObserver:self forKeyPath:SRSourceAlpha];
    
    [_dataSource release];
    [_copiedDOMNodes release];
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Transparent --
//--------------------------------------------------------------//

- (void)updateTransparency
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    BOOL    useTransparent;
    float   alpha;
    useTransparent = [defaults boolForKey:SRSourceTransparentWindow];
    if (useTransparent) {
        alpha = [defaults floatForKey:SRSourceAlpha];
    }
    else {
        alpha = 1.0f;
    }
    
    // Make transparent
#if 1
    [[self window] setOpaque:NO];
    [[self window] setAlphaValue:alpha];
#else
    CGSConnection   cid;
    cid = _CGSDefaultConnection();
    CGSSetWindowAlpha(cid, [[self window] windowNumber], alpha);
#endif
}

//--------------------------------------------------------------//
#pragma mark -- Accessors --
//--------------------------------------------------------------//

- (void)_copyDOMNode:(DOMNode*)node inArray:(NSMutableArray*)array
{
    // Copy DOM node attributes to dictionary
    NSMutableDictionary*    dict;
    dict = [NSMutableDictionary dictionary];
    
    NSString*   nodeName;
    if (nodeName = [node nodeName]) {
        [dict setObject:nodeName forKey:@"type"];
    }
    
    [dict setObject:node forKey:@"node"];
    [dict setObject:NSStringFromClass([node class]) forKey:@"class"];
    
    // Copy children
    if ([node hasChildNodes]) {
        NSMutableArray* childrenArray;
        childrenArray = [NSMutableArray array];
        
        // Get children
        DOMNodeList*    children;
        children = [node childNodes];
        
        unsigned long   i;
        for (i = 0; i < [children length]; i++) {
            // Get child
            DOMNode*    child;
            child = [children item:i];
            
            // Copy child
            [self _copyDOMNode:child inArray:childrenArray];
        }
        
        [dict setObject:childrenArray forKey:@"children"];
    }
    
    // Add copied node
    [array addObject:dict];
}

- (void)_copyDOMDocumentToTreeController
{
    // Get DOM document
    DOMDocument*    document;
    document = [[_dataSource webFrame] DOMDocument];
    
    // Copy DOM document
    [_copiedDOMNodes removeAllObjects];
    [self _copyDOMNode:document inArray:_copiedDOMNodes];
}

- (void)_copyXMLNode:(SRXMLNode*)node inArray:(NSMutableArray*)array
{
    // Copy XML node attributes to dictionary
    NSMutableDictionary*    dict;
    dict = [NSMutableDictionary dictionary];
    
    NSString*   nodeName;
    nodeName = [node name];
    if (!nodeName) {
        nodeName = @"(No Name)";
    }
    [dict setObject:nodeName forKey:@"type"];
    
    [dict setObject:node forKey:@"node"];
    //[dict setObject:NSStringFromClass([node class]) forKey:@"class"];
    
    // Copy children
    if ([node childCount] > 0) {
        NSMutableArray* childrenArray;
        childrenArray = [NSMutableArray array];
        
        // Get children
        NSArray*    children;
        children = [node children];
        
        unsigned long   i;
        for (i = 0; i < [children count]; i++) {
            // Get child
            SRXMLNode*  child;
            child = [children objectAtIndex:i];
            
            // Copy child
            [self _copyXMLNode:child inArray:childrenArray];
        }
        
        [dict setObject:childrenArray forKey:@"children"];
    }
    
    // Add copied node
    [array addObject:dict];
}

- (void)_copyXMLDocumentToTreeController
{
    // Copy XML document
    [_copiedDOMNodes removeAllObjects];
    [self _copyXMLNode:(SRXMLNode*)_XMLDocument inArray:_copiedDOMNodes];
}

- (WebDataSource*)dataSource
{
    return _dataSource;
}

- (void)setDataSource:(WebDataSource*)dataSource;
{
    if(_dataSource != dataSource){
        id<WebDocumentRepresentation>   representation;
        NSString*                       documentSource;

        [_dataSource release];
        _dataSource=[dataSource retain];

        if (!dataSource) {
            return;
        }

        // Get page source        
        representation = [dataSource representation];
        if (!representation) {
            return;
        }
        documentSource = [representation documentSource];
        if (!documentSource) {
            return;
        }
        [self setSource:documentSource];

        // Configure header popup
        [_headerKindPopup removeAllItems];
        
        NSURLRequest*   initialRequest;
        NSURLRequest*   request;
        initialRequest = [dataSource initialRequest];
        request = [dataSource request];
        if (request && [[request allHTTPHeaderFields] count] > 0) {
#if 1
            [_headerKindPopup addItemWithTitle:NSLocalizedString(@"Request", @"Request")];
            [[_headerKindPopup lastItem] setTag:_SRHTTPHeaderRequestTag];
#else
            if (initialRequest && [[initialRequest allHTTPHeaderFields] count] > 0) {
                [_headerKindPopup addItemWithTitle:@"Initial Request"];
                [[_headerKindPopup lastItem] setTag:_SRHTTPHeaderInitialRequestTag];
                [_headerKindPopup addItemWithTitle:@"Final Request"];
                [[_headerKindPopup lastItem] setTag:_SRHTTPHeaderFinalRequestTag];
            }
            else {
                [_headerKindPopup addItemWithTitle:NSLocalizedString(@"Request", @"Request")];
                [[_headerKindPopup lastItem] setTag:_SRHTTPHeaderRequestTag];
            }
#endif
        }
        if ([dataSource response]) {
            [_headerKindPopup addItemWithTitle:NSLocalizedString(@"Response", @"Response")];
            [[_headerKindPopup lastItem] setTag:_SRHTTPHeaderResposneTag];
        }
        
        [_HTTPHeadersTable reloadData];
        
        if([self title]) [[self window]setTitle:[self title]];
    }
    
    // Set document type
    id<NSMenuItem>  menuItem;
    menuItem = [_documentTypePopup itemAtIndex:0];
    [menuItem setTitle:@"HTML"];
    
    // Copy DOM document
    [self _copyDOMDocumentToTreeController];
    [_DOMOutlineView reloadData];
}

- (SRXMLDocument*)XMLDocument
{
    return _XMLDocument;
}

- (void)setXMLDocument:(SRXMLDocument*)XMLDocument;
{
    [_XMLDocument release];
    _XMLDocument = [XMLDocument retain];
    if (!_XMLDocument) {
        return;
    }
    
    // Get source
    NSString*           documentSource;
    NSString*           characterEncoding;
    NSStringEncoding    encoding = NSUTF8StringEncoding;
    characterEncoding = [XMLDocument characterEncoding];
    if (characterEncoding) {
        CFStringEncoding    cfEncoding;
        cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)characterEncoding);
        if (cfEncoding != kCFStringEncodingInvalidId) {
            encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
        }
    }
    documentSource = [[NSString alloc] 
            initWithData:[XMLDocument XMLDataWithOptions:NSXMLNodePrettyPrint] encoding:encoding];
    [documentSource autorelease];
    if (!documentSource) {
        NSLog(@"documentSource is nil");
    }
    else {
        [self setSource:documentSource];
    }
    
    // Configure header popup
    [_headerKindPopup removeAllItems];
    
    if([self title]) [[self window]setTitle:[self title]];
    
    // Set document type
    id<NSMenuItem>  menuItem;
    menuItem = [_documentTypePopup itemAtIndex:0];
    [menuItem setTitle:@"XML"];
    
    // Copooy XML node
    [self _copyXMLDocumentToTreeController];
    [_DOMOutlineView reloadData];
}

- (NSString*)title
{
    return [_dataSource pageTitle];
}

- (NSURL*)URL
{
    return [[_dataSource request]URL];
}

- (NSTextView*)sourceTextView
{
    return _sourceTextView;
}

- (NSTextView*)DOMTextView
{
    return _DOMTextView;
}

- (NSTextView*)selectedTextView
{
    int index;
    index = [_sourceTab indexOfTabViewItem:[_sourceTab selectedTabViewItem]];
    
    if (index == 0) {
        return [self sourceTextView];
    }
    if (index == 1) {
        return [self DOMTextView];
    }
    
    return nil;
}

- (void)setSource:(NSString*)source
{
    if (!source) {
        return;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get back color
    NSColor*    backColor;
    if (!(backColor = [NSUnarchiver unarchiveObjectWithData:[defaults objectForKey:SRSourceBackgroundColor]])) {
        backColor = [NSColor whiteColor];
    }
    [_sourceTextView setBackgroundColor:backColor];
    
    // Get colorized source string
    NSAttributedString* attrString;
    attrString = [self colorizedSourceOfString:source];
    
    // Set string
    [[_sourceTextView textStorage] setAttributedString:attrString];
    [_sourceTextView setSelectedRange:NSMakeRange(0, 0)];
}

- (void)setDOMSource:(NSString*)source
{
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get back color
    NSColor*    backColor;
    if (!(backColor = [NSUnarchiver unarchiveObjectWithData:[defaults objectForKey:SRSourceBackgroundColor]])) {
        backColor = [NSColor whiteColor];
    }
    [_DOMTextView setBackgroundColor:backColor];
    
    // Get colorized source string
    NSAttributedString* attrString;
    attrString = [self colorizedSourceOfString:source];
    
    // Set string
    [[_DOMTextView textStorage] setAttributedString:attrString];
    [_DOMTextView setSelectedRange:NSMakeRange(0, 0)];
}

- (NSAttributedString*)colorizedSourceOfString:(NSString*)source
{
    if (!source) {
        return nil;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get colors
    static NSColor* _textColor = nil;
    static NSColor* _tagColor = nil;
    static NSColor* _commentColor = nil;
    if (!_textColor) {
        _textColor = [[NSColor blackColor] retain];
        _tagColor = [[NSColor colorWithDeviceRed:0.1 green:0.1 blue:0.648 alpha:1.0] retain];
        _commentColor = [[NSColor colorWithDeviceRed:0.632 green:0 blue:0 alpha:1.0] retain];
    }
    
    NSColor*    textColor;
    if (!(textColor = [NSUnarchiver unarchiveObjectWithData:[defaults objectForKey:SRSourceDefaultColor]])) {
        textColor = _textColor;
    }
    NSColor*    tagColor;
    if (!(tagColor = [NSUnarchiver unarchiveObjectWithData:[defaults objectForKey:SRSourceTagColor]])) {
        tagColor = _tagColor;
    }
    NSColor*    commentColor;
    if (!(commentColor = [NSUnarchiver unarchiveObjectWithData:[defaults objectForKey:SRSourceCommentColor]])) {
        commentColor = _commentColor;
    }
    
    // Get font
    NSString*   fontName;
    if (!(fontName = [defaults stringForKey:SRSourceFontName])) {
        fontName = @"Monaco";
    }
    int fontSize;
    if ((fontSize = [defaults integerForKey:SRSourceFontSize]) < 1) {
        fontSize = 12;
    }
    
    NSFont* font;
    font = [NSFont fontWithName:fontName size:fontSize];
    
    // Create attributed string
    NSMutableAttributedString*  attrString;
    attrString = [[NSMutableAttributedString alloc] initWithString:source];
    [attrString autorelease];
    [attrString addAttribute:NSForegroundColorAttributeName 
            value:textColor range:NSMakeRange(0, [attrString length])];
    
    // Scan source
    NSScanner*  scanner;
    scanner = [NSScanner scannerWithString:source];
    while (![scanner isAtEnd]) {
        // Find next '<' character
        int tagStart, tagEnd;
        [scanner scanUpToString:@"<" intoString:nil];
        tagStart = [scanner scanLocation];		
        
        // For '<!--'
        if ([source length] > tagStart + 6 && 
            [[source substringWithRange:NSMakeRange(tagStart + 1, 3)] isEqualToString:@"!--"])
        {
            // Find '-->'
            if ([scanner scanUpToString:@"-->" intoString:nil]) {
                tagEnd = [scanner scanLocation] + 3;
                if ([source length] > tagEnd) {
                    [attrString addAttribute:NSForegroundColorAttributeName 
                            value:commentColor range:NSMakeRange(tagStart, tagEnd - tagStart)];
                }
            }
        }
        else if ([source length] > tagStart) {
            // Find '>'
            if ([scanner scanUpToString:@">" intoString:nil]) {
                tagEnd = [scanner scanLocation] + 1;
                [attrString addAttribute:NSForegroundColorAttributeName
                        value:tagColor range:NSMakeRange(tagStart, tagEnd - tagStart)];
            }
        }
    }
    
    // Set font
    [attrString addAttribute:NSFontAttributeName
            value:font range:NSMakeRange(0, [attrString length])];
    
    return attrString;
}

//--------------------------------------------------------------//
#pragma mark -- Actions --
//--------------------------------------------------------------//

- (IBAction)disclosureBtnAction:(id)sender
{
//CGSConnection   connection;
//connection = _CGSDefaultConnection();
//CGSSetWindowAlpha(connection, [[self window] windowNumber], 0.5);
    [self _doDisclosure:[sender state]];
}

- (void)_doDisclosure:(BOOL)state
{
	NSArray* subviews = [_splitView subviews];
    if([subviews count]<2) return;
    
    NSView* topView=[subviews objectAtIndex:0];
    if(state){
        NSSize aSize=[topView frame].size;
        if(aSize.height==0){
            NSView* bottomView=[subviews objectAtIndex:1];
            NSSize  bSize=[bottomView frame].size;
            float   splitHeight=[_splitView frame].size.height;

            aSize.height=[_splitView savedFirstViewHeight];
            [topView setFrameSize:aSize];
            
            bSize.height=splitHeight-aSize.height-16;
            [bottomView setFrameSize:bSize];
            
        }
        
        // Show popup
        [_headerKindPopup setHidden:NO];
    }else{
        NSSize aSize=[topView frame].size;
        [_splitView setSavedFirstViewHeight:aSize.height];
        aSize.height=0;
        [topView setFrameSize:aSize];
        
        // Hide popup
        [_headerKindPopup setHidden:YES];
    }
	[_splitView adjustSubviews];
}

- (void)selectHeaderKindAction:(id)sender
{
    [_HTTPHeadersTable reloadData];
}

- (void)DOMOutlineSelectedAction:(id)sender
{
    // Get selected item
    id  item;
    item = [_DOMOutlineView itemAtRow:[_DOMOutlineView selectedRow]];
    if (!item) {
        [self setDOMSource:@""];
        return;
    }
    
    // Get node
    id  node = nil;
    if ([item respondsToSelector:@selector(objectForKey:)]) {
        node = [item objectForKey:@"node"];
    }
    if (!node) {
        [self setDOMSource:@""];
        return;
    }
    
    // Set outer HTML text
    if ([node respondsToSelector:@selector(outerHTML)]) {
        NSString*   outerHTML;
        outerHTML = [(DOMHTMLElement*)node outerHTML];
        if (!outerHTML) {
            outerHTML = @"";
        }
        
        [self setDOMSource:outerHTML];
        return;
    }
    
    // Set character data
    if ([node respondsToSelector:@selector(data)]) {
        NSString*   data;
        data = [(DOMCharacterData*)node data];
        if (!data) {
            data = @"";
        }
        
        [self setDOMSource:data];
        return;
    }
    
    // Set XML string
    if ([node respondsToSelector:@selector(XMLString)]) {
        NSString*   data;
        data = [node XMLStringWithOptions:NSXMLNodePrettyPrint];
        if (!data) {
            data = @"";
        }
        
        [self setDOMSource:data];
        return;
    }
    
    [self setDOMSource:@""];
}

- (void)selectSourceStyleAction:(id)sender
{
    // Get selected tag
    int tag;
    tag = [[sender selectedItem] tag];
    
    // Select tab
    [_sourceTab selectTabViewItemAtIndex:tag];
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView Delegate --
//--------------------------------------------------------------//

- (void)splitViewDidResizeSubviews:(NSNotification *)aNotification
{
    if([_splitView firstViewHeight]==0){
        [_disclosureBtn setState:NSOffState];
        [_headerKindPopup setHidden:YES];
    }else{
        [_disclosureBtn setState:NSOnState];
        [_headerKindPopup setHidden:NO];
    }

}

#if 0
- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedMin ofSubviewAt:(int)offset
{
    if(offset==0 && proposedMin<32)   return 32;
    else    return proposedMin;
}
#endif

//--------------------------------------------------------------//
#pragma mark -- NSTableDataSource --
//--------------------------------------------------------------//

- (NSDictionary*)_headerFields
{
    // Get selected header kind
    int kind;
    kind = [[_headerKindPopup selectedItem] tag];
    
    switch (kind) {
    case _SRHTTPHeaderInitialRequestTag: {
        return [[_dataSource initialRequest] allHTTPHeaderFields];
    }
    case _SRHTTPHeaderFinalRequestTag:
    case _SRHTTPHeaderRequestTag: {
        return [[_dataSource request] allHTTPHeaderFields];
    }
    case _SRHTTPHeaderResposneTag: {
        if ([[_dataSource response] respondsToSelector:@selector(allHeaderFields)]) {
            return [(NSHTTPURLResponse*)[_dataSource response] allHeaderFields];
        }
        break;
    }
    }
    
    return  nil;
}

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    if (tableView == _HTTPHeadersTable) {
        // Get header fields
        NSDictionary*   headerFields;
        headerFields = [self _headerFields];
        
        return [headerFields count];
    }
    
    return 0;
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    // Get identifier
    id  identifier;
    identifier = [tableColumn identifier];
    
    if (tableView == _HTTPHeadersTable) {
        // Get header fields
        NSDictionary*   headerFields;
        headerFields = [self _headerFields];
        
        // For key
        if ([identifier isEqualToString:@"key"]) {
            return [[headerFields allKeys] objectAtIndex:rowIndex];
        }
        
        // For value
        if ([identifier isEqualToString:@"value"]) {
            NSString*   key;
            key = [[headerFields allKeys] objectAtIndex:rowIndex];
            return [headerFields objectForKey:key];
        }
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlienView data source --
//--------------------------------------------------------------//

- (int)outlineView:(NSOutlineView*)outlineView numberOfChildrenOfItem:(id)item
{
    if (outlineView == _DOMOutlineView) {
        // For root
        if (!item) {
            return [_copiedDOMNodes count];
        }
        
        // For node
        id  children;
        children = [item objectForKey:@"children"];
        if ([children respondsToSelector:@selector(count)]) {
            return [children count];
        }
    }
    
    return 0;
}

- (BOOL)outlineView:(NSOutlineView*)outlineView isItemExpandable:(id)item
{
    if (outlineView == _DOMOutlineView) {
        // For root
        if (!item) {
            return [_copiedDOMNodes count] > 0;
        }
        
        // For node
        id  children;
        children = [item objectForKey:@"children"];
        if ([children respondsToSelector:@selector(count)]) {
            return [children count] > 0;
        }
    }
    
    return NO;
}

- (id)outlineView:(NSOutlineView*)outlineView child:(int)index ofItem:(id)item
{
    if (outlineView == _DOMOutlineView) {
        // For root
        if (!item) {
            return [_copiedDOMNodes objectAtIndex:index];
        }
        
        id  children;
        children = [item objectForKey:@"children"];
        if ([children respondsToSelector:@selector(objectAtIndex:)]) {
            return [children objectAtIndex:index];
        }
    }
    
    return nil;
}

- (id)outlineView:(NSOutlineView*)outlineView objectValueForTableColumn:(NSTableColumn*)tableColumn byItem:(id)item
{
    id  identifier;
    identifier = [tableColumn identifier];
    
    if (outlineView == _DOMOutlineView) {
        if ([item respondsToSelector:@selector(objectForKey:)]) {
            return [item objectForKey:identifier];
        }
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- NSOutlienView delegate --
//--------------------------------------------------------------//

- (void)outlineViewSelectionDidChange:(NSNotification*)notification
{
    [self DOMOutlineSelectedAction:self];
}

//--------------------------------------------------------------//
#pragma mark -- NSWindow delegate --
//--------------------------------------------------------------//

- (void)windowDidBecomeKey:(NSNotification*)notification
{
    // Update file menu
    [[[[NSApp mainMenu] itemWithTag:SRFileTag] submenu] update];
}

- (void)windowWillClose:(NSNotification*)notification
{
    [[SRSourceWindowController sourceWindows]removeObject:self];
}

//--------------------------------------------------------------//
#pragma mark -- Key value observation --
//--------------------------------------------------------------//

- (void)observeValueForKeyPath:(NSString*)keyPath 
        ofObject:(id)object 
        change:(NSDictionary*)change 
        context:(void *)context
{
    // Source code preferences
    if ([keyPath isEqualToString:SRSourceFontName] || 
        [keyPath isEqualToString:SRSourceFontSize] || 
        [keyPath isEqualToString:SRSourceDefaultColor] || 
        [keyPath isEqualToString:SRSourceBackgroundColor] || 
        [keyPath isEqualToString:SRSourceTagColor] || 
        [keyPath isEqualToString:SRSourceCommentColor])
    {
        // Update text
        id<WebDocumentRepresentation>   representation;
        NSString*                       documentSource;
        representation = [_dataSource representation];
        documentSource = [representation documentSource];
        if (!documentSource) {
            return;
        }
        
        [self setSource:documentSource];
        
        return;
    }
    if ([keyPath isEqualToString:SRSourceTransparentWindow] || 
        [keyPath isEqualToString:SRSourceAlpha])
    {
        [self updateTransparency];
    }
}

@end

#pragma mark -

@implementation SRSourceSplitView

- (float)savedFirstViewHeight
{
    if(_savedFirstViewHeight<=0){
        _savedFirstViewHeight=100;  //rough
    }
    return _savedFirstViewHeight;
    
}
- (void)setSavedFirstViewHeight:(float)value
{
     _savedFirstViewHeight=value;
}

- (float)dividerThickness
{
    if([self firstViewHeight]==0){
        return 0;
    }
    return [super dividerThickness];
}

- (float)firstViewHeight
{
	NSArray* subviews = [self subviews];
    if([subviews count]>0){
        NSView* topView=[subviews objectAtIndex: 0];
        NSSize aSize=[topView frame].size;
        return aSize.height;
    }
    return -1;
}

/*
- (void)drawRect:(NSRect)aRect
{

    if([self firstViewHeight]==0){
        float x=aRect.origin.x;
        float y=aRect.origin.y+aRect.size.height-0.5;
		NSColor*	lineColor;
		if([[self window]styleMask] & NSTexturedBackgroundWindowMask){
			lineColor=[NSColor darkGrayColor];
		}else{
			lineColor=[NSColor controlShadowColor];
		}

		[lineColor set];
		[NSBezierPath strokeLineFromPoint:NSMakePoint(x, y) toPoint:NSMakePoint(x+aRect.size.width, y)];
    }
    [super drawRect:aRect];
}
*/
@end

