File:  [mozdev] / chimera / BookmarksService.mm
Revision 1.30: download - view: text, annotated - select for diffs - revision graph
Sat Apr 20 00:44:56 2002 UTC (17 years, 1 month ago) by macserv
Branches: MAIN
CVS tags: HEAD
A bunch of tweaks here... no changes in functionality.  Updated trilicense header on all files that were missing it.  Fixed initialization code syntax errors all over the place.  Put back fade code, and switched fading off for now...

    1: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
    2: /* ***** BEGIN LICENSE BLOCK *****
    3:  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
    4:  *
    5:  * The contents of this file are subject to the Netscape Public License
    6:  * Version 1.1 (the "License"); you may not use this file except in
    7:  * compliance with the License. You may obtain a copy of the License at
    8:  * http://www.mozilla.org/NPL/
    9:  *
   10:  * Software distributed under the License is distributed on an "AS IS" basis,
   11:  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   12:  * for the specific language governing rights and limitations under the
   13:  * License.
   14:  *
   15:  * The Original Code is mozilla.org code.
   16:  *
   17:  * The Initial Developer of the Original Code is 
   18:  * Netscape Communications Corporation.
   19:  * Portions created by the Initial Developer are Copyright (C) 2002
   20:  * the Initial Developer. All Rights Reserved.
   21:  *
   22:  * Contributor(s):
   23:  *
   24:  * Alternatively, the contents of this file may be used under the terms of
   25:  * either the GNU General Public License Version 2 or later (the "GPL"), or 
   26:  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
   27:  * in which case the provisions of the GPL or the LGPL are applicable instead
   28:  * of those above. If you wish to allow use of your version of this file only
   29:  * under the terms of either the GPL or the LGPL, and not to allow others to
   30:  * use your version of this file under the terms of the NPL, indicate your
   31:  * decision by deleting the provisions above and replace them with the notice
   32:  * and other provisions required by the GPL or the LGPL. If you do not delete
   33:  * the provisions above, a recipient may use your version of this file under
   34:  * the terms of any one of the NPL, the GPL or the LGPL.
   35:  *
   36:  * ***** END LICENSE BLOCK ***** */
   37: 
   38: #import "NSBrowserView.h"
   39: #include "BookmarksService.h"
   40: #include "nsIDocument.h"
   41: #include "nsIContent.h"
   42: #include "nsIAtom.h"
   43: #include "nsITextContent.h"
   44: #include "nsIDOMWindow.h"
   45: #include "nsIDOMHTMLDocument.h"
   46: #include "nsIDOMElement.h"
   47: #include "nsString.h"
   48: #include "nsIFile.h"
   49: #include "nsAppDirectoryServiceDefs.h"
   50: #include "nsIXMLHttpRequest.h"
   51: #include "nsIDOMSerializer.h"
   52: #include "nsNetUtil.h"
   53: #include "nsINamespaceManager.h"
   54: #include "nsIXBLService.h"
   55: #include "nsIWebBrowser.h"
   56: 
   57: @implementation BookmarksDataSource
   58: 
   59: -(id) init
   60: {
   61:     if ( (self = [super init]) ) {
   62:         mBookmarks = nsnull;
   63:         mCachedHref = nil;
   64:     }
   65:     return self;
   66: }
   67: 
   68: -(void) dealloc
   69: {
   70:   [super dealloc];
   71: }
   72: 
   73: -(void) windowClosing
   74: {
   75:   if (mBookmarks) {
   76:     mBookmarks->RemoveObserver();
   77:     delete mBookmarks;
   78:   }
   79: }
   80: 
   81: -(void) ensureBookmarks
   82: {
   83:     if (mBookmarks)
   84:         return;
   85:     
   86:     mBookmarks = new BookmarksService(self);
   87:     mBookmarks->AddObserver();
   88:     
   89:     [mOutlineView setTarget: self];
   90:     [mOutlineView setDoubleAction: @selector(openBookmark:)];
   91:     [mOutlineView setDeleteAction: @selector(deleteBookmarks:)];
   92:     [mOutlineView reloadData];
   93: }
   94: 
   95: -(IBAction)addBookmark:(id)aSender
   96: {
   97:   [self addBookmark: aSender useSelection: YES isFolder: NO];
   98: }
   99: 
  100: -(IBAction)addFolder:(id)aSender
  101: {
  102:   [self addBookmark: aSender useSelection: YES isFolder: YES];
  103: }
  104: 
  105: -(void)addBookmark:(id)aSender useSelection:(BOOL)aUseSel isFolder:(BOOL)aIsFolder
  106: {
  107:   if (!mBookmarks)
  108:     return;
  109: 
  110:   // We use the selected item to determine the parent only if aUseSel is YES.
  111:   BookmarkItem* item = nil;
  112:   if (aUseSel && ([mOutlineView numberOfSelectedRows] == 1)) {
  113:     // There is only one selected row.  If it is a folder, use it as our parent.
  114:     // Otherwise, use our parent,
  115:     int index = [mOutlineView selectedRow];
  116:     item = [mOutlineView itemAtRow: index];
  117:     if (![mOutlineView isExpandable: item]) {
  118:       // We can't be used as the parent.  Try our parent.
  119:       nsIContent* content = [item contentNode];
  120:       nsCOMPtr<nsIContent> parentContent;
  121:       content->GetParent(*getter_AddRefs(parentContent));
  122:       nsCOMPtr<nsIContent> root;
  123:       mBookmarks->GetRootContent(getter_AddRefs(root));
  124:       
  125:       // The root has no item, so we don't need to do a lookup unless we
  126:       // aren't the root.
  127:       if (parentContent != root) {
  128:         PRUint32 contentID;
  129:         parentContent->GetContentID(&contentID);
  130:         item = [(BookmarksService::gDictionary) objectForKey: [NSNumber numberWithInt: contentID]];
  131:       }
  132:     }
  133:   }
  134: 
  135:   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mBookmarks->gBookmarks));
  136:   
  137:   // Fetch the title of the current page and the URL.
  138:   nsAutoString title, href;
  139:   if (!aIsFolder) {
  140:     BookmarksService::GetTitleAndHrefForBrowserView([[mBrowserWindowController getMyBrowserView] getBrowserView],
  141:                                                     title, href);
  142: 
  143:     mCachedHref = [NSString stringWithCharacters: href.get() length: nsCRT::strlen(href.get())];
  144:     [mCachedHref retain];
  145:   }
  146:   else {
  147:     mCachedHref = nil;
  148:     title = NS_LITERAL_STRING("New Folder");
  149:   }
  150:   
  151:   NSTextField* textField = [mBrowserWindowController getAddBookmarkTitle];
  152:   [textField setStringValue: [NSString stringWithCharacters: title.get() length: nsCRT::strlen(title.get())]];
  153: 
  154:   [mBrowserWindowController cacheBookmarkDS: self];
  155: 
  156:   // Show/hide the bookmark all tabs checkbox as appropriate.
  157:   NSTabView* tabView = [mBrowserWindowController getTabBrowser];
  158:   id checkbox = [mBrowserWindowController getAddBookmarkCheckbox];
  159:   BOOL hasSuperview = [checkbox superview] != nil;
  160:   if (aIsFolder && hasSuperview) {
  161:     // Just don't show it at all.
  162:     [checkbox removeFromSuperview];
  163:     [checkbox retain];
  164:   }
  165:   else if (!aIsFolder && !hasSuperview) {
  166:     // Put it back in.
  167:     [[[mBrowserWindowController getAddBookmarkSheetWindow] contentView] addSubview: checkbox];
  168:     [checkbox autorelease];
  169:   }
  170: 
  171:   // Enable the bookmark all tabs checkbox if appropriate.
  172:   if (!aIsFolder)
  173:     [[mBrowserWindowController getAddBookmarkCheckbox] setEnabled: ([tabView numberOfTabViewItems] > 1)];
  174:   
  175:   // Build up the folder list.
  176:   NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder];
  177:   BookmarksService::ConstructAddBookmarkFolderList(popup, item);
  178:   
  179:   [NSApp beginSheet:	[mBrowserWindowController getAddBookmarkSheetWindow]
  180:      modalForWindow:	[mBrowserWindowController window]
  181:       modalDelegate:	nil //self
  182:      didEndSelector:	nil //@selector(sheetDidEnd:)
  183:         contextInfo:	nil];
  184: }
  185: 
  186: -(void)endAddBookmark: (int)aCode
  187: {
  188:   if (aCode == 0)
  189:     return;
  190: 
  191:   BOOL isGroup = NO;
  192:   id checkbox = [mBrowserWindowController getAddBookmarkCheckbox];
  193:   if (([checkbox superview] != nil) && [checkbox isEnabled] && ([checkbox state] == NSOnState)) {
  194:     mCachedHref = nil;
  195:     isGroup = YES;
  196:   }
  197:   
  198:   const char* titleC = [[[mBrowserWindowController getAddBookmarkTitle] stringValue] cString];
  199:   nsAutoString title; title.AssignWithConversion(titleC);
  200: 
  201:   nsAutoString tagName;
  202:   if (mCachedHref)
  203:     tagName = NS_LITERAL_STRING("bookmark");
  204:   else
  205:     tagName = NS_LITERAL_STRING("folder");
  206:   
  207:   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(mBookmarks->gBookmarks));
  208:   nsCOMPtr<nsIDOMElement> elt;
  209:   domDoc->CreateElementNS(NS_LITERAL_STRING("http://chimera.mozdev.org/bookmarks/"),
  210:                           tagName,
  211:                           getter_AddRefs(elt));
  212: 
  213:   elt->SetAttribute(NS_LITERAL_STRING("name"), title);
  214: 
  215:   if (mCachedHref) {
  216:     nsAutoString href; href.AssignWithConversion([mCachedHref cString]);
  217:     [mCachedHref release];
  218:     elt->SetAttribute(NS_LITERAL_STRING("href"), href);
  219:   }
  220: 
  221:   if (isGroup) {
  222:     // We have to iterate over each tab and create content nodes using the
  223:     // title/href of all the pages.  They are inserted underneath the parent.
  224:     elt->SetAttribute(NS_LITERAL_STRING("group"), NS_LITERAL_STRING("true"));
  225:     id tabBrowser = [mBrowserWindowController getTabBrowser];
  226:     int count = [tabBrowser numberOfTabViewItems];
  227:     for (int i = 0; i < count; i++) {
  228:       id browserView = [[[tabBrowser tabViewItemAtIndex: i] view] getBrowserView];
  229:       nsAutoString title, href;
  230:       BookmarksService::GetTitleAndHrefForBrowserView(browserView, title, href);
  231:       nsCOMPtr<nsIDOMElement> childElt;
  232:       domDoc->CreateElementNS(NS_LITERAL_STRING("http://chimera.mozdev.org/bookmarks/"),
  233:                               NS_LITERAL_STRING("bookmark"),
  234:                               getter_AddRefs(childElt));
  235:       childElt->SetAttribute(NS_LITERAL_STRING("name"), title);
  236:       childElt->SetAttribute(NS_LITERAL_STRING("href"), href);
  237:       nsCOMPtr<nsIDOMNode> dummy;
  238:       elt->AppendChild(childElt, getter_AddRefs(dummy));
  239:     }
  240:   }
  241:   
  242:   // Figure out the parent element.
  243:   nsCOMPtr<nsIDOMElement> parentElt;
  244:   nsCOMPtr<nsIContent> parentContent;
  245:   NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder];
  246:   NSMenuItem* selectedItem = [popup selectedItem];
  247:   int tag = [selectedItem tag];
  248:   if (tag == -1) {
  249:     mBookmarks->GetRootContent(getter_AddRefs(parentContent));
  250:     parentElt = do_QueryInterface(parentContent);
  251:   }
  252:   else {
  253:     BookmarkItem* item = [(BookmarksService::gDictionary) objectForKey: [NSNumber numberWithInt: tag]];
  254:     // Get the content node.
  255:     parentContent = [item contentNode];
  256:     parentElt = do_QueryInterface(parentContent);
  257:   }
  258:   
  259:   nsCOMPtr<nsIDOMNode> dummy;
  260:   parentElt->AppendChild(elt, getter_AddRefs(dummy));
  261: 
  262:   nsCOMPtr<nsIContent> childContent(do_QueryInterface(elt));
  263:   mBookmarks->BookmarkAdded(parentContent, childContent);
  264: }
  265: 
  266: -(IBAction)deleteBookmarks: (id)aSender
  267: {
  268:   if (!mBookmarks)
  269:     return;
  270: 
  271:   int index = [mOutlineView selectedRow];
  272:   if (index == -1)
  273:     return;
  274:   if ([mOutlineView numberOfSelectedRows] == 1) {
  275:     BookmarkItem* item = [mOutlineView itemAtRow: index];
  276:     [self deleteBookmark: item];
  277:     int total = [mOutlineView numberOfRows];
  278:     if (index == total)
  279:       index--;
  280:     [mOutlineView selectRow: index byExtendingSelection: NO];
  281:   }
  282:   else {
  283:     NSMutableArray* itemsToDelete = [[[NSMutableArray alloc] init] autorelease];
  284:     NSEnumerator* selRows = [mOutlineView selectedRowEnumerator];
  285:     for (NSNumber* currIndex = [selRows nextObject];
  286:          currIndex != nil;
  287:          currIndex = [selRows nextObject]) {
  288:       index = [currIndex intValue];
  289:       BookmarkItem* item = [mOutlineView itemAtRow: index];
  290:       [itemsToDelete addObject: item];
  291:     }
  292: 
  293:     int count = [itemsToDelete count];
  294:     for (int i = 0; i < count; i++) {
  295:       BookmarkItem* item = [itemsToDelete objectAtIndex: i];
  296:       [self deleteBookmark: item];	
  297:     }
  298:   }
  299: }	
  300: 
  301: -(void)deleteBookmark:(id)aItem
  302: {
  303:   nsCOMPtr<nsIContent> content = [aItem contentNode];
  304:   nsCOMPtr<nsIDOMElement> child(do_QueryInterface(content));
  305:   if (child == BookmarksService::gToolbarRoot)
  306:     return; // Don't allow the personal toolbar to be deleted.
  307:   
  308:   nsCOMPtr<nsIDOMNode> parent;
  309:   child->GetParentNode(getter_AddRefs(parent));
  310:   nsCOMPtr<nsIContent> parentContent(do_QueryInterface(parent));
  311:   nsCOMPtr<nsIDOMNode> dummy;
  312:   parent->RemoveChild(child, getter_AddRefs(dummy));
  313:   mBookmarks->BookmarkRemoved(parentContent, content);
  314: }
  315: 
  316: -(IBAction)openBookmark: (id)aSender
  317: {
  318:   int index = [mOutlineView selectedRow];
  319:   if (index == -1)
  320:     return;
  321: 
  322:   id item = [mOutlineView itemAtRow: index];
  323:   if (!item)
  324:     return;
  325: 
  326:   nsIContent* content = [item contentNode];
  327:   nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(content));
  328:   nsAutoString group;
  329:   elt->GetAttribute(NS_LITERAL_STRING("group"), group);
  330:   if (!group.IsEmpty())
  331:     mBookmarks->OpenBookmarkGroup([mBrowserWindowController getTabBrowser], elt);
  332:   else if ([mOutlineView isExpandable: item]) {
  333:     if ([mOutlineView isItemExpanded: item])
  334:       [mOutlineView collapseItem: item];
  335:     else
  336:       [mOutlineView expandItem: item];
  337:   }
  338:   else {
  339:     nsAutoString href;
  340:     content->GetAttr(kNameSpaceID_None, BookmarksService::gHrefAtom, href);
  341:     if (!href.IsEmpty()) {
  342:       nsCAutoString cstr; cstr.AssignWithConversion(href);
  343:       NSString* url = [NSString stringWithCString: cstr.get()];
  344:       [[[mBrowserWindowController getMyBrowserView] getBrowserView] loadURI:[NSURL URLWithString: url] flags:			NSLoadFlagsNone];
  345:       // Focus and activate our content area.
  346:       [[[mBrowserWindowController getMyBrowserView] getBrowserView] setActive: YES];
  347:     }
  348:   }
  349: }
  350: 
  351: - (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
  352: {
  353:     return NO;
  354: }
  355: 
  356: - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
  357: {
  358:     if (!mBookmarks)
  359:         return nil;
  360:        
  361:     nsCOMPtr<nsIContent> content;
  362:     if (!item)
  363:         mBookmarks->GetRootContent(getter_AddRefs(content));
  364:     else
  365:         content = [item contentNode];
  366:     
  367:     nsCOMPtr<nsIContent> child;
  368:     content->ChildAt(index, *getter_AddRefs(child));
  369:     return mBookmarks->GetWrapperFor(child);
  370: }
  371: 
  372: - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
  373: {
  374:     if (!mBookmarks)
  375:         return NO;
  376:     
  377:     if (!item)
  378:         return YES; // The root node is always open.
  379:     
  380:     nsCOMPtr<nsIAtom> tagName;
  381:     nsIContent* content = [item contentNode];
  382:     content->GetTag(*getter_AddRefs(tagName));
  383:     
  384:     return (tagName == BookmarksService::gFolderAtom);
  385: }
  386: 
  387: - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
  388: {
  389:     if (!mBookmarks)
  390:         return 0;
  391:   
  392:     nsCOMPtr<nsIContent> content;
  393:     if (!item)
  394:         mBookmarks->GetRootContent(getter_AddRefs(content));
  395:     else 
  396:         content = [item contentNode];
  397:     
  398:     PRInt32 childCount;
  399:     content->ChildCount(childCount);
  400:     
  401:     return childCount;
  402: }
  403: 
  404: - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
  405: {
  406:     NSString 					*columnName = [tableColumn identifier];
  407:     NSMutableAttributedString 	*cellValue = [[NSMutableAttributedString alloc] init];
  408:     NSFileWrapper				*fileWrapper = [[NSFileWrapper alloc] initRegularFileWithContents:nil];
  409:     NSTextAttachment			*textAttachment = [[NSTextAttachment alloc] initWithFileWrapper:fileWrapper];
  410:     NSMutableAttributedString   *attachmentAttrString = nil;
  411:     NSCell 						*attachmentAttrStringCell;
  412: 
  413:     if ([columnName isEqualToString: @"name"]) {
  414:         nsIContent* content = [item contentNode];
  415:         nsAutoString nameAttr;
  416:         content->GetAttr(kNameSpaceID_None, BookmarksService::gNameAtom, nameAttr);
  417:         nsCAutoString cStr; cStr.AssignWithConversion(nameAttr);
  418:         
  419:         //Set cell's textual contents
  420:         [cellValue replaceCharactersInRange:NSMakeRange(0, [cellValue length])
  421:                                  withString:[NSString stringWithCString: cStr.get()]];
  422:         
  423:         //Create an attributed string to hold the empty attachment, then release the components.
  424:         attachmentAttrString = [[NSMutableAttributedString attributedStringWithAttachment:textAttachment] retain];
  425:         [textAttachment release];
  426:         [fileWrapper release];
  427: 
  428:         //Get the cell of the text attachment.
  429:         attachmentAttrStringCell = (NSCell *)[(NSTextAttachment *)[attachmentAttrString attribute:NSAttachmentAttributeName
  430:                                                                                           atIndex:0
  431:                                                                                    effectiveRange:nil] attachmentCell];
  432:         //Figure out which image to add, and set the cell's image.
  433:         if ( [self outlineView:outlineView isItemExpandable:item] ) {
  434:             [attachmentAttrStringCell setImage:[NSImage imageNamed:@"folder"]];
  435:         } else {
  436:             [attachmentAttrStringCell setImage:[NSImage imageNamed:@"smallbookmark"]];
  437:         }
  438:         //Insert the image
  439:         [cellValue replaceCharactersInRange:NSMakeRange(0, 0) withAttributedString:attachmentAttrString];
  440:         
  441:         //Tweak the baseline to vertically center the text.
  442:         [cellValue addAttribute:NSBaselineOffsetAttributeName
  443:                           value:[NSNumber numberWithFloat:-3.0]
  444:                           range:NSMakeRange(0, 1)];
  445:     }
  446:     return cellValue;
  447: }
  448: 
  449: - (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
  450: {
  451: }
  452: 
  453: - (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren
  454: {
  455:     printf("Reloading?\n");
  456:     if (!item)
  457:         [mOutlineView reloadData];
  458:     else if ([mOutlineView isItemExpanded: item])
  459:         [mOutlineView reloadItem: item reloadChildren: aReloadChildren];
  460: }
  461: 
  462: @end
  463: 
  464: @implementation BookmarkItem
  465: -(nsIContent*)contentNode
  466: {
  467:     return mContentNode;
  468: }
  469: 
  470: -(void)setContentNode: (nsIContent*)aContentNode
  471: {
  472:     mContentNode = aContentNode;
  473: }
  474: 
  475: - (id)copyWithZone:(NSZone *)aZone
  476: {
  477:     BookmarkItem* copy = [[[self class] allocWithZone: aZone] init];
  478:     [copy setContentNode: mContentNode];
  479:     return copy;
  480: }
  481: 
  482: @end
  483: 
  484: // Helper for stripping whitespace
  485: static void
  486: StripWhitespaceNodes(nsIContent* aElement)
  487: {
  488:     PRInt32 childCount;
  489:     aElement->ChildCount(childCount);
  490:     for (PRInt32 i = 0; i < childCount; i++) {
  491:         nsCOMPtr<nsIContent> child;
  492:         aElement->ChildAt(i, *getter_AddRefs(child));
  493:         nsCOMPtr<nsITextContent> text = do_QueryInterface(child);
  494:         if (text) {
  495:             PRBool isEmpty;
  496:             text->IsOnlyWhitespace(&isEmpty);
  497:             if (isEmpty) {
  498:                 // This node contained nothing but whitespace.
  499:                 // Remove it from the content model.
  500:                 aElement->RemoveChildAt(i, PR_TRUE);
  501:                 i--; // Decrement our count, since we just removed this child.
  502:                 childCount--; // Also decrement our total count.
  503:             }
  504:         }
  505:         else StripWhitespaceNodes(child);
  506:     }
  507: }
  508: 
  509: PRUint32 BookmarksService::gRefCnt = 0;
  510: nsIDocument* BookmarksService::gBookmarks = nsnull;
  511: NSMutableDictionary* BookmarksService::gDictionary = nil;
  512: MainController* BookmarksService::gMainController = nil;
  513: NSMenu* BookmarksService::gBookmarksMenu = nil;
  514: nsIDOMElement* BookmarksService::gToolbarRoot = nsnull;
  515: nsIAtom* BookmarksService::gFolderAtom = nsnull;
  516: nsIAtom* BookmarksService::gBookmarkAtom = nsnull;
  517: nsIAtom* BookmarksService::gHrefAtom = nsnull;
  518: nsIAtom* BookmarksService::gNameAtom = nsnull;
  519: nsVoidArray* BookmarksService::gInstances = nsnull;
  520: 
  521: BookmarksService::BookmarksService(BookmarksDataSource* aDataSource)
  522: {
  523:   mDataSource = aDataSource;
  524:   mToolbar = nil;
  525: }
  526: 
  527: BookmarksService::BookmarksService(BookmarksToolbar* aToolbar)
  528: {
  529:   mDataSource = nil;
  530:   mToolbar = aToolbar;
  531: }
  532: 
  533: BookmarksService::~BookmarksService()
  534: {
  535: }
  536: 
  537: void
  538: BookmarksService::GetRootContent(nsIContent** aResult)
  539: {
  540:     *aResult = nsnull;
  541:     if (gBookmarks) {
  542:         nsCOMPtr<nsIDOMElement> elt;
  543:         nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(gBookmarks));
  544:         domDoc->GetDocumentElement(getter_AddRefs(elt));
  545:         elt->QueryInterface(NS_GET_IID(nsIContent), (void**)aResult); // Addref happens here.
  546:     }
  547: }
  548: 
  549: BookmarkItem*
  550: BookmarksService::GetWrapperFor(nsIContent* aContent)
  551: {
  552:     if (!gDictionary)
  553:         gDictionary = [[NSMutableDictionary alloc] initWithCapacity: 30];
  554:     
  555:     PRUint32 contentID;
  556:     aContent->GetContentID(&contentID);
  557:     
  558:     BookmarkItem* item = [gDictionary objectForKey: [NSNumber numberWithInt: contentID]];
  559:     if (item)
  560:         return item;
  561:     else {
  562:         // Create an item.
  563:         item = [[[BookmarkItem alloc] init] autorelease]; // The dictionary retains us.
  564:         [item setContentNode: aContent];
  565:         [gDictionary setObject: item forKey: [NSNumber numberWithInt: contentID]];
  566:     }
  567:     return item;
  568: }
  569: 
  570: NSMenu*
  571: BookmarksService::LocateMenu(nsIContent* aContent)
  572: {
  573:   nsCOMPtr<nsIContent> parent;
  574:   aContent->GetParent(*getter_AddRefs(parent));
  575:   if (!parent) {
  576:     return BookmarksService::gBookmarksMenu;
  577:   }
  578:   
  579:   NSMenu* parentMenu = LocateMenu(parent);
  580:   
  581:   PRUint32 contentID;
  582:   aContent->GetContentID(&contentID);
  583: 
  584:   NSMenuItem* childMenu = [parentMenu itemWithTag: contentID];
  585:   return [childMenu submenu];
  586: }
  587: 
  588: void
  589: BookmarksService::BookmarkAdded(nsIContent* aContainer, nsIContent* aChild)
  590: {
  591:   if (!gInstances || !gDictionary)
  592:     return;
  593: 
  594:   PRInt32 count = gInstances->Count();
  595:   for (PRInt32 i = 0; i < count; i++) {
  596:     BookmarksService* instance = (BookmarksService*)gInstances->ElementAt(i);
  597: 
  598:     if (instance->mDataSource) {
  599:       // We're a tree view.
  600:       nsCOMPtr<nsIContent> parent;
  601:       aContainer->GetParent(*getter_AddRefs(parent));
  602: 
  603:       BookmarkItem* item = nil;
  604:       if (parent)
  605:         // We're not the root.
  606:         item = GetWrapperFor(aContainer);
  607: 
  608:       [(instance->mDataSource) reloadDataForItem: item reloadChildren: YES];
  609:     }
  610:     else if (instance->mToolbar) {
  611:       // We're a personal toolbar.
  612:       nsCOMPtr<nsIDOMElement> parentElt(do_QueryInterface(aContainer));
  613:       if (parentElt == gToolbarRoot) {
  614:         // We only care about changes that occur to the personal toolbar's immediate
  615:         // children.
  616:         PRInt32 index = -1;
  617:         aContainer->IndexOf(aChild, index);
  618:         nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(aChild));
  619:         [(instance->mToolbar) addButton: elt atIndex: index];
  620:       }
  621:     }
  622:     else {
  623:       // We're the menu.
  624:       PRInt32 index = -1;
  625:       aContainer->IndexOf(aChild, index);
  626:       NSMenu* menu = LocateMenu(aContainer);
  627:       AddMenuBookmark(menu, aContainer, aChild, index);
  628:     }
  629:   }
  630:   
  631:   FlushBookmarks();  
  632: }
  633: 
  634: void
  635: BookmarksService::BookmarkChanged(nsIContent* aItem)
  636: {
  637:   if (!gInstances || !gDictionary)
  638:     return;
  639: 
  640:   PRInt32 count = gInstances->Count();
  641:   for (PRInt32 i = 0; i < count; i++) {
  642:     BookmarksService* instance = (BookmarksService*)gInstances->ElementAt(i);
  643:    
  644:     if (instance->mDataSource) {
  645:       BookmarkItem* item = GetWrapperFor(aItem);
  646:       [(instance->mDataSource) reloadDataForItem: item reloadChildren: NO];
  647:     }
  648:   }
  649: 
  650:   FlushBookmarks();  
  651: }
  652: 
  653: void
  654: BookmarksService::BookmarkRemoved(nsIContent* aContainer, nsIContent* aChild)
  655: {
  656:   if (!gInstances)
  657:     return;
  658: 
  659:   PRInt32 count = gInstances->Count();
  660:   for (PRInt32 i = 0; i < count; i++) {
  661:     BookmarksService* instance = (BookmarksService*)gInstances->ElementAt(i);
  662: 
  663:     if (instance->mDataSource) {
  664:       // We're a tree view.
  665:       nsCOMPtr<nsIContent> parent;
  666:       aContainer->GetParent(*getter_AddRefs(parent));
  667: 
  668:       BookmarkItem* item = nil;
  669:       if (parent)
  670:         // We're not the root.
  671:         item = GetWrapperFor(aContainer);
  672: 
  673:       [(instance->mDataSource) reloadDataForItem: item reloadChildren: YES];
  674:     }
  675:     else if (instance->mToolbar) {
  676:       // We're a personal toolbar.
  677:       nsCOMPtr<nsIDOMElement> parentElt(do_QueryInterface(aContainer));
  678:       if (parentElt == gToolbarRoot) {
  679:         // We only care about changes that occur to the personal toolbar's immediate
  680:         // children.
  681:         nsCOMPtr<nsIDOMElement> childElt(do_QueryInterface(aChild));
  682:         [(instance->mToolbar) removeButton: childElt];
  683:       }
  684:     }    
  685:     else {
  686:       // We're the menu.
  687:       NSMenu* menu = LocateMenu(aContainer);
  688:       PRUint32 contentID;
  689:       aChild->GetContentID(&contentID);
  690:       NSMenuItem* childItem = [menu itemWithTag: contentID];
  691:       [menu removeItem: childItem];
  692:     }
  693:   }
  694: 
  695:   FlushBookmarks(); 
  696: }
  697: 
  698: void
  699: BookmarksService::AddObserver()
  700: {
  701:     gRefCnt++;
  702:     if (gRefCnt == 1) {
  703:         gBookmarkAtom = NS_NewAtom("bookmark");
  704:         gFolderAtom = NS_NewAtom("folder");
  705:         gNameAtom = NS_NewAtom("name");
  706:         gHrefAtom = NS_NewAtom("href");
  707:         gInstances = new nsVoidArray();
  708:                 
  709:         nsCOMPtr<nsIFile> profileDir;
  710:         NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profileDir));
  711:         profileDir->Append("bookmarks.xml");
  712:     
  713:         nsCAutoString bookmarksFileURL;
  714:         NS_GetURLSpecFromFile(profileDir, bookmarksFileURL);
  715:         
  716:         nsCOMPtr<nsIURI> uri;
  717:         NS_NewURI(getter_AddRefs(uri), bookmarksFileURL.get());
  718:     
  719:         nsCOMPtr<nsIXBLService> xblService(do_GetService("@mozilla.org/xbl;1"));    
  720:         xblService->FetchSyncXMLDocument(uri, &gBookmarks); // The addref is here.
  721:         
  722:         nsCOMPtr<nsIContent> rootNode;
  723:         GetRootContent(getter_AddRefs(rootNode));
  724:         StripWhitespaceNodes(rootNode);
  725:     }
  726:     
  727:     gInstances->AppendElement(this);
  728: }
  729: 
  730: void
  731: BookmarksService::RemoveObserver()
  732: {
  733:     if (gRefCnt == 0)
  734:         return;
  735:  
  736:     gInstances->RemoveElement(this);
  737:      
  738:     gRefCnt--;
  739:     if (gRefCnt == 0) {
  740:         NS_IF_RELEASE(gBookmarks);
  741:         NS_RELEASE(gBookmarkAtom);
  742:         NS_RELEASE(gFolderAtom);
  743:         NS_RELEASE(gNameAtom);
  744:         NS_RELEASE(gHrefAtom);
  745:         [gDictionary release];
  746:     }
  747: }
  748: 
  749: void
  750: BookmarksService::FlushBookmarks()
  751: {
  752:     nsCOMPtr<nsIFile> bookmarksFile;
  753:     NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(bookmarksFile));
  754:     bookmarksFile->Append("bookmarks.xml");
  755: 
  756:     nsCOMPtr<nsIOutputStream> outputStream;
  757:     NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), bookmarksFile);
  758: 
  759:     nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(gBookmarks));
  760:     
  761:     nsCOMPtr<nsIDOMSerializer> domSerializer(do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID));
  762:     domSerializer->SerializeToStream(domDoc, outputStream, nsnull);
  763: }
  764: 
  765: void BookmarksService::EnsureToolbarRoot()
  766: {
  767:   if (gToolbarRoot)
  768:     return;
  769: 
  770:   nsCOMPtr<nsIDOMDocument> domDoc(do_QueryInterface(gBookmarks));
  771:   nsCOMPtr<nsIDOMElement> rootElt;
  772:   domDoc->GetDocumentElement(getter_AddRefs(rootElt));
  773:   
  774:   nsCOMPtr<nsIDOMNode> child;
  775:   rootElt->GetFirstChild(getter_AddRefs(child));
  776:   nsAutoString typeValue;
  777:   while (child) {
  778:     nsCOMPtr<nsIDOMElement> childElt(do_QueryInterface(child));
  779:     if (childElt) {
  780:       childElt->GetAttribute(NS_LITERAL_STRING("type"), typeValue);
  781:       if (typeValue.Equals(NS_LITERAL_STRING("toolbar")))
  782:         gToolbarRoot = childElt;
  783:     }
  784:     
  785:     nsCOMPtr<nsIDOMNode> temp;
  786:     child->GetNextSibling(getter_AddRefs(temp));
  787:     child = temp;
  788:   }
  789: 
  790:   if (!gToolbarRoot) {
  791:     printf("Repairing personal toolbar.\n");
  792:     nsCOMPtr<nsIDOMElement> elt;
  793:     domDoc->CreateElementNS(NS_LITERAL_STRING("http://chimera.mozdev.org/bookmarks/"),
  794:                             NS_LITERAL_STRING("folder"),
  795:                             getter_AddRefs(elt));
  796: 
  797:     elt->SetAttribute(NS_LITERAL_STRING("name"), NS_LITERAL_STRING("Toolbar Bookmarks"));
  798:     elt->SetAttribute(NS_LITERAL_STRING("type"), NS_LITERAL_STRING("toolbar"));
  799: 
  800:     nsCOMPtr<nsIDOMNode> dummy;
  801:     rootElt->AppendChild(elt, getter_AddRefs(dummy));
  802:     gToolbarRoot = elt;
  803:   }
  804: }
  805: 
  806: static
  807: void RecursiveAddBookmarkConstruct(NSPopUpButton* aPopup, NSMenu* aMenu, int aTagToMatch)
  808: {
  809:   // Get the menu item children.
  810:   NSArray* children = [aMenu itemArray];
  811:   int startPosition = 0;
  812:   if (aMenu == BookmarksService::gBookmarksMenu)
  813:     startPosition = 3;
  814: 
  815:   int count = [children count];
  816:   for (int i = startPosition; i < count; i++) {
  817:     NSMenuItem* menuItem = [children objectAtIndex: i];
  818:     NSMenu* submenu = [menuItem submenu];
  819:     if (submenu) {
  820:       // This is a folder.  Add it to our list and then recur.
  821:       [aPopup addItemWithTitle: [menuItem title]];
  822:       NSMenuItem* lastItem = [aPopup lastItem];
  823:       if ([menuItem tag] == aTagToMatch)
  824:         [aPopup selectItem: lastItem];
  825:       
  826:       [lastItem setTag: [menuItem tag]];
  827:       RecursiveAddBookmarkConstruct(aPopup, submenu, aTagToMatch);
  828:     }
  829:   }
  830: }
  831: 
  832: void
  833: BookmarksService::ConstructAddBookmarkFolderList(NSPopUpButton* aPopup, BookmarkItem* aItem)
  834: {
  835:   [aPopup removeAllItems];
  836:   [aPopup addItemWithTitle: [gBookmarksMenu title]];
  837:   NSMenuItem* lastItem = [aPopup lastItem];
  838:   [lastItem setTag: -1];
  839:   int tag = -1;
  840:   if (aItem) {
  841:     nsIContent* content = [aItem contentNode];
  842:     PRUint32 utag;
  843:     content->GetContentID(&utag);
  844:     tag = (int)utag;
  845:   }
  846:   RecursiveAddBookmarkConstruct(aPopup, gBookmarksMenu, tag);
  847: }
  848: 
  849: void
  850: BookmarksService::GetTitleAndHrefForBrowserView(id aBrowserView, nsString& aTitle, nsString& aHref)
  851: {
  852:   nsCOMPtr<nsIWebBrowser> webBrowser = getter_AddRefs([aBrowserView getWebBrowser]);
  853:   nsCOMPtr<nsIDOMWindow> window;
  854:   webBrowser->GetContentDOMWindow(getter_AddRefs(window));
  855:   nsCOMPtr<nsIDOMDocument> htmlDoc;
  856:   window->GetDocument(getter_AddRefs(htmlDoc));
  857:   nsCOMPtr<nsIDocument> pageDoc(do_QueryInterface(htmlDoc));
  858: 
  859:   if (pageDoc) {
  860:     nsCOMPtr<nsIURI> url;
  861:     pageDoc->GetDocumentURL(getter_AddRefs(url));
  862:     nsCAutoString spec;
  863:     url->GetSpec(spec);
  864:     aHref.AssignWithConversion(spec.get());
  865:   }
  866: 
  867:   nsCOMPtr<nsIDOMHTMLDocument> htmlDocument(do_QueryInterface(htmlDoc));
  868:   if (htmlDocument)
  869:     htmlDocument->GetTitle(aTitle);
  870:   if (aTitle.IsEmpty())
  871:     aTitle = aHref;  
  872: }
  873: 
  874: void
  875: BookmarksService::ConstructBookmarksMenu(NSMenu* aMenu, nsIContent* aContent)
  876: {
  877:     nsCOMPtr<nsIContent> content = aContent;
  878:     if (!content) {
  879:         GetRootContent(getter_AddRefs(content));
  880:         GetWrapperFor(content);
  881:         gBookmarksMenu = aMenu;
  882:     }
  883:     
  884:     // Now walk our children, and for folders also recur into them.
  885:     PRInt32 childCount;
  886:     content->ChildCount(childCount);
  887:     
  888:     for (PRInt32 i = 0; i < childCount; i++) {
  889:       nsCOMPtr<nsIContent> child;
  890:       content->ChildAt(i, *getter_AddRefs(child));
  891:       AddMenuBookmark(aMenu, content, child, -1);
  892:     }
  893: }
  894: 
  895: void
  896: BookmarksService::AddMenuBookmark(NSMenu* aMenu, nsIContent* aParent, nsIContent* aChild, PRInt32 aIndex)
  897: {
  898:   nsAutoString name;
  899:   aChild->GetAttr(kNameSpaceID_None, gNameAtom, name);
  900:   nsCAutoString nameCStr; nameCStr.AssignWithConversion(name);
  901:   NSString* title = [NSString stringWithCString: nameCStr.get()];
  902: 
  903:   // Create a menu or menu item for the child.
  904:   NSMenuItem* menuItem = [[[NSMenuItem alloc] initWithTitle: title action: NULL keyEquivalent: @""] autorelease];
  905:   GetWrapperFor(aChild);
  906: 
  907:   if (aIndex == -1)
  908:     [aMenu addItem: menuItem];
  909:   else
  910:     [aMenu insertItem: menuItem atIndex: aIndex];
  911:   
  912:   nsCOMPtr<nsIAtom> tagName;
  913:   aChild->GetTag(*getter_AddRefs(tagName));
  914: 
  915:   if (tagName == gFolderAtom) {
  916:     NSMenu* menu = [[[NSMenu alloc] initWithTitle: title] autorelease];
  917:     [aMenu setSubmenu: menu forItem: menuItem];
  918:     [menu setAutoenablesItems: NO];
  919:     ConstructBookmarksMenu(menu, aChild);
  920:   }
  921:   else {
  922:     [menuItem setTarget: gMainController];
  923:     [menuItem setAction: @selector(openMenuBookmark:)];
  924:   }
  925: 
  926:   PRUint32 contentID;
  927:   aChild->GetContentID(&contentID);
  928:   [menuItem setTag: contentID];
  929: }
  930: 
  931: void 
  932: BookmarksService::OpenMenuBookmark(BrowserWindowController* aController, id aMenuItem)
  933: {
  934:     // Get the corresponding bookmark item.
  935:     BookmarkItem* item = [gDictionary objectForKey: [NSNumber numberWithInt: [aMenuItem tag]]];
  936:         
  937:     // Get the content node.
  938:     nsIContent* content = [item contentNode];
  939:         
  940:     // Get the href attribute.  This is the URL we want to load.
  941:     nsAutoString href;
  942:     content->GetAttr(kNameSpaceID_None, gHrefAtom, href);
  943:     nsCAutoString cref; cref.AssignWithConversion(href);
  944:     if (cref.IsEmpty())
  945:         return;
  946:         
  947:     NSString* url = [NSString stringWithCString: cref.get()];
  948:     
  949:     // Now load the URL in the window.
  950:     [aController loadURL:[NSURL URLWithString: url]];
  951:     
  952:     // Focus and activate our content area.
  953:     [[[aController getMyBrowserView] getBrowserView] setActive: YES];
  954: }
  955: 
  956: void
  957: BookmarksService::OpenBookmarkGroup(id aTabView, nsIDOMElement* aFolder)
  958: {
  959:   // We might conceivably have to make new tabs in order to load all
  960:   // the items in the group.
  961:   int currentIndex = 0;
  962:   int total = [aTabView numberOfTabViewItems];
  963:   nsCOMPtr<nsIDOMNode> child;
  964:   aFolder->GetFirstChild(getter_AddRefs(child));
  965:   while (child) {
  966:     nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(child));
  967:     if (elt) {
  968:       nsAutoString href;
  969:       elt->GetAttribute(NS_LITERAL_STRING("href"), href);
  970:       if (!href.IsEmpty()) {
  971:         nsCAutoString cref; cref.AssignWithConversion(href);
  972:         NSString* url = [NSString stringWithCString: cref.get()];
  973:         NSTabViewItem* tabViewItem = nil;
  974:         if (currentIndex >= total) {
  975:           // We need to make a new tab.
  976:           tabViewItem = [[[NSTabViewItem alloc] initWithIdentifier: nil] autorelease];
  977:           MyBrowserView* newView = [[[MyBrowserView alloc] initWithTab: tabViewItem andWindow: [aTabView window]] autorelease];
  978:           [tabViewItem setLabel: @"Untitled"];
  979:           [tabViewItem setView: newView];
  980:           [aTabView addTabViewItem: tabViewItem];
  981:         }
  982:         else
  983:           tabViewItem = [aTabView tabViewItemAtIndex: currentIndex];
  984: 
  985:         [[[tabViewItem view] getBrowserView] loadURI:[NSURL URLWithString: url]
  986:                                                flags: NSLoadFlagsNone];
  987:       }
  988:     }
  989:     
  990:     nsCOMPtr<nsIDOMNode> temp = child;
  991:     temp->GetNextSibling(getter_AddRefs(child));
  992:     currentIndex++;
  993:   }
  994: 
  995:   // XXXdwh Select and activate the first tab.
  996: }

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