/*
* 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([[[mBrowserWindowController getMyBrowserView] 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()];
[[[mBrowserWindowController getMyBrowserView] 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
{
NSString *columnName = [tableColumn identifier];
NSMutableAttributedString *cellValue = [[NSMutableAttributedString alloc] init];
NSFileWrapper *fileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:nil];
NSTextAttachment *textAttachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
NSMutableAttributedString *attachmentAttrString = nil;
NSCell *attachmentAttrStringCell;
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()]];
//Create an attributed string to hold the empty attachment, then release the components.
attachmentAttrString = [[NSMutableAttributedString attributedStringWithAttachment:textAttachment] retain];
[textAttachment release];
[fileWrapper release];
//Get the cell of the text attachment.
attachmentAttrStringCell = (NSCell *)[(NSTextAttachment *)[attachmentAttrString attribute:NSAttachmentAttributeName
atIndex:0
effectiveRange:nil] attachmentCell];
//Figure out which image to add, and set the cell's image.
if ( [self outlineView:outlineView isItemExpandable:item] ) {
[attachmentAttrStringCell setImage:[NSImage imageNamed:@"folder"]];
} else {
[attachmentAttrStringCell setImage:[NSImage imageNamed:@"smallDocument"]];
}
//Insert the image
[cellValue replaceCharactersInRange:NSMakeRange(0, 0) withAttributedString:attachmentAttrString];
//Tweak the baseline to vertically center the text.
[cellValue addAttribute:NSBaselineOffsetAttributeName
value:[NSNumber numberWithFloat:-3.0]
range:NSMakeRange(0, 1)];
}
return cellValue;
}
- (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>