File:  [mozdev] / chimera / BookmarksService.mm
Revision 1.12: download - view: text, annotated - select for diffs - revision graph
Mon Feb 11 10:28:30 2002 UTC (15 years, 10 months ago) by macserv
Branches: MAIN
CVS tags: HEAD
/me removes an NSLog.

/*
 *  BookmarksService.cpp
 *  Chimera
 *
 *  Created by David Hyatt on Thu Feb 07 2002.
 *  Copyright (c) 2001 __MyCompanyName__. All rights reserved.
 *
 */

#import "NSBrowserView.h"
#include "BookmarksService.h"
#include "nsIDocument.h"
#include "nsIContent.h"
#include "nsIAtom.h"
#include "nsITextContent.h"
#include "nsIDOMWindow.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMElement.h"
#include "nsString.h"
#include "nsIFile.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsIXMLHttpRequest.h"
#include "nsIDOMSerializer.h"
#include "nsNetUtil.h"
#include "nsINamespaceManager.h"
#include "nsIXBLService.h"
#include "nsIWebBrowser.h"

@implementation BookmarksDataSource

-(id) init
{
    [super init];
    mBookmarks = nsnull;
    return self;
}

-(void) dealloc
{
    [super dealloc];
}

-(void) windowClosing
{
    if (mBookmarks) {
        mBookmarks->RemoveObserver();
        delete mBookmarks;
    }
}

-(void) ensureBookmarks
{
    if (mBookmarks)
        return;
    
    mBookmarks = new BookmarksService(self);
    mBookmarks->AddObserver();
    
    [mOutlineView setTarget: self];
    [mOutlineView setDoubleAction: @selector(openBookmark:)];
    [mOutlineView reloadData];
}

-(IBAction)addBookmark:(id)aSender
{
    if (!mBookmarks)
        return;
    
    nsCOMPtr<nsIContent> content;
    int index = [mOutlineView selectedRow];
   
    if (index >= 0) {
        BookmarkItem* item = [mOutlineView itemAtRow: index];
        if ([mOutlineView isExpandable: item]) 
            content = [item contentNode];
    }
    
    if (!content)
        mBookmarks->GetRootContent(getter_AddRefs(content));
        
    nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mBookmarks->gBookmarks));
    nsCOMPtr<nsIDOMElement> elt;
    domDoc->CreateElementNS(NS_LITERAL_STRING("http://chimera.mozdev.org/bookmarks"), 
                            NS_LITERAL_STRING("bookmark"), 
                            getter_AddRefs(elt));
    
    // Fetch the title of the current page and the URL.
    nsCOMPtr<nsIWebBrowser> webBrowser = getter_AddRefs([[mBrowserView getBrowserView] getWebBrowser]);
    nsCOMPtr<nsIDOMWindow> window;
    webBrowser->GetContentDOMWindow(getter_AddRefs(window));
    nsCOMPtr<nsIDOMDocument> htmlDoc;
    window->GetDocument(getter_AddRefs(htmlDoc));
    nsCOMPtr<nsIDocument> pageDoc(do_QueryInterface(htmlDoc));
    
    nsAutoString href; 
    if (pageDoc) {
        nsCOMPtr<nsIURI> url;
        pageDoc->GetDocumentURL(getter_AddRefs(url));
        nsXPIDLCString spec;
        url->GetSpec(getter_Copies(spec));
        href.AssignWithConversion(spec.get());
    }
    
    nsAutoString title;
    nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(htmlDoc));
    if (htmlDocument)
        htmlDocument->GetTitle(title);
    if (title.IsEmpty())
        title = href;
        
    elt->SetAttribute(NS_LITERAL_STRING("name"), title);
    elt->SetAttribute(NS_LITERAL_STRING("href"), href);    
    
    nsCOMPtr<nsIDOMElement> parent(do_QueryInterface(content));
    nsCOMPtr<nsIDOMNode> dummy;
    parent->AppendChild(elt, getter_AddRefs(dummy));

    mBookmarks->NotifyObservers(content, PR_TRUE);
}

-(IBAction)deleteBookmark: (id)aSender
{
    if (!mBookmarks)
        return;
    
    int index = [mOutlineView selectedRow];
    if (index == -1)
        return;
        
    BookmarkItem* item = [mOutlineView itemAtRow: index];
    nsCOMPtr<nsIContent> content = [item contentNode];
    nsCOMPtr<nsIDOMElement> child(do_QueryInterface(content));
    nsCOMPtr<nsIDOMNode> parent;
    child->GetParentNode(getter_AddRefs(parent));
    nsCOMPtr<nsIDOMNode> dummy;
    parent->RemoveChild(child, getter_AddRefs(dummy));
    nsCOMPtr<nsIContent> parentContent(do_QueryInterface(parent));
    mBookmarks->NotifyObservers(parentContent, PR_TRUE);
    
    int total = [mOutlineView numberOfRows];
    if (index == total)
        index--;
        
    [mOutlineView selectRow: index byExtendingSelection: NO];
}

-(IBAction)openBookmark: (id)aSender
{
    int index = [mOutlineView selectedRow];
    if (index == -1)
        return;
    
    id item = [mOutlineView itemAtRow: index];
    if (!item)
        return;
        
    if ([mOutlineView isExpandable: item]) {
        if ([mOutlineView isItemExpanded: item])
            [mOutlineView collapseItem: item];
        else
            [mOutlineView expandItem: item];
    }
    else {
        nsIContent* content = [item contentNode];
        nsAutoString href;
        content->GetAttr(kNameSpaceID_None, BookmarksService::gHrefAtom, href);
        if (!href.IsEmpty()) {
            nsCAutoString cstr; cstr.AssignWithConversion(href);
            NSString* url = [NSString stringWithCString: cstr.get()];
            [[mBrowserView getBrowserView] loadURI:[NSURL URLWithString: url] flags:NSLoadFlagsNone];
        }
    } 
}

- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
    return NO;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
{
    if (!mBookmarks)
        return nil;
       
    nsCOMPtr<nsIContent> content;
    if (!item)
        mBookmarks->GetRootContent(getter_AddRefs(content));
    else
        content = [item contentNode];
    
    nsCOMPtr<nsIContent> child;
    content->ChildAt(index, *getter_AddRefs(child));
    return mBookmarks->GetWrapperFor(child);
}

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{
    if (!mBookmarks)
        return NO;
    
    if (!item)
        return YES; // The root node is always open.
    
    nsCOMPtr<nsIAtom> tagName;
    nsIContent* content = [item contentNode];
    content->GetTag(*getter_AddRefs(tagName));
    
    return (tagName == BookmarksService::gFolderAtom);
}

- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{
    if (!mBookmarks)
        return 0;
  
    nsCOMPtr<nsIContent> content;
    if (!item)
        mBookmarks->GetRootContent(getter_AddRefs(content));
    else 
        content = [item contentNode];
    
    PRInt32 childCount;
    content->ChildCount(childCount);
    
    return childCount;
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
    NSMutableAttributedString 	*cellValue = [[NSMutableAttributedString alloc] init];
    NSMutableAttributedString   *attachmentAttrString = nil;
    NSString 					*columnName = [tableColumn identifier];
    NSCell 						*attachmentCell;

    if ( item ) {
        if ([columnName isEqualToString: @"name"]) {
            nsIContent* content = [item contentNode];
            nsAutoString nameAttr;
            content->GetAttr(kNameSpaceID_None, BookmarksService::gNameAtom, nameAttr);
            nsCAutoString cStr; cStr.AssignWithConversion(nameAttr);
            
        //set cell's textual contents
            [cellValue replaceCharactersInRange:NSMakeRange(0, [cellValue length])
                                     withString:[NSString stringWithCString: cStr.get()]];
            
        //Add image to cell's text in front of the text.
            NSFileWrapper *wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:nil];
            NSTextAttachment *attachment = [[NSTextAttachment allocWithZone:NULL] initWithFileWrapper:wrapper];
            attachmentAttrString = [[NSMutableAttributedString attributedStringWithAttachment:attachment] retain];
            [attachment release];
            [wrapper release];

            attachmentCell = (NSCell *)[(NSTextAttachment *)[attachmentAttrString attribute:NSAttachmentAttributeName
                                                                                    atIndex:0
                                                                             effectiveRange:NULL] attachmentCell];

            [attachmentCell setImage:[NSImage imageNamed:@"folder"]];
            [cellValue replaceCharactersInRange:NSMakeRange(0, 0) withAttributedString:attachmentAttrString];
            [cellValue addAttribute:NSBaselineOffsetAttributeName
                              value:[NSNumber numberWithFloat:-3.0]
                              range:NSMakeRange(0, 1)];
        //Done adding image. 
        }
        return cellValue;
    } else {
        return nil;
    }
}

- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
{
}

- (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren
{
    printf("Reloading?\n");
    if (!item)
        [mOutlineView reloadData];
    else if ([mOutlineView isItemExpanded: item])
        [mOutlineView reloadItem: item reloadChildren: aReloadChildren];
}

@end

@implementation BookmarkItem
-(nsIContent*)contentNode
{
    return mContentNode;
}

-(void)setContentNode: (nsIContent*)aContentNode
{
    mContentNode = aContentNode;
}

- (id)copyWithZone:(NSZone *)aZone
{
    BookmarkItem* copy = [[[self class] allocWithZone: aZone] init];
    [copy setContentNode: mContentNode];
    return copy;
}

@end

// Helper for stripping whitespace
static void
StripWhitespaceNodes(nsIContent* aElement)
{
    PRInt32 childCount;
    aElement->ChildCount(childCount);
    for (PRInt32 i = 0; i < childCount; i++) {
        nsCOMPtr<nsIContent> child;
        aElement->ChildAt(i, *getter_AddRefs(child));
        nsCOMPtr<nsITextContent> text = do_QueryInterface(child);
        if (text) {
            PRBool isEmpty;
            text->IsOnlyWhitespace(&isEmpty);
            if (isEmpty) {
                // This node contained nothing but whitespace.
                // Remove it from the content model.
                aElement->RemoveChildAt(i, PR_TRUE);
                i--; // Decrement our count, since we just removed this child.
                childCount--; // Also decrement our total count.
            }
        }
        else StripWhitespaceNodes(child);
    }
}

PRUint32 BookmarksService::gRefCnt = 0;
nsIDocument* BookmarksService::gBookmarks = nsnull;
NSMutableDictionary* BookmarksService::gDictionary = nil;
MainController* BookmarksService::gMainController = nil;
nsIAtom* BookmarksService::gFolderAtom = nsnull;
nsIAtom* BookmarksService::gBookmarkAtom = nsnull;
nsIAtom* BookmarksService::gHrefAtom = nsnull;
nsIAtom* BookmarksService::gNameAtom = nsnull;
nsVoidArray* BookmarksService::gInstances = nsnull;

BookmarksService::BookmarksService(BookmarksDataSource* aDataSource)
{
    mDataSource = aDataSource;
}

BookmarksService::~BookmarksService()
{
}

void
BookmarksService::GetRootContent(nsIContent** aResult)
{
    *aResult = nsnull;
    if (gBookmarks) {
        nsCOMPtr<nsIDOMElement> elt;
        nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(gBookmarks));
        domDoc->GetDocumentElement(getter_AddRefs(elt));
        elt->QueryInterface(NS_GET_IID(nsIContent), (void**)aResult); // Addref happens here.
    }
}

BookmarkItem*
BookmarksService::GetWrapperFor(nsIContent* aContent)
{
    if (!gDictionary)
        gDictionary = [[NSMutableDictionary alloc] initWithCapacity: 30];
    
    PRUint32 contentID;
    aContent->GetContentID(&contentID);
    
    BookmarkItem* item = [gDictionary objectForKey: [NSNumber numberWithInt: contentID]];
    if (item)
        return item;
    else {
        // Create an item.
        item = [[[BookmarkItem alloc] init] autorelease]; // The dictionary retains us.
        [item setContentNode: aContent];
        [gDictionary setObject: item forKey: [NSNumber numberWithInt: contentID]];
    }
    return item;
}

void
BookmarksService::NotifyObservers(nsIContent* aContainer, PRBool aReloadChildren)
{
    if (!gInstances)
        return;
    
    PRInt32 count = gInstances->Count();
    for (PRInt32 i = 0; i < count; i++) {
        BookmarksService* instance = (BookmarksService*)gInstances->ElementAt(i);
        instance->NotifyObserver(aContainer, aReloadChildren);
    }
    
    FlushBookmarks();
}


void
BookmarksService::NotifyObserver(nsIContent* aContainer, PRBool aReloadChildren)
{
    if (!gDictionary)
        return;
   
    nsCOMPtr<nsIContent> parent;
    aContainer->GetParent(*getter_AddRefs(parent));
    
    BookmarkItem* item = nil;
    if (parent)
        // We're not the root.
        item = GetWrapperFor(aContainer);
    
    [mDataSource reloadDataForItem: item reloadChildren: aReloadChildren];
}

void
BookmarksService::AddObserver()
{
    gRefCnt++;
    if (gRefCnt == 1) {
        gBookmarkAtom = NS_NewAtom("bookmark");
        gFolderAtom = NS_NewAtom("folder");
        gNameAtom = NS_NewAtom("name");
        gHrefAtom = NS_NewAtom("href");
        gInstances = new nsVoidArray();
                
        nsCOMPtr<nsIFile> profileDir;
        NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
        profileDir->Append("bookmarks.xml");
    
        nsXPIDLCString bookmarksFileURL;
        NS_GetURLSpecFromFile(profileDir, getter_Copies(bookmarksFileURL));
        
        nsCOMPtr<nsIURI> uri;
        NS_NewURI(getter_AddRefs(uri), bookmarksFileURL.get());
    
        nsCOMPtr<nsIXBLService> xblService(do_GetService("@mozilla.org/xbl;1"));    
        xblService->FetchSyncXMLDocument(uri, &gBookmarks); // The addref is here.
        
        nsCOMPtr<nsIContent> rootNode;
        GetRootContent(getter_AddRefs(rootNode));
        StripWhitespaceNodes(rootNode);
    }
    
    gInstances->AppendElement(this);
}

void
BookmarksService::RemoveObserver()
{
    if (gRefCnt == 0)
        return;
 
    gInstances->RemoveElement(this);
     
    gRefCnt--;
    if (gRefCnt == 0) {
        NS_IF_RELEASE(gBookmarks);
        NS_RELEASE(gBookmarkAtom);
        NS_RELEASE(gFolderAtom);
        NS_RELEASE(gNameAtom);
        NS_RELEASE(gHrefAtom);
        [gDictionary release];
    }
}

void
BookmarksService::FlushBookmarks()
{
    nsCOMPtr<nsIFile> bookmarksFile;
    NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(bookmarksFile));
    bookmarksFile->Append("bookmarks.xml");

    nsCOMPtr<nsIOutputStream> outputStream;
    NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), bookmarksFile);

    nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(gBookmarks));
    
    nsCOMPtr<nsIDOMSerializer> domSerializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID));
    domSerializer->SerializeToStream(domDoc, outputStream, nsnull);
}

void
BookmarksService::ConstructBookmarksMenu(NSMenu* aMenu, nsIContent* aContent)
{
    nsCOMPtr<nsIContent> content = aContent;
    if (!content) {
        GetRootContent(getter_AddRefs(content));
        GetWrapperFor(content);
    }
    
    // Now walk our children, and for folders also recur into them.
    PRInt32 childCount;
    content->ChildCount(childCount);
    
    for (PRInt32 i = 0; i < childCount; i++) {
        nsCOMPtr<nsIContent> child;
        content->ChildAt(i, *getter_AddRefs(child));
        
        // Obtain our name attribute.
        nsAutoString name;
        child->GetAttr(kNameSpaceID_None, gNameAtom, name);
        nsCAutoString nameCStr; nameCStr.AssignWithConversion(name);
        NSString* title = [NSString stringWithCString: nameCStr.get()];
                
        // Create a menu or menu item for the child.
        NSMenuItem* menuItem = [[[NSMenuItem alloc] initWithTitle: title action: NULL keyEquivalent: @""] autorelease];
        GetWrapperFor(child);
        [aMenu addItem: menuItem];
        
        nsCOMPtr<nsIAtom> tag;
        child->GetTag(*getter_AddRefs(tag));
        
        if (tag == gFolderAtom) {
            NSMenu* menu = [[[NSMenu alloc] initWithTitle: title] autorelease];
            [aMenu setSubmenu: menu forItem: menuItem];
            [menu setAutoenablesItems: NO];
            ConstructBookmarksMenu(menu, child);
        }
        else {
            [menuItem setTarget: gMainController];
            [menuItem setAction: @selector(openMenuBookmark:)];
        }
        
        PRUint32 contentID;
        child->GetContentID(&contentID);
        [menuItem setTag: contentID];
    }
}

void 
BookmarksService::OpenMenuBookmark(BrowserWindowController* aController, id aMenuItem)
{
    // Get the corresponding bookmark item.
    BookmarkItem* item = [gDictionary objectForKey: [NSNumber numberWithInt: [aMenuItem tag]]];
        
    // Get the content node.
    nsIContent* content = [item contentNode];
        
    // Get the href attribute.  This is the URL we want to load.
    nsAutoString href;
    content->GetAttr(kNameSpaceID_None, gHrefAtom, href);
    nsCAutoString cref; cref.AssignWithConversion(href);
    if (cref.IsEmpty())
        return;
        
    NSString* url = [NSString stringWithCString: cref.get()];
    
    // Now load the URL in the window.
    [aController loadURL:[NSURL URLWithString: url]];
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>