/*
SRPageInfoController.m

Author: Makoto Kinoshita

Copyright 2004-2006 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 "SRAppController.h"
#import "SRDefaultKeys.h"
#import "SRPageController.h"
#import "SRPageInfoController.h"

#import "SRPrefDefaultKeys.h"

#import "SRImageInfoCell.h"

// Frame auto save name
NSString*   SRPageInfoPanelFrameAutoSaveName = @"SRPageInfoPanelFrameAutoSaveName";

static NSMutableArray*  _instances = nil;

static int              _imageCellSizeWidth = 80;
static int              _imageCellSizeHeight = 80;

@interface SRPageInfoController (private)
- (void)_updateInfoView;
- (void)_updateImagesMatrix;
@end

@implementation SRPageInfoController

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

+ (SRPageInfoController*)pageInfoControllerWithPageController:(SRPageController*)pageController
{
    // Get URL string
    NSString*   URLString;
    URLString = [[[[[[pageController webView] mainFrame] dataSource] request] URL] absoluteString];
    if (URLString) {
        // Find page info controller
        NSEnumerator*           enumerator;
        SRPageInfoController*   pageInfoController;
        enumerator = [_instances objectEnumerator];
        while (pageInfoController = [enumerator nextObject]) {
            if ([[pageInfoController URLString] isEqualToString:URLString]) {
                return pageInfoController;
            }
        }
    }
    
    // Create new page info controller
    SRPageInfoController*   pageInfoController;
    pageInfoController = [[SRPageInfoController alloc] init];
    [pageInfoController loadWindow];
    [pageInfoController setPageInfoWithPageController:pageController];
    
    return pageInfoController;
}

- (void)_setProfileObjects
{
    static NSDictionary*    _attr = nil;
    if (!_attr) {
        _attr = [[NSDictionary alloc] initWithObjectsAndKeys:
                [NSFont boldSystemFontOfSize:11], NSFontAttributeName, 
                [NSColor grayColor], NSForegroundColorAttributeName, 
                nil];
    }
    
    // For HTML page
    NSMutableDictionary*    general;
    general = [NSMutableDictionary dictionary];
    [general setValue:NSLocalizedString(@"General Info", nil) forKey:@"title"];
    [general setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:general];
    
    NSMutableDictionary*    advanced;
    NSAttributedString*     advancedAttrTitle;
    advanced = [NSMutableDictionary dictionary];
    [advanced setValue:NSLocalizedString(@"Advanced Info", nil) forKey:@"title"];
    [advanced setValue:@"category" forKey:@"type"];
    
    advancedAttrTitle = [[NSAttributedString alloc] 
            initWithString:NSLocalizedString(@"Advanced Info", nil) attributes:_attr];
    [advanced setValue:advancedAttrTitle forKey:@"attributedTitle"];
    [advancedAttrTitle release];
    
    [_profiles addObject:advanced];
    
    NSMutableDictionary*    links;
    links = [NSMutableDictionary dictionary];
    [links setValue:NSLocalizedString(@"Links", nil) forKey:@"title"];
    [links setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:links];
    
    NSMutableDictionary*    files;
    NSAttributedString*     filesAttrTitle;
    files = [NSMutableDictionary dictionary];
    [files setValue:NSLocalizedString(@"Files", nil) forKey:@"title"];
    [files setValue:@"category" forKey:@"type"];
    
    filesAttrTitle = [[NSAttributedString alloc] 
            initWithString:NSLocalizedString(@"Files", nil) attributes:_attr];
    [files setValue:filesAttrTitle forKey:@"attributedTitle"];
    [filesAttrTitle release];
    
    [_profiles addObject:files];
    
    NSMutableDictionary*    css;
    css = [NSMutableDictionary dictionary];
    [css setValue:NSLocalizedString(@"CSS", nil) forKey:@"title"];
    [css setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:css];
    
    NSMutableDictionary*    javaScript;
    javaScript = [NSMutableDictionary dictionary];
    [javaScript setValue:NSLocalizedString(@"JavaScript", nil) forKey:@"title"];
    [javaScript setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:javaScript];
    
    NSMutableDictionary*    images;
    images = [NSMutableDictionary dictionary];
    [images setValue:NSLocalizedString(@"Images", nil) forKey:@"title"];
    [images setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:images];
    
    NSMutableDictionary*    source;
    source = [NSMutableDictionary dictionary];
    [source setValue:NSLocalizedString(@"Source", nil) forKey:@"title"];
    [source setValue:@"HTML" forKey:@"type"];
    [_profiles addObject:source];
}

- (id)init
{
    self = [super initWithWindowNibName:@"PageInfo"];
    if (!self) {
        return nil;
    }
    
    // Initialize
    _isInitialized = NO;
    _profiles = [[NSMutableArray array] retain];
    _images = [[NSMutableArray array] retain];
    
    // Register itself
    if (!_instances) {
        _instances = [[NSMutableArray array] retain];
    }
    [_instances addObject:self];
    
    // Register observer
    NSNotificationCenter*   center;
    center = [NSNotificationCenter defaultCenter];
    [center addObserver:self selector:@selector(matrixFrameDidChange:) 
            name:NSViewFrameDidChangeNotification object:_imagesMatrix];
    
    return self;
}

- (void)awakeFromNib
{
    // Configure window
    NSWindow*   window;
    window = [self window];
    [window setFrameAutosaveName:SRPageInfoPanelFrameAutoSaveName];
    
    // Set image text cell
    NSTableColumn*          column;
    NSCell*                 oldCell;
    HMImageTextFieldCell*   cell;
    column = [_profileTableView tableColumnWithIdentifier:@"profile"];
    oldCell = [column dataCell];
    cell = [[HMImageTextFieldCell alloc] init];
    [cell setFont:[oldCell font]];
    [cell setUseBoldForHighlight:YES];
    [column setDataCell:cell];
    [cell release];
    
    // Set header cell
    NSCell*             oldHeaderCell;
    HMTableHeaderCell*  headerCell;
    column = [_profileTableView tableColumnWithIdentifier:@"profile"];
    oldHeaderCell = [column headerCell];
    headerCell = [[HMTableHeaderCell alloc] init];
    [headerCell setObjectValue:[oldHeaderCell objectValue]];
    [headerCell setAlignment:[oldHeaderCell alignment]];
    [headerCell setFont:[oldHeaderCell font]];
    [column setHeaderCell:headerCell];
    [headerCell release];
    
    // Set header view
    NSTableHeaderView*  oldHeaderView;
    HMTableHeaderView*  headerView;
    oldHeaderView = [_profileTableView headerView];
    headerView = [[HMTableHeaderView alloc] initWithFrame:[oldHeaderView frame]];
    [headerView setSplitView:_splitView];
    [_profileTableView setHeaderView:headerView];
    
    // Configure split view
    [_splitView setSplitType:HMSplitLine];
    
    float   sideWidth;
    sideWidth = [[NSUserDefaults standardUserDefaults] floatForKey:SRPageInfoSideWidth];
    if ([[_splitView subviews] count] > 0 && sideWidth > 0) {
        // Set subview width
        NSView* subview;
        NSRect  frame;
        float   delta;
        
        subview = [[_splitView subviews] objectAtIndex:0];
        frame = [subview frame];
        delta = frame.size.width - sideWidth;
        frame.size.width = sideWidth;
        [subview setFrame:frame];
        
        subview = [[_splitView subviews] objectAtIndex:1];
        frame = [subview frame];
        frame.size.width += delta;
        [subview setFrame:frame];
        
        [_splitView adjustSubviews];
    }
    
    _isInitialized = YES;
    
    // Configure links view
    [_linksSplitView setSplitType:HMSplitWide];
    
    // Configure CSS view
    [_cssSplitView setSplitType:HMSplitWide];
    
    // Configure JavaScirpt view
    [_javaScriptSplitView setSplitType:HMSplitWide];
    
    // Configure images view
    [_imagesSplitView setSplitType:HMSplitWide];
    
    // Configure matrix
    _imagesMatrix = [[NSMatrix alloc] initWithFrame:NSZeroRect 
            mode:NSRadioModeMatrix 
            cellClass:[SRImageInfoCell class] 
//            cellClass:[NSImageCell class] 
            numberOfRows:0 
            numberOfColumns:0];
    [_imagesMatrix autorelease];
    [_imagesMatrix setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
    [_imagesMatrix setPostsFrameChangedNotifications:YES];
    [_imagesMatrix setAllowsEmptySelection:YES];
    [_imagesMatrix setBackgroundColor:[NSColor colorWithDeviceWhite:0.8f alpha:1.0f]];
    [_imagesMatrix setDrawsBackground:YES];
    [_imagesMatrix setTarget:self];
    [_imagesMatrix setAction:@selector(imageSelectedAction:)];
    [_imagesScrollView setDocumentView:_imagesMatrix];
    
    // Set profiles
    [self _setProfileObjects];
    
    // Relaod data
    [_profileTableView reloadData];
}

- (void)dealloc
{
    // Remove observer
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    
    [_profiles release], _profiles = nil;
    [_images release], _images = nil;
    [_htmlSourceString release], _htmlSourceString = nil;
    
    [super dealloc];
}

//--------------------------------------------------------------//
#pragma mark -- Page info handling --
//--------------------------------------------------------------//

- (NSString*)URLString
{
    return [[_objectController content] valueForKey:@"generalURL"];
}

- (NSString*)profileTitle
{
    // Get selected profile
    int             row;
    NSDictionary*   profile;
    row = [_profileTableView selectedRow];
    if (row == NSNotFound || row >= [_profiles count]) {
        return nil;
    }
    profile = [_profiles objectAtIndex:row];
    
    // Get title
    return [profile valueForKey:@"title"];
}

- (NSIndexPath*)_indexPathWithTitle:(NSString*)title node:(id)node
{
    // Get observed object
    id  profile;
    profile = [node observedObject];
    
    // Check title
    if ([[profile objectForKey:@"title"] isEqualToString:title]) {
        return [node indexPath];
    }
    
    // Get subnode
    unsigned int    i;
    for (i = 0; i < [node count]; i++) {
        id  indexPath;
        indexPath = [self _indexPathWithTitle:title node:[node subnodeAtIndex:i]];
        if (indexPath) {
            return indexPath;
        }
    }
    
    return nil;
}

- (void)setProfileWithTitle:(NSString*)title
{
#if 0
    // Get arranged objects
    id  arrangedObjects;
    arrangedObjects = [_profileTreeController arrangedObjects];
    
    // Get index path with title
    NSIndexPath*    indexPath;
    unsigned int    i;
    for (i = 0; i < [arrangedObjects count]; i++) {
        indexPath = [self _indexPathWithTitle:title 
                node:[arrangedObjects nodeAtIndexPath:[NSIndexPath indexPathWithIndex:i]]];
        if (indexPath) {
            break;
        }
    }
    if (!indexPath) {
        return;
    }
    
    // Set index path
    [_profileTreeController setSelectionIndexPath:indexPath];
    
    // Update appearance
    [self _updateInfoView];
#endif
}

- (NSArray*)_createFieldItemsWithHeaderFields:(NSDictionary*)headerFields
{
    NSMutableArray*    items;
    items = [NSMutableArray array];
    
    NSEnumerator*   enumerator;
    NSString*       key;
    enumerator = [headerFields keyEnumerator];
    while (key = [enumerator nextObject]) {
        NSString*   value;
        value = [headerFields objectForKey:key];
        if (!value) {
            continue;
        }
        
        NSDictionary*   item;
        item = [NSDictionary dictionaryWithObjectsAndKeys:
                key, @"key", 
                value, @"value", nil];
        
        [items addObject:item];
    }
    
    return items;
}

- (NSAttributedString*)_colorizedSourceWithString:(NSString*)source
{
    if (!source) {
        return nil;
    }
    
    NSUserDefaults* defaults;
    defaults = [NSUserDefaults standardUserDefaults];
    
    // Get colors
    static NSColor* _textColor = nil;
    if (!_textColor) {
        _textColor = [[NSColor blackColor] retain];
    }
    
    NSData*     data;
    NSColor*    textColor = nil;
    data = [defaults objectForKey:SRSourceDefaultColor];
    if (data) {
        textColor = [NSUnarchiver unarchiveObjectWithData:data];
    }
    if (!textColor) {
        textColor = _textColor;
    }
    
    // Get font
    NSString*   fontName;
    int         fontSize;
    NSFont*     font;
    fontName = [defaults stringForKey:SRSourceFontName];
    if (!fontName) {
        fontName = @"Monaco";
    }
    fontSize = [defaults integerForKey:SRSourceFontSize];
    if (fontSize < 4) {
        fontSize = 12;
    }
    
    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])];
    
    // Set font
    [attrString addAttribute:NSFontAttributeName
            value:font range:NSMakeRange(0, [attrString length])];
    
    return attrString;
}

- (NSAttributedString*)_colorizedHTMLSourceWithString:(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];
    }
    
    NSData*     data;
    NSColor*    textColor = nil;
    NSColor*    tagColor = nil;
    NSColor*    commentColor = nil;
    data = [defaults objectForKey:SRSourceDefaultColor];
    if (data) {
        textColor = [NSUnarchiver unarchiveObjectWithData:data];
    }
    if (!textColor) {
        textColor = _textColor;
    }
    
    data = [defaults objectForKey:SRSourceTagColor];
    if (data) {
        tagColor = [NSUnarchiver unarchiveObjectWithData:data];
    }
    if (!tagColor) {
        tagColor = _tagColor;
    }
    
    data = [defaults objectForKey:SRSourceCommentColor];
    if (data) {
        commentColor = [NSUnarchiver unarchiveObjectWithData:data];
    }
    if (!commentColor) {
        commentColor = _commentColor;
    }
    
    // Get font
    NSString*   fontName;
    int         fontSize;
    NSFont*     font;
    fontName = [defaults stringForKey:SRSourceFontName];
    if (!fontName) {
        fontName = @"Monaco";
    }
    fontSize = [defaults integerForKey:SRSourceFontSize];
    if (fontSize < 4) {
        fontSize = 12;
    }
    
    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;
    int         sourceLength;
    scanner = [NSScanner scannerWithString:source];
    sourceLength = [source length];
    while (![scanner isAtEnd]) {
        // Find next '<' character
        int tagStart, tagEnd;
        [scanner scanUpToString:@"<" intoString:nil];
        tagStart = [scanner scanLocation];		
        
        // For '<!--'
        if (sourceLength > tagStart + 6 && 
            [[source substringWithRange:NSMakeRange(tagStart + 1, 3)] isEqualToString:@"!--"])
        {
            // Find '-->'
            if ([scanner scanUpToString:@"-->" intoString:nil]) {
                tagEnd = [scanner scanLocation] + 3;
                if (tagEnd > sourceLength) {
                    tagEnd = sourceLength;
                }
                if (sourceLength > tagEnd) {
                    [attrString addAttribute:NSForegroundColorAttributeName 
                            value:commentColor range:NSMakeRange(tagStart, tagEnd - tagStart)];
                }
            }
        }
        else if (sourceLength > tagStart) {
            // Find '>'
            if ([scanner scanUpToString:@">" intoString:nil]) {
                tagEnd = [scanner scanLocation] + 1;
                if (tagEnd > sourceLength) {
                    tagEnd = sourceLength;
                }
                [attrString addAttribute:NSForegroundColorAttributeName
                        value:tagColor range:NSMakeRange(tagStart, tagEnd - tagStart)];
            }
        }
    }
    
    // Set font
    [attrString addAttribute:NSFontAttributeName
            value:font range:NSMakeRange(0, [attrString length])];
    
    return attrString;
}

- (void)_setGeneralPageInfoWithPageController:(SRPageController*)pageController
{
    // Get web view and web data source
    WebView*        webView;
    WebDataSource*  dataSource;
    webView = [pageController webView];
    dataSource = [[webView mainFrame] dataSource];
    
    // Set general info
    id  content;
    content = [_objectController content];
    
    [content setValue:[dataSource pageTitle] forKey:@"generalTitle"];
    [content setValue:[[[dataSource request] URL] absoluteString] forKey:@"generalURL"];
    [content setValue:[dataSource textEncodingName] forKey:@"generalTextEncoding"];
    
    // Get loading time
    float       loadingTime;
    NSString*   loadingTimeStr;
    loadingTime = [pageController loadingTime];
    if (loadingTime == -1) {
        loadingTimeStr = NSLocalizedString(UTF8STR("Now loading…"), nil);
    }
    else {
        loadingTimeStr = [NSString stringWithFormat:@"%5.2f %@", 
                loadingTime, NSLocalizedString(@"seconds", nil)];
    }
    [content setValue:loadingTimeStr forKey:@"generalLoadingTime"];
    
    // Clear HTTP header
    content = [_httpHeaderTreeController content];
    [content removeAllObjects];
    
    // Get request header fields
    NSURLRequest*   request;
    NSDictionary*   headerFields;
    request = [dataSource request];
    headerFields = [request allHTTPHeaderFields];
    if (headerFields) {
        [content addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                NSLocalizedString(@"Request", nil), @"key", 
                [self _createFieldItemsWithHeaderFields:headerFields], @"children", 
                nil]];
    }
    
    // Get response header fields
    NSURLResponse*  response;
    response = [dataSource response];
    if ([response respondsToSelector:@selector(allHeaderFields)]) {
        headerFields = [(NSHTTPURLResponse*)response allHeaderFields];
        if (headerFields) {
            [content addObject:[NSDictionary dictionaryWithObjectsAndKeys:
                    NSLocalizedString(@"Response", nil), @"key", 
                    [self _createFieldItemsWithHeaderFields:headerFields], @"children", 
                    nil]];
        }
    }
    
    // Reload data
    [_httpHeaderOutlineView reloadData];
}

- (void)_setLinksPageInfoWithPageController:(SRPageController*)pageController
{
    // Remove old links
    [_linksArrayController removeObjects:[_linksArrayController arrangedObjects]];
    
    // Get DOM document
    WebView*        webView;
    DOMDocument*    domDocument;
    webView = [pageController webView];
    domDocument = [[webView mainFrame] DOMDocument];
    if (!domDocument) {
        return;
    }
    
    // Get anchor tags
    NSMutableArray*     anchorObjects;
    HMAnchorNodeFilter* filter;
    DOMNodeIterator*    iterator;
    DOMNode*            node;
    anchorObjects = [NSMutableArray array];
    filter = [[HMAnchorNodeFilter alloc] init];
    [filter autorelease];
    iterator = [domDocument createNodeIterator:[domDocument documentElement] 
            :DOM_SHOW_ELEMENT :filter :NO];
    while (node = [iterator nextNode]) {
        DOMHTMLAnchorElement*   anchor;
        anchor = (DOMHTMLAnchorElement*)node;
        
        // Check href
        NSString*   href;
        href = [anchor href];
        if (!href || [href length] == 0) {
            continue;
        }
        
        // Create anchor object
        NSMutableDictionary*    anchorObject;
        anchorObject = [NSMutableDictionary dictionary];
        
        if (href) {
            [anchorObject setObject:href forKey:@"href"];
        }
        
        NSString*   name;
        name = [anchor name];
        if (name) {
            [anchorObject setObject:name forKey:@"name"];
        }
        
        NSString*   html;
        html = [anchor outerHTML];
        if (html) {
            [anchorObject setObject:html forKey:@"html"];
        }
        
        NSString*   text;
        text = [anchor innerText];
        if (!text || [text length] == 0) {
            text = NSLocalizedString(@" (No text)", nil);
        }
        [anchorObject setObject:text forKey:@"text"];
        
        [anchorObject setObject:anchor forKey:@"domNode"];
        
        [anchorObjects addObject:anchorObject];
    }
    
    // Set anchor objects
    [_linksArrayController addObjects:anchorObjects];
    [_linksArrayController rearrangeObjects];
    [_linksArrayController setSelectionIndex:0];
}

- (void)_setCSSPageInfoWithPageController:(SRPageController*)pageController
{
    // Remove old CSSs
    [_cssArrayController removeObjects:[_cssArrayController arrangedObjects]];
    
    // Get web view
    WebView*    webView;
    webView = [pageController webView];
    
    // Get CSS subresources
    NSArray*        subresources;
    NSArray*        cssResources;
    NSEnumerator*   enumerator;
    WebResource*    resource;
    subresources = [[webView mainFrame] subresources];
    cssResources = [subresources filteredArrayUsingPredicate:
            [NSPredicate predicateWithFormat:@"MIMEType == 'text/css'"]];
    enumerator = [cssResources objectEnumerator];
    while (resource = [enumerator nextObject]) {
        // Get resource info
        NSString*           URLString;
        NSString*           textEncodingName;
        NSStringEncoding    encoding = NSASCIIStringEncoding;
        NSData*             data;
        URLString = [[resource URL] absoluteString];
        if (!URLString) {
            URLString = @"(No URL)";
        }
        
        textEncodingName = [resource textEncodingName];
        if (textEncodingName) {
            CFStringEncoding    cfEncoding;
            cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName);
            encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
        }
        
        data = [resource data];
        
        // Get source
        NSString*           source;
        NSAttributedString* attrSource;
        source = [[NSString alloc] initWithData:data encoding:encoding];
        attrSource = [self _colorizedSourceWithString:source];
        [source release];
        
        // Add CSS info
        NSMutableDictionary*    css;
        css = [NSMutableDictionary dictionary];
        [css setObject:URLString forKey:@"file"];
        if (attrSource) {
            [css setObject:attrSource forKey:@"source"];
        }
        
        [_cssArrayController addObject:css];
    }
    [_cssArrayController setSelectionIndex:0];
}

- (void)_setJavaScriptPageInfoWithPageController:(SRPageController*)pageController
{
    // Remove old JavaScripts
    [_javaScriptArrayController removeObjects:[_javaScriptArrayController arrangedObjects]];
    
    // Get web view
    WebView*    webView;
    webView = [pageController webView];
    
    // Get JavaScript subresources
    NSArray*        subresources;
    NSArray*        jsResources;
    NSEnumerator*   enumerator;
    WebResource*    resource;
    subresources = [[webView mainFrame] subresources];
    jsResources = [subresources filteredArrayUsingPredicate:
            [NSPredicate predicateWithFormat:@"MIMEType == 'application/x-javascript'"]];
    enumerator = [jsResources objectEnumerator];
    while (resource = [enumerator nextObject]) {
        // Get resource info
        NSString*           URLString;
        NSString*           textEncodingName;
        NSStringEncoding    encoding = NSASCIIStringEncoding;
        NSData*             data;
        URLString = [[resource URL] absoluteString];
        if (!URLString) {
            URLString = @"(No URL)";
        }
        
        textEncodingName = [resource textEncodingName];
        if (textEncodingName) {
            CFStringEncoding    cfEncoding;
            cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)textEncodingName);
            encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
        }
        
        data = [resource data];
        
        // Get source
        NSString*           source;
        NSAttributedString* attrSource;
        source = [[NSString alloc] initWithData:data encoding:encoding];
        attrSource = [self _colorizedSourceWithString:source];
        [source release];
        
        // Add JavaScirpt info
        NSMutableDictionary*    javaScript;
        javaScript = [NSMutableDictionary dictionary];
        [javaScript setObject:URLString forKey:@"file"];
        if (attrSource) {
            [javaScript setObject:attrSource forKey:@"source"];
        }
        
        [_javaScriptArrayController addObject:javaScript];
    }
    [_javaScriptArrayController setSelectionIndex:0];
}

- (void)_setImagesPageInfoWithPageController:(SRPageController*)pageController
{
    // Remove old images
    [_images removeAllObjects];
    
    // Get web view
    WebView*    webView;
    webView = [pageController webView];
    
    // Get image subresources
    NSArray*        subresources;
    NSMutableArray* imageResources;
    NSEnumerator*   enumerator;
    WebResource*    resource;
    subresources = [[webView mainFrame] subresources];
    imageResources = [NSMutableArray array];
    enumerator = [subresources objectEnumerator];
    while (resource = [enumerator nextObject]) {
        NSString*   MIMEType;
        MIMEType = [resource MIMEType];
        if ([MIMEType hasPrefix:@"image/"]) {
            [imageResources addObject:resource];
        }
    }
    
    enumerator = [imageResources objectEnumerator];
    while (resource = [enumerator nextObject]) {
        // Get resource info
        NSString*   URLString;
        NSString*   MIMEType;
        NSData*     data;
        URLString = [[resource URL] absoluteString];
        if (!URLString) {
            URLString = @"(No URL)";
        }
        MIMEType = [resource MIMEType];
        data = [resource data];
        
        // Add image info
        NSMutableDictionary*    imageInfo;
        imageInfo = [NSMutableDictionary dictionary];
        [imageInfo setObject:URLString forKey:@"urlString"];
        if (MIMEType) {
            [imageInfo setObject:MIMEType forKey:@"mimeType"];
        }
        if (data) {
            [imageInfo setObject:data forKey:@"data"];
        }
        
        [_images addObject:imageInfo];
    }
    
    // Update images matrix
    [self _updateImagesMatrix];
}

- (void)_setSourcePageInfoWithPageController:(SRPageController*)pageController
{
    // Get web view and web data source
    WebView*        webView;
    WebDataSource*  dataSource;
    webView = [pageController webView];
    dataSource = [[webView mainFrame] dataSource];
    
    // Set HTML sourcce
    NSString*   source;
    source = [[dataSource representation] documentSource];
    if (source) {
        _htmlSourceString = [[self _colorizedHTMLSourceWithString:source] retain];
    }
}

- (void)setPageInfoWithPageController:(SRPageController*)pageController
{
    // Set window title
    NSString*   title;
    title = [[[[pageController webView] mainFrame] dataSource] pageTitle];
    if (title) {
        title = [NSString stringWithFormat:NSLocalizedString(@"Page Info of '%@'", nil), title];
        [[self window] setTitle:title];
    }
    
    // Set page info
    [self _setGeneralPageInfoWithPageController:pageController];
    [self _setLinksPageInfoWithPageController:pageController];
    [self _setCSSPageInfoWithPageController:pageController];
    [self _setJavaScriptPageInfoWithPageController:pageController];
    [self _setImagesPageInfoWithPageController:pageController];
    [self _setSourcePageInfoWithPageController:pageController];
    
    // Select general
    [_profileTableView selectRow:0 byExtendingSelection:NO];
}

//--------------------------------------------------------------//
#pragma mark -- Appearance --
//--------------------------------------------------------------//

- (void)_updateGeneral
{
    // Expand HTTP header outline
    [_httpHeaderOutlineView expandAllItems];
}

- (void)_updateImagesMatrix
{
    // Get content size
    NSSize  contentSize;
    contentSize = [_imagesScrollView contentSize];
    
    // Decide number of columns and rows
    int numberOfColumns, numberOfRows;
    numberOfColumns = contentSize.width / _imageCellSizeWidth;
    if (numberOfColumns == 0) {
        numberOfColumns = 1;
    }
    numberOfRows = ([_images count] / numberOfColumns) + (([_images count] % numberOfColumns) ? 1 : 0);
    
    if (numberOfRows == [_imagesMatrix numberOfRows] && 
        numberOfColumns == [_imagesMatrix numberOfColumns])
    {
        return;
    }
    
    // Set cell size
    int interCellSpace;
    interCellSpace = [_imagesMatrix intercellSpacing].width * (numberOfColumns - 1);
    [_imagesMatrix setCellSize:NSMakeSize(
            (contentSize.width - interCellSpace) / numberOfColumns, 
            _imageCellSizeHeight)];
    
    // Keep selected cell
    NSArray*    cells;
    cells = [_imagesMatrix selectedCells];
    
    // Renew matrix rows and columns
    [_imagesMatrix renewRows:numberOfRows columns:numberOfColumns];
    
    // Select cells again
    NSEnumerator*   enumerator;
    NSCell*         cell;
    enumerator = [cells objectEnumerator];
    while (cell = [enumerator nextObject]) {
        int row, col;
        if ([_imagesMatrix getRow:&row column:&col ofCell:cell]) {
            // Select cell
            [_imagesMatrix selectCellAtRow:row column:col];
        }
    }
    
    // Set images to cells
    int row, col, index = 0;
    for (row = 0; row < numberOfRows; row++) {
        for (col = 0; col < numberOfColumns; col++) {
            // Get cell
            NSImageCell*    cell;
            cell = [_imagesMatrix cellAtRow:row column:col];
            if (!cell) {
                continue;
            }
            
            // Check index
            if (index >= [_images count]) {
                [cell setImage:nil];
                continue;
            }
            
            // Get image info
            NSDictionary*   imageInfo;
            imageInfo = [_images objectAtIndex:index++];
            
            // Get data
            NSData* data;
            data = [imageInfo objectForKey:@"data"];
            if (!data) {
                [cell setImage:nil];
                continue;
            }
            
            // Create image
            NSImage*    image;
            image = [[NSImage alloc] initWithData:data];
            if (!image) {
                [cell setImage:nil];
                continue;
            }
            
            // Set image
            [cell setImage:image];
            [image release];
        }
    }
    
    // Make matrix size to fit
    [_imagesMatrix sizeToCells];
    [_imagesMatrix setNeedsDisplay:YES];
}

- (void)_updateSource
{
    NSAttributedString* attrString;
    attrString = _htmlSourceString;
    if (!attrString) {
        attrString = [[NSAttributedString alloc] initWithString:@""];
        [attrString autorelease];
    }
    
    // Set attributed string
    [[_sourceTextView textStorage] setAttributedString:attrString];
    [_sourceTextView setSelectedRange:NSMakeRange(0, 0)];
}

- (void)_updateInfoView
{
    // Get selected profile
    NSString*   title;
    title = [self profileTitle];
    if (!title) {
        return;
    }
    
    NSView* infoView = nil;
    
    // For general
    if ([title isEqualToString:NSLocalizedString(@"General Info", nil)]) {
        // Decide info view
        infoView = _generalView;
        
        // Update source
        [self _updateGeneral];
    }
    // For links
    else if ([title isEqualToString:NSLocalizedString(@"Links", nil)]) {
        // Decide info view
        infoView = _linksView;
    }
    // For CSS
    else if ([title isEqualToString:NSLocalizedString(@"CSS", nil)]) {
        // Decide info view
        infoView = _cssView;
    }
    // For JavaScript
    else if ([title isEqualToString:NSLocalizedString(@"JavaScript", nil)]) {
        // Decide info view
        infoView = _javaScriptView;
    }
    // For images
    else if ([title isEqualToString:NSLocalizedString(@"Images", nil)]) {
        // Decide info view
        infoView = _imagesView;
    }
    // For source
    else if ([title isEqualToString:NSLocalizedString(@"Source", nil)]) {
        // Decide info view
        infoView = _sourceView;
        
        // Update source
        [self _updateSource];
    }
    
    // Set info view
    if (!infoView) {
        return;
    }
    if (![[_infoView subviews] containsObject:infoView]) {
        [[_infoView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
        
        [infoView setFrame:[_infoView bounds]];
        [_infoView addSubview:infoView];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView data source --
//--------------------------------------------------------------//

- (int)numberOfRowsInTableView:(NSTableView*)tableView
{
    // For profile
    if (tableView == _profileTableView) {
        return [_profiles count];
    }
    
    return 0;
}

- (id)tableView:(NSTableView*)tableView 
        objectValueForTableColumn:(NSTableColumn*)tableColumn 
        row:(int)rowIndex
{
    // For profile
    if (tableView == _profileTableView) {
        // Get profile at row
        NSDictionary*   profile;
        profile = [_profiles objectAtIndex:rowIndex];
        
        // For category
        if ([[profile objectForKey:@"type"] isEqualToString:@"category"]) {
            NSAttributedString* attrTitle;
            attrTitle = [profile objectForKey:@"attributedTitle"];
            if (attrTitle) {
                return attrTitle;
            }
        }
        
        return [profile objectForKey:@"title"];
    }
    
    return nil;
}

//--------------------------------------------------------------//
#pragma mark -- NSTableView delegate --
//--------------------------------------------------------------//

- (void)tableViewSelectionDidChange:(NSNotification*)notification
{
    // Get table view
    NSTableView*    tableView;
    tableView = [notification object];
    
    // For profile
    if (tableView == _profileTableView) {
        // Update info view
        [self _updateInfoView];
    }
}

- (BOOL)tableView:(NSTableView*)tableView shouldSelectRow:(int)rowIndex
{
    // For profile
    if (tableView == _profileTableView) {
        // Get profile at row
        NSDictionary*   profile;
        profile = [_profiles objectAtIndex:rowIndex];
        
        // Check category
        if ([[profile objectForKey:@"type"] isEqualToString:@"category"]) {
            return NO;
        }
        
        return YES;
    }
    
    return YES;
}

//--------------------------------------------------------------//
#pragma mark -- HMTableView delegate --
//--------------------------------------------------------------//

- (BOOL)tableView:(NSTableView*)tableView 
        frameOfCellAtColumn:(int)column 
        row:(int)row 
        proposedFrame:(NSRect*)proposedFrame
{
    // For profile
    if (tableView == _profileTableView) {
        // Get profile at row
        NSDictionary*   profile;
        profile = [_profiles objectAtIndex:row];
        
        // Check category
        if ([[profile objectForKey:@"type"] isEqualToString:@"category"]) {
            return NO;
        }
        
        // Shrink frame
        proposedFrame->origin.x += 11.0f;
        proposedFrame->size.width -= 11.0f;
        
        return YES;
    }
    
    return NO;
}

//--------------------------------------------------------------//
#pragma mark -- Action --
//--------------------------------------------------------------//

- (void)imageSelectedAction:(id)sender
{
    // Get selected image
    int             row, column, index = NSNotFound;
    NSDictionary*   image;
    row = [_imagesMatrix selectedRow];
    column = [_imagesMatrix selectedColumn];
    if (row != -1 && column != -1) {
        index = row * [_imagesMatrix numberOfColumns] + column;
    }
    
    if (index == NSNotFound || index >= [_images count]) {
        [_imagesContent setValue:@"" forKey:@"url"];
        [_imagesContent setValue:@"" forKey:@"mimeType"];
        [_imagesContent setValue:nil forKey:@"data"];
        return;
    }
    
    image = [_images objectAtIndex:index];
    
    // Set image info
    [_imagesContent setValue:[image objectForKey:@"urlString"] forKey:@"url"];
    [_imagesContent setValue:[image objectForKey:@"mimeType"] forKey:@"mimeType"];
    [_imagesContent setValue:[image objectForKey:@"data"] forKey:@"data"];
}

- (void)openSelectedLinksAction:(id)sender
{
#if 0
    // Get selected objects
    NSArray*    objects;
    objects = [_linksArrayController selectedObjects];
    if (!objects || [objects count] == 0) {
        return;
    }
    
    // Get selected link
    NSString*   href;
    href = [[objects objectAtIndex:0] objectForKey:@"href"];
    if (!href || [href length] == 0) {
        return;
    }
    
    // Open link
    [[SRBrowserController mainBrowserController] openURLString:href];
#endif
}

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

- (void)windowWillClose:(NSNotification*)notification
{
    // Unregister itself
    if ([_instances containsObject:self]) {
        [_instances removeObject:self];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSSplitView delegate --
//--------------------------------------------------------------//

- (float)splitView:(NSSplitView*)splitView 
        constrainMinCoordinate:(float)proposedMin 
        ofSubviewAt:(int)offset
{
    return 100.0f;
}

- (float)splitView:(NSSplitView*)splitView 
        constrainMaxCoordinate:(float)proposedMax 
        ofSubviewAt:(int)offset
{
    return 150.0f;
}

- (void)splitViewDidResizeSubviews:(NSNotification*)notification
{
    // Set shelf table width
    if (_isInitialized) {
        [[NSUserDefaults standardUserDefaults] 
                setFloat:[_profileTableView frame].size.width forKey:SRPageInfoSideWidth];
    }
}

//--------------------------------------------------------------//
#pragma mark -- NSView notification --
//--------------------------------------------------------------//

- (void)matrixFrameDidChange:(NSNotification*)notification
{
    // Update images matrix
    [self _updateImagesMatrix];
}

@end
