\n",
+ padString,
+ [NSString stringWithUTF8String:[[mTitle stringByAddingAmpEscapes] UTF8String]],
+ padString];
+ // Toolbar Folder
+ else if ([self isToolbar])
+ htmlString = [NSString stringWithFormat:@"%@
\n",
+ padString,
+ [NSString stringWithUTF8String:[[mTitle stringByAddingAmpEscapes] UTF8String]],
+ padString];
+ // Root Folder
+ else if ([self isRoot])
+ htmlString = [NSString stringWithFormat:@"%@\n%@\n%@\n%@\n\n
\n",
+ @"",
+ @"",
+ @"
Bookmarks",
+ @"Bookmarks
"];
+ // Folder-Not-Appearing-In-This-File Folder (ie, smart folders)
+ else
+ htmlString = [NSString stringWithString:@""];
+ NSEnumerator* enumerator = [mChildArray objectEnumerator];
+ //get chillins first
+ while ((item = [enumerator nextObject]))
+ htmlString = [htmlString stringByAppendingString:[item writeHTML:aPad+1]];
+ return [htmlString stringByAppendingString:[NSString stringWithFormat:@"%@
\n",padString]];
+}
+
+#pragma mark -
+//
+// Scripting methods - thanks for the sketch example, Apple
+//
+- (NSScriptObjectSpecifier *)objectSpecifier
+{
+ id parent = [self parent];
+ NSScriptObjectSpecifier *containerRef = [parent objectSpecifier];
+ NSScriptClassDescription *arrayScriptClassDesc = (NSScriptClassDescription *)[NSClassDescription classDescriptionForClass:[BookmarkFolder class]];
+ if ([parent isKindOfClass:[BookmarkFolder class]]) {
+ unsigned index = [[parent childArray] indexOfObjectIdenticalTo:self];
+ if (index != NSNotFound) {
+ // need to get the appropriate class description
+ return [[[NSIndexSpecifier allocWithZone:[self zone]] initWithContainerClassDescription:arrayScriptClassDesc containerSpecifier:containerRef key:@"childArray" index:index] autorelease];
+ } else
+ return nil;
+ } else // root bookmark
+ return [[[NSPropertySpecifier allocWithZone:[self zone]] initWithContainerSpecifier:containerRef key:@"rootArray"] autorelease];
+ return nil;
+}
+
+- (NSArray *)folderItemWithClass:(Class)theClass {
+ NSArray *kiddies = [self childArray];
+ NSMutableArray *result = [NSMutableArray array];
+ unsigned i, c = [kiddies count];
+ id curItem;
+ for (i=0; i= 0) && (baseIndex < kiddiesCount)) {
+ if (keyIsArray) {
+ [result addObject:[NSNumber numberWithInt:baseIndex]];
+ break;
+ } else {
+ curObj = [kiddies objectAtIndex:baseIndex];
+ curKeyIndex = [relKeyObjects indexOfObjectIdenticalTo:curObj];
+ if (curKeyIndex != NSNotFound) {
+ [result addObject:[NSNumber numberWithInt:curKeyIndex]];
+ break;
+ }
+ }
+ if (relPos == NSRelativeBefore)
+ baseIndex--;
+ else
+ baseIndex++;
+ }
+ return result;
+ }
+ }
+ return nil;
+}
+
+- (NSArray *)indicesOfObjectsByEvaluatingRangeSpecifier:(NSRangeSpecifier *)rangeSpec
+{
+ NSString *key = [rangeSpec key];
+ if ([key isEqualToString:@"childBookmarks"] ||
+ [key isEqualToString:@"childArray"] ||
+ [key isEqualToString:@"childFolders"] )
+ {
+ NSScriptObjectSpecifier *startSpec = [rangeSpec startSpecifier];
+ NSScriptObjectSpecifier *endSpec = [rangeSpec endSpecifier];
+ NSString *startKey = [startSpec key];
+ NSString *endKey = [endSpec key];
+ NSArray *kiddies = [self childArray];
+
+ if ((startSpec == nil) && (endSpec == nil))
+ return nil;
+ if ([kiddies count] == 0)
+ return [NSArray array];
+
+ if ((!startSpec || [startKey isEqualToString:@"childBookmarks"] || [startKey isEqualToString:@"childArray"] || [startKey isEqualToString:@"childFolders"]) && (!endSpec || [endKey isEqualToString:@"childBookmarks"] || [endKey isEqualToString:@"childArray"] || [endKey isEqualToString:@"childFolders"]))
+ {
+ unsigned startIndex;
+ unsigned endIndex;
+ if (startSpec) {
+ id startObject = [startSpec objectsByEvaluatingSpecifier];
+ if ([startObject isKindOfClass:[NSArray class]]) {
+ if ([startObject count] == 0)
+ startObject = nil;
+ else
+ startObject = [startObject objectAtIndex:0];
+ }
+ if (!startObject)
+ return nil;
+ startIndex = [kiddies indexOfObjectIdenticalTo:startObject];
+ if (startIndex == NSNotFound)
+ return nil;
+ } else
+ startIndex = 0;
+
+ if (endSpec) {
+ id endObject = [endSpec objectsByEvaluatingSpecifier];
+ if ([endObject isKindOfClass:[NSArray class]]) {
+ unsigned endObjectsCount = [endObject count];
+ if (endObjectsCount == 0)
+ endObject = nil;
+ else
+ endObject = [endObject objectAtIndex:(endObjectsCount-1)];
+ }
+ if (!endObject)
+ return nil;
+ endIndex = [kiddies indexOfObjectIdenticalTo:endObject];
+ if (endIndex == NSNotFound)
+ return nil;
+ } else
+ endIndex = [kiddies count] - 1;
+
+ if (endIndex < startIndex) {
+ int temp = endIndex;
+ endIndex = startIndex;
+ startIndex = temp;
+ }
+
+ NSMutableArray *result = [NSMutableArray array];
+ BOOL keyIsArray = [key isEqual:@"childArray"];
+ NSArray *rangeKeyObjects = (keyIsArray ? nil : [self valueForKey:key]);
+ id curObj;
+ unsigned curKeyIndex, i;
+ for (i=startIndex; i<=endIndex; i++) {
+ if (keyIsArray)
+ [result addObject:[NSNumber numberWithInt:i]];
+ else {
+ curObj = [kiddies objectAtIndex:i];
+ curKeyIndex = [rangeKeyObjects indexOfObjectIdenticalTo:curObj];
+ if (curKeyIndex != NSNotFound)
+ [result addObject:[NSNumber numberWithInt:curKeyIndex]];
+ }
+ }
+ return result;
+ }
+ }
+ return nil;
+}
+
+@end;
diff --git a/camino/src/bookmarks/BookmarkImportDlgController.h b/camino/src/bookmarks/BookmarkImportDlgController.h
new file mode 100644
index 000000000000..970d340f3fd4
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkImportDlgController.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+
+
+@interface BookmarkImportDlgController : NSWindowController {
+ IBOutlet NSPopUpButton* mBrowserListButton;
+ IBOutlet NSButton* mCancelButton;
+ IBOutlet NSButton* mImportButton;
+}
+
+-(void) buildAvailableFileList;
+-(IBAction) cancel:(id)aSender;
+-(IBAction) import:(id)aSender;
+-(IBAction) loadOpenPanel:(id)aSender;
+-(IBAction) nullAction:(id)aSender;
+
+
+@end
diff --git a/camino/src/bookmarks/BookmarkImportDlgController.mm b/camino/src/bookmarks/BookmarkImportDlgController.mm
new file mode 100644
index 000000000000..1142989796a1
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkImportDlgController.mm
@@ -0,0 +1,193 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkImportDlgController.h"
+#import "BookmarkManager.h"
+#import "BookmarkFolder.h"
+
+@implementation BookmarkImportDlgController
+
+-(void) windowDidLoad
+{
+ [self buildAvailableFileList];
+}
+
+-(void) buildAvailableFileList
+{
+ // check for common webbrower bookmark files and, if they exist, add to button.
+ NSFileManager *fm = [NSFileManager defaultManager];
+ NSMenuItem *browserItem;
+ NSEnumerator *enumerator;
+ id aTempItem;
+ // iCab
+ NSString *pathString = [[NSString stringWithString:@"~/Library/Preferences/iCab Preferences/Hotlist.html"] stringByExpandingTildeInPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"iCab" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+ // Firebird
+ pathString = [[NSString stringWithString:@"~/Library/Phoenix/Profiles/default/"] stringByExpandingTildeInPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ NSArray *saltArray = [fm directoryContentsAtPath:pathString];
+ enumerator = [saltArray objectEnumerator];
+ while ((aTempItem = [enumerator nextObject])) {
+ if (![aTempItem hasPrefix:@"."])
+ break;
+ }
+ if (aTempItem) {
+ pathString = [pathString stringByAppendingFormat:@"/%@/bookmarks.html",aTempItem];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"Mozilla Firebird" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+ }
+ }
+
+ // Netscape/Mozilla
+ pathString = [[NSString stringWithString:@"~/Library/Mozilla/Profiles/default/"] stringByExpandingTildeInPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ NSArray *saltArray = [fm directoryContentsAtPath:pathString];
+ enumerator = [saltArray objectEnumerator];
+ while ((aTempItem = [enumerator nextObject])) {
+ if (![aTempItem hasPrefix:@"."])
+ break;
+ }
+ if (aTempItem) {
+ pathString = [pathString stringByAppendingFormat:@"/%@/bookmarks.html",aTempItem];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"Netscape/Mozilla" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+ }
+ }
+
+ // Omniweb
+ pathString = [[NSString stringWithString:@"~/Library/Application Support/Omniweb/Bookmarks.html"] stringByStandardizingPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"Omniweb" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+
+ // IE
+ pathString = [[NSString stringWithString:@"~/Library/Preferences/Explorer/Favorites.html"] stringByStandardizingPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"Internet Explorer" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+
+ // Safari
+ pathString = [[NSString stringWithString:@"~/Library/Safari/Bookmarks.plist"] stringByStandardizingPath];
+ if ([fm fileExistsAtPath:pathString]) {
+ [mBrowserListButton insertItemWithTitle:@"Safari" atIndex:0];
+ browserItem = [mBrowserListButton itemAtIndex:0];
+ [browserItem setTarget:self];
+ [browserItem setAction:@selector(nullAction:)];
+ [browserItem setRepresentedObject:pathString];
+ }
+ [mBrowserListButton selectItemAtIndex:0];
+ [mBrowserListButton synchronizeTitleAndSelectedItem];
+}
+
+// keeps browsers turned on
+-(IBAction) nullAction:(id)aSender
+{
+}
+
+-(IBAction) cancel:(id)aSender
+{
+ [NSApp stopModal];
+ [NSApp endSheet:[self window]];
+ [[self window] orderOut:self];
+
+}
+-(IBAction) import:(id)aSender
+{
+ [NSApp stopModal];
+ [NSApp endSheet:[self window]];
+ [[self window] orderOut:self];
+ NSMenuItem *selectedItem = [mBrowserListButton selectedItem];
+ BookmarkFolder *importFolder = [[[BookmarkManager sharedBookmarkManager] rootBookmarks] addBookmarkFolder];
+ NSString *titleString;
+ if ([[selectedItem title] isEqualToString:@"Internet Explorer"])
+ titleString = [[NSString alloc] initWithString:NSLocalizedString(@"Imported IE Favorites",@"Imported IE Favorites")];
+ else
+ titleString = [[NSString alloc] initWithFormat:NSLocalizedString(@"Imported %@ Bookmarks",@"Imported %@ Bookmarks"),[selectedItem title]];
+ [importFolder setTitle:titleString];
+ [titleString release];
+ [[BookmarkManager sharedBookmarkManager] importBookmarks:[selectedItem representedObject] intoFolder:importFolder];
+}
+
+-(IBAction) loadOpenPanel:(id)aSender
+{
+ [NSApp stopModal];
+ [NSApp endSheet:[self window]];
+ [[self window] orderOut:self];
+ NSOpenPanel* openPanel = [NSOpenPanel openPanel];
+ [openPanel setCanChooseFiles: YES];
+ [openPanel setCanChooseDirectories: NO];
+ [openPanel setAllowsMultipleSelection: NO];
+ NSArray* array = [NSArray arrayWithObjects: @"htm",@"html",@"xml", @"plist",nil];
+ int result = [openPanel runModalForDirectory: nil
+ file: nil
+ types: array];
+ if (result == NSOKButton) {
+ NSString *pathToFile = [[openPanel filenames] objectAtIndex:0];
+ BookmarkManager *bm = [BookmarkManager sharedBookmarkManager];
+ BookmarkFolder *importFolder = [[bm rootBookmarks] addBookmarkFolder];
+ [importFolder setTitle:NSLocalizedString(@"Imported Bookmarks",@"Imported Bookmarks")];
+ [bm importBookmarks:pathToFile intoFolder:importFolder];
+ }
+}
+
+@end
diff --git a/camino/src/bookmarks/BookmarkInfoController.h b/camino/src/bookmarks/BookmarkInfoController.h
index 880f6f6c6d3d..d5789bf9c120 100644
--- a/camino/src/bookmarks/BookmarkInfoController.h
+++ b/camino/src/bookmarks/BookmarkInfoController.h
@@ -1,49 +1,73 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 (the "License"); you may not use this file
-* except in compliance with the License. You may obtain a copy of
-* the License at http://www.mozilla.org/MPL/
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
*
-* Software distributed under the License is distributed on an "AS
-* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-* implied. See the License for the specific language governing
-* rights and limitations under the License.
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
*
-* The Original Code is the Mozilla browser.
+* The Original Code is mozilla.org code.
*
-* The Initial Developer of the Original Code is Netscape
-* Communications Corporation. Portions created by Netscape are
-* Copyright (C) 2002 Netscape Communications Corporation. All
-* Rights Reserved.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ben Goodger (Original Author)
* Simon Fraser
* David Haas
-*/
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
#import
-#import "BookmarksService.h"
+#import "BookmarksClient.h"
+
+@class BookmarkItem;
@interface BookmarkInfoController : NSWindowController
{
- IBOutlet NSTextField* mNameField;
- IBOutlet NSTextField* mLocationField;
- IBOutlet NSTextField* mKeywordField;
- IBOutlet NSTextField* mDescriptionField;
- IBOutlet NSTextField* mNameLabel;
- IBOutlet NSTextField* mLocationLabel;
- IBOutlet NSTextField* mKeywordLabel;
- IBOutlet NSTextField* mDescriptionLabel;
-
- IBOutlet NSView* mVariableFieldsContainer;
+ IBOutlet NSTabView* mTabView;
+ IBOutlet NSTextField* mBookmarkNameField;
+ IBOutlet NSTextField* mBookmarkLocationField;
+ IBOutlet NSTextField* mBookmarkKeywordField;
+ IBOutlet NSTextField* mBookmarkDescField;
+ IBOutlet NSTextField* mFolderNameField;
+ IBOutlet NSTextField* mFolderKeywordField;
+ IBOutlet NSTextField* mFolderKeywordLabel;
+ IBOutlet NSTextField* mFolderDescField;
+ IBOutlet NSTextField* mLastVisitField;
+ IBOutlet NSTextField* mStatusField;
+ IBOutlet NSTextField* mNumberVisitsField;
- IBOutlet NSButton* mDockMenuCheckbox;
- IBOutlet NSButton* mTabgroupCheckbox;
-
- BookmarkItem* mBookmarkItem;
- NSTextView* mFieldEditor;
+ IBOutlet NSButton* mTabgroupCheckbox;
+ IBOutlet NSButton* mDockMenuCheckbox;
+ IBOutlet NSButton* mClearNumberVisitsButton;
+
+ NSTabViewItem *mBookmarkInfoTabView;
+ NSTabViewItem *mBookmarkUpdateTabView;
+ NSTabViewItem *mFolderInfoTabView;
+ BookmarkItem *mBookmarkItem;
+ NSTextView *mFieldEditor;
}
+ (id)sharedBookmarkInfoController;
@@ -52,7 +76,8 @@
-(void)setBookmark:(BookmarkItem*)aBookmark;
-(BookmarkItem*)bookmark;
-- (IBAction)dockMenuCheckboxClicked:(id)sender;
- (IBAction)tabGroupCheckboxClicked:(id)sender;
+- (IBAction)dockMenuCheckboxClicked:(id)sender;
+- (IBAction)clearVisitCount:(id)sender;
@end
diff --git a/camino/src/bookmarks/BookmarkInfoController.mm b/camino/src/bookmarks/BookmarkInfoController.mm
index abdafb089d06..85d424afbd29 100644
--- a/camino/src/bookmarks/BookmarkInfoController.mm
+++ b/camino/src/bookmarks/BookmarkInfoController.mm
@@ -24,17 +24,17 @@
*/
#import "NSString+Utils.h"
-
#import "BookmarkInfoController.h"
+#import "Bookmark.h"
+#import "BookmarkFolder.h"
-#include "nsIContent.h"
+// determined through weeks of trial and error
+#define kMaxLengthOfWindowTitle 49
@interface BookmarkInfoController(Private)
-- (void)showUIElementPair: (id)aLabel control: (id) aControl;
-- (void)hideUIElementPair: (id)aLabel control: (id) aControl;
- (void)commitChanges:(id)sender;
-- (BOOL)commitField:(id)textField toProperty:(nsIAtom*)propertyAtom;
+- (void)updateUI:(BookmarkItem *)anItem;
@end;
@@ -48,7 +48,6 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
if (!sharedBookmarkInfoController) {
sharedBookmarkInfoController = [[BookmarkInfoController alloc] initWithWindowNibName:@"BookmarkInfoPanel"];
}
-
return sharedBookmarkInfoController;
}
@@ -66,26 +65,31 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
mFieldEditor = [[NSTextView alloc] init];
[mFieldEditor setAllowsUndo:YES];
[mFieldEditor setFieldEditor:YES];
-
}
return self;
}
-- (void)awakeFromNib
+- (void)windowDidLoad
{
// keep a ref so that we can remove and add to the its superview with impunity
- [mNameField retain];
- [mLocationField retain];
- [mKeywordField retain];
- [mDescriptionField retain];
- [mNameLabel retain];
- [mLocationLabel retain];
- [mKeywordLabel retain];
- [mDescriptionLabel retain];
- [mDockMenuCheckbox retain];
+ [mFolderKeywordField retain];
+ [mFolderKeywordLabel retain];
[mTabgroupCheckbox retain];
-
- [[BookmarksManager sharedBookmarksManager] addBookmarksClient:self];
+ // find the TabViewItems & retain them, too. Like to do this
+ // in IB, but just doesn't want to connect. So do it here.
+ int tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"bminfo"];
+ mBookmarkInfoTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain];
+ tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"bmupdate"];
+ mBookmarkUpdateTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain];
+ tabIndex = [mTabView indexOfTabViewItemWithIdentifier:@"folinfo"];
+ mFolderInfoTabView = [[mTabView tabViewItemAtIndex:tabIndex] retain];
+ // it would be nice to do this in IB, but I can't make it connect.
+ [mBookmarkInfoTabView setInitialFirstResponder:mBookmarkNameField];
+ [mFolderInfoTabView setInitialFirstResponder:mFolderNameField];
+ [mBookmarkUpdateTabView setInitialFirstResponder:mClearNumberVisitsButton];
+ // Generic notifications for Bookmark Client - only care if there's a deletion
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
}
-(void)dealloc
@@ -94,21 +98,16 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
if (self == sharedBookmarkInfoController)
sharedBookmarkInfoController = nil;
- [[BookmarksManager sharedBookmarksManagerDontAlloc] removeBookmarksClient:self];
-
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [mBookmarkItem release];
+ mBookmarkItem = nil;
[mFieldEditor release];
-
- [mNameField release];
- [mLocationField release];
- [mKeywordField release];
- [mDescriptionField release];
- [mNameLabel release];
- [mLocationLabel release];
- [mKeywordLabel release];
- [mDescriptionLabel release];
- [mDockMenuCheckbox release];
+ [mFolderKeywordField release];
+ [mFolderKeywordLabel release];
[mTabgroupCheckbox release];
-
+ [mBookmarkInfoTabView release];
+ [mBookmarkUpdateTabView release];
+ [mFolderInfoTabView release];
[super dealloc];
}
@@ -120,160 +119,221 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
-(void)windowDidBecomeKey:(NSNotification*) aNotification
{
- [[self window] makeFirstResponder:mNameField];
+ NSTabViewItem *tabViewItem = [mTabView selectedTabViewItem];
+ if (tabViewItem == mBookmarkInfoTabView)
+ [[self window] makeFirstResponder:mBookmarkNameField];
+ else if (tabViewItem == mFolderInfoTabView)
+ [[self window] makeFirstResponder:mFolderNameField];
+ else if (tabViewItem == mBookmarkUpdateTabView)
+ [[self window] makeFirstResponder:mClearNumberVisitsButton];
}
-(void)windowDidResignKey:(NSNotification*) aNotification
{
[[self window] makeFirstResponder:[self window]];
- if (![[self window] isVisible])
- mBookmarkItem = nil;
+ if (![[self window] isVisible])
+ [self setBookmark:nil];
}
- (void)windowWillClose:(NSNotification *)aNotification
{
[self commitChanges:nil];
- mBookmarkItem = nil;
+ [self setBookmark:nil];
}
- (void)commitChanges:(id)changedField
{
- if (![mBookmarkItem contentNode])
- return;
-
- BOOL changed = NO;
- // Name
- if ((!changedField && [mNameField superview]) || changedField == mNameField)
- changed |= [self commitField:mNameField toProperty:BookmarksService::gNameAtom];
-
- // Location
- if ((!changedField && [mLocationField superview]) || changedField == mLocationField)
- changed |= [self commitField:mLocationField toProperty:BookmarksService::gHrefAtom];
-
- // Keyword
- if ((!changedField && [mKeywordField superview]) || changedField == mKeywordField)
- changed |= [self commitField:mKeywordField toProperty:BookmarksService::gKeywordAtom];
-
- // Description
- if ((!changedField && [mDescriptionField superview]) || changedField == mDescriptionField)
- changed |= [self commitField:mDescriptionField toProperty:BookmarksService::gDescriptionAtom];
-
- [[mFieldEditor undoManager] removeAllActions];
- if (changed)
- [mBookmarkItem itemChanged:YES];
-}
-
-// return YES if changed
-- (BOOL)commitField:(id)textField toProperty:(nsIAtom*)propertyAtom
-{
- NSString* newValue = [textField stringValue];
- NSString* oldValue = [mBookmarkItem getAttributeValue:propertyAtom];
-
- if (![newValue isEqualToString:oldValue])
- {
- [mBookmarkItem setAttribute:propertyAtom toValue:newValue];
- return YES;
+ NSTabViewItem *tabViewItem = [mTabView selectedTabViewItem];
+ BOOL isBookmark;
+ if ((isBookmark = [mBookmarkItem isKindOfClass:[Bookmark class]])) {
+ if ([(Bookmark *)mBookmarkItem isSeparator] || ![[mBookmarkItem parent] isKindOfClass:[BookmarkItem class]])
+ return;
}
-
- return NO;
+ if (!changedField) {
+ if ((tabViewItem == mBookmarkInfoTabView) && isBookmark) {
+ [mBookmarkItem setTitle:[mBookmarkNameField stringValue]];
+ [mBookmarkItem setDescription:[mBookmarkDescField stringValue]];
+ [mBookmarkItem setKeyword:[mBookmarkKeywordField stringValue]];
+ [(Bookmark *)mBookmarkItem setUrl:[mBookmarkLocationField stringValue]];
+ }
+ else if (tabViewItem == mFolderInfoTabView && !isBookmark) {
+ [mBookmarkItem setTitle:[mFolderNameField stringValue]];
+ [mBookmarkItem setDescription:[mFolderDescField stringValue]];
+ if ([(BookmarkFolder *)mBookmarkItem isGroup])
+ [mBookmarkItem setKeyword:[mFolderKeywordField stringValue]];
+ }
+ }
+ else if ((changedField == mBookmarkNameField) || (changedField == mFolderNameField))
+ [mBookmarkItem setTitle:[changedField stringValue]];
+ else if ((changedField == mBookmarkKeywordField) || (changedField == mFolderKeywordField))
+ [mBookmarkItem setKeyword:[changedField stringValue]];
+ else if ((changedField == mBookmarkDescField) || (changedField == mFolderDescField))
+ [mBookmarkItem setDescription:[changedField stringValue]];
+ else if ((changedField == mBookmarkLocationField) && isBookmark)
+ [(Bookmark *)mBookmarkItem setUrl:[changedField stringValue]];
+
+ [[mFieldEditor undoManager] removeAllActions];
}
-- (IBAction)dockMenuCheckboxClicked:(id)sender
+// there's a bug on first load. I don't know why. But this fixes it, so I'll leave it in.
+-(IBAction)showWindow:(id)sender
{
- if ([sender state] == NSOnState)
- BookmarksService::SetDockMenuRoot([mBookmarkItem contentNode]);
- else
- BookmarksService::SetDockMenuRoot(NULL);
+ [self updateUI:mBookmarkItem];
+ [super showWindow:sender];
}
- (IBAction)tabGroupCheckboxClicked:(id)sender
{
- [mBookmarkItem setIsGroup:[sender state] == NSOnState];
- [mBookmarkItem itemChanged:YES];
+ if ([mBookmarkItem isKindOfClass:[BookmarkFolder class]])
+ [(BookmarkFolder *)mBookmarkItem setIsGroup:[sender state] == NSOnState];
+}
+
+- (IBAction)dockMenuCheckboxClicked:(id)sender
+{
+ if ([mBookmarkItem isKindOfClass:[BookmarkFolder class]]) {
+ [(BookmarkFolder *)mBookmarkItem setIsDockMenu:([sender state] == NSOnState)];
+ [mDockMenuCheckbox setEnabled:NO];
+ }
+}
+
+- (IBAction)clearVisitCount:(id)sender
+{
+ if ([mBookmarkItem isKindOfClass:[Bookmark class]])
+ [(Bookmark *)mBookmarkItem setNumberOfVisits:0];
+ [mNumberVisitsField setIntValue:0];
}
-(void)setBookmark: (BookmarkItem*) aBookmark
{
- // See bug 154081 - don't show this window if Bookmark doesn't exist
- // after fix - this should never happen unless disaster strikes.
- if (![aBookmark contentNode])
- return;
-
- BOOL isGroup = [aBookmark isGroup];
- BOOL isFolder = !isGroup && [aBookmark isFolder];
-
- // First, Show/Hide the appropriate UI
- if (isGroup)
- {
- [self showUIElementPair: mNameLabel control: mNameField];
- [self hideUIElementPair: mLocationLabel control: mLocationField];
- [self showUIElementPair: mKeywordLabel control: mKeywordField];
- [self showUIElementPair: mDescriptionLabel control: mDescriptionField];
-
- [mVariableFieldsContainer addSubview:mTabgroupCheckbox];
- [mVariableFieldsContainer addSubview:mDockMenuCheckbox];
- [mNameField setNextKeyView:mTabgroupCheckbox];
- [mTabgroupCheckbox setNextKeyView:mDockMenuCheckbox];
- [mDockMenuCheckbox setNextKeyView:mKeywordField];
- [mKeywordField setNextKeyView:mDescriptionField];
-
- [mTabgroupCheckbox setEnabled:![aBookmark isToobarRoot]];
+ // to avoid a hard-to-find bug, we do UI stuff before setting
+ if (aBookmark) {
+ [self updateUI:aBookmark];
+ [aBookmark retain];
}
- else if (isFolder)
- {
- [self showUIElementPair: mNameLabel control: mNameField];
- [self hideUIElementPair: mLocationLabel control: mLocationField];
- [self hideUIElementPair: mKeywordLabel control: mKeywordField];
- [self showUIElementPair: mDescriptionLabel control: mDescriptionField];
+ [mBookmarkItem release];
+ mBookmarkItem = aBookmark;
+}
- [mVariableFieldsContainer addSubview:mDockMenuCheckbox];
- [mVariableFieldsContainer addSubview:mTabgroupCheckbox];
- [mNameField setNextKeyView:mDockMenuCheckbox];
- [mDockMenuCheckbox setNextKeyView:mTabgroupCheckbox];
- [mTabgroupCheckbox setNextKeyView:mDescriptionField];
-
- [mTabgroupCheckbox setEnabled:![aBookmark isToobarRoot]];
+-(void)updateUI:(BookmarkItem *)aBookmark
+{
+ if (aBookmark) {
+ //
+ // setup for bookmarks
+ //
+ int numTabs = [mTabView numberOfTabViewItems];
+ if ([aBookmark isKindOfClass:[Bookmark class]]) {
+ if (numTabs == 1) {
+ [mTabView removeTabViewItem:mFolderInfoTabView];
+ [mTabView setTabViewType:NSTopTabsBezelBorder];
+ [mTabView insertTabViewItem:mBookmarkInfoTabView atIndex:0];
+ [mTabView insertTabViewItem:mBookmarkUpdateTabView atIndex:1];
+ [mTabView selectLastTabViewItem:self];//have to do this to avoid "2 selected tabs" ugliness
+ [mTabView selectFirstTabViewItem:self];
+ }
+ else if (numTabs == 3) {
+ [mTabView removeTabViewItem:mFolderInfoTabView];
+ [mTabView selectFirstTabViewItem:self];
+ }
+ [mBookmarkNameField setStringValue: [aBookmark title]];
+ [mBookmarkDescField setStringValue: [aBookmark description]];
+ [mBookmarkKeywordField setStringValue: [aBookmark keyword]];
+ [mBookmarkLocationField setStringValue: [(Bookmark *)aBookmark url]];
+ [mNumberVisitsField setIntValue:[(Bookmark *)aBookmark numberOfVisits]];
+ [mLastVisitField setStringValue: [[(Bookmark *)aBookmark lastVisit] descriptionWithCalendarFormat:[[mLastVisitField formatter] dateFormat] timeZone:[NSTimeZone localTimeZone] locale:nil]];
+ NSString *statusString = nil;
+ unsigned status = [(Bookmark *)aBookmark status];
+ switch (status) {
+ case (kBookmarkOKStatus):
+ case (kBookmarkSpacerStatus):
+ statusString = NSLocalizedString(@"OK",@"OK");
+ break;
+ case (kBookmarkBrokenLinkStatus):
+ statusString = NSLocalizedString(@"Link Broken",@"Link Broken");
+ break;
+ case (kBookmarkMovedLinkStatus):
+ statusString = NSLocalizedString(@"Link has Moved",@"Link has Moved");
+ break;
+ case (kBookmarkServerErrorStatus):
+ statusString = NSLocalizedString(@"Server Unreachable",@"Server Unreachable");
+ break;
+ case (kBookmarkNeverCheckStatus):
+ statusString = NSLocalizedString(@"Uncheckable",@"Uncheckable");
+ break;
+ default:
+ statusString = [NSString string];
+ }
+ [mStatusField setStringValue:statusString];
+ // if it's parent is a smart folder or it's a menu separator,
+ // we turn off all the fields. if it isn't, then we turn them all on
+ id parent = [aBookmark parent];
+ if (([parent isKindOfClass:[BookmarkItem class]]) &&
+ (![parent isSmartFolder]) &&
+ (![(Bookmark *)aBookmark isSeparator]))
+ {
+ [mBookmarkNameField setEditable:YES];
+ [mBookmarkDescField setEditable:YES];
+ [mBookmarkKeywordField setEditable:YES];
+ [mBookmarkLocationField setEditable:YES];
+ } else
+ {
+ [mBookmarkNameField setEditable:NO];
+ [mBookmarkDescField setEditable:NO];
+ [mBookmarkKeywordField setEditable:NO];
+ [mBookmarkLocationField setEditable:NO];
+ }
+ }
+ //
+ // Folders
+ //
+ else if ([aBookmark isKindOfClass:[BookmarkFolder class]]) {
+ if (numTabs == 2) {
+ [mTabView removeTabViewItem:mBookmarkInfoTabView];
+ [mTabView removeTabViewItem:mBookmarkUpdateTabView];
+ [mTabView insertTabViewItem:mFolderInfoTabView atIndex:0];
+ [mTabView setTabViewType:NSNoTabsNoBorder];
+ }
+ else if (numTabs == 3) {
+ [mTabView removeTabViewItem:mBookmarkInfoTabView];
+ [mTabView removeTabViewItem:mBookmarkUpdateTabView];
+ [mTabView setTabViewType:NSNoTabsNoBorder];
+ }
+ NSView *superview = [mFolderKeywordField superview];
+ if ([(BookmarkFolder *)aBookmark isGroup]) {
+ if (!superview) {
+ superview = [mFolderNameField superview];
+ [superview addSubview:mFolderKeywordField];
+ [superview addSubview:mFolderKeywordLabel];
+ [mFolderNameField setNextKeyView:mFolderKeywordField];
+ }
+ [mTabgroupCheckbox setState:NSOnState];
+ }
+ else {
+ if (superview) {
+ [mFolderKeywordField removeFromSuperview];
+ [mFolderKeywordLabel removeFromSuperview];
+ [mFolderNameField setNextKeyView:mFolderDescField];
+ }
+ [mTabgroupCheckbox setState:NSOffState];
+ }
+ [mFolderNameField setStringValue: [aBookmark title]];
+ [mFolderDescField setStringValue: [aBookmark description]];
+ //
+ // we can't just unselect dock menu - we have to pick a new one
+ //
+ if ([(BookmarkFolder *)aBookmark isDockMenu]) {
+ [mDockMenuCheckbox setState:NSOnState];
+ [mDockMenuCheckbox setEnabled:NO];
+ } else {
+ [mDockMenuCheckbox setState:NSOffState];
+ [mDockMenuCheckbox setEnabled:YES];
+ }
+ }
+ // Header
+ NSMutableString *truncatedTitle = [NSMutableString stringWithString:[aBookmark title]];
+ [truncatedTitle truncateTo:kMaxLengthOfWindowTitle at:kTruncateAtEnd];
+ NSString* infoForString = [NSString stringWithFormat:NSLocalizedString(@"BookmarkInfoTitle", @"Info for "), truncatedTitle];
+ [[self window] setTitle: infoForString];
}
- else
- {
- [self showUIElementPair: mNameLabel control: mNameField];
- [self showUIElementPair: mLocationLabel control: mLocationField];
- [self showUIElementPair: mKeywordLabel control: mKeywordField];
- [self showUIElementPair: mDescriptionLabel control: mDescriptionField];
-
- [mDockMenuCheckbox removeFromSuperview];
- [mTabgroupCheckbox removeFromSuperview];
- [mNameField setNextKeyView:mLocationField];
- [mLocationField setNextKeyView:mKeywordField];
- [mKeywordField setNextKeyView:mDescriptionField];
- }
-
- // Then, fill with appropriate values from Bookmarks
- NSString* bookmarkName = [aBookmark name];
- NSString* infoForString = [NSString stringWithFormat:NSLocalizedString(@"BookmarkInfoTitle", @"Info for "), bookmarkName];
- [[self window] setTitle: infoForString];
-
- if (isGroup)
- {
- [mDockMenuCheckbox setState:([aBookmark isDockMenuRoot] ? NSOnState : NSOffState)];
- [mTabgroupCheckbox setState:NSOnState];
- [mKeywordField setStringValue: [aBookmark keyword]];
- }
- else if (isFolder)
- {
- [mDockMenuCheckbox setState:([aBookmark isDockMenuRoot] ? NSOnState : NSOffState)];
- [mTabgroupCheckbox setState:NSOffState];
- }
- else
- {
- [mKeywordField setStringValue: [aBookmark keyword]];
- [mLocationField setStringValue: [aBookmark url]];
- }
-
- [mNameField setStringValue: bookmarkName];
- [mDescriptionField setStringValue: [aBookmark descriptionString]];
-
- mBookmarkItem = aBookmark;
}
-(BookmarkItem *)bookmark
@@ -281,29 +341,6 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
return mBookmarkItem;
}
--(void)showUIElementPair: (id)aLabel control:(id)aControl
-{
- if ([aLabel superview] == nil)
- [mVariableFieldsContainer addSubview: aLabel];
-
- if ([aControl superview] == nil)
- [mVariableFieldsContainer addSubview: aControl];
-
- // we need to resize the fields in case the user resized the window when they were hidden
- NSRect containerBounds = [mVariableFieldsContainer bounds];
- NSRect controlFrame = [aControl frame];
- controlFrame.size.width = (containerBounds.size.width - controlFrame.origin.x - 20.0);
- [aControl setFrame:controlFrame];
-}
-
--(void)hideUIElementPair: (id)aLabel control:(id)aControl
-{
- if ([aLabel superview] != nil)
- [aLabel removeFromSuperview];
-
- if ([aControl superview] != nil)
- [aControl removeFromSuperview];
-}
-(NSText *)windowWillReturnFieldEditor:(NSWindow *)aPanel toObject:(id)aObject
{
@@ -312,21 +349,21 @@ static BookmarkInfoController *sharedBookmarkInfoController = nil;
#pragma mark -
-- (void)bookmarkAdded:(nsIContent*)bookmark inContainer:(nsIContent*)container isChangedRoot:(BOOL)isRoot
+- (void)bookmarkAdded:(NSNotification *)aNote
{
}
-- (void)bookmarkRemoved:(nsIContent*)bookmark inContainer:(nsIContent*)container isChangedRoot:(BOOL)isRoot
+- (void)bookmarkRemoved:(NSNotification *)aNote
{
- if ([mBookmarkItem contentNode] == bookmark)
- mBookmarkItem = nil;
+ NSDictionary *dict = [aNote userInfo];
+ BookmarkItem *item = [dict objectForKey:BookmarkFolderChildKey];
+ if ((item == [self bookmark]) && ![item parent]) {
+ [self setBookmark:nil];
+ [[self window] close];
+ }
}
-- (void)bookmarkChanged:(nsIContent*)bookmark
-{
-}
-
-- (void)specialFolder:(EBookmarksFolderType)folderType changedTo:(nsIContent*)newFolderContent
+- (void)bookmarkChanged:(NSNotification *)aNote
{
}
diff --git a/camino/src/bookmarks/BookmarkItem.h b/camino/src/bookmarks/BookmarkItem.h
new file mode 100644
index 000000000000..92aa64de5ccf
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkItem.h
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+// superclass for Bookmark & BookmarkFolder.
+// Basically here to aid in scripting support.
+
+#import
+
+@interface BookmarkItem : NSObject
+{
+ id mParent; //subclasses will use a BookmarkFolder
+ NSString* mTitle;
+ NSString* mDescription;
+ NSString* mKeyword;
+ NSImage* mIcon;
+}
+
+// Setters/Getters
+-(id) parent;
+-(NSString *) title;
+-(NSString *) description;
+-(NSString *) keyword;
+-(NSImage *) icon;
+
+-(void) setParent:(id)aParent;
+-(void) setTitle:(NSString *)aString;
+-(void) setDescription:(NSString *)aString;
+-(void) setKeyword:(NSString *)aKeyword;
+-(void) setIcon:(NSImage *)aIcon;
+
+// Status checks
+-(BOOL) isChildOfItem:(BookmarkItem *)anItem;
+
+// Notificaiton of Change
+-(void) itemUpdatedNote; //right now, just on title & icon - for BookmarkButton & BookmarkMenu notes
+
+// Methods called on startup for both bookmark & folder
+-(void) refreshIcon;
+
+ // for reading/writing to disk - unimplemented in BookmarkItem.
+-(BOOL) readNativeDictionary:(NSDictionary *)aDict;
+-(BOOL) readSafariDictionary:(NSDictionary *)aDict;
+-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef;
+
+-(NSDictionary *)writeNativeDictionary;
+-(NSString *)writeHTML:(unsigned)aPad;
+
+
+
+@end
diff --git a/camino/src/bookmarks/BookmarkItem.m b/camino/src/bookmarks/BookmarkItem.m
new file mode 100644
index 000000000000..645a5c4699d1
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkItem.m
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkItem.h"
+
+// Notifications
+NSString *BookmarkItemChangedNotification = @"bi_cg";
+
+
+@implementation BookmarkItem
+//Initialization
+-(id) init
+{
+ if ((self = [super init]))
+ {
+// NSString *tempString = [[NSString alloc] init];
+ mParent = NULL;
+ mTitle = [[NSString alloc] init];
+ mKeyword = [[NSString alloc] init];
+ mDescription = [[NSString alloc] init];
+ // if we set the icon here, we will get a memory leak. so don't.
+ // subclass will provide icon.
+ mIcon = NULL;
+ }
+ return self;
+}
+
+-(id) copyWithZone:(NSZone *)zone
+{
+ //descend from NSObject - so don't call super
+ id doppleganger = [[[self class] allocWithZone:zone] init];
+ [doppleganger setTitle:[self title]];
+ [doppleganger setDescription:[self description]];
+ [doppleganger setKeyword:[self keyword]];
+ [doppleganger setParent:[self parent]];
+ [doppleganger setIcon:[self icon]];
+ return doppleganger;
+}
+
+-(void)dealloc
+{
+ [mTitle release];
+ [mDescription release];
+ [mKeyword release];
+ [mIcon release];
+ [super dealloc];
+}
+
+
+// Basic properties
+-(id) parent
+{
+ return mParent;
+}
+
+-(NSString *) title
+{
+ return mTitle;
+}
+
+-(NSString *) description
+{
+ return mDescription;
+}
+
+-(NSString *) keyword
+{
+ return mKeyword;
+}
+
+
+-(NSImage *)icon
+{
+ return mIcon;
+}
+
+-(BOOL) isChildOfItem:(BookmarkItem *)anItem
+{
+ if (![[self parent] isKindOfClass:[BookmarkItem class]])
+ return NO;
+ if ([self parent] == anItem)
+ return YES;
+ return [[self parent] isChildOfItem:anItem];
+}
+
+-(void) setParent:(id) aParent
+{
+ mParent = aParent; // no reference on the parent, so it better not disappear on us.
+}
+
+-(void) setTitle:(NSString *)aTitle
+{
+ if (!aTitle)
+ return;
+ [aTitle retain];
+ [mTitle release];
+ mTitle = aTitle;
+ [self itemUpdatedNote];
+}
+
+-(void) setDescription:(NSString *)aDescription
+{
+ if (!aDescription)
+ return;
+ [aDescription retain];
+ [mDescription release];
+ mDescription = aDescription;
+}
+
+- (void) setKeyword:(NSString *)aKeyword
+{
+ if (!aKeyword)
+ return;
+ [aKeyword retain];
+ [mKeyword release];
+ mKeyword = aKeyword;
+}
+
+-(void) setIcon:(NSImage *)aIcon
+{
+ if (!aIcon)
+ return;
+ [aIcon retain];
+ [mIcon release];
+ mIcon = aIcon;
+ [self itemUpdatedNote];
+}
+
+-(void) itemUpdatedNote
+{
+ NSNotification *note = [NSNotification notificationWithName:BookmarkItemChangedNotification object:self userInfo:nil];
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc postNotification:note];
+ return;
+}
+
+// stub functions to avoid warning
+
+-(void) refreshIcon
+{
+}
+
+//Reading/writing to & from disk - all just stubs.
+
+-(BOOL) readNativeDictionary:(NSDictionary *)aDict
+{
+ return NO;
+}
+
+-(BOOL) readSafariDictionary:(NSDictionary *)aDict
+{
+ return NO;
+}
+
+-(BOOL) readCaminoXML:(CFXMLTreeRef)aTreeRef
+{
+ return NO;
+}
+
+-(NSDictionary *)writeNativeDictionary
+{
+ return [NSDictionary dictionary];
+}
+
+-(NSString *)writeHTML:(unsigned)aPad
+{
+ return [NSString string];
+}
+
+
+@end
+
+
+
diff --git a/camino/src/bookmarks/BookmarkManager.h b/camino/src/bookmarks/BookmarkManager.h
new file mode 100644
index 000000000000..f8a2e82cc005
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkManager.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+#import "BookmarksClient.h"
+
+@class BookmarkFolder;
+@class BookmarkImportDlgController;
+@class KindaSmartFolderManager;
+@class RunLoopMessenger;
+
+#define kBookmarkMenuContainerIndex 0
+#define kToolbarContainerIndex 1
+#define kHistoryContainerIndex 2
+#define kTop10ContainerIndex 3
+#define kBrokenBookmarkContainerIndex 4
+#define kRendezvousContainerIndex 5
+#define kAddressBookContainerIndex 6
+
+// check 1 bookmark every 2 minutes, but only if we haven't been there in a day
+#define kTimeSinceBookmarkLastChecked 86400.0
+#define kTimeToCheckAnotherBookmark 120
+
+@interface BookmarkManager : NSObject {
+ BookmarkFolder *mRootBookmarks; // root bookmark object
+ KindaSmartFolderManager *mSmartFolderManager; //brains behind 4 smart folders
+ NSUndoManager *mUndoManager;// handles deletes, adds of bookmarks
+ BookmarkImportDlgController *mImportDlgController;
+ NSString *mPathToBookmarkFile; //exactly what it looks like
+ NSTimer *mUpdateTimer; //we don't actually retain this
+}
+
+// Class Methods & shutdown stuff
++ (void)startBookmarksManager:(RunLoopMessenger *)mainThreadRunLoopMessenger;
++ (BookmarkManager*)sharedBookmarkManager;
+- (void)shutdown;
+
+// Getters/Setters
+-(BookmarkFolder *) rootBookmarks;
+-(BookmarkFolder *) toolbarFolder;
+-(BookmarkFolder *) bookmarkMenuFolder;
+-(BookmarkFolder *) dockMenuFolder;
+-(BookmarkFolder *) top10Folder;
+-(BookmarkFolder *) brokenLinkFolder;
+-(BookmarkFolder *) rendezvousFolder;
+-(BookmarkFolder *) addressBookFolder;
+-(BookmarkFolder *) historyFolder;
+-(NSUndoManager *) undoManager;
+-(void) setRootBookmarks:(BookmarkFolder *)anArray;
+
+// Informational things
+-(NSArray *)resolveBookmarksKeyword:(NSString *)keyword;
+-(NSArray *)searchBookmarksForString:(NSString *)searchString;
+-(unsigned) firstUserCollection;
+-(BOOL) isDropValid:(NSArray *)items toFolder:(BookmarkFolder *)parent;
+
+// Reading bookmark files
+-(BOOL) readBookmarks;
+-(void) startImportBookmarks;
+-(void) importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder;
+-(NSString *)decodedHTMLfile:(NSString *)pathToFile;
+-(BOOL)readHTMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder;
+-(BOOL)readCaminoXMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder;
+-(BOOL)readPropertyListFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder;
+
+// Writing bookmark files
+-(void)writeHTMLFile:(NSString *)pathToFile;
+-(void)writePropertyListFile:(NSString *)pathToFile;
+
+@end
diff --git a/camino/src/bookmarks/BookmarkManager.mm b/camino/src/bookmarks/BookmarkManager.mm
new file mode 100644
index 000000000000..22d164cf95be
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkManager.mm
@@ -0,0 +1,813 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#include "nsString.h"
+#include "nsIContent.h"
+#include "nsIFile.h"
+#include "nsAppDirectoryServiceDefs.h"
+#import "NSString+Utils.h"
+#import "PreferenceManager.h"
+#import "RunLoopMessenger.h"
+#import "BookmarkManager.h"
+#import "Bookmark.h"
+#import "BookmarkFolder.h"
+#import "BookmarkImportDlgController.h"
+#import "KindaSmartFolderManager.h"
+#import "MainController.h"
+
+@interface BookmarkManager (Private)
+- (void)setPathToBookmarkFile:(NSString *)aString;
+- (void)setupSmartCollections;
+- (void)delayedStartupItems;
+- (void)writeBookmarks:(NSNotification *)note;
+- (void)checkForUpdates:(NSTimer *)aTimer;
+- (BookmarkFolder *)findDockMenuFolderInFolder:(BookmarkFolder *)aFolder;
+- (Bookmark *)findABookmarkToCheckInFolder:(BookmarkFolder *)aFolder;
+@end
+
+@implementation BookmarkManager
+
+static NSString *WriteBookmarkNotification = @"write_bms";
+static BookmarkManager* gBookmarksManager = nil;
+static NSLock *startupLock = nil;
+static unsigned gFirstUserCollection = 0;
+
+
+//
+// Class Methods - we only need RunLoopMessenger for 10.1 Compat. On 10.2+, there
+// are built-in methods for running something on the main thread.
+//
++ (void)startBookmarksManager:(RunLoopMessenger *)mainThreadRunLoopMessenger
+{
+ NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ if (!gBookmarksManager && !startupLock)
+ {
+ startupLock = [[NSLock alloc] init];
+ NSLock *avoidRaceLock;
+ BookmarkManager *aManager = [[BookmarkManager alloc] init];
+ [startupLock lock];
+ gBookmarksManager = aManager;
+ avoidRaceLock = startupLock;
+ startupLock = nil;
+ [avoidRaceLock unlock];
+ [avoidRaceLock release];
+ [[NSApp delegate] setupBookmarkMenus:gBookmarksManager];
+ [mainThreadRunLoopMessenger target:gBookmarksManager performSelector:@selector(delayedStartupItems)];
+ }
+ [pool release];
+}
+
++ (BookmarkManager*)sharedBookmarkManager
+{
+ BookmarkManager *theManager;
+ [startupLock lock];
+ theManager = gBookmarksManager;
+ [startupLock unlock];
+ return theManager;
+}
+
+//
+// Init, dealloc - better get inited on background thread.
+//
+- (id)init
+{
+ if ((self = [super init]))
+ {
+ BookmarkFolder* root = [[BookmarkFolder alloc] init];
+ [root setParent:self];
+ [root setIsRoot:YES];
+ [root setTitle:NSLocalizedString(@"BookmarksRootName", @"")];
+ [self setRootBookmarks:root];
+ [root release];
+ if (![self readBookmarks]) {
+ // one of two things happened. we are importing off an old xml file
+ // for startup, OR we totally muffed reading the bookmarks. we'll hope
+ // it was the former.
+ if ([root count] > 0) {
+ // find the xml toolbar menu. it'll be in top level of bookmark menu folder
+ NSMutableArray *childArray = [[self bookmarkMenuFolder] childArray];
+ unsigned i, j=[childArray count];
+ id anObject;
+ for (i=0;i < j; i++) {
+ anObject = [childArray objectAtIndex:i];
+ if ([anObject isKindOfClass:[BookmarkFolder class]]) {
+ if ([(BookmarkFolder *)anObject isToolbar]) { //triumph!
+ [[self bookmarkMenuFolder] moveChild:anObject toBookmarkFolder:root atIndex:kToolbarContainerIndex];
+ break;
+ }
+ }
+ }
+ } else { //we are so totally screwed
+ BookmarkFolder *aFolder = [root addBookmarkFolder];
+ if ([root count] == 1) {
+ [aFolder setTitle:NSLocalizedString(@"Bookmark Menu",@"Bookmark Menu")];
+ aFolder = [root addBookmarkFolder];
+ }
+ [aFolder setTitle:NSLocalizedString(@"Bookmark Toolbar",@"Bookmark Toolbar")];
+ }
+ }
+ // setup special folders
+ [self setupSmartCollections];
+ mSmartFolderManager = [[KindaSmartFolderManager alloc] initWithBookmarkManager:self];
+ // don't do this until after we've read in the bookmarks
+ mUndoManager = [[NSUndoManager alloc] init];
+ // Generic notifications for Bookmark Client
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil];
+ [nc addObserver:self selector:@selector(writeBookmarks:) name:WriteBookmarkNotification object:nil];
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ if (mUpdateTimer)
+ [mUpdateTimer invalidate]; //we don't retain this, so don't release it.
+ [mUndoManager release];
+ [mRootBookmarks release];
+ [mPathToBookmarkFile release];
+ [mSmartFolderManager release];
+ if (mImportDlgController)
+ [mImportDlgController release];
+ if (self == gBookmarksManager)
+ gBookmarksManager = nil;
+ [super dealloc];
+}
+
+- (void)delayedStartupItems
+{
+ // check update status of 1 bookmark every 2 minutes.
+ mUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeToCheckAnotherBookmark target:self selector:@selector(checkForUpdates:) userInfo:nil repeats:YES];
+ [mSmartFolderManager postStartupInitialization:self];
+ [[[self toolbarFolder] objectAtIndex:0] itemUpdatedNote];//makes sure we have toolbar on 1st window
+ if ([[PreferenceManager sharedInstance] getBooleanPref:"browser.chrome.favicons" withSuccess:NULL])
+ [mRootBookmarks refreshIcon];
+}
+
+- (void)shutdown;
+{
+ [self writeBookmarks:nil];
+}
+
+//
+// smart collections, as of now, are Rendezvous, Address Book, Top 10 List, Broken Bookmarks,
+// We also have history, but that just points to the real history stuff.
+- (void)setupSmartCollections
+{
+ NSArray *names = nil;
+ if (floor(NSAppKitVersionNumber) <= NSAppKitVersionNumber10_1) //10.1
+ names = [[NSArray alloc] initWithObjects:
+ NSLocalizedString(@"History",@"History"),
+ NSLocalizedString(@"Top 10 List",@"Top 10 List"),
+ NSLocalizedString(@"Broken Bookmarks",@"Broken Bookmarks"),
+ nil];
+ else // 10.2 +
+ names = [[NSArray alloc] initWithObjects:
+ NSLocalizedString(@"History",@"History"),
+ NSLocalizedString(@"Top 10 List",@"Top 10 List"),
+ NSLocalizedString(@"Broken Bookmarks",@"Broken Bookmarks"),
+ NSLocalizedString(@"Rendezvous",@"Rendezvous"),
+ NSLocalizedString(@"Address Book",@"Address Book"),
+ nil];
+ gFirstUserCollection = [names count]+2;
+ unsigned i, j=[names count];
+ for (i=0; i < j; i++) {
+ BookmarkFolder *temp = [[BookmarkFolder alloc] init];
+ [temp setTitle:[names objectAtIndex:i]];
+ [temp setIsSmartFolder:YES];
+ [mRootBookmarks insertChild:temp atIndex:(i+2) isMove:NO];
+ [temp release];
+ }
+ [names release];
+ // set pretty icons
+ [[self historyFolder] setIcon:[NSImage imageNamed:@"historyicon"]];
+ [[self top10Folder] setIcon:[NSImage imageNamed:@"top10_icon"]];
+ [[self bookmarkMenuFolder] setIcon:[NSImage imageNamed:@"bookmarkmenu_icon"]];
+ [[self toolbarFolder] setIcon:[NSImage imageNamed:@"bookmarktoolbar_icon"]];
+ [[self rendezvousFolder] setIcon:[NSImage imageNamed:@"rendezvous_icon"]];
+ [[self addressBookFolder] setIcon:[NSImage imageNamed:@"addressbook_icon"]];
+ [[self brokenLinkFolder] setIcon:[NSImage imageNamed:@"brokenbookmark_icon"]];
+}
+
+//
+// Getter/Setter methods
+//
+
+-(BookmarkFolder *) rootBookmarks
+{
+ return mRootBookmarks;
+}
+
+-(BookmarkFolder *) dockMenuFolder
+{
+ BookmarkFolder *folder = [self findDockMenuFolderInFolder:[self rootBookmarks]];
+ if (folder)
+ return folder;
+ else
+ return [self top10Folder];
+}
+
+- (BookmarkFolder *)findDockMenuFolderInFolder:(BookmarkFolder *)aFolder
+{
+ NSEnumerator *enumerator = [[aFolder childArray] objectEnumerator];
+ id aKid;
+ BookmarkFolder *foundFolder = nil;
+ while ((!foundFolder) && (aKid = [enumerator nextObject])) {
+ if ([aKid isKindOfClass:[BookmarkFolder class]]) {
+ if ([(BookmarkFolder *)aKid isDockMenu])
+ return aKid;
+ else
+ foundFolder = [self findDockMenuFolderInFolder:aKid];
+ }
+ }
+ return foundFolder;
+}
+
+-(BookmarkFolder *)top10Folder
+{
+ return [[self rootBookmarks] objectAtIndex:kTop10ContainerIndex];
+}
+
+-(BookmarkFolder *) brokenLinkFolder
+{
+ return [[self rootBookmarks] objectAtIndex:kBrokenBookmarkContainerIndex];
+}
+
+-(BookmarkFolder *) toolbarFolder
+{
+ return [[self rootBookmarks] objectAtIndex:kToolbarContainerIndex];
+}
+
+-(BookmarkFolder *) bookmarkMenuFolder
+{
+ return [[self rootBookmarks] objectAtIndex:kBookmarkMenuContainerIndex];
+}
+
+-(BookmarkFolder *) historyFolder
+{
+ return [[self rootBookmarks] objectAtIndex:kHistoryContainerIndex];
+}
+
+-(BookmarkFolder *) rendezvousFolder
+{
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1)
+ return [[self rootBookmarks] objectAtIndex:kRendezvousContainerIndex];
+ else
+ return nil;
+}
+
+-(BookmarkFolder *) addressBookFolder
+{
+ if (floor(NSAppKitVersionNumber) > NSAppKitVersionNumber10_1)
+ return [[self rootBookmarks] objectAtIndex:kAddressBookContainerIndex];
+ else
+ return nil;
+}
+
+-(NSUndoManager *) undoManager
+{
+ return mUndoManager;
+}
+
+-(unsigned) firstUserCollection
+{
+ return gFirstUserCollection;
+}
+
+- (void)setPathToBookmarkFile:(NSString *)aString
+{
+ [aString retain];
+ [mPathToBookmarkFile release];
+ mPathToBookmarkFile = aString;
+}
+
+-(void) setRootBookmarks:(BookmarkFolder *)anArray
+{
+ if (anArray != mRootBookmarks) {
+ [anArray retain];
+ [mRootBookmarks release];
+ mRootBookmarks = anArray;
+ }
+}
+
+-(NSArray *)resolveBookmarksKeyword:(NSString *)keyword
+{
+ NSArray *resolvedArray = nil;
+ if (![keyword isEqualToString:@""])
+ resolvedArray = [[self rootBookmarks] resolveKeyword:keyword];
+ if (resolvedArray)
+ return resolvedArray;
+ return [NSArray arrayWithObject:keyword];
+}
+
+-(NSArray *)searchBookmarksForString:(NSString *)searchString
+{
+ NSMutableArray *matchingArray = nil;
+ if ((searchString) && ![searchString isEqualToString:@""]) {
+ NSSet *matchingSet = [[self rootBookmarks] bookmarksWithString:searchString];
+ NSEnumerator *enumerator = [matchingSet objectEnumerator];
+ id aThingy;
+ matchingArray = [NSMutableArray array];
+ while ((aThingy = [enumerator nextObject]))
+ [matchingArray addObject:aThingy];
+ }
+ return matchingArray;
+}
+
+//
+// every couple of minutes, this gets called
+// it finds the first bookmark we haven't been to in 24 hours
+// and makes sure it's still there
+//
+- (void)checkForUpdates:(NSTimer *)aTimer
+{
+ Bookmark *bm = [self findABookmarkToCheckInFolder:[self rootBookmarks]];
+ if (bm)
+ [bm checkForUpdate];
+}
+
+-(Bookmark *)findABookmarkToCheckInFolder:(BookmarkFolder *)aFolder
+{
+ NSEnumerator *enumerator = [[aFolder childArray] objectEnumerator];
+ id aKid;
+ Bookmark *foundBookmark = nil;
+ while ((!foundBookmark) && (aKid = [enumerator nextObject])) {
+ if ([aKid isKindOfClass:[Bookmark class]]) {
+ if (([(Bookmark *)aKid isCheckable]) &&
+ ([[(Bookmark *)aKid lastVisit] timeIntervalSinceNow] < -kTimeSinceBookmarkLastChecked))
+ foundBookmark = aKid;
+ } else if ([aKid isKindOfClass:[BookmarkFolder class]])
+ foundBookmark = [self findABookmarkToCheckInFolder:aKid];
+ }
+ return foundBookmark;
+}
+
+//
+// Drag & drop
+//
+
+-(BOOL) isDropValid:(NSArray *)items toFolder:(BookmarkFolder *)parent
+{
+ // Enumerate through items, make sure we're not being dropped into
+ // a child OR ourself OR that the a bookmark or group is going into root bookmarks.
+ NSEnumerator *enumerator = [items objectEnumerator];
+ id aBookmark;
+ while ((aBookmark = [enumerator nextObject])) {
+ if ([aBookmark isKindOfClass:[BookmarkFolder class]]) {
+ if (aBookmark == parent)
+ return NO;
+ if ((parent == [self rootBookmarks]) && [(BookmarkFolder *)aBookmark isGroup])
+ return NO;
+ } else if ([aBookmark isKindOfClass:[Bookmark class]]) {
+ if (parent == [self rootBookmarks])
+ return NO;
+ BookmarkFolder *menuFolder = [self bookmarkMenuFolder];
+ if ([aBookmark isSeparator] &&
+ ((![parent isChildOfItem:menuFolder]) && (parent != menuFolder)))
+ return NO;
+ }
+ if ([parent isChildOfItem:aBookmark])
+ return NO;
+ }
+ return YES;
+}
+
+#pragma mark -
+//
+// BookmarkClient protocol - so we know when to write out
+//
+- (void)bookmarkAdded:(NSNotification *)note
+{
+ [self bookmarkChanged:nil];
+}
+
+- (void)bookmarkRemoved:(NSNotification *)note
+{
+ [self bookmarkChanged:nil];
+}
+
+- (void)bookmarkChanged:(NSNotification *)aNote
+{
+ NSNotificationQueue* nq = [NSNotificationQueue defaultQueue];
+ NSNotification *note = [NSNotification notificationWithName:WriteBookmarkNotification object:self userInfo:nil];
+ [nq enqueueNotification:note postingStyle:NSPostASAP coalesceMask:NSNotificationCoalescingOnName forModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
+}
+
+- (void)writeBookmarks:(NSNotification *)note
+{
+ [self writePropertyListFile:mPathToBookmarkFile];
+}
+
+#pragma mark -
+//
+// Reading/Importing bookmark files
+//
+-(BOOL) readBookmarks
+{
+ nsCOMPtr aDir;
+ NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aDir));
+ if (!aDir) return NO; // should be smarter
+ nsCAutoString aDirPath;
+ nsresult rv = aDir->GetNativePath(aDirPath);
+ if (NS_FAILED(rv)) return NO; // should be smarter.
+ NSString *profileDir = [NSString stringWithUTF8String:aDirPath.get()];
+ //
+ // figure out where Bookmarks.plist is and store it as mPathToBookmarkFile
+ // if there is a Bookmarks.plist, read it
+ // if there isn't a Bookmarks.plist, but there is a bookmarks.xml, read it.
+ // if there isn't either, move default Bookmarks.plist to profile dir & read it.
+ //
+ NSFileManager *fM = [NSFileManager defaultManager];
+ NSString *bookmarkPath = [profileDir stringByAppendingPathComponent:@"bookmarks.plist"];
+ [self setPathToBookmarkFile:bookmarkPath];
+ if ([fM isReadableFileAtPath:bookmarkPath]) {
+ if ([self readPropertyListFile:bookmarkPath intoFolder:[self rootBookmarks]])
+ return YES; // triumph!
+ } else if ([fM isReadableFileAtPath:[profileDir stringByAppendingPathComponent:@"bookmarks.xml"]]){
+ BookmarkFolder *aFolder = [[self rootBookmarks] addBookmarkFolder];
+ [aFolder setTitle:NSLocalizedString(@"Bookmark Menu",@"Bookmark Menu")];
+ if ([self readCaminoXMLFile:[profileDir stringByAppendingPathComponent:@"bookmarks.xml"] intoFolder:[self bookmarkMenuFolder]])
+ return NO; // triumph! - will do post processing in init
+ } else {
+ NSString *defaultBookmarks = [[NSBundle mainBundle] pathForResource:@"bookmarks" ofType:@"plist"];
+ if ([fM copyPath:defaultBookmarks toPath:bookmarkPath handler:nil]) {
+ if ([self readPropertyListFile:bookmarkPath intoFolder:[self rootBookmarks]])
+ return YES; //triumph!
+ }
+ }
+ // if we're here, we've had a problem
+ NSString *alert = NSLocalizedString(@"CorruptedBookmarksAlert",@"");
+ NSString *message = NSLocalizedString(@"CorruptedBookmarksMsg",@"");
+ NSString *okButton = NSLocalizedString(@"OKButtonText",@"");
+ NSRunAlertPanel(alert, message, okButton, nil, nil);
+ return NO;
+}
+
+-(void) startImportBookmarks
+{
+ if (!mImportDlgController)
+ mImportDlgController = [[BookmarkImportDlgController alloc] initWithWindowNibName:@"BookmarkImportDlg"];
+ [mImportDlgController buildAvailableFileList];
+ [NSApp beginSheet:[mImportDlgController window]
+ modalForWindow:[[NSApp delegate] getFrontmostBrowserWindow]
+ modalDelegate:nil
+ didEndSelector:nil
+ contextInfo:nil];
+}
+
+-(void) importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder
+{
+ //I feel dirty doing it this way. But we'll check file extension
+ //to figure out how to handle this. Damn you, Steve Jobs!!
+ NSUndoManager *undoManager =[self undoManager];
+ [undoManager beginUndoGrouping];
+ BOOL success = NO;
+ NSString *extension =[pathToFile pathExtension];
+ if ([extension isEqualToString:@"html"] || [extension isEqualToString:@"htm"])
+ success = [self readHTMLFile:pathToFile intoFolder:aFolder];
+ else if ([extension isEqualToString:@"xml"])
+ success = [self readCaminoXMLFile:pathToFile intoFolder:aFolder];
+ else if ([extension isEqualToString:@"plist"] || !success)
+ success = [self readPropertyListFile:pathToFile intoFolder:aFolder];
+ // we don't know the extension, or we failed to load. we'll take another
+ // crack at it trying everything we know.
+ if (!success) {
+ success = [self readHTMLFile:pathToFile intoFolder:aFolder];
+ if (!success)
+ [self readCaminoXMLFile:pathToFile intoFolder:aFolder];
+ }
+ [[undoManager prepareWithInvocationTarget:[self rootBookmarks]] deleteChild:aFolder];
+ [undoManager endUndoGrouping];
+ [undoManager setActionName:NSLocalizedString(@"Import Bookmarks",@"Import Bookmarks")];
+}
+
+// spits out html file as NSString with proper encoding. it's pretty shitty, frankly.
+-(NSString *)decodedHTMLfile:(NSString *)pathToFile
+{
+ NSData* fileAsData = [[NSData alloc] initWithContentsOfFile:pathToFile];
+ if (!fileAsData) {
+ NSLog(@"decodedHTMLfile: file %@ cannot be read.",pathToFile);
+ return nil;
+ }
+ // we're gonna assume for now it's ascii and hope for the best.
+ // i'm doing this because I think we can always read it in as ascii,
+ // while it might fail if we assume default system encoding. i don't
+ // know this for sure. but we'll have to do 2 decodings. big whoop.
+ NSString *fileString = [[NSString alloc] initWithData:fileAsData encoding:NSASCIIStringEncoding];
+ if (!fileString) {
+ NSLog(@"decodedHTMLfile: file %@ doesn't want to become a string. Exiting.",pathToFile);
+ [fileAsData release];
+ return nil;
+ }
+
+ // Create a dictionary with possible encodings. As I figure out more possible encodings,
+ // I'll add them to the dictionary.
+ NSString *utfdash8Key = @"content=\"text/html; charset=utf-8" ;
+ NSString *xmacromanKey = @"content=\"text/html; charset=x-mac-roman";
+ NSString *xmacsystemKey = @"CONTENT=\"text/html; charset=X-MAC-SYSTEM";
+
+ NSDictionary *encodingDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ [NSNumber numberWithUnsignedInt:NSUTF8StringEncoding],utfdash8Key,
+ [NSNumber numberWithUnsignedInt:NSMacOSRomanStringEncoding],xmacromanKey,
+ [NSNumber numberWithUnsignedInt:[NSString defaultCStringEncoding]],xmacsystemKey,
+ nil];
+
+ NSEnumerator *keyEnumerator = [encodingDict keyEnumerator];
+ id key;
+ NSRange aRange;
+ while ((key = [keyEnumerator nextObject])) {
+ aRange = [fileString rangeOfString:key options:NSCaseInsensitiveSearch];
+ if (aRange.location != NSNotFound) {
+ [fileString release];
+ fileString = [[NSString alloc] initWithData:fileAsData encoding:[[encodingDict objectForKey:key] unsignedIntValue]];
+ [fileAsData release];
+ return [fileString autorelease];
+ }
+ }
+ // if we're here, we don't have a clue as to the encoding. we'll guess default
+ [fileString release];
+ if ((fileString = [[NSString alloc] initWithData:fileAsData encoding:[NSString defaultCStringEncoding]])) {
+ NSLog(@"decodedHTMLFile: file %@ encoding unknown. Assume default and proceed.",pathToFile);
+ [fileAsData release];
+ return [fileString autorelease];
+ }
+ // we suck. this is almost certainly wrong, but oh well.
+ NSLog(@"decodedHTMLFile: file %@ encoding unknown, and NOT default. Use ASCII and proceed.",pathToFile);
+ fileString = [[NSString alloc] initWithData:fileAsData encoding:NSASCIIStringEncoding];
+ [fileAsData release];
+ return [fileString autorelease];
+}
+
+
+-(BOOL)readHTMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder;
+{
+ // get file as NSString
+ NSString* fileAsString = [self decodedHTMLfile:pathToFile];
+ if (!fileAsString) {
+ NSLog(@"couldn't read file. bailing out");
+ return NO;
+ }
+ // Set up to scan the bookmark file
+ NSScanner *fileScanner = [[NSScanner alloc] initWithString:fileAsString];
+ BOOL isNetscape = YES;
+ // See if it's a netscape/IE style bookmark file, or omniweb
+ NSRange aRange = [fileAsString rangeOfString:@"" options:NSCaseInsensitiveSearch];
+ if (aRange.location != NSNotFound) {
+ // netscape/IE setup - start after Title attribute
+ [fileScanner scanUpToString:@"" intoString:NULL];
+ [fileScanner setScanLocation:([fileScanner scanLocation] + 7)];
+ } else {
+ isNetscape = NO;
+ aRange = [fileAsString rangeOfString:@"" options:NSCaseInsensitiveSearch];
+ if (aRange.location != NSNotFound)
+ // omniweb setup - start at
+ [fileScanner scanUpToString:@"" intoString:NULL];
+ else {
+ NSLog(@"Unrecognized style of Bookmark File. Read fails.");
+ [fileScanner release];
+ return NO;
+ }
+ }
+ BookmarkFolder *currentArray = aFolder;
+ BookmarkItem *currentItem;
+ NSScanner *tokenScanner;
+ NSString *tokenTag, *tokenString, *tempItem;
+ unsigned scanIndex;
+ BOOL justSetTitle = NO;
+ // Scan through file. As we find a token, do something useful with it.
+ while (![fileScanner isAtEnd]) {
+ [fileScanner scanUpToString:@"<" intoString:&tokenString];
+ scanIndex = [fileScanner scanLocation];
+ if ((scanIndex+3) < [fileAsString length]) {
+ tokenTag = [[NSString alloc] initWithString:[fileAsString substringWithRange:NSMakeRange(scanIndex,3)]];
+ // now we pick out if it's something we want to save.
+ // check in a "most likely thing first" order
+ if (([tokenTag isEqualToString:@"- "]) || ([tokenTag isEqualToString:@"
"])) {
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ else if (([tokenTag isEqualToString:@"" intoString:&tokenString];
+ tokenScanner = [[NSScanner alloc] initWithString:tokenString];
+ [tokenScanner scanUpToString:@"href=\"" intoString:NULL];
+ [tokenScanner setScanLocation:([tokenScanner scanLocation]+6)];
+ [tokenScanner scanUpToString:@"\"" intoString:&tempItem];
+ currentItem = [currentArray addBookmark];
+ [(Bookmark *)currentItem setUrl:[tempItem stringByRemovingAmpEscapes]];
+ [tokenScanner scanUpToString:@">" intoString:NULL];
+ [currentItem setTitle:[[tokenString substringFromIndex:([tokenScanner scanLocation]+1)] stringByRemovingAmpEscapes]];
+ [tokenScanner release];
+ justSetTitle = YES;
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ else if (([tokenTag isEqualToString:@"- " intoString:NULL];
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ [fileScanner scanUpToString:@"<" intoString:&tokenString];
+ [currentItem setDescription:[tokenString stringByRemovingAmpEscapes]];
+ justSetTitle = NO;
+ }
+ else if (([tokenTag isEqualToString:@"
" intoString:&tokenString];
+ currentItem = [currentArray addBookmarkFolder];
+ currentArray = (BookmarkFolder *)currentItem;
+ tokenScanner = [[NSScanner alloc] initWithString:tokenString];
+ if (isNetscape) {
+ [tokenScanner scanUpToString:@">" intoString:NULL];
+ [currentItem setTitle:[[tokenString substringFromIndex:([tokenScanner scanLocation]+1)] stringByRemovingAmpEscapes]];
+ } else {
+ [tokenScanner scanUpToString:@"" intoString:NULL];
+ [tokenScanner setScanLocation:([tokenScanner scanLocation]+3)];
+ [tokenScanner scanUpToString:@"" intoString:&tempItem];
+ [currentItem setTitle:[tempItem stringByRemovingAmpEscapes]];
+ }
+ [tokenScanner release];
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ else if (([tokenTag isEqualToString:@"" intoString:NULL];
+ [fileScanner scanUpToString:@"" intoString:NULL];
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ else if (([tokenTag isEqualToString:@"" intoString:NULL];
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ else if (([tokenTag isEqualToString:@"
" intoString:NULL];
+ else {
+ [tempItem release];
+ tempItem = [[NSString alloc] initWithString:[@"<" stringByAppendingString:[tokenString stringByRemovingAmpEscapes]]];
+ if (justSetTitle)
+ [currentItem setTitle:[[currentItem title] stringByAppendingString:tempItem]];
+ else
+ [currentItem setDescription:[[currentItem description] stringByAppendingString:tempItem]];
+ [fileScanner setScanLocation:([fileScanner scanLocation]+1)];
+ }
+ [tempItem release];
+ }
+ else { //beats me. just close the tag out and continue.
+ [fileScanner scanUpToString:@">" intoString:NULL];
+ }
+ [tokenTag release];
+ }
+ }
+ [fileScanner release];
+ return YES;
+}
+
+-(BOOL)readCaminoXMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder
+{
+ NSURL* fileURL = [NSURL fileURLWithPath:pathToFile];
+ if (!fileURL) {
+ NSLog(@"URL creation failed");
+ return NO;
+ }
+ // Thanks, Apple, for example XML parsing code.
+ // Create CFXMLTree from file. This needs to be released later
+ CFXMLTreeRef XMLFileTree = CFXMLTreeCreateWithDataFromURL (kCFAllocatorDefault,
+ (CFURLRef)fileURL,
+ kCFXMLParserSkipWhitespace,
+ kCFXMLNodeCurrentVersion);
+ if (!XMLFileTree) {
+ NSLog(@"XMLTree creation failed");
+ return NO;
+ }
+ // process top level nodes. I think we'll find DTD
+ // before data - so only need to make 1 pass.
+ int count, index;
+ CFXMLTreeRef subFileTree;
+ CFXMLNodeRef bookmarkNode;
+ CFXMLDocumentTypeInfo *docTypeInfo;
+ CFURLRef dtdURL;
+ BOOL aBool;
+ count = CFTreeGetChildCount(XMLFileTree);
+ for (index=0;index < count;index++) {
+ subFileTree = CFTreeGetChildAtIndex(XMLFileTree,index);
+ if (subFileTree) {
+ bookmarkNode = CFXMLTreeGetNode(subFileTree);
+ if (bookmarkNode) {
+ switch (CFXMLNodeGetTypeCode(bookmarkNode)) {
+ // make sure it's Camino/Chimera DTD
+ case (kCFXMLNodeTypeDocumentType):
+ docTypeInfo = (CFXMLDocumentTypeInfo *)CFXMLNodeGetInfoPtr(bookmarkNode);
+ dtdURL = docTypeInfo->externalID.systemID;
+ if (![[(NSURL *)dtdURL absoluteString] isEqualToString:@"http://www.mozilla.org/DTDs/ChimeraBookmarks.dtd"]) {
+ NSLog(@"not a ChimeraBookmarks xml file. Bail");
+ CFRelease(XMLFileTree);
+ return NO;
+ }
+ break;
+ case (kCFXMLNodeTypeElement):
+ aBool = [aFolder readCaminoXML:subFileTree];
+ CFRelease (XMLFileTree);
+ return aBool;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ CFRelease(XMLFileTree);
+ NSLog(@"run through the tree and didn't find anything interesting. Bailed out");
+ return NO;
+}
+
+-(BOOL)readPropertyListFile:(NSString *)pathToFile intoFolder:aFolder
+{
+ NSDictionary* dict = [NSDictionary dictionaryWithContentsOfFile:pathToFile];
+ // see if it's safari
+ if (![dict objectForKey:@"WebBookmarkType"])
+ return [aFolder readNativeDictionary:dict];
+ else
+ return [aFolder readSafariDictionary:dict];
+}
+
+//
+// Writing bookmark files
+//
+
+- (void) writeHTMLFile:(NSString *)pathToFile
+{
+ NSString *htmlString = [[self rootBookmarks] writeHTML:0];
+ if (![htmlString writeToFile:[pathToFile stringByStandardizingPath] atomically:YES])
+ NSLog(@"writeHTML: Failed to write file %@",pathToFile);
+ return;
+}
+
+-(void)writePropertyListFile:(NSString *)pathToFile
+{
+ NSDictionary* dict = [[self rootBookmarks] writeNativeDictionary];
+ if (![dict writeToFile:[pathToFile stringByStandardizingPath] atomically:YES])
+ NSLog(@"writePropertyList: Failed to write file %@",pathToFile);
+ return;
+}
+
+
+@end
diff --git a/camino/src/bookmarks/BookmarkMenu.h b/camino/src/bookmarks/BookmarkMenu.h
new file mode 100644
index 000000000000..8fa7ae2935c6
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkMenu.h
@@ -0,0 +1,54 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is Chimera code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* Simon Fraser
+* David Haas
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+#import "BookmarksClient.h"
+
+@class BookmarkFolder;
+
+@interface BookmarkMenu : NSObject
+{
+ NSMenu* mMenu; // retained
+ BookmarkFolder* mRootFolder;
+ int mFirstItemIndex;
+ BOOL mIsDockMenu;
+}
+
+- (id)initWithMenu:(NSMenu *)aMenu firstItem:(int)anIndex rootBookmarkFolder:(BookmarkFolder *)aFolder;
+
+@end
diff --git a/camino/src/bookmarks/BookmarkMenu.mm b/camino/src/bookmarks/BookmarkMenu.mm
new file mode 100644
index 000000000000..d854477a7e57
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkMenu.mm
@@ -0,0 +1,265 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is Chimera code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* Simon Fraser
+* David Haas
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkManager.h"
+#import "BookmarkMenu.h"
+#import "BookmarkFolder.h"
+#import "Bookmark.h"
+#import "NSString+Utils.h"
+
+// Definitions
+#define MENU_TRUNCATION_CHARS 60
+
+@interface BookmarkMenu(Private)
+- (NSMenu *)menu;
+- (BookmarkFolder *)rootBookmarkFolder;
+- (int)firstItemIndex;
+- (void)setFirstItemIndex:(int)anIndex;
+- (void)setRootBookmarkFolder:(BookmarkFolder *)anArray;
+- (NSMenu *)locateMenuForItem:(BookmarkItem *)anItem;
+- (void)constructMenu:(NSMenu *)menu forBookmarkFolder:(BookmarkFolder *)aFolder;
+- (void)flushMenu;
+- (void)addItem:(BookmarkItem *)anItem toMenu:(NSMenu *)aMenu atIndex:(int)aIndex;
+- (void)dockMenuChanged:(NSNotification *)note;
+@end
+
+@implementation BookmarkMenu
+// init & dealloc
+
+- (id)initWithMenu:(NSMenu *)aMenu firstItem:(int)anIndex rootBookmarkFolder:(BookmarkFolder *)aFolder
+{
+ if ((self = [super init]))
+ {
+ mMenu = [aMenu retain];
+ mFirstItemIndex = anIndex;
+ [self setRootBookmarkFolder:aFolder];
+ [self constructMenu:mMenu forBookmarkFolder:aFolder];
+ // Generic notifications for Bookmark Client
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil];
+ if (aFolder == [[BookmarkManager sharedBookmarkManager] dockMenuFolder])
+ [nc addObserver:self selector:@selector(dockMenuChanged:) name:BookmarkFolderDockMenuChangeNotificaton object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [mMenu release];
+ [mRootFolder release];
+ [super dealloc];
+}
+
+// Getters & setters
+-(NSMenu *)menu
+{
+ return mMenu;
+}
+
+-(BookmarkFolder *)rootBookmarkFolder
+{
+ return mRootFolder;
+}
+
+-(int)firstItemIndex
+{
+ return mFirstItemIndex;
+}
+
+-(void)setRootBookmarkFolder:(BookmarkFolder *)aFolder
+{
+ [aFolder retain];
+ [mRootFolder release];
+ mRootFolder = aFolder;
+}
+
+-(void)setFirstItemIndex:(int)anIndex
+{
+ mFirstItemIndex=anIndex;
+}
+
+//
+// Utility methods
+//
+
+- (void)flushMenu
+{
+ int firstItemIndex = [self firstItemIndex];
+ NSMenu *menu = [self menu];
+ while ([menu numberOfItems] > firstItemIndex)
+ [menu removeItemAtIndex:firstItemIndex];
+}
+
+- (void)constructMenu:(NSMenu *)menu forBookmarkFolder:(BookmarkFolder *)aFolder
+{
+ unsigned i, childCount = [aFolder count];
+ for (i = 0; i < childCount; i++)
+ [self addItem:[aFolder objectAtIndex:i] toMenu:menu atIndex:i];
+}
+
+- (void)addItem:(BookmarkItem *)anItem toMenu:(NSMenu *)aMenu atIndex:(int)aIndex
+{
+ NSMenuItem *menuItem;
+ NSString *title = [[anItem title] stringByTruncatingTo:MENU_TRUNCATION_CHARS at:kTruncateAtMiddle];
+ unsigned realIndex = aIndex;
+ if (aMenu == [self menu])
+ realIndex += [self firstItemIndex];
+
+ if ([anItem isKindOfClass:[Bookmark class]]) {
+ if (![(Bookmark *)anItem isSeparator]) { // normal bookmark
+ menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""];
+ [menuItem setTarget:[NSApp delegate]];
+ [menuItem setAction:@selector(openMenuBookmark:)];
+ [menuItem setImage:[anItem icon]];
+ } else {//separator
+ menuItem = [NSMenuItem separatorItem];
+ }
+ [aMenu insertItem:menuItem atIndex:realIndex];
+ } else if ([anItem isKindOfClass:[BookmarkFolder class]]){
+ if (![(BookmarkFolder *)anItem isGroup]) { //normal folder
+ menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""];
+ [aMenu insertItem:menuItem atIndex:realIndex];
+ [menuItem setImage: [anItem icon]];
+ NSMenu* subMenu = [[NSMenu alloc] initWithTitle:title];
+ [aMenu setSubmenu: subMenu forItem: menuItem];
+ [subMenu setAutoenablesItems: NO];
+ [self constructMenu:subMenu forBookmarkFolder:(BookmarkFolder *)anItem];
+ [subMenu release];
+ } else { //group
+ menuItem = [[NSMenuItem alloc] initWithTitle:title action: NULL keyEquivalent: @""];
+ [aMenu insertItem:menuItem atIndex:realIndex];
+ [menuItem setTarget:[NSApp delegate]];
+ [menuItem setAction:@selector(openMenuBookmark:)];
+ [menuItem setImage:[anItem icon]];
+ }
+ }
+ [menuItem setRepresentedObject:anItem];
+ if (![menuItem isSeparatorItem])
+ [menuItem release];
+}
+
+- (NSMenu *)locateMenuForItem:(BookmarkItem *)anItem
+{
+ if (![anItem isKindOfClass:[BookmarkItem class]])
+ return nil; //make sure we haven't gone to top of menu item doesn't live in.
+ if (anItem == [self rootBookmarkFolder])
+ return [self menu];
+ NSMenu* parentMenu = [self locateMenuForItem:[anItem parent]];
+ if (parentMenu) {
+ int index = [parentMenu indexOfItemWithRepresentedObject:anItem];
+ if (index != -1) {
+ if ([anItem isKindOfClass:[BookmarkFolder class]]) {
+ NSMenuItem* childMenu = [parentMenu itemAtIndex:index];
+ return [childMenu submenu];
+ } else if ([anItem isKindOfClass:[Bookmark class]])
+ return parentMenu;
+ }
+ }
+ return nil;
+}
+
+#pragma mark -
+// For the BookmarksClient Protocol
+
+- (void)bookmarkAdded:(NSNotification *)note
+{
+ BookmarkFolder *aFolder = [note object];
+ NSMenu* menu = nil;
+ if (aFolder == [self rootBookmarkFolder])
+ menu = [self menu];
+ else if (![aFolder isGroup])
+ menu = [self locateMenuForItem:aFolder];
+ if (menu) {
+ NSDictionary *dict = [note userInfo];
+ [self addItem:[dict objectForKey:BookmarkFolderChildKey] toMenu:menu atIndex:[[dict objectForKey:BookmarkFolderChildIndexKey] unsignedIntValue]];
+ }
+}
+- (void)bookmarkRemoved:(NSNotification *)note
+{
+ BookmarkFolder *aFolder = [note object];
+ NSMenu* menu = nil;
+ if (aFolder == [self rootBookmarkFolder])
+ menu = [self menu];
+ else if (![aFolder isGroup])
+ menu = [self locateMenuForItem:aFolder];
+ if (menu) {
+ BookmarkItem *anItem = [[note userInfo] objectForKey:BookmarkFolderChildKey];
+ [menu removeItemAtIndex:[menu indexOfItemWithRepresentedObject:anItem]];
+ }
+}
+
+- (void)bookmarkChanged:(NSNotification *)note
+{
+ BookmarkItem* anItem = [note object];
+ NSMenu *menu = nil;
+ BOOL isSeparator = NO;
+ if ([[self rootBookmarkFolder] isSmartFolder])
+ menu = [self menu];
+ else if ([anItem isKindOfClass:[Bookmark class]]) {
+ menu = [self locateMenuForItem:anItem];
+ isSeparator = [(Bookmark *)anItem isSeparator];
+ }
+ else if ([anItem isKindOfClass:[BookmarkFolder class]])
+ menu = [self locateMenuForItem:[anItem parent]];
+ if (menu) {
+ int index = [menu indexOfItemWithRepresentedObject:anItem];
+ if (index != -1) {
+ if (!isSeparator) {
+ NSMenuItem *menuItem = [menu itemAtIndex:index];
+ [menuItem setTitle:[[anItem title] stringByTruncatingTo:MENU_TRUNCATION_CHARS at:kTruncateAtMiddle]];
+ [menuItem setImage:[anItem icon]];
+ } else {
+ [menu removeItemAtIndex:index];
+ [menu insertItem:[NSMenuItem separatorItem] atIndex:index];
+ }
+ }
+ }
+}
+
+- (void)dockMenuChanged:(NSNotification *)note
+{
+ BookmarkFolder *aFolder = [note object];
+ [self flushMenu];
+ [self setRootBookmarkFolder:aFolder];
+ [self constructMenu:[self menu] forBookmarkFolder:aFolder];
+}
+
+@end
diff --git a/camino/src/bookmarks/BookmarkOutlineView.h b/camino/src/bookmarks/BookmarkOutlineView.h
new file mode 100644
index 000000000000..0a9672f2d640
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkOutlineView.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* Joe Hewitt (Original Author)
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+#import "ExtendedOutlineView.h"
+
+@interface BookmarkOutlineView : ExtendedOutlineView
+{
+}
+@end
diff --git a/camino/src/bookmarks/BookmarkOutlineView.mm b/camino/src/bookmarks/BookmarkOutlineView.mm
new file mode 100644
index 000000000000..cbb2de5aec48
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkOutlineView.mm
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* Joe Hewitt (Original Author)
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkOutlineView.h"
+#import "BookmarkFolder.h"
+#import "Bookmark.h"
+#import "NSArray+Utils.h"
+
+
+@implementation BookmarkOutlineView
+
+- (void)awakeFromNib
+{
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"MozURLType", @"MozBookmarkType", NSStringPboardType, NSURLPboardType, nil]];
+}
+
+- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+{
+ if (operation == NSDragOperationDelete)
+ {
+ NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ NSArray* bookmarks = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[pboard propertyListForType: @"MozBookmarkType"]];
+ if (bookmarks)
+ {
+ for (unsigned int i = 0; i < [bookmarks count]; ++i)
+ {
+ BookmarkItem* item = [bookmarks objectAtIndex:i];
+ [[item parent] deleteChild:item];
+ }
+ }
+ }
+}
+
+// don't edit URL field of folders or menu separators
+- (void)_editItem:(id)dummy
+{
+ id itemToEdit = [self itemAtRow:mRowToBeEdited];
+ if ([itemToEdit isKindOfClass:[BookmarkFolder class]]) {
+ if ((mColumnToBeEdited == [self columnWithIdentifier:@"url"]) ||
+ ((![itemToEdit isGroup]) && (mColumnToBeEdited == [self columnWithIdentifier:@"keyword"]))) {
+ [super _cancelEditItem];
+ return;
+ }
+ } else if ([itemToEdit isKindOfClass:[Bookmark class]]) {
+ if ([(Bookmark *)itemToEdit isSeparator]) {
+ [super _cancelEditItem];
+ return;
+ }
+ }
+ [super _editItem:dummy];
+}
+
+- (unsigned int)draggingSourceOperationMaskForLocal:(BOOL)localFlag
+{
+ if (localFlag)
+ return (NSDragOperationCopy | NSDragOperationGeneric | NSDragOperationMove);
+
+ return (NSDragOperationDelete | NSDragOperationGeneric);
+}
+
+@end
diff --git a/camino/src/bookmarks/BookmarkToolbar.h b/camino/src/bookmarks/BookmarkToolbar.h
new file mode 100644
index 000000000000..d4bcf899a691
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkToolbar.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Hyatt (Original Author)
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+
+#import "BookmarksClient.h"
+
+@class BookmarkButton;
+@class BookmarkItem;
+
+@interface BookmarkToolbar : NSView
+{
+ NSMutableArray* mButtons;
+ BookmarkButton* mDragInsertionButton;
+ int mDragInsertionPosition;
+ BOOL mIsShowing;
+ BOOL mDrawBorder;
+}
+
+ // Called to construct & edit the initial set of personal toolbar buttons.
+-(void)buildButtonList;
+-(void)addButton:(BookmarkItem*)aItem atIndex:(int)aIndex;
+-(void)editButton:(BookmarkItem*)aItem;
+-(void)removeButton:(BookmarkItem*)aItem;
+
+ // Called to lay out the buttons on the toolbar.
+-(void)reflowButtons;
+-(void)reflowButtonsStartingAtIndex: (int)aIndex;
+
+-(BOOL)isShown;
+-(void)setDrawBottomBorder:(BOOL)drawBorder;
+-(void)showBookmarksToolbar: (BOOL)aShow;
+
+-(IBAction)addFolder:(id)aSender;
+
+@end
diff --git a/camino/src/bookmarks/BookmarkToolbar.mm b/camino/src/bookmarks/BookmarkToolbar.mm
new file mode 100644
index 000000000000..ac57e0d627d4
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkToolbar.mm
@@ -0,0 +1,592 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Hyatt (Original Author)
+* Kathy Brade
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkToolbar.h"
+
+#import "CHBrowserService.h"
+#import "BookmarkButton.h"
+#import "BookmarkManager.h"
+#import "BrowserWindowController.h"
+#import "Bookmark.h"
+#import "BookmarkFolder.h"
+#import "NSArray+Utils.h"
+
+
+#define CHInsertNone 0
+#define CHInsertInto 1
+#define CHInsertBefore 2
+#define CHInsertAfter 3
+
+@interface BookmarkToolbar(Private)
+
+- (void)setButtonInsertionPoint:(id )sender;
+- (NSRect)insertionRectForButton:(NSView*)aButton position:(int)aPosition;
+- (BookmarkButton*)makeNewButtonWithItem:(BookmarkItem*)aItem;
+
+@end
+
+@implementation BookmarkToolbar
+
+- (id)initWithFrame:(NSRect)frame
+{
+ if ( (self = [super initWithFrame:frame]) )
+ {
+ mButtons = [[NSMutableArray alloc] init];
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+ mDrawBorder = YES;
+ [self registerForDraggedTypes:[NSArray arrayWithObjects:@"MozURLType", @"MozBookmarkType", NSStringPboardType, NSURLPboardType, nil]];
+ mIsShowing = YES;
+ // Generic notifications for Bookmark Client
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil];
+ }
+ return self;
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [mButtons release];
+ [super dealloc];
+}
+
+- (void)drawRect:(NSRect)aRect
+{
+ if (mDrawBorder)
+ {
+ [[NSColor controlShadowColor] set];
+ float height = [self bounds].size.height;
+ NSRectFill(NSMakeRect(aRect.origin.x, height - 1.0, aRect.size.width, height));
+ }
+
+ // The buttons will paint themselves. Just call our base class method.
+ [super drawRect: aRect];
+
+ // draw a separator at drag n drop insertion point if there is one
+ if (mDragInsertionPosition)
+ {
+ [[[NSColor controlShadowColor] colorWithAlphaComponent:0.6] set];
+ NSRectFill([self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]);
+ }
+}
+
+-(void)buildButtonList
+{
+ BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder];
+
+ for (unsigned int i = 0; i < [toolbar count]; i ++)
+ {
+ BookmarkButton* button = [self makeNewButtonWithItem:[toolbar objectAtIndex:i]];
+ [self addSubview: button];
+ [mButtons addObject: button];
+ }
+ if ([self isShown])
+ [self reflowButtons];
+}
+
+- (void)resetButtonList
+{
+ int count = [mButtons count];
+ for (int i = 0; i < count; i++)
+ {
+ BookmarkButton* button = [mButtons objectAtIndex: i];
+ [button removeFromSuperview];
+ }
+ [mButtons removeAllObjects];
+ [self setNeedsDisplay:YES];
+}
+
+-(void)addButton:(BookmarkItem*)aItem atIndex:(int)aIndex
+{
+ BookmarkButton* button = [self makeNewButtonWithItem:aItem];
+ [self addSubview: button];
+ [mButtons insertObject: button atIndex: aIndex];
+ if ([self isShown])
+ [self reflowButtonsStartingAtIndex: aIndex];
+}
+
+-(void)editButton:(BookmarkItem*)aItem
+{
+ int count = [mButtons count];
+ for (int i = 0; i < count; i++)
+ {
+ BookmarkButton* button = [mButtons objectAtIndex: i];
+ if ([button BookmarkItem] == aItem)
+ {
+ [button setBookmarkItem: aItem];
+ if (count > i && [self isShown])
+ [self reflowButtonsStartingAtIndex: i];
+ break;
+ }
+ }
+ [self setNeedsDisplay:YES];
+}
+
+-(void)removeButton:(BookmarkItem*)aItem
+{
+ int count = [mButtons count];
+ for (int i = 0; i < count; i++)
+ {
+ BookmarkButton* button = [mButtons objectAtIndex: i];
+ if ([button BookmarkItem] == aItem)
+ {
+ [mButtons removeObjectAtIndex: i];
+ [button removeFromSuperview];
+ if (count > i && [self isShown])
+ [self reflowButtonsStartingAtIndex: i];
+ break;
+ }
+ }
+ [self setNeedsDisplay:YES];
+}
+
+-(void)reflowButtons
+{
+ [self reflowButtonsStartingAtIndex: 0];
+}
+
+#define kBookmarkButtonHeight 16.0
+#define kMinBookmarkButtonWidth 16.0
+#define kMaxBookmarkButtonWidth 150.0
+#define kBookmarkButtonHorizPadding 2.0
+#define kBookmarkButtonVerticalPadding 1.0
+#define kBookmarkToolbarBottomPadding 1.0
+
+-(void)reflowButtonsStartingAtIndex: (int)aIndex
+{
+ if (![self isShown])
+ return;
+
+ // coordinates for this view are flipped, making it easier to lay out from top left
+ // to bottom right.
+ float oldHeight = [self frame].size.height;
+ int count = [mButtons count];
+ float curRowYOrigin = 0.0;
+ float curX = kBookmarkButtonHorizPadding;
+
+ for (int i = 0; i < count; i ++)
+ {
+ BookmarkButton* button = [mButtons objectAtIndex: i];
+ NSRect buttonRect;
+
+ if (i < aIndex)
+ {
+ buttonRect = [button frame];
+ curRowYOrigin = NSMinY(buttonRect) - kBookmarkButtonVerticalPadding;
+ curX = NSMaxX(buttonRect) + kBookmarkButtonHorizPadding;
+ }
+ else
+ {
+ [button sizeToFit];
+ float width = [button frame].size.width;
+
+ if (width > kMaxBookmarkButtonWidth)
+ width = kMaxBookmarkButtonWidth;
+
+ buttonRect = NSMakeRect(curX, curRowYOrigin + kBookmarkButtonVerticalPadding, width, kBookmarkButtonHeight);
+ curX += NSWidth(buttonRect) + kBookmarkButtonHorizPadding;
+
+ if (NSMaxX(buttonRect) > NSWidth([self bounds]))
+ {
+ curRowYOrigin += (kBookmarkButtonHeight + 2 * kBookmarkButtonVerticalPadding);
+ buttonRect = NSMakeRect(kBookmarkButtonHorizPadding, curRowYOrigin + kBookmarkButtonVerticalPadding, width, kBookmarkButtonHeight);
+ curX = NSWidth(buttonRect);
+ }
+
+ [button setFrame: buttonRect];
+ }
+ }
+
+ float computedHeight = curRowYOrigin + (kBookmarkButtonHeight + 2 * kBookmarkButtonVerticalPadding + kBookmarkToolbarBottomPadding);
+
+ // our size has changed, readjust our view's frame and the content area
+ if (computedHeight != oldHeight)
+ {
+ [super setFrame: NSMakeRect([self frame].origin.x, [self frame].origin.y + (oldHeight - computedHeight),
+ [self frame].size.width, computedHeight)];
+ [self setNeedsDisplay:YES];
+
+ // tell the superview to resize its subviews
+ [[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size];
+ }
+}
+
+-(BOOL)isFlipped
+{
+ return YES; // Use flipped coords, so we can layout out from top row to bottom row.
+}
+
+-(void)setFrame:(NSRect)aRect
+{
+ NSRect oldFrame = [self frame];
+ [super setFrame:aRect];
+
+ if (oldFrame.size.width == aRect.size.width || aRect.size.height == 0)
+ return;
+
+ int count = [mButtons count];
+ int reflowStart = 0;
+
+ // find out where we need to start reflowing
+ for (int i = 0; i < count; i ++)
+ {
+ BookmarkButton* button = [mButtons objectAtIndex:i];
+ NSRect buttonFrame = [button frame];
+
+ if ((NSMaxX(buttonFrame) > NSMaxX(aRect)) || // we're overhanging the right
+ (NSMaxY(buttonFrame) > kBookmarkButtonHeight)) // we're on the second row
+ {
+ reflowStart = i;
+ break;
+ }
+ }
+
+ [self reflowButtonsStartingAtIndex:reflowStart];
+}
+
+-(BOOL)isShown
+{
+ return mIsShowing;
+}
+
+-(void)setDrawBottomBorder:(BOOL)drawBorder
+{
+ if (mDrawBorder != drawBorder)
+ {
+ mDrawBorder = drawBorder;
+ NSRect dirtyRect = [self bounds];
+ dirtyRect.origin.y = dirtyRect.size.height - 1.0;
+ dirtyRect.size.height = 1.0;
+ [self setNeedsDisplayInRect:dirtyRect];
+ }
+}
+//
+// if the toolbar gets the message, we can only make a new folder.
+// kinda dull. but we'll do this on the fly.
+//
+-(NSMenu*)menuForEvent:(NSEvent*)aEvent
+{
+ NSMenu* myMenu = [[NSMenu alloc] initWithTitle:@"snookums"];
+ NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create New Folder...",@"Create New Folder...") action:@selector(addFolder:) keyEquivalent:[NSString string]];
+ [menuItem setTarget:self];
+ [myMenu addItem:menuItem];
+ [menuItem release];
+ return [myMenu autorelease];
+}
+
+//
+// context menu has only what we need
+//
+-(BOOL)validateMenuItem:(NSMenuItem*)aMenuItem
+{
+ return YES;
+}
+
+-(IBAction)addFolder:(id)aSender
+{
+ BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder];
+ BookmarkFolder* aFolder = [toolbar addBookmarkFolder];
+ [aFolder setTitle:NSLocalizedString(@"NewBookmarkFolder",@"New Folder")];
+}
+
+-(void)showBookmarksToolbar: (BOOL)aShow
+{
+ mIsShowing = aShow;
+
+ if (!aShow)
+ {
+ [[self superview] setNeedsDisplayInRect:[self frame]];
+ NSRect newFrame = [self frame];
+ newFrame.origin.y += newFrame.size.height;
+ newFrame.size.height = 0;
+ [self setFrame: newFrame];
+
+ // tell the superview to resize its subviews
+ [[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size];
+ }
+ else
+ {
+ [self reflowButtons];
+ [self setNeedsDisplay:YES];
+ }
+
+}
+
+- (void)setButtonInsertionPoint:(id )sender
+{
+ NSPoint dragLocation = [sender draggingLocation];
+ NSPoint superviewLoc = [[self superview] convertPoint:dragLocation fromView:nil]; // convert from window
+ NSButton* sourceButton = [sender draggingSource];
+
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertAfter;
+
+ NSView* foundView = [self hitTest:superviewLoc];
+ if (foundView && [foundView isMemberOfClass:[BookmarkButton class]])
+ {
+ BookmarkButton* targetButton = foundView;
+ BookmarkItem* targetItem = [targetButton BookmarkItem];
+
+ if ([targetItem isKindOfClass:[BookmarkFolder class]])
+ {
+ mDragInsertionButton = targetButton;
+ mDragInsertionPosition = CHInsertInto;
+ }
+ else if (targetButton != sourceButton)
+ {
+ NSPoint localLocation = [[self superview] convertPoint:superviewLoc toView:foundView];
+ mDragInsertionButton = targetButton;
+ if (localLocation.x < NSWidth([targetButton bounds]) / 2.0)
+ mDragInsertionPosition = CHInsertBefore;
+ else
+ mDragInsertionPosition = CHInsertAfter;
+ }
+ }
+ else
+ {
+ // throw it in at the end
+ mDragInsertionButton = ([mButtons count] > 0) ? [mButtons objectAtIndex:[mButtons count] - 1] : 0;
+ mDragInsertionPosition = CHInsertAfter;
+ }
+}
+
+- (BOOL)dropDestinationValid:(id )sender
+{
+ NSPasteboard* draggingPasteboard = [sender draggingPasteboard];
+ NSArray* types = [draggingPasteboard types];
+ BookmarkManager *bmManager = [BookmarkManager sharedBookmarkManager];
+ BookmarkFolder* toolbar = [bmManager toolbarFolder];
+
+ if (!toolbar)
+ return NO;
+
+ if ([types containsObject: @"MozBookmarkType"])
+ {
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[draggingPasteboard propertyListForType: @"MozBookmarkType"]];
+ BookmarkItem* destItem = nil;
+ int index = 0;
+
+ if (mDragInsertionPosition == CHInsertInto)
+ // drop onto folder
+ {
+ destItem = [mDragInsertionButton BookmarkItem];
+ index = 0;
+ }
+ else if (mDragInsertionPosition == CHInsertBefore ||
+ mDragInsertionPosition == CHInsertAfter) // drop onto toolbar
+ {
+ index = [mButtons indexOfObjectIdenticalTo:mDragInsertionButton];
+ if (mDragInsertionPosition == CHInsertAfter)
+ ++index;
+ }
+ if (![bmManager isDropValid:draggedItems toFolder:toolbar])
+ return NO;
+ }
+ return YES;
+}
+
+// NSDraggingDestination ///////////
+
+- (unsigned int)draggingEntered:(id )sender
+{
+ // we have to set the drag target before we can test for drop validation
+ [self setButtonInsertionPoint:sender];
+
+ if (![self dropDestinationValid:sender]) {
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+ return NSDragOperationNone;
+ }
+
+ return NSDragOperationGeneric;
+}
+
+- (void)draggingExited:(id )sender
+{
+ if (mDragInsertionPosition)
+ [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]];
+
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+}
+
+- (unsigned int)draggingUpdated:(id )sender
+{
+ if (mDragInsertionPosition)
+ [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]];
+
+ // we have to set the drag target before we can test for drop validation
+ [self setButtonInsertionPoint:sender];
+
+ if (![self dropDestinationValid:sender]) {
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+ return NSDragOperationNone;
+ }
+
+ if (mDragInsertionPosition)
+ [self setNeedsDisplayInRect:[self insertionRectForButton:mDragInsertionButton position:mDragInsertionPosition]];
+
+ return NSDragOperationGeneric;
+}
+
+- (BOOL)prepareForDragOperation:(id )sender
+{
+ return YES;
+}
+
+- (BOOL)performDragOperation:(id )sender
+{
+ unsigned index = 0;
+ BookmarkFolder* toolbar = [[BookmarkManager sharedBookmarkManager] toolbarFolder];
+ if (!toolbar)
+ return NO;
+
+ if (mDragInsertionPosition == CHInsertInto) // drop onto folder
+ {
+ toolbar = (BookmarkFolder *)[mDragInsertionButton BookmarkItem];
+ index = 0;
+ }
+ else if (mDragInsertionPosition == CHInsertBefore ||
+ mDragInsertionPosition == CHInsertAfter) // drop onto toolbar
+ {
+ index = [mButtons indexOfObjectIdenticalTo: mDragInsertionButton];
+ if (index == NSNotFound)
+ index = [toolbar count];
+ else if (mDragInsertionPosition == CHInsertAfter)
+ index++;
+ }
+ else
+ {
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+ [self setNeedsDisplay:YES];
+ return NO;
+ }
+
+ BOOL dropHandled = NO;
+ BOOL isCopy = ([sender draggingSourceOperationMask] == NSDragOperationCopy);
+
+ NSArray *draggedTypes = [[sender draggingPasteboard] types];
+
+ if ( [draggedTypes containsObject:@"MozBookmarkType"] )
+ {
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ NSEnumerator *enumerator = [draggedItems objectEnumerator];
+ id aKid;
+ while ((aKid = [enumerator nextObject])) {
+ if (isCopy) {
+ [[aKid parent] copyChild:aKid toBookmarkFolder:toolbar atIndex:index];
+ } else {
+ [[aKid parent] moveChild:aKid toBookmarkFolder:toolbar atIndex:index];
+ }
+ }
+ dropHandled = YES;
+ }
+ else if ( [draggedTypes containsObject:@"MozURLType"] )
+ {
+ NSDictionary* proxy = [[sender draggingPasteboard] propertyListForType: @"MozURLType"];
+ [toolbar addBookmark:[proxy objectForKey:@"title"] url:[proxy objectForKey:@"url"] inPosition:index isSeparator:NO];
+ dropHandled = YES;
+ }
+ else if ( [draggedTypes containsObject:NSStringPboardType] )
+ {
+ NSString* draggedText = [[sender draggingPasteboard] stringForType:NSStringPboardType];
+ [toolbar addBookmark:draggedText url:draggedText inPosition:index isSeparator:NO];
+ dropHandled = YES;
+ }
+ else if ([draggedTypes containsObject: NSURLPboardType])
+ {
+ NSURL* urlData = [NSURL URLFromPasteboard:[sender draggingPasteboard]];
+ [toolbar addBookmark:[urlData absoluteString] url:[urlData absoluteString] inPosition:index isSeparator:NO];
+ dropHandled = YES;
+ }
+ mDragInsertionButton = nil;
+ mDragInsertionPosition = CHInsertNone;
+ [self setNeedsDisplay:YES];
+ return dropHandled;
+}
+
+- (NSRect)insertionRectForButton:(NSView*)aButton position:(int) aPosition
+{
+ if (aPosition == CHInsertInto) {
+ return NSMakeRect([aButton frame].origin.x, [aButton frame].origin.y, [aButton frame].size.width, [aButton frame].size.height);
+ } else if (aPosition == CHInsertAfter) {
+ return NSMakeRect([aButton frame].origin.x+[aButton frame].size.width, [aButton frame].origin.y, 2, [aButton frame].size.height);
+ } else {// if (aPosition == BookmarksService::CHInsertBefore) {
+ return NSMakeRect([aButton frame].origin.x - 2, [aButton frame].origin.y, 2, [aButton frame].size.height);
+ }
+ }
+
+- (BookmarkButton*)makeNewButtonWithItem:(BookmarkItem*)aItem
+{
+ return [[[BookmarkButton alloc] initWithFrame: NSMakeRect(2, 1, 100, 17) item:aItem] autorelease];
+}
+
+#pragma mark -
+
+- (void)bookmarkAdded:(NSNotification *)aNote
+{
+ BookmarkFolder *anArray = [aNote object];
+ if (![anArray isEqual:[[BookmarkManager sharedBookmarkManager] toolbarFolder]])
+ return;
+ NSDictionary *dict = [aNote userInfo];
+ [self addButton:[dict objectForKey:BookmarkFolderChildKey] atIndex:[[dict objectForKey:BookmarkFolderChildIndexKey] unsignedIntValue]];
+}
+
+- (void)bookmarkRemoved:(NSNotification *)aNote
+{
+ BookmarkFolder *aFolder = [aNote object];
+ if (![aFolder isEqual:[[BookmarkManager sharedBookmarkManager] toolbarFolder]])
+ return;
+ NSDictionary *dict = [aNote userInfo];
+ [self removeButton:[dict objectForKey:BookmarkFolderChildKey]];
+}
+
+- (void)bookmarkChanged:(NSNotification *)aNote
+{
+ [self editButton:[aNote object]];
+}
+
+@end
diff --git a/camino/src/bookmarks/BookmarkViewController.h b/camino/src/bookmarks/BookmarkViewController.h
new file mode 100644
index 000000000000..0d90c8888a9b
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkViewController.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* Combines old BookmarkController and BookmarkDataSource classes. When
+* history gets brought in to new Bookmark system, HistoryDataSource will
+* go away, too.
+*
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+#import "BookmarksClient.h"
+
+@class HistoryDataSource;
+@class ExtendedTableView;
+@class BrowserWindowController;
+@class BookmarkOutlineView;
+@class BookmarkFolder;
+
+
+@interface BookmarkViewController : NSObject {
+ IBOutlet NSButton* mAddCollectionButton;
+ IBOutlet NSButton* mAddBookmarkButton;
+ IBOutlet NSButton* mAddFolderButton;
+ IBOutlet NSButton* mAddSeparatorButton;
+ IBOutlet NSButton* mInfoButton;
+ IBOutlet NSButton* mSearchButton;
+ IBOutlet NSTextField *mSearchField;
+
+ IBOutlet NSSplitView* mContainersSplit; // vertical split
+ IBOutlet NSSplitView* mItemSearchSplit; // horizontal split
+
+ IBOutlet BrowserWindowController* mBrowserWindowController;
+ IBOutlet ExtendedTableView* mContainerPane;
+ IBOutlet BookmarkOutlineView* mItemPane;
+ IBOutlet NSTableView* mSearchPane; // shows search results, can be hidden
+
+ IBOutlet HistoryDataSource* mHistorySource; //can swap to this for history data
+
+ NSMutableDictionary *mExpandedStatus;
+ NSString* mCachedHref;
+ BookmarkFolder *mActiveRootCollection;
+ BookmarkFolder *mRootBookmarks;
+ NSArray *mSearchResultArray;
+ int mOpenActionFlag;
+}
+
+//
+// IBActions
+//
+-(IBAction) setAsDockMenuFolder:(id)aSender;
+-(IBAction) addCollection:(id)aSender;
+-(IBAction) addBookmark:(id)aSender;
+-(IBAction) addFolder:(id)aSender;
+-(IBAction) addSeparator:(id)aSender;
+-(IBAction) openBookmark: (id)aSender;
+-(IBAction) openBookmarkInNewTab:(id)aSender;
+-(IBAction) openBookmarkInNewWindow:(id)aSender;
+-(IBAction) deleteBookmarks:(id)aSender;
+-(IBAction) showBookmarkInfo:(id)aSender;
+-(IBAction) startSearch:(id)aSender;
+-(IBAction) locateBookmark:(id)aSender;
+
+
+-(void) selectContainer:(int)inRowIndex;
+-(void) selectLastContainer;
+-(NSMutableDictionary *)expandedStatusDictionary;
+-(void) restoreFolderExpandedStates;
+-(BOOL) isExpanded:(id)anItem;
+-(BOOL) haveSelectedRow;
+-(void) setItem:(BookmarkFolder *)anItem isExpanded:(BOOL)aBool;
+-(void) setActiveCollection:(BookmarkFolder *)aFolder;
+-(BookmarkFolder *)activeCollection;
+
+-(void)addItem:(id)aSender useSelection:(BOOL)aSel isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle;
+-(void)addItem:(id)aSender withParent:(BookmarkFolder *)parent isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle;
+-(void)endAddBookmark: (int)aCode;
+
+-(void)deleteCollection:(id)aSender;
+-(void) focus;
+-(void) windowDidLoad;
+-(void) ensureBookmarks;
+
+@end
diff --git a/camino/src/bookmarks/BookmarkViewController.mm b/camino/src/bookmarks/BookmarkViewController.mm
new file mode 100644
index 000000000000..fed840586012
--- /dev/null
+++ b/camino/src/bookmarks/BookmarkViewController.mm
@@ -0,0 +1,1275 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* Simon Fraser
+* Max Horn
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "BookmarkViewController.h"
+#import "NSString+Utils.h"
+#import "NSArray+Utils.h"
+#import "NSPasteboard+Utils.h"
+#import "BookmarkManager.h"
+#import "BookmarkInfoController.h"
+#import "BookmarkFolder.h"
+#import "Bookmark.h"
+#import "MainController.h"
+#import "BrowserWindowController.h"
+#import "PreferenceManager.h"
+#import "ImageAndTextCell.h"
+#import "ExtendedTableView.h"
+#import "BookmarkOutlineView.h"
+#import "HistoryDataSource.h"
+#import "BookmarksClient.h"
+#import "NetworkServices.h"
+
+#define kNoOpenAction 0
+#define kOpenBookmarkAction 1
+#define kOpenInNewTabAction 2
+#define kOpenInNewWindowAction 3
+
+#define kGetInfoContextMenuItemTag 9
+
+@interface BookmarkViewController (Private)
+-(void) setSearchResultArray:(NSArray *)anArray;
+-(void) displayBookmarkInOutlineView:(BookmarkItem *)aBookmarkItem;
+@end
+
+@implementation BookmarkViewController
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [mCachedHref release];
+ [mExpandedStatus release];
+ [mActiveRootCollection release];
+ [mRootBookmarks release];
+ [mSearchResultArray release];
+ [super dealloc];
+}
+
+
+- (void)windowDidLoad
+{
+ mCachedHref = nil;
+ mRootBookmarks = nil;
+ mOpenActionFlag = 0;
+ [self setSearchResultArray:[NSMutableArray array]];
+ // the standard table item doesn't handle text and icons. Replace it
+ // with a custom cell that does.
+ ImageAndTextCell* imageAndTextCell = [[[ImageAndTextCell alloc] init] autorelease];
+ [imageAndTextCell setEditable: YES];
+ [imageAndTextCell setWraps: NO];
+ NSTableColumn* itemNameColumn = [mItemPane tableColumnWithIdentifier: @"title"];
+ [itemNameColumn setDataCell:imageAndTextCell];
+ NSTableColumn* containerNameColumn = [mContainerPane tableColumnWithIdentifier: @"title"];
+ [containerNameColumn setDataCell:imageAndTextCell];
+ NSTableColumn* searchNameColumn = [mSearchPane tableColumnWithIdentifier: @"title"];
+ [searchNameColumn setDataCell:imageAndTextCell];
+
+ // set up the font on the item & search views to be smaller
+ NSArray* columns = [mItemPane tableColumns];
+ if ( columns ) {
+ int numColumns = [columns count];
+ NSFont* smallerFont = [NSFont systemFontOfSize:11];
+ for ( int i = 0; i < numColumns; ++i )
+ [[[columns objectAtIndex:i] dataCell] setFont:smallerFont];
+ }
+ columns = [mSearchPane tableColumns];
+ if ( columns ) {
+ int numColumns = [columns count];
+ NSFont* smallerFont = [NSFont systemFontOfSize:11];
+ for ( int i = 0; i < numColumns; ++i )
+ [[[columns objectAtIndex:i] dataCell] setFont:smallerFont];
+ }
+ // Generic notifications for Bookmark Client
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkAdded:) name:BookmarkFolderAdditionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil];
+ [nc addObserver:self selector:@selector(serviceResolved:) name:NetworkServicesResolutionSuccess object:nil];
+ // register for dragged types
+ [mContainerPane registerForDraggedTypes:[NSArray arrayWithObject:@"MozBookmarkType"]];
+ [mSearchButton setEnabled:NO];
+ [self ensureBookmarks];
+ //
+ // these should be settable in the nib. however, whenever
+ // I try, they disappear as soon as I've saved. Very annoying.
+ [mItemPane setAutosaveName:@"BMOutlineView"];
+ [mSearchPane setAutosaveName:@"BMSearchView"];
+ [mContainerPane setAutosaveName:@"BMContainerView"];
+ [mItemPane setAutosaveTableColumns:YES];
+ [mSearchPane setAutosaveTableColumns:YES];
+ [mContainerPane setAutosaveTableColumns:YES];
+}
+
+-(void)ensureBookmarks
+{
+ if (!mRootBookmarks) {
+ [mContainerPane setTarget:self];
+ [mContainerPane setDeleteAction:@selector(deleteCollection:)];
+ [mItemPane setTarget: self];
+ [mItemPane setDoubleAction: @selector(openBookmark:)];
+ [mItemPane setDeleteAction: @selector(deleteBookmarks:)];
+ [mItemPane reloadData];
+ [mSearchPane setTarget: self];
+ [mSearchPane setDoubleAction: @selector(openBookmark:)];
+ [self restoreFolderExpandedStates];
+ [mItemPane setAutosaveTableColumns:YES];
+ [mSearchPane setAutosaveTableColumns:YES];
+ [mContainerPane setAutosaveTableColumns:YES];
+ mRootBookmarks = [[[BookmarkManager sharedBookmarkManager] rootBookmarks] retain];
+ }
+}
+
+-(void) setSearchResultArray:(NSArray *)anArray
+{
+ [anArray retain];
+ [mSearchResultArray release];
+ mSearchResultArray = anArray;
+ [mSearchPane reloadData];
+}
+
+//
+// IBActions
+//
+
+- (IBAction) setAsDockMenuFolder:(id)aSender
+{
+ int rowIndex = [mContainerPane selectedRow];
+ if (rowIndex >= 0) {
+ BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:[mContainerPane selectedRow]];
+ [aFolder setIsDockMenu:YES];
+ }
+}
+
+-(IBAction)addBookmark:(id)aSender
+{
+ [self addItem: aSender useSelection: YES isFolder: NO URL:nil title:nil];
+}
+
+-(IBAction)addFolder:(id)aSender
+{
+ [self addItem: aSender useSelection: YES isFolder: YES URL:nil title:nil];
+}
+
+-(IBAction)addCollection:(id)aSender
+{
+ BookmarkFolder *aFolder = [mRootBookmarks addBookmarkFolder];
+ [aFolder setTitle:NSLocalizedString(@"NewBookmarkFolder",@"New Folder")];
+ unsigned index = [mRootBookmarks indexOfObjectIdenticalTo:aFolder];
+ [self selectContainer:index];
+ [mContainerPane editColumn:0 row:index withEvent:nil select:YES];
+}
+
+-(IBAction) addSeparator:(id)aSender;
+{
+ Bookmark *aBookmark = [[Bookmark alloc] init];
+ [aBookmark setIsSeparator:YES];
+ [[[BookmarkManager sharedBookmarkManager] bookmarkMenuFolder] insertChild:aBookmark];
+}
+
+
+
+-(void)addItem:(id)aSender useSelection:(BOOL)aUseSel isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle
+{
+ // We use the selected item to determine the parent only if aUseSel is YES.
+ BookmarkFolder *parentFolder = nil;
+ BookmarkItem* item = nil;
+ if (aUseSel && ([mItemPane numberOfSelectedRows] == 1))
+ {
+ // There is only one selected row. If it is a folder, use it as our parent.
+ // Otherwise, use selected row's parent.
+ int index = [mItemPane selectedRow];
+ item = [mItemPane itemAtRow: index];
+ if ([item isKindOfClass:[BookmarkFolder class]])
+ parentFolder = item;
+ else
+ parentFolder = [item parent];
+ } else
+ parentFolder = [self activeCollection];
+ [self addItem:aSender withParent:parentFolder isFolder:aIsFolder URL:aURL title:aTitle];
+}
+
+-(void)addItem:(id)aSender withParent:(BookmarkFolder*)parent isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle
+{
+ NSString* titleString = aTitle;
+ NSString* urlString = aURL;
+
+ if (!aIsFolder)
+ {
+ // If no URL and title were specified, get them from the current page.
+ if (!aURL || !aTitle)
+ [[mBrowserWindowController getBrowserWrapper] getTitle:&titleString andHref:&urlString];
+
+ mCachedHref = [NSString stringWithString:urlString];
+ [mCachedHref retain];
+
+ } else { // Folder
+ mCachedHref = nil;
+ titleString = NSLocalizedString(@"NewBookmarkFolder", @"");
+ }
+
+ NSTextField* textField = [mBrowserWindowController getAddBookmarkTitle];
+ NSString* bookmarkTitle = titleString;
+ NSString* cleanedTitle = [bookmarkTitle stringByReplacingCharactersInSet:[NSCharacterSet controlCharacterSet] withString:@" "];
+
+ [textField setStringValue: cleanedTitle];
+
+ [mBrowserWindowController cacheBookmarkVC: self];
+
+ // Show/hide the bookmark all tabs checkbox as appropriate.
+ NSTabView* tabView = [mBrowserWindowController getTabBrowser];
+ id checkbox = [mBrowserWindowController getAddBookmarkCheckbox];
+ BOOL hasSuperview = [checkbox superview] != nil;
+ if (aIsFolder && hasSuperview) {
+ // Just don't show it at all.
+ [checkbox removeFromSuperview];
+ [checkbox retain];
+ }
+ else if (!aIsFolder && !hasSuperview) {
+ // Put it back in.
+ [[[mBrowserWindowController getAddBookmarkSheetWindow] contentView] addSubview: checkbox];
+ [checkbox autorelease];
+ }
+
+ // Enable the bookmark all tabs checkbox if appropriate.
+ if (!aIsFolder)
+ [[mBrowserWindowController getAddBookmarkCheckbox] setEnabled: ([tabView numberOfTabViewItems] > 1)];
+
+ // Build up the folder list.
+ NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder];
+ [popup removeAllItems];
+ [mRootBookmarks buildFlatFolderList:[popup menu] depth:1];
+
+ int itemIndex = [popup indexOfItemWithRepresentedObject:parent];
+ if (itemIndex != -1)
+ [popup selectItemAtIndex:itemIndex];
+
+ [popup synchronizeTitleAndSelectedItem];
+
+ [NSApp beginSheet: [mBrowserWindowController getAddBookmarkSheetWindow]
+ modalForWindow: [mBrowserWindowController window]
+ modalDelegate: nil //self
+ didEndSelector: nil //@selector(sheetDidEnd:)
+ contextInfo: nil];
+}
+
+-(void)endAddBookmark: (int)aCode
+{
+ if (aCode == 0)
+ return;
+
+ BOOL isGroup = NO;
+ id checkbox = [mBrowserWindowController getAddBookmarkCheckbox];
+ if (([checkbox superview] != nil) && [checkbox isEnabled] && ([checkbox state] == NSOnState))
+ {
+ [mCachedHref release];
+ mCachedHref = nil;
+ isGroup = YES;
+ }
+
+ NSString* titleString = [[mBrowserWindowController getAddBookmarkTitle] stringValue];
+ NSPopUpButton* popup = [mBrowserWindowController getAddBookmarkFolder];
+ NSMenuItem* selectedItem = [popup selectedItem];
+ BookmarkFolder *parentFolder = [selectedItem representedObject];
+
+ if (isGroup)
+ {
+ BookmarkFolder *newGroup = [parentFolder addBookmarkFolder:titleString inPosition:[parentFolder count] isGroup:YES];
+ id tabBrowser = [mBrowserWindowController getTabBrowser];
+ int count = [tabBrowser numberOfTabViewItems];
+ for (int i = 0; i < count; i++)
+ {
+ BrowserWrapper* browserWrapper = (BrowserWrapper*)[[tabBrowser tabViewItemAtIndex: i] view];
+ NSString* titleString = nil;
+ NSString* hrefString = nil;
+ [browserWrapper getTitle:&titleString andHref:&hrefString];
+ [newGroup addBookmark:titleString url:hrefString inPosition:i isSeparator:NO];
+ }
+ }
+ else
+ {
+ if (mCachedHref)
+ {
+ [parentFolder addBookmark:titleString url:mCachedHref inPosition:[parentFolder count] isSeparator:NO];
+ [mCachedHref release];
+ mCachedHref = nil;
+ }
+ else
+ [parentFolder addBookmarkFolder:titleString inPosition:[parentFolder count] isGroup:NO];
+ }
+}
+
+-(IBAction)deleteCollection:(id)aSender
+{
+ BookmarkManager *manager = [BookmarkManager sharedBookmarkManager];
+ int index = [mContainerPane selectedRow];
+ if (index < (int)[manager firstUserCollection])
+ return;
+ [self selectContainer:(index - 1)];
+ [[manager rootBookmarks] deleteChild:[[manager rootBookmarks] objectAtIndex:index]];
+}
+
+-(IBAction)deleteBookmarks: (id)aSender
+{
+ int index = [mItemPane selectedRow];
+ if (index == -1)
+ return;
+
+ // A cheap way of having to avoid scanning the list to remove children is to have the
+ // outliner collapse all items that are being deleted. This will cull the selection
+ // for us and eliminate any children that happened to be selected.
+
+ BOOL allCollapsed = NO;
+ id doomedItem;
+ NSEnumerator* selRows;
+ while (!allCollapsed) {
+ allCollapsed = YES;
+ selRows = [mItemPane selectedRowEnumerator];
+ while (allCollapsed && (doomedItem = [selRows nextObject])) {
+ doomedItem = [mItemPane itemAtRow:[doomedItem intValue]];
+ if ([mItemPane isItemExpanded:doomedItem]) {
+ allCollapsed = NO;
+ [mItemPane collapseItem:doomedItem];
+ }
+ }
+ }
+
+ // create array of items we need to delete. Deleting items out of of the
+ // selection array is problematic for some reason.
+ NSMutableArray *itemsToDelete = [[NSMutableArray alloc] init];
+ selRows = [mItemPane selectedRowEnumerator];
+ for (NSNumber* currIndex = [selRows nextObject];
+ currIndex != nil;
+ currIndex = [selRows nextObject]) {
+ index = [currIndex intValue];
+ BookmarkItem* item = [mItemPane itemAtRow: index];
+ [itemsToDelete addObject: item];
+ }
+
+ // delete all bookmarks that are in our array
+ int count = [itemsToDelete count];
+ for (int i = 0; i < count; i++) {
+ doomedItem = [itemsToDelete objectAtIndex:i];
+ [[doomedItem parent] deleteChild:doomedItem];
+ }
+ [itemsToDelete release];
+
+ // restore selection to location near last item deleted or last item
+ int total = [mItemPane numberOfRows];
+ if (index >= total)
+ index = total - 1;
+ [mItemPane selectRow: index byExtendingSelection: NO];
+}
+
+-(IBAction)openBookmark: (id)aSender
+{
+ id item = nil;
+ if (aSender == mItemPane) {
+ int index = [mItemPane selectedRow];
+ if (index == -1)
+ return;
+ item = [mItemPane itemAtRow: index];
+ } else if (aSender == mSearchPane) {
+ int index = [mSearchPane selectedRow];
+ if (index == -1)
+ return;
+ item = [mSearchResultArray objectAtIndex:index];
+ } else if ([aSender isKindOfClass:[BookmarkItem class]])
+ item = aSender;
+
+ if (!item)
+ return;
+ // see if it's a rendezvous item
+ id parent = [item parent];
+ if (![parent isKindOfClass:[BookmarkItem class]]) {
+ [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item];
+ mOpenActionFlag = kOpenBookmarkAction;
+ return;
+ }
+
+ if ([item isKindOfClass:[Bookmark class]]) {
+ // if the command key is down, follow the command-click pref
+ if (([[NSApp currentEvent] modifierFlags] & NSCommandKeyMask) &&
+ [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.opentabfor.middleclick" withSuccess:NULL])
+ {
+ BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL];
+ [mBrowserWindowController openNewTabWithURL:[item url] referrer:nil loadInBackground: loadInBackground];
+ }
+ else
+ [[mBrowserWindowController getBrowserWrapper] loadURI:[(Bookmark *)item url] referrer:nil flags:NSLoadFlagsNone activate:YES];
+ } else if ([item isKindOfClass:[BookmarkFolder class]]) {
+ if ([item isGroup])
+ [mBrowserWindowController openTabGroup:[item childURLs] replaceExistingTabs:YES];
+ else if ([mItemPane isItemExpanded:item])
+ [mItemPane collapseItem: item];
+ else
+ [mItemPane expandItem: item];
+ }
+}
+
+-(IBAction)openBookmarkInNewTab:(id)aSender
+{
+ id item = nil;
+
+ if (![aSender isKindOfClass:[BookmarkItem class]]) {
+ int index = [mItemPane selectedRow];
+ if (index == -1)
+ return;
+ if ([mItemPane numberOfSelectedRows] == 1)
+ item = [mItemPane itemAtRow:index];
+ } else
+ item = aSender;
+
+ if (!item)
+ return;
+ // see if it's a rendezvous item
+ id parent = [item parent];
+ if (![parent isKindOfClass:[BookmarkItem class]]) {
+ [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item];
+ mOpenActionFlag = kOpenInNewTabAction;
+ return;
+ }
+ if ([item isKindOfClass:[Bookmark class]]) {
+ BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL];
+ [mBrowserWindowController openNewTabWithURL:[item url] referrer:nil loadInBackground: loadInBackground];
+ } else if ([item isKindOfClass:[BookmarkFolder class]]) {
+ [mBrowserWindowController openTabGroup:[item childURLs] replaceExistingTabs:YES];
+ }
+}
+
+-(IBAction)openBookmarkInNewWindow:(id)aSender
+{
+ id item = nil;
+ if (![aSender isKindOfClass:[BookmarkItem class]]) {
+ int index = [mItemPane selectedRow];
+ if (index == -1)
+ return;
+ if ([mItemPane numberOfSelectedRows] == 1)
+ item = [mItemPane itemAtRow:index];
+ } else
+ item = aSender;
+ if (!item)
+ return;
+
+ // see if it's a rendezvous item
+ id parent = [item parent];
+ if (![parent isKindOfClass:[BookmarkItem class]]) {
+ [[NetworkServices sharedNetworkServices] attemptResolveService:[parent intValue] forSender:item];
+ mOpenActionFlag = kOpenInNewWindowAction;
+ return;
+ }
+ BOOL loadInBackground = [[PreferenceManager sharedInstance] getBooleanPref:"browser.tabs.loadInBackground" withSuccess:NULL];
+ if ([item isKindOfClass:[Bookmark class]])
+ [mBrowserWindowController openNewWindowWithURL:[item url] referrer: nil loadInBackground: loadInBackground];
+ else if ([item isKindOfClass:[BookmarkFolder class]]) {
+ [mBrowserWindowController openNewWindowWithGroupURLs:[item childURLs] loadInBackground:loadInBackground];
+ }
+}
+
+-(IBAction)showBookmarkInfo:(id)aSender
+{
+ BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController];
+ BookmarkItem* item = nil;
+ if ([[mItemPane window] firstResponder] == mSearchPane) {
+ int index = [mSearchPane selectedRow];
+ item = [mSearchResultArray objectAtIndex:index];
+ } else {
+ int index = [mItemPane selectedRow];
+ item = [mItemPane itemAtRow: index];
+ }
+ [bic setBookmark:item];
+ [bic showWindow:bic];
+}
+
+-(IBAction)startSearch:(id)aSender
+{
+ NSString *searchString = [mSearchField stringValue];
+ if ([searchString length] > 0)
+ [self setSearchResultArray:[[BookmarkManager sharedBookmarkManager] searchBookmarksForString:searchString]];
+}
+
+-(IBAction) locateBookmark:(id)aSender
+{
+ int index = [mSearchPane selectedRow];
+ if (index == -1)
+ return;
+ BookmarkItem *item = [mSearchResultArray objectAtIndex:index];
+ [self displayBookmarkInOutlineView:item];
+ [mItemPane selectRow:[mItemPane rowForItem:item] byExtendingSelection:NO];
+}
+
+-(void) displayBookmarkInOutlineView:(BookmarkItem *)aBookmarkItem
+{
+ BookmarkItem *parent = [aBookmarkItem parent];
+ if (parent != mRootBookmarks)
+ [self displayBookmarkInOutlineView:parent];
+ else {
+ int index = [mRootBookmarks indexOfObject:aBookmarkItem];
+ [mContainerPane selectRow:index byExtendingSelection:NO];
+ [self selectContainer:index];
+ return;
+ }
+ [mItemPane expandItem:aBookmarkItem];
+}
+
+
+- (void) focus
+{
+ [[mItemPane window] makeFirstResponder:mItemPane];
+}
+
+- (void) setCanEditSelectedContainerContents:(BOOL)inCanEdit
+{
+ [mItemPane setAllowsEditing:inCanEdit];
+ // update buttons
+ [mAddBookmarkButton setEnabled:inCanEdit];
+ [mAddFolderButton setEnabled:inCanEdit];
+ [mInfoButton setEnabled:inCanEdit];
+}
+
+-(void) setActiveCollection:(BookmarkFolder *)aFolder
+{
+ [aFolder retain];
+ [mActiveRootCollection release];
+ mActiveRootCollection = aFolder;
+}
+
+-(BookmarkFolder *)activeCollection
+{
+ return mActiveRootCollection;
+}
+
+- (BOOL)isExpanded:(id)anItem
+{
+ NSMutableDictionary *dict = [self expandedStatusDictionary];
+ NSNumber *aBool = [dict objectForKey:[NSNumber numberWithUnsignedInt:[anItem hash]]];
+ if (aBool)
+ return [aBool boolValue];
+ return NO;
+}
+
+-(BOOL) isExpandedInView:(NSOutlineView *)aView
+{
+ return NO;
+}
+
+-(void) setItem:(BookmarkFolder *)anItem isExpanded:(BOOL)aBool
+{
+ NSMutableDictionary *dict = [self expandedStatusDictionary];
+ [dict setObject:[NSNumber numberWithBool:aBool] forKey:[NSNumber numberWithUnsignedInt:[anItem hash]]];
+}
+
+- (void)restoreFolderExpandedStates
+{
+ int curRow = 0;
+ while (curRow < [mItemPane numberOfRows])
+ {
+ id item = [mItemPane itemAtRow:curRow];
+ if ([item isKindOfClass:[BookmarkFolder class]])
+ {
+ if ([self isExpanded:item])
+ [mItemPane expandItem: item];
+ else
+ [mItemPane collapseItem: item];
+ }
+ curRow ++;
+ }
+}
+
+-(NSMutableDictionary *)expandedStatusDictionary
+{
+ if (mExpandedStatus)
+ return mExpandedStatus;
+ mExpandedStatus = [[NSMutableDictionary alloc] initWithCapacity:20];
+ return mExpandedStatus;
+}
+
+
+#pragma mark -
+//
+// table view things
+//
+- (void) selectContainer:(int)inRowIndex
+{
+ [mContainerPane selectRow:inRowIndex byExtendingSelection:NO];
+ if ( inRowIndex == kHistoryContainerIndex ) {
+ [mItemPane setDataSource:mHistorySource];
+ [mItemPane setDelegate:mHistorySource];
+ [mHistorySource ensureDataSourceLoaded];
+ [self setCanEditSelectedContainerContents:NO];
+ [mItemPane setTarget:mHistorySource];
+ [mItemPane setDoubleAction: @selector(openHistoryItem:)];
+ [mItemPane setDeleteAction: @selector(deleteHistoryItems:)];
+ } else {
+ [mItemPane setDataSource:self];
+ [mItemPane setDelegate:self];
+ BookmarkFolder *activeCollection = [mRootBookmarks objectAtIndex:inRowIndex];
+ [self setActiveCollection:activeCollection];
+ [self restoreFolderExpandedStates];
+ [mItemPane setTarget:self];
+ [mItemPane setDoubleAction: @selector(openBookmark:)];
+ if ([activeCollection isSmartFolder])
+ [self setCanEditSelectedContainerContents:NO];
+ else
+ [self setCanEditSelectedContainerContents:YES];
+ [mItemPane setDeleteAction: @selector(deleteBookmarks:)];
+ }
+ [mItemPane reloadData];
+}
+
+- (void) selectLastContainer
+{
+ [self selectContainer:[mContainerPane selectedRow]];
+}
+
+- (int)numberOfRowsInTableView:(NSTableView *)tableView
+{
+ if ( tableView == mContainerPane )
+ return [mRootBookmarks count];
+ else if ( tableView == mSearchPane )
+ return [mSearchResultArray count];
+ return 0;
+}
+
+- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
+{
+ id retValue = nil;
+ id item = nil;
+ if ( tableView == mContainerPane )
+ item = [mRootBookmarks objectAtIndex:row];
+ else if (tableView == mSearchPane )
+ item = [mSearchResultArray objectAtIndex:row];
+ NS_DURING
+ retValue = [item valueForKey:[tableColumn identifier]];
+ NS_HANDLER
+ retValue = nil;
+ NS_ENDHANDLER
+ return retValue;
+}
+
+- (void)tableView:(NSTableView *)inTableView willDisplayCell:(id)inCell forTableColumn:(NSTableColumn *)inTableColumn row:(int)inRowIndex
+{
+ if ( inTableView == mContainerPane ) {
+ BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:inRowIndex];
+ [inCell setImage:[aFolder icon]];
+ } else if (inTableView == mSearchPane && [[inTableColumn identifier] isEqualToString:@"title"]) {
+ BookmarkItem *anItem = [mSearchResultArray objectAtIndex:inRowIndex];
+ [inCell setImage:[anItem icon]];
+ }
+}
+
+- (BOOL)tableView:(NSTableView *)aTableView shouldEditTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
+{
+ if (aTableView == mContainerPane) {
+ if (rowIndex >= (int)[[BookmarkManager sharedBookmarkManager] firstUserCollection])
+ return YES;
+ return NO;
+ }
+ return NO;
+}
+
+- (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row;
+{
+ if (tableView == mContainerPane) {
+ BookmarkFolder *aFolder = [mRootBookmarks objectAtIndex:row];
+ [aFolder setTitle:object];
+ }
+}
+
+- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
+{
+ int count = [rows count];
+ if (count == 0)
+ return NO;
+ NSMutableArray *itemArray = [[NSMutableArray alloc] initWithCapacity:count];
+ BookmarkManager *manager = [BookmarkManager sharedBookmarkManager];
+ NSEnumerator *enumerator = [rows objectEnumerator];
+ unsigned firstUserCollection = [manager firstUserCollection];
+ int rowVal;
+ id aRow;
+ while ((aRow = [enumerator nextObject])) {
+ rowVal = [aRow intValue];
+ if (rowVal > (int)firstUserCollection)
+ [itemArray addObject:[mRootBookmarks objectAtIndex:rowVal]];
+ }
+ if ([itemArray count] == 0) {
+ [itemArray release];
+ return NO;
+ }
+ // Pack pointers to bookmark items into this array
+ NSArray *pointerArray = [NSArray dataArrayFromPointerArrayForMozBookmarkDrop:itemArray];
+ [itemArray release];
+ [pboard declareTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self];
+ [pboard setPropertyList:pointerArray forType:@"MozBookmarkType"];
+ return YES;
+}
+
+- (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id )info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
+{
+ NSArray* types = [[info draggingPasteboard] types];
+ if ([types containsObject:@"MozBookmarkType"] && tv == mContainerPane) {
+ BookmarkManager *manager = [BookmarkManager sharedBookmarkManager];
+ BookmarkFolder *dropFolder = nil;
+ if ((row < 2) && (op == NSTableViewDropOn))
+ dropFolder = [mRootBookmarks objectAtIndex:row];
+ else if (row >= (int)[manager firstUserCollection]) {
+ if (op == NSTableViewDropAbove)
+ dropFolder = mRootBookmarks;
+ else
+ dropFolder = [mRootBookmarks objectAtIndex:row];
+ }
+ if (dropFolder) {
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ BOOL isOK = [manager isDropValid:draggedItems toFolder:dropFolder];
+ return (isOK) ? NSDragOperationGeneric: NSDragOperationNone;
+ }
+ }
+ return NSDragOperationNone;
+}
+
+- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id )info row:(int)row dropOperation:(NSTableViewDropOperation)op
+{
+ NSArray *types = [[info draggingPasteboard] types];
+ if ([types containsObject:@"MozBookmarkType"] && (tv == mContainerPane)) {
+ BookmarkFolder *dropFolder;
+ if (op == NSTableViewDropAbove)
+ dropFolder = mRootBookmarks;
+ else
+ dropFolder = [mRootBookmarks objectAtIndex:row];
+ BOOL isCopy = ([info draggingSourceOperationMask] == NSDragOperationCopy);
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ NSEnumerator *enumerator = [draggedItems objectEnumerator];
+ id aKid;
+ while ((aKid = [enumerator nextObject])) {
+ if (isCopy) {
+ [[aKid parent] copyChild:aKid toBookmarkFolder:dropFolder atIndex:row];
+ } else {
+ [[aKid parent] moveChild:aKid toBookmarkFolder:dropFolder atIndex:row];
+ }
+ }
+ return YES;
+ }
+ return NO;
+}
+
+- (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex
+{
+ if (aTableView == mContainerPane)
+ [self selectContainer:rowIndex];
+ return YES;
+}
+
+-(void)tableViewSelectionDidChange:(NSNotification *)note
+{
+ if ([note object] == mSearchPane) {
+ BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController];
+ int index = [mSearchPane selectedRow];
+ if (index != -1)
+ [mSearchButton setEnabled:YES];
+ else
+ [mSearchButton setEnabled:NO];
+ if ([[bic window] isVisible])
+ [bic setBookmark:[mSearchResultArray objectAtIndex:index]];
+ }
+}
+
+-(NSMenu *)tableView:(NSTableView *)aTableView contextMenuForRow:(int)rowIndex
+{
+ if (aTableView == mContainerPane) {
+ NSMenu* contextMenu = nil;
+ if (rowIndex >= 0) {
+ contextMenu = [aTableView menu];
+ int numItems = [contextMenu numberOfItems];
+ while (numItems > 2)
+ [contextMenu removeItemAtIndex:(--numItems)];
+ if (rowIndex >= (int)[[BookmarkManager sharedBookmarkManager] firstUserCollection]) {
+ [contextMenu addItem:[NSMenuItem separatorItem]];
+ NSMenuItem *deleteItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteCollection:) keyEquivalent:[NSString string]];
+ [deleteItem setTarget:self];
+ [contextMenu addItem:deleteItem];
+ [deleteItem release];
+ }
+ }
+ return contextMenu;
+ }
+ return nil;
+}
+
+#pragma mark -
+
+//
+// outlineView:shouldEditTableColumn:item: (delegate method)
+//
+// Called by the outliner to determine whether or not we should allow the
+// user to edit this item. We always return NO, because we invoke the
+// edit methods manually.
+//
+- (BOOL)outlineView:(NSOutlineView *)outlineView shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ return NO;
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
+{
+ if (item)
+ return [item objectAtIndex:index];
+ return [[self activeCollection] objectAtIndex:index];
+}
+
+- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
+{
+ if (!(item) || [item isKindOfClass:[BookmarkFolder class]])
+ return YES;
+ return NO;
+}
+
+- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
+{
+ if (item)
+ return [item count];
+ return [[self activeCollection] count];
+}
+
+- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
+{
+ id retValue = nil;
+ NS_DURING
+ retValue = [item valueForKey:[tableColumn identifier]];
+ NS_HANDLER
+ if ([item isKindOfClass:[BookmarkFolder class]] && [[tableColumn identifier] isEqualToString:@"url"]) {
+ NSString* itemCountStr = [NSString stringWithFormat:NSLocalizedString(@"Contains Items", @"%u Items"),[item count]];
+ NSDictionary* colorAttributes = [NSDictionary dictionaryWithObject:[NSColor disabledControlTextColor] forKey:NSForegroundColorAttributeName];
+ retValue = [[[NSAttributedString alloc] initWithString:itemCountStr attributes:colorAttributes] autorelease];
+ } else
+ retValue = nil;
+ NS_ENDHANDLER
+ return retValue;
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
+{
+ // set the image on the name column. the url column doesn't have an image.
+ if ([[tableColumn identifier] isEqualToString: @"title"])
+ [cell setImage:[item icon]];
+}
+
+- (void)outlineView:(NSOutlineView *)outlineView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
+{
+ NS_DURING
+ [item takeValue:object forKey:[tableColumn identifier]];
+ NS_HANDLER
+ return;
+ NS_ENDHANDLER
+}
+
+- (BOOL)outlineView:(NSOutlineView *)ov writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard
+{
+ int count = [items count];
+ if ((count == 0) || [mActiveRootCollection isSmartFolder])
+ return NO;
+
+ // Pack pointers to bookmark items into this array.
+ NSArray *pointerArray = [NSArray dataArrayFromPointerArrayForMozBookmarkDrop:items];
+ if (count == 1) {
+ id aBookmark = [items objectAtIndex:0];
+ if ([aBookmark isKindOfClass:[Bookmark class]]) {
+ [pboard declareURLPasteboardWithAdditionalTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner:self];
+ [pboard setDataForURL:[aBookmark url] title:[aBookmark title]];
+ [pboard setPropertyList:pointerArray forType:@"MozBookmarkType"];
+ return YES;
+ }
+ }
+ // it's either a folder or we've got more than 1 thing. ship it
+ // out as MozBookmarkType
+ [pboard declareTypes:[NSArray arrayWithObject:@"MozBookmarkType"] owner: self];
+ [pboard setPropertyList: pointerArray forType:@"MozBookmarkType"];
+ return YES;
+}
+
+
+- (NSDragOperation)outlineView:(NSOutlineView*)ov validateDrop:(id )info proposedItem:(id)item proposedChildIndex:(int)index
+{
+ NSArray* types = [[info draggingPasteboard] types];
+
+ // if the index is -1, deny the drop
+ if (index == NSOutlineViewDropOnItemIndex)
+ return NSDragOperationNone;
+
+ if ([types containsObject: @"MozBookmarkType"])
+ {
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ BookmarkFolder* parent = (item) ? item : [self activeCollection];
+ BOOL isOK = [[BookmarkManager sharedBookmarkManager] isDropValid:draggedItems toFolder:parent];
+ return (isOK) ? NSDragOperationGeneric : NSDragOperationNone;
+ }
+
+ if ([types containsObject: NSStringPboardType] ||
+ [types containsObject: NSURLPboardType] ||
+ [types containsObject: @"MozURLType"] )
+ return NSDragOperationGeneric;
+
+ return NSDragOperationNone;
+}
+
+
+- (BOOL)outlineView:(NSOutlineView*)ov acceptDrop:(id )info item:(id)item childIndex:(int)index
+{
+ NSArray* types = [[info draggingPasteboard] types];
+ BOOL isCopy = ([info draggingSourceOperationMask] == NSDragOperationCopy);
+ if (!item)
+ item = [self activeCollection];
+
+ if ([types containsObject: @"MozBookmarkType"])
+ {
+ NSArray *draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[info draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ NSEnumerator *enumerator = [draggedItems objectEnumerator];
+ id aKid;
+ while ((aKid = [enumerator nextObject])) {
+ if (isCopy) {
+ [[aKid parent] copyChild:aKid toBookmarkFolder:item atIndex:index];
+ } else {
+ [[aKid parent] moveChild:aKid toBookmarkFolder:item atIndex:index];
+ }
+ }
+ return YES;
+ }
+ else if ([types containsObject: @"MozURLType"])
+ {
+ NSDictionary* proxy = [[info draggingPasteboard] propertyListForType: @"MozURLType"];
+ [item addBookmark:[proxy objectForKey:@"title"] url:[proxy objectForKey:@"url"] inPosition:index isSeparator:NO];
+ return YES;
+ }
+ else if ([types containsObject: NSStringPboardType])
+ {
+ NSString* draggedText = [[info draggingPasteboard] stringForType:NSStringPboardType];
+ [item addBookmark:draggedText url:draggedText inPosition:index isSeparator:NO];
+ return YES;
+ }
+ else if ([types containsObject: NSURLPboardType])
+ {
+ NSURL* urlData = [NSURL URLFromPasteboard:[info draggingPasteboard]];
+ [item addBookmark:[urlData absoluteString] url:[urlData absoluteString] inPosition:index isSeparator:NO];
+ return YES;
+ }
+ return NO;
+}
+
+- (NSString *)outlineView:(NSOutlineView *)outlineView tooltipStringForItem:(id)item
+{
+ if ([item isKindOfClass:[Bookmark class]]) {
+ if ([[item description] length] > 0)
+ return [NSString stringWithFormat:@"%@\n%@",[item url], [item description]];
+ else
+ return [item url];
+ }
+ else if ([item isKindOfClass:[BookmarkFolder class]])
+ return [item description];
+ else
+ return nil;
+}
+
+
+ - (NSMenu *)outlineView:(NSOutlineView *)outlineView contextMenuForItem:(id)item
+{
+ NSMenu *contextMenu = nil;
+ if (item) {
+ NSString *nulString = [NSString string];
+ contextMenu = [mItemPane menu];
+ // clean the menu out
+ int numItems = [contextMenu numberOfItems];
+ int itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(showBookmarkInfo:)];
+ while (numItems > (itemIndex+1))
+ [contextMenu removeItemAtIndex:(--numItems)];
+ if ([item isKindOfClass:[Bookmark class]]) {
+ itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)];
+ [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Window",@"Open in New Window")];
+ itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)];
+ [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in New Tab",@"Open in New Tab")];
+ } else if ([item isKindOfClass:[BookmarkFolder class]]) {
+ itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewWindow:)];
+ [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open Tabs in New Window",@"Open Tabs in New Window")];
+ itemIndex = [contextMenu indexOfItemWithTarget:self andAction:@selector(openBookmarkInNewTab:)];
+ [[contextMenu itemAtIndex:itemIndex] setTitle:NSLocalizedString(@"Open in Tabs",@"Open in Tabs")];
+ NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Use as Dock Menu",@"Use as Dock Menu") action:@selector(makeDockMenu:) keyEquivalent:nulString];
+ [menuItem setTarget:item];
+ [contextMenu addItem:menuItem];
+ [menuItem release];
+ [contextMenu addItem:[NSMenuItem separatorItem]];
+ }
+ id parent = [item parent];
+ if ([parent isKindOfClass:[BookmarkFolder class]] && ![parent isSmartFolder]) {
+ // delete
+ NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Delete",@"Delete") action:@selector(deleteBookmarks:) keyEquivalent:nulString];
+ [menuItem setTarget:self];
+ [contextMenu addItem:menuItem];
+ [menuItem release];
+ }
+ if (![[self activeCollection] isSmartFolder]) {
+ // space
+ [contextMenu addItem:[NSMenuItem separatorItem]];
+ // create new folder
+ NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Create New Folder...",@"Create New Folder...") action:@selector(addFolder:) keyEquivalent:nulString];
+ [menuItem setTarget:self];
+ [contextMenu addItem:menuItem];
+ [menuItem release];
+ }
+ }
+ return contextMenu;
+}
+
+- (void)reloadDataForItem:(id)item reloadChildren: (BOOL)aReloadChildren
+{
+ if (!item)
+ [mItemPane reloadData];
+ else
+ [mItemPane reloadItem: item reloadChildren: aReloadChildren];
+}
+
+- (BOOL)haveSelectedRow
+{
+ return ([mItemPane selectedRow] != -1);
+}
+
+-(void)outlineViewSelectionDidChange: (NSNotification*) aNotification
+{
+ BookmarkInfoController *bic = [BookmarkInfoController sharedBookmarkInfoController];
+ int index = [mItemPane selectedRow];
+ if (index == -1)
+ {
+ [mInfoButton setEnabled:NO];
+ [bic close];
+ }
+ else
+ {
+ id item = [mItemPane itemAtRow: index];
+ [mInfoButton setEnabled:YES];
+ if ([[bic window] isVisible])
+ [bic setBookmark:item];
+ }
+}
+
+/*
+-(BOOL)validateMenuItem:(NSMenuItem*)aMenuItem
+{
+ int index = [mItemPane selectedRow];
+ BOOL haveSelection = (index != -1);
+ BOOL multiSelection = ([mItemPane numberOfSelectedRows] > 1);
+ BOOL isBookmark = NO;
+ BOOL isToolbar = NO;
+ BOOL isGroup = NO;
+
+ id item = nil;
+
+ if (haveSelection)
+ item = [mItemPane itemAtRow: index];
+ if ([item isKindOfClass:[Bookmark class]])
+ isBookmark = YES;
+ else if ([item isKindOfClass:[BookmarkFolder class]]) {
+ isGroup = [item isGroup];
+ isToolbar = [item isToolbar];
+ }
+
+ // Bookmarks and Bookmark Groups can be opened in a new window
+ if (([aMenuItem action] == @selector(openBookmarkInNewWindow:)))
+ return (isBookmark || isGroup);
+
+ // Only Bookmarks can be opened in new tabs
+ if (([aMenuItem action] == @selector(openBookmarkInNewTab:)))
+ return isBookmark && [mBrowserWindowController newTabsAllowed];
+
+ if (([aMenuItem action] == @selector(showBookmarkInfo:)))
+ return haveSelection;
+
+ if (([aMenuItem action] == @selector(deleteBookmarks:)))
+ return (multiSelection || (haveSelection && !isToolbar));
+
+ if (([aMenuItem action] == @selector(addFolder:)))
+ return YES;
+
+ return YES;
+}
+*/
+- (void)outlineViewItemDidExpand:(NSNotification *)notification
+{
+ id item = [[notification userInfo] objectForKey:@"NSObject"];
+ [self setItem:item isExpanded:YES];
+}
+
+- (void)outlineViewItemDidCollapse:(NSNotification *)notification
+{
+ id item = [[notification userInfo] objectForKey:@"NSObject"];
+ [self setItem:item isExpanded:NO];
+}
+
+#pragma mark -
+//
+// Network services protocol
+//
+- (void)availableServicesChanged:(NSNotification *)note
+{
+}
+
+//
+// we've got to to a delayed call here in case we received
+// the note before the bookmark was updated
+//
+- (void)serviceResolved:(NSNotification *)note
+{
+ if (mOpenActionFlag == kNoOpenAction)
+ return;
+ NSDictionary *dict = [note userInfo];
+ id aClient = [dict objectForKey:NetworkServicesClientKey];
+ if ([aClient isKindOfClass:[Bookmark class]]) {
+ switch (mOpenActionFlag) {
+ case (kOpenBookmarkAction):
+ [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmark:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
+ break;
+ case (kOpenInNewTabAction):
+ [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmarkInNewTab:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
+ break;
+ case (kOpenInNewWindowAction):
+ [[NSRunLoop currentRunLoop] performSelector:@selector(openBookmarkInNewWindow:) target:self argument:aClient order:10 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
+ break;
+ default:
+ break;
+ }
+ mOpenActionFlag = kNoOpenAction;
+ }
+}
+
+- (void)serviceResolutionFailed:(NSNotification *)note
+{
+}
+
+
+
+#pragma mark -
+//
+// BookmarksClient protocol
+//
+- (void)bookmarkAdded:(NSNotification *)note
+{
+ BookmarkFolder *aFolder = [note object];
+ if ((aFolder == [[BookmarkManager sharedBookmarkManager] rootBookmarks]))
+ {
+ [mContainerPane reloadData];
+ BookmarkFolder *updatedFolder = [[note userInfo] objectForKey:BookmarkFolderChildKey];
+ [self selectContainer:[aFolder indexOfObjectIdenticalTo:updatedFolder]];
+ return;
+ }
+ else if (aFolder == mActiveRootCollection)
+ aFolder = nil;
+ [self reloadDataForItem:aFolder reloadChildren:YES];
+}
+
+- (void)bookmarkRemoved:(NSNotification *)note
+{
+ BookmarkFolder *aFolder = [note object];
+ if ((aFolder == [[BookmarkManager sharedBookmarkManager] rootBookmarks])) {
+ [mContainerPane reloadData];
+ return;
+ } else if (aFolder == mActiveRootCollection)
+ aFolder = nil;
+ [self reloadDataForItem:aFolder reloadChildren:YES];
+}
+
+- (void)bookmarkChanged:(NSNotification *)note
+{
+ [self reloadDataForItem:[note object] reloadChildren:NO];
+}
+
+#pragma mark -
+
+//
+// - splitViewDidResizeSubviews:
+//
+// Called when one of the views got resized. We want to ensure that the "add bookmark
+// item" button gets lined up with the left edge of the item panel. If the container/item
+// split was the one that changed, move it accordingly
+//
+- (void)splitViewDidResizeSubviews:(NSNotification *)notification
+{
+ const int kButtonGutter = 8;
+
+ if ( [notification object] == mContainersSplit ) {
+ // get the position of the item view relative to the window and set the button
+ // to that X value. Yes, this will fall down if the bookmark view is inset from the window
+ // but i think we can safely assume it won't be.
+ NSRect windowRect = [mItemPane convertRect:[mItemPane bounds] toView:nil];
+ NSRect newButtonLocation = [mAddBookmarkButton frame];
+ newButtonLocation.origin.x = windowRect.origin.x;
+ [mAddBookmarkButton setFrame:newButtonLocation];
+ [mAddBookmarkButton setNeedsDisplay:YES];
+
+ // offset by the width of the button and the gutter and we've got the location
+ // of the add folder button next to it.
+ newButtonLocation.origin.x += newButtonLocation.size.width + kButtonGutter;
+ [mAddFolderButton setFrame:newButtonLocation];
+ [mAddFolderButton setNeedsDisplay:YES];
+ }
+}
+
+//
+// - splitView:canCollapseSubview:
+//
+// Called when appkit wants to ask if it can collapse a subview. The only subview
+// of our splits that we allow to be hidden is the search panel.
+//
+- (BOOL)splitView:(NSSplitView *)sender canCollapseSubview:(NSView *)subview
+{
+ BOOL retVal = NO;
+ // subview will be a NSScrollView, so we have to get the superview of the
+ // search pane for comparison.
+ if ( sender == mItemSearchSplit && subview == [mSearchPane superview] )
+ retVal = YES;
+ return retVal;
+}
+
+
+- (float)splitView:(NSSplitView *)sender constrainMinCoordinate:(float)proposedCoord ofSubviewAt:(int)offset
+{
+ const int kMinimumContainerSplitWidth = 150;
+ float retVal = proposedCoord;
+ if ( sender == mContainersSplit )
+ retVal = kMinimumContainerSplitWidth;
+ return retVal;
+}
+
+
+@end
diff --git a/camino/src/bookmarks/BookmarksClient.h b/camino/src/bookmarks/BookmarksClient.h
new file mode 100644
index 000000000000..ec23ce95d628
--- /dev/null
+++ b/camino/src/bookmarks/BookmarksClient.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+#import
+
+@protocol BookmarksClient
+
+- (void)bookmarkAdded:(NSNotification *)note;
+- (void)bookmarkRemoved:(NSNotification *)note;
+- (void)bookmarkChanged:(NSNotification *)note;
+
+@end
+
+// Notification keys
+// defined in Bookmark.h
+extern NSString *URLLoadNotification; //object is NSString of URL, userinfo has NSNum of success/fail
+extern NSString *URLLoadSuccessKey; //key for bool of load success/fail
+// defined in BookmarkFolder.h
+extern NSString* BookmarkFolderAdditionNotification; //self is obj, userinfo has added item/index
+extern NSString* BookmarkFolderDeletionNotification; //self is obj, userinfo dict has removed item
+extern NSString* BookmarkFolderChildKey;//key for added/removed object in userinfo dict
+extern NSString* BookmarkFolderChildIndexKey; // key for added object index in userinfo dict
+extern NSString* BookmarkFolderDockMenuChangeNotificaton; //self is NEW dock menu OR nil
+// Defined in BookmarkItem.h
+extern NSString *BookmarkItemChangedNotification; //no userinfo, self is object
+
+
+
diff --git a/camino/src/bookmarks/KindaSmartFolderManager.h b/camino/src/bookmarks/KindaSmartFolderManager.h
new file mode 100644
index 000000000000..d4f17d4a777c
--- /dev/null
+++ b/camino/src/bookmarks/KindaSmartFolderManager.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* By the way - this is a total hack. Somebody should really do this in
+* a more intelligent manner.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+
+@class BookmarkFolder;
+@class BookmarkManager;
+
+@interface KindaSmartFolderManager : NSObject {
+ BookmarkFolder* mBrokenBookmarkFolder;
+ BookmarkFolder* mUpdatedBookmarkFolder;
+ BookmarkFolder* mTop10Folder;
+ BookmarkFolder* mAddressBookFolder;
+ BookmarkFolder* mRendezvousFolder;
+ id mAddressBookManager;
+ unsigned mFewestVisits;
+}
+
+-(id)initWithBookmarkManager:(BookmarkManager *)manager;
+-(void)postStartupInitialization:(BookmarkManager *)manager;
+
+@end
diff --git a/camino/src/bookmarks/KindaSmartFolderManager.mm b/camino/src/bookmarks/KindaSmartFolderManager.mm
new file mode 100644
index 000000000000..1e3654e26697
--- /dev/null
+++ b/camino/src/bookmarks/KindaSmartFolderManager.mm
@@ -0,0 +1,301 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* By the way - this is a total hack. Somebody should really do this in
+* a more intelligent manner.
+*
+* ***** END LICENSE BLOCK ***** */
+#import "KindaSmartFolderManager.h"
+#import "BookmarkFolder.h"
+#import "Bookmark.h"
+#import "BookmarkManager.h"
+#import "NetworkServices.h"
+#import "BookmarksClient.h"
+
+@interface KindaSmartFolderManager (Private)
+-(void)addBookmark:(Bookmark *)aBookmark toSmartFolder:(BookmarkFolder *)aFolder;
+-(void)removeBookmark:(Bookmark *)aBookmark fromSmartFolder:(BookmarkFolder *)aFolder;
+-(void)checkForNewTop10:(Bookmark *)aBookmark;
+-(void)setupAddressBook;
+-(void)rebuildTop10List;
+@end
+
+@implementation KindaSmartFolderManager
+
+-(id)initWithBookmarkManager:(BookmarkManager *)manager
+{
+ if ((self = [super init])) {
+ // retain all our smart folders, just to be safe
+ mBrokenBookmarkFolder = [[manager brokenLinkFolder] retain];
+ mTop10Folder = [[manager top10Folder] retain];
+ mAddressBookFolder = [[manager addressBookFolder] retain];
+ mRendezvousFolder = [[manager rendezvousFolder] retain];
+ mFewestVisits = 0;
+ // client notifications
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(bookmarkRemoved:) name:BookmarkFolderDeletionNotification object:nil];
+ [nc addObserver:self selector:@selector(bookmarkChanged:) name:BookmarkItemChangedNotification object:nil];
+ }
+ return self;
+}
+
+-(void) dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [mBrokenBookmarkFolder release];
+ [mTop10Folder release];
+ [mAddressBookFolder release];
+ [mRendezvousFolder release];
+ [super dealloc];
+}
+
+-(void)postStartupInitialization:(BookmarkManager *)manager
+{
+ // register for Rendezvous - if we need to
+ if (mRendezvousFolder) {
+ [NetworkServices sharedNetworkServices];
+ NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
+ [nc addObserver:self selector:@selector(availableServicesChanged:) name:NetworkServicesAvailableServicesChanged object:nil];
+ [nc addObserver:self selector:@selector(serviceResolved:) name:NetworkServicesResolutionSuccess object:nil];
+ }
+ if (mAddressBookFolder)
+ [self setupAddressBook];
+ // get top 10 list started
+ NSArray *bookmarkArray = [[manager rootBookmarks] allChildBookmarks];
+ unsigned i, j = [bookmarkArray count];
+ for (i=0; i < j; i++) {
+ Bookmark *aBookmark = [bookmarkArray objectAtIndex:i];
+ [self checkForNewTop10:aBookmark];
+ if ([aBookmark isSick])
+ [self addBookmark:aBookmark toSmartFolder:mBrokenBookmarkFolder];
+ }
+}
+
+// when 10.1 support is dropped, most of "init" method of AddressBookManager goes here.
+// we'd also need to add that class' fillAddressBook method to this class
+-(void) setupAddressBook
+{
+ NSBundle *appBundle = [NSBundle mainBundle];
+ NSString *addressBookManagerBundlePath = [[appBundle resourcePath] stringByAppendingPathComponent:@"AddressBookManager.bundle"];
+ NSBundle *addressBookBundle = [NSBundle bundleWithPath:addressBookManagerBundlePath];
+ Class principalClass = [addressBookBundle principalClass];
+ mAddressBookManager = [[principalClass alloc] initWithFolder:mAddressBookFolder];
+ if (mAddressBookManager)
+ [mAddressBookFolder release];
+}
+
+//
+// flush top 10 list & rebuild from scratch
+//
+-(void)rebuildTop10List
+{
+ unsigned i, count = [mTop10Folder count];
+ for (i=0;i < count;i++)
+ [mTop10Folder deleteFromSmartFolderChildAtIndex:0];
+ mFewestVisits = 0;
+ // get top 10 list started
+ BookmarkManager *manager = [BookmarkManager sharedBookmarkManager];
+ NSArray *bookmarkArray = [[manager rootBookmarks] allChildBookmarks];
+ count = [bookmarkArray count];
+ for (i=0; i < count; i++) {
+ Bookmark *aBookmark = [bookmarkArray objectAtIndex:i];
+ [self checkForNewTop10:aBookmark];
+ }
+}
+
+
+-(void)checkForNewTop10:(Bookmark *)aBookmark
+{
+ unsigned smallVisit = [aBookmark numberOfVisits];
+ if (smallVisit == 0) {
+ if ([mTop10Folder indexOfObjectIdenticalTo:aBookmark] != NSNotFound)
+ //whoops. we just cleared the visits on a top 10 item. rebuild from scratch.
+ [self rebuildTop10List];
+ return;
+ }
+ if (([mTop10Folder indexOfObjectIdenticalTo:aBookmark] != NSNotFound))
+ return;
+ // cycle through list of children
+ // find item with fewest visits, mark it for destruction
+ // if the URL from the new bookmark is already present in the
+ // list, we bail out
+ NSMutableArray *childArray = [mTop10Folder childArray];
+ unsigned i, kidVisit, count = [childArray count]; //j should be 10
+ if ((count >=10) && (smallVisit < mFewestVisits))
+ return;
+ Bookmark *aKid = nil;
+ NSString *newURL = [aBookmark url];
+ int doomedKidIndex = -1;
+ for (i=0; i< count; i++) {
+ aKid = [childArray objectAtIndex:i];
+ if ([newURL isEqualToString:[aKid url]])
+ return;
+ kidVisit = [aKid numberOfVisits];
+ if (kidVisit == mFewestVisits)
+ doomedKidIndex = i;
+ else if (smallVisit > kidVisit)
+ smallVisit = kidVisit;
+ }
+ if ((doomedKidIndex != -1) && (count >= 10))
+ [mTop10Folder deleteFromSmartFolderChildAtIndex:doomedKidIndex];
+ [mTop10Folder insertIntoSmartFolderChild:aBookmark];
+ mFewestVisits = smallVisit;
+}
+
+
+//
+// if we don't already have it, add it
+//
+-(void)addBookmark:(Bookmark *)aBookmark toSmartFolder:(BookmarkFolder *)aFolder
+{
+ unsigned index = [aFolder indexOfObjectIdenticalTo:aBookmark];
+ if (index == NSNotFound)
+ [aFolder insertIntoSmartFolderChild:aBookmark];
+}
+//
+// if we have this item, remove it
+//
+-(void)removeBookmark:(Bookmark *)anItem fromSmartFolder:(BookmarkFolder *)aFolder
+{
+ unsigned index = [aFolder indexOfObjectIdenticalTo:anItem];
+ if (index != NSNotFound)
+ [aFolder deleteFromSmartFolderChildAtIndex:index];
+}
+
+#pragma mark -
+
+static int SortByProtocolAndName(NSDictionary* item1, NSDictionary* item2, void *context)
+{
+ NSComparisonResult protocolCompare = [[item1 objectForKey:@"name"] compare:[item2 objectForKey:@"name"] options:NSCaseInsensitiveSearch];
+ if (protocolCompare != NSOrderedSame)
+ return protocolCompare;
+
+ return [[item1 objectForKey:@"protocol"] compare:[item2 objectForKey:@"protocol"] options:NSCaseInsensitiveSearch];
+}
+
+- (void)availableServicesChanged:(NSNotification *)note
+{
+ // empty the rendezvous folder
+ unsigned i, count = [mRendezvousFolder count];
+ for (i=0; i < count; i++)
+ [mRendezvousFolder deleteChild:[mRendezvousFolder objectAtIndex:0]];
+ NetworkServices *netserv = [note object];
+ NSEnumerator* keysEnumerator = [netserv serviceEnumerator];
+ NSMutableArray* servicesArray = [[NSMutableArray alloc] initWithCapacity:10];
+ id key;
+ while ((key = [keysEnumerator nextObject])) {
+ NSDictionary* serviceDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ key, @"id",
+ [netserv serviceName:[key intValue]], @"name",
+ [netserv serviceProtocol:[key intValue]], @"protocol",
+ nil];
+ [servicesArray addObject:serviceDict];
+ }
+ if ([servicesArray count] != 0) {
+ // sort on protocol, then name
+ [servicesArray sortUsingFunction:SortByProtocolAndName context:NULL];
+ unsigned count = [servicesArray count];
+ for (unsigned int i = 0; i < count; i ++)
+ {
+ NSDictionary* serviceDict = [servicesArray objectAtIndex:i];
+ NSString* itemName = [[serviceDict objectForKey:@"name"] stringByAppendingString:NSLocalizedString([serviceDict objectForKey:@"protocol"], @"")];
+ Bookmark *aBookmark = [mRendezvousFolder addBookmark];
+ [aBookmark setTitle:itemName];
+ [aBookmark setParent:[serviceDict objectForKey:@"id"]];
+ }
+ }
+ [servicesArray release];
+}
+
+- (void)serviceResolved:(NSNotification *)note
+{
+ NSDictionary *dict = [note userInfo];
+ id aClient = [dict objectForKey:NetworkServicesClientKey];
+ if ([aClient isKindOfClass:[Bookmark class]]) {
+ Bookmark *aKid;
+ NSEnumerator* enumerator = [[mRendezvousFolder childArray] objectEnumerator];
+ while ((aKid = [enumerator nextObject])) {
+ if (aKid == aClient)
+ {
+ [aClient setUrl:[dict objectForKey:NetworkServicesResolvedURLKey]];
+ [aClient setParent:mRendezvousFolder];
+ }
+ }
+ }
+ return;
+}
+- (void)serviceResolutionFailed:(NSNotification *)note
+{
+ return;
+}
+
+
+#pragma mark -
+//
+// BookmarkClient protocol
+//
+- (void)bookmarkAdded:(NSNotification *)note
+{
+}
+
+//
+// need to tell top 10 list, broken items
+//
+- (void)bookmarkRemoved:(NSNotification *)note
+{
+ BookmarkItem *anItem = [[note userInfo] objectForKey:BookmarkFolderChildKey];
+ if (![anItem parent] && [anItem isKindOfClass:[Bookmark class]]) {
+ [self removeBookmark:anItem fromSmartFolder:mBrokenBookmarkFolder];
+ [self removeBookmark:anItem fromSmartFolder:mTop10Folder];
+ }
+}
+
+- (void)bookmarkChanged:(NSNotification *)note
+{
+ BookmarkItem *anItem = [note object];
+ if ([anItem isKindOfClass:[Bookmark class]]) {
+ [self checkForNewTop10:anItem];
+ // see what the status is
+ if ([(Bookmark *)anItem isSick])
+ [self addBookmark:anItem toSmartFolder:mBrokenBookmarkFolder];
+ else {
+ [self removeBookmark:anItem fromSmartFolder:mBrokenBookmarkFolder];
+ }
+ }
+}
+
+@end
diff --git a/camino/src/browser/AutoCompleteTextField.h b/camino/src/browser/AutoCompleteTextField.h
index bb076bbfaaa7..3c8c1f9f4eff 100644
--- a/camino/src/browser/AutoCompleteTextField.h
+++ b/camino/src/browser/AutoCompleteTextField.h
@@ -28,7 +28,7 @@
#include "nsIAutoCompleteResults.h"
#include "nsIAutoCompleteListener.h"
-@class BookmarksOutlineView, PageProxyIcon;
+@class PageProxyIcon;
@interface AutoCompleteTextField : NSTextField
{
diff --git a/camino/src/browser/AutoCompleteTextField.mm b/camino/src/browser/AutoCompleteTextField.mm
index e46b3527a602..e41a7f124350 100644
--- a/camino/src/browser/AutoCompleteTextField.mm
+++ b/camino/src/browser/AutoCompleteTextField.mm
@@ -509,7 +509,7 @@ NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
if (url) {
[self clearResults];
NSTextView *fieldEditor = [self fieldEditor];
- [[fieldEditor undoManager] removeAllActions];
+ [[fieldEditor undoManager] removeAllActionsWithTarget:self];
[fieldEditor setString:url];
[fieldEditor selectAll:self];
}
@@ -679,7 +679,8 @@ NS_IMPL_ISUPPORTS1(AutoCompleteListener, nsIAutoCompleteListener)
- (void)controlTextDidEndEditing:(NSNotification *)aNote
{
[self clearResults];
- [[[[aNote userInfo] objectForKey:@"NSFieldEditor"] undoManager] removeAllActions];
+ id fieldEditor = [[aNote userInfo] objectForKey:@"NSFieldEditor"];
+ [[fieldEditor undoManager] removeAllActionsWithTarget:fieldEditor];
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)command
diff --git a/camino/src/browser/BrowserContentViews.h b/camino/src/browser/BrowserContentViews.h
index 46a970317021..3973a0d5e330 100644
--- a/camino/src/browser/BrowserContentViews.h
+++ b/camino/src/browser/BrowserContentViews.h
@@ -38,12 +38,12 @@
#import
-@class BookmarksToolbar;
+@class BookmarkToolbar;
@class BrowserTabView;
@interface BrowserContentView : NSView
{
- IBOutlet BookmarksToolbar *mBookmarksToolbar;
+ IBOutlet BookmarkToolbar *mBookmarksToolbar;
IBOutlet NSView *mBrowserContainerView; // manages tabs and web content
IBOutlet NSView *mBookmarkManagerView; // swapped in and out by activating bm manager, replacing browser container
IBOutlet NSView *mStatusBar;
diff --git a/camino/src/browser/BrowserContentViews.mm b/camino/src/browser/BrowserContentViews.mm
index 804437240282..7572bfaae138 100644
--- a/camino/src/browser/BrowserContentViews.mm
+++ b/camino/src/browser/BrowserContentViews.mm
@@ -38,7 +38,7 @@
#import "BrowserContentViews.h"
-#import "BookmarksToolbar.h"
+#import "BookmarkToolbar.h"
#import "BrowserTabView.h"
@@ -65,7 +65,7 @@
| _________________________________________________________________
| BrowserContentView |
| ____________________________________________________________ |
- | | BookmarksToolbar | |
+ | | BookmarkToolbar | |
| |___________________________________________________________| |
| |
| ____________________________________________________________ |
diff --git a/camino/src/browser/BrowserTabView.mm b/camino/src/browser/BrowserTabView.mm
index 92c25723f236..453cad8fd721 100644
--- a/camino/src/browser/BrowserTabView.mm
+++ b/camino/src/browser/BrowserTabView.mm
@@ -39,11 +39,14 @@
#import "NSString+Utils.h"
#import "NSPasteboard+Utils.h"
+#import "NSArray+Utils.h"
#import "BrowserTabView.h"
-#import "BookmarksService.h"
#import "BrowserWrapper.h"
#import "BrowserWindowController.h"
+#import "BookmarkFolder.h"
+#import "Bookmark.h"
+#import "BookmarkToolbar.h"
//////////////////////////
// NEEDS IMPLEMENTED : Implement drag tracking for moving tabs around.
@@ -228,7 +231,7 @@
if (tabVisibilityChanged)
{
- [[[[self window] windowController] bookmarksToolbar] setDrawBottomBorder:!tabsVisible];
+ [[[[self window] windowController] bookmarkToolbar] setDrawBottomBorder:!tabsVisible];
// tell the superview to resize its subviews
[[self superview] resizeSubviewsWithOldSize:[[self superview] frame].size];
@@ -363,27 +366,30 @@ const float kTabsInvisibleTopGap = -7.0; // space removed to push tab content
if ([pasteBoardTypes containsObject: @"MozBookmarkType"])
{
- NSArray* contentIds = [[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"];
- if (contentIds)
+ NSArray* draggedItems = [NSArray pointerArrayFromDataArrayForMozBookmarkDrop:[[sender draggingPasteboard] propertyListForType: @"MozBookmarkType"]];
+ if (draggedItems)
{
- BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager];
-
- // drag type is chimera bookmarks
- for (unsigned int i = 0; i < [contentIds count]; ++i)
- {
- BookmarkItem* item = [bmManager getWrapperForNumber:[contentIds objectAtIndex:i]];
- if ([item isGroup])
- {
- NSArray* groupURLs = [bmManager getBookmarkGroupURIs:item];
- [[[self window] windowController] openTabGroup:groupURLs replaceExistingTabs:YES];
+ id aBookmark;
+ if ([draggedItems count] == 1) {
+ aBookmark = [draggedItems objectAtIndex:0];
+ if ([aBookmark isKindOfClass:[Bookmark class]])
+ return [self handleDropOnTab:overTabViewItem overContent:overContentArea withURL:[aBookmark url]];
+ else if ([aBookmark isKindOfClass:[BookmarkFolder class]]) {
+ [[[self window] windowController] openTabGroup:[aBookmark childURLs] replaceExistingTabs:YES];
+ return YES;
}
- else
- {
- // handle multiple items?
- return [self handleDropOnTab:overTabViewItem overContent:overContentArea withURL:[item url]];
+ } else if ([draggedItems count] > 1) {
+ NSMutableArray *urlArray = [NSMutableArray arrayWithCapacity:[draggedItems count]];
+ NSEnumerator *enumerator = [draggedItems objectEnumerator];
+ while ((aBookmark = [enumerator nextObject])) {
+ if ([aBookmark isKindOfClass:[Bookmark class]])
+ [urlArray addObject:[aBookmark url]];
+ else if ([aBookmark isKindOfClass:[BookmarkFolder class]])
+ [urlArray addObjectsFromArray:[aBookmark childURLs]];
}
-
- } // for each item
+ [[[self window] windowController] openTabGroup:urlArray replaceExistingTabs:YES];
+ return YES;
+ }
}
}
else if ([pasteBoardTypes containsObject: @"MozURLType"])
diff --git a/camino/src/browser/BrowserTabViewItem.mm b/camino/src/browser/BrowserTabViewItem.mm
index f857b90e016e..f12234c6b8f2 100644
--- a/camino/src/browser/BrowserTabViewItem.mm
+++ b/camino/src/browser/BrowserTabViewItem.mm
@@ -293,7 +293,7 @@
NSWindowController *windowController = [[[mTabViewItem view] window] windowController];
if ([windowController isMemberOfClass:[BrowserWindowController class]])
{
- if (sender == [windowController proxyIconView])
+ if (sender == [(BrowserWindowController *)windowController proxyIconView])
return NO;
}
@@ -421,7 +421,7 @@
{
// set the tag of every menu item to the tab view item's tag,
// so that the target of the menu commands know which one they apply to.
- for (unsigned int i = 0; i < [aMenu numberOfItems]; i ++)
+ for (int i = 0; i < [aMenu numberOfItems]; i ++)
[[aMenu itemAtIndex:i] setTag:[mTabViewItem tag]];
[super setMenu:aMenu];
diff --git a/camino/src/browser/BrowserWindowController.h b/camino/src/browser/BrowserWindowController.h
index 9a14af772350..da69cb627f30 100644
--- a/camino/src/browser/BrowserWindowController.h
+++ b/camino/src/browser/BrowserWindowController.h
@@ -38,14 +38,12 @@
#import
#import "BrowserWrapper.h"
#import "Find.h"
-#import "BookmarksToolbar.h"
class nsIURIFixup;
class nsIBrowserHistory;
class nsIDOMEvent;
class nsIDOMNode;
-@class BookmarksController;
//
// ThrobberHandler
@@ -87,7 +85,8 @@ typedef enum
} ENewTabContents;
-@class BookmarksDataSource;
+@class BookmarkViewController;
+@class BookmarkToolbar;
@class HistoryDataSource;
@class BrowserTabView;
@class PageProxyIcon;
@@ -99,9 +98,6 @@ typedef enum
@interface BrowserWindowController : NSWindowController
{
IBOutlet BrowserTabView* mTabBrowser;
- IBOutlet NSDrawer* mSidebarDrawer;
- IBOutlet NSTabView* mSidebarTabView;
- IBOutlet NSTabView* mSidebarSourceTabView;
IBOutlet NSView* mLocationToolbarView;
IBOutlet AutoCompleteTextField* mURLBar;
IBOutlet NSTextField* mStatus;
@@ -114,12 +110,9 @@ typedef enum
IBOutlet PageProxyIcon* mProxyIcon;
IBOutlet BrowserContentView* mContentView;
- IBOutlet BookmarksDataSource* mSidebarBookmarksDataSource;
+ IBOutlet BookmarkViewController* mBookmarkViewController;
+ IBOutlet BookmarkToolbar* mPersonalToolbar;
IBOutlet HistoryDataSource* mHistoryDataSource;
- IBOutlet BookmarksController* mBookmarksController;
-
- IBOutlet BookmarksToolbar* mPersonalToolbar;
-
IBOutlet NSWindow* mAddBookmarkSheetWindow;
IBOutlet NSTextField* mAddBookmarkTitleField;
IBOutlet NSPopUpButton* mAddBookmarkFolderField;
@@ -158,10 +151,6 @@ typedef enum
BOOL mShouldAutosave;
BOOL mShouldLoadHomePage;
- BOOL mDrawerCachedFrame;
- NSRect mCachedFrameBeforeDrawerOpen; // This is used by the drawer to figure out if the window should
- // be returned to its original position when the drawer closes.
- NSRect mCachedFrameAfterDrawerOpen;
unsigned int mChromeMask; // Indicates which parts of the window to show (e.g., don't show toolbars)
@@ -171,7 +160,7 @@ typedef enum
nsIDOMNode* mContextMenuNode;
// Cached bookmark ds used when adding through a sheet
- BookmarksDataSource* mCachedBMDS;
+ BookmarkViewController* mCachedBMVC;
// Throbber state variables.
ThrobberHandler* mThrobberHandler;
@@ -229,7 +218,7 @@ typedef enum
- (IBAction)cancelAddBookmarkSheet:(id)sender;
- (IBAction)endAddBookmarkSheet:(id)sender;
-- (void)cacheBookmarkDS:(BookmarksDataSource*)aDS;
+- (void)cacheBookmarkVC:(BookmarkViewController *)aDS;
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize;
@@ -259,7 +248,6 @@ typedef enum
- (void)addBookmarkExtended: (BOOL)aIsFromMenu isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle;
- (IBAction)manageBookmarks: (id)aSender;
- (IBAction)manageHistory: (id)aSender;
-- (void)importBookmarks: (NSString*)aURLSpec;
- (IBAction)toggleSidebar:(id)aSender;
- (BOOL)bookmarksAreVisible:(BOOL)inRequireSelection;
@@ -290,7 +278,7 @@ typedef enum
- (IBAction)frameToThisWindow:(id)sender;
- (void)openNewWindowWithURL: (NSString*)aURLSpec referrer:(NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG;
-- (void)openNewWindowWithGroup: (nsIContent*)aFolderContent loadInBackground: (BOOL)aLoadInBG;
+- (void)openNewWindowWithGroupURLs: (NSArray *)urlArray loadInBackground: (BOOL)aLoadInBG;
- (void)openNewTabWithURL: (NSString*)aURLSpec referrer: (NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG;
- (void)openTabGroup:(NSArray*)urlArray replaceExistingTabs:(BOOL)replaceExisting;
@@ -334,7 +322,7 @@ typedef enum
- (IBAction)copyImage:(id)sender;
- (IBAction)copyImageLocation:(id)sender;
-- (BookmarksToolbar*) bookmarksToolbar;
+- (BookmarkToolbar*) bookmarkToolbar;
- (NSProgressIndicator*) progressIndicator;
- (void) showProgressIndicator;
@@ -357,14 +345,11 @@ typedef enum
// cache the toolbar defaults we parse from a plist
+ (NSArray*) toolbarDefaults;
-// Accessor to get the sidebar drawer
-- (NSDrawer *)sidebarDrawer;
-
// Accessor to get the proxy icon view
- (PageProxyIcon *)proxyIconView;
// Accessor for the bm data source
-- (BookmarksDataSource*)bookmarksDataSource;
+- (BookmarkViewController *)bookmarkViewController;
- (void)toggleBookmarkManager:(id)sender;
- (void)ensureBrowserVisible:(id)sender;
diff --git a/camino/src/browser/BrowserWindowController.mm b/camino/src/browser/BrowserWindowController.mm
index 307a5f0e48a2..c6be76e30e72 100644
--- a/camino/src/browser/BrowserWindowController.mm
+++ b/camino/src/browser/BrowserWindowController.mm
@@ -40,19 +40,22 @@
#import "BrowserWindowController.h"
#import "BrowserWindow.h"
+#import "BookmarkToolbar.h"
+#import "BookmarkViewController.h"
+#import "BookmarkManager.h"
+
#import "BrowserContentViews.h"
#import "BrowserWrapper.h"
#import "PreferenceManager.h"
-#import "BookmarksDataSource.h"
#import "HistoryDataSource.h"
#import "BrowserTabView.h"
#import "UserDefaults.h"
#import "PageProxyIcon.h"
#import "AutoCompleteTextField.h"
-#import "BookmarksController.h"
#import "SearchTextField.h"
#import "SearchTextFieldCell.h"
#import "STFPopUpButtonCell.h"
+#import "MainController.h"
#include "nsIWebNavigation.h"
#include "nsIDOMDocument.h"
@@ -121,6 +124,7 @@ static NSArray* sToolbarDefaults = nil;
@interface AutoCompleteTextFieldEditor : NSTextView
{
NSFont* mDefaultFont; // will be needed if editing empty field
+ NSUndoManager *mUndoManager; //we handle our own undo to avoid stomping on bookmark undo
}
- (id)initWithFrame:(NSRect)bounds defaultFont:(NSFont*)defaultFont;
@end
@@ -132,10 +136,18 @@ static NSArray* sToolbarDefaults = nil;
{
if ((self = [super initWithFrame:bounds])) {
mDefaultFont = defaultFont;
+ mUndoManager = [[NSUndoManager alloc] init];
+ [self setDelegate:self];
}
return self;
}
+-(void) dealloc
+{
+ [mUndoManager release];
+ [super dealloc];
+}
+
-(void)paste:(id)sender
{
NSPasteboard *pboard = [NSPasteboard generalPasteboard];
@@ -160,6 +172,13 @@ static NSArray* sToolbarDefaults = nil;
}
}
+- (NSUndoManager *)undoManagerForTextView:(NSTextView *)aTextView
+{
+ if (aTextView == self)
+ return mUndoManager;
+ return nil;
+}
+
@end
//////////////////////////////////////
@@ -344,8 +363,6 @@ static NSArray* sToolbarDefaults = nil;
#if DEBUG
NSLog(@"Window will close notification.");
#endif
- [mSidebarBookmarksDataSource windowClosing];
-
[self autosaveWindowFrame];
{ // scope...
@@ -511,8 +528,6 @@ static NSArray* sToolbarDefaults = nil;
mustResizeChrome = YES;
mInitialized = YES;
-
- mDrawerCachedFrame = NO;
[[self window] setAcceptsMouseMovedEvents: YES];
@@ -542,11 +557,6 @@ static NSArray* sToolbarDefaults = nil;
mPendingURL = mPendingReferrer = nil;
}
-#if USE_DRAWER_FOR_BOOKMARKS
- [mSidebarDrawer setDelegate: self];
- [self setupSidebarTabs];
-#endif
-
if ( mChromeMask && !(mChromeMask & nsIWebBrowserChrome::CHROME_PERSONAL_TOOLBAR) ) {
// remove the personal toolbar and adjust the content area upwards. Removing it
// from the parent view releases it, so we have to clear out the member var.
@@ -557,7 +567,7 @@ static NSArray* sToolbarDefaults = nil;
}
else
{
- [mPersonalToolbar initializeToolbar];
+ [mPersonalToolbar buildButtonList];
if (![self shouldShowBookmarkToolbar])
[mPersonalToolbar showBookmarksToolbar:NO];
@@ -590,7 +600,7 @@ static NSArray* sToolbarDefaults = nil;
}
// let the in-window bookmark controller finish up some initialization
- [mBookmarksController windowDidLoad];
+ [mBookmarkViewController windowDidLoad];
}
- (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
@@ -604,7 +614,7 @@ static NSArray* sToolbarDefaults = nil;
#if 0
- (void)drawerWillOpen: (NSNotification*)aNotification
{
- [mSidebarBookmarksDataSource ensureBookmarks];
+ [mBookmarkViewController ensureBookmarks];
if ([[[mSidebarTabView selectedTabViewItem] identifier] isEqual:@"historySidebarCHIconTabViewItem"]) {
[mHistoryDataSource ensureDataSourceLoaded];
@@ -1100,19 +1110,19 @@ static NSArray* sToolbarDefaults = nil;
{
[mAddBookmarkSheetWindow orderOut:self];
[NSApp endSheet:mAddBookmarkSheetWindow returnCode:0];
- [mCachedBMDS endAddBookmark: 0];
+ [mCachedBMVC endAddBookmark: 0];
}
-(IBAction)endAddBookmarkSheet:(id)sender
{
[mAddBookmarkSheetWindow orderOut:self];
[NSApp endSheet:mAddBookmarkSheetWindow returnCode:0];
- [mCachedBMDS endAddBookmark: 1];
+ [mCachedBMVC endAddBookmark: 1];
}
-- (void)cacheBookmarkDS:(BookmarksDataSource*)aDS
+- (void)cacheBookmarkVC:(BookmarkViewController *)aVC
{
- mCachedBMDS = aDS;
+ mCachedBMVC = aVC;
}
-(IBAction)manageBookmarks: (id)aSender
@@ -1120,7 +1130,7 @@ static NSArray* sToolbarDefaults = nil;
if ( ![mContentView isBookmarkManagerVisible] )
[self toggleBookmarkManager: self];
- [mBookmarksController selectContainer:kBookmarksMenuContainer];
+ [mBookmarkViewController selectContainer:kBookmarkMenuContainerIndex];
}
-(IBAction)manageHistory: (id)aSender
@@ -1128,20 +1138,7 @@ static NSArray* sToolbarDefaults = nil;
if ( ![mContentView isBookmarkManagerVisible] )
[self toggleBookmarkManager: self];
- [mBookmarksController selectContainer:kHistoryContainer];
-}
-
-- (void)importBookmarks: (NSString*)aURLSpec
-{
- // Open the bookmarks sidebar.
- [self manageBookmarks: self];
-
- // Now do the importing.
- BrowserWrapper* newView = [[[BrowserWrapper alloc] initWithTab: nil andWindow: [self window]] autorelease];
- [newView setFrame: NSZeroRect];
- [newView setIsBookmarksImport: YES];
- [[[self window] contentView] addSubview: newView];
- [newView loadURI:aURLSpec referrer: nil flags:NSLoadFlagsNone activate:NO];
+ [mBookmarkViewController selectContainer:kHistoryContainerIndex];
}
- (IBAction)goToLocationFromToolbarURLField:(id)sender
@@ -1150,7 +1147,7 @@ static NSArray* sToolbarDefaults = nil;
NSString *theURL = [[sender stringValue] stringByTrimmingWhitespace];
// look for bookmarks keywords match
- NSArray *resolvedURLs = [[BookmarksManager sharedBookmarksManager] resolveBookmarksKeyword:theURL];
+ NSArray *resolvedURLs = [[BookmarkManager sharedBookmarkManager] resolveBookmarksKeyword:theURL];
NSString* resolvedURL = nil;
if ([resolvedURLs count] == 1)
@@ -1466,14 +1463,13 @@ static NSArray* sToolbarDefaults = nil;
- (void)addBookmarkExtended: (BOOL)aIsFromMenu isFolder:(BOOL)aIsFolder URL:(NSString*)aURL title:(NSString*)aTitle
{
- [mSidebarBookmarksDataSource ensureBookmarks];
+ [mBookmarkViewController ensureBookmarks];
BOOL useSel = aIsFromMenu;
if (aIsFromMenu) {
// Use selection only if the sidebar is open and the bookmarks panel is displaying.
useSel = [self bookmarksAreVisible:NO];
}
-
- [mSidebarBookmarksDataSource addBookmark: self useSelection: useSel isFolder: aIsFolder URL:aURL title:aTitle];
+ [mBookmarkViewController addItem: self useSelection: useSel isFolder: aIsFolder URL:aURL title:aTitle];
}
- (BOOL)bookmarksAreVisible:(BOOL)inRequireSelection
@@ -1481,7 +1477,7 @@ static NSArray* sToolbarDefaults = nil;
BOOL bookmarksShowing = [mContentView isBookmarkManagerVisible];
if (inRequireSelection)
- bookmarksShowing &= ([mSidebarBookmarksDataSource haveSelectedRow]);
+ bookmarksShowing &= ([mBookmarkViewController haveSelectedRow]);
return bookmarksShowing;
}
@@ -1849,21 +1845,6 @@ static NSArray* sToolbarDefaults = nil;
}
}
-- (void)tabView:(NSTabView *)tabView willSelectTabViewItem:(NSTabViewItem *)tabViewItem
-{
- // we'll get called for browser tab views as well. ignore any calls coming from
- // there, we're only interested in the sidebar.
- if (tabView != mSidebarTabView)
- return;
-
- if ([[tabViewItem identifier] isEqual:@"historySidebarCHIconTabViewItem"]) {
- [mHistoryDataSource ensureDataSourceLoaded];
- [mHistoryDataSource enableObserver];
- }
- else
- [mHistoryDataSource disableObserver];
-}
-
- (void)tabView:(NSTabView *)aTabView didSelectTabViewItem:(NSTabViewItem *)aTabViewItem
{
// we'll get called for the sidebar tabs as well. ignore any calls coming from
@@ -1922,7 +1903,7 @@ static NSArray* sToolbarDefaults = nil;
[browser showWindow:self];
}
-- (void)openNewWindowWithGroup: (nsIContent*)aFolderContent loadInBackground: (BOOL)aLoadInBG
+- (void)openNewWindowWithGroupURLs: (NSArray *)urlArray loadInBackground: (BOOL)aLoadInBG
{
// Autosave our dimensions before we open a new window. That ensures the size ends up matching.
[self autosaveWindowFrame];
@@ -1938,12 +1919,7 @@ static NSArray* sToolbarDefaults = nil;
}
else
[browser showWindow:self];
-
- BookmarksManager* bmManager = [BookmarksManager sharedBookmarksManager];
- BookmarkItem* item = [bmManager getWrapperForContent:aFolderContent];
-
- NSArray* groupURLs = [bmManager getBookmarkGroupURIs:item];
- [browser openTabGroup:groupURLs replaceExistingTabs:YES];
+ [browser openTabGroup:urlArray replaceExistingTabs:YES];
}
-(void)openNewTabWithURL: (NSString*)aURLSpec referrer:(NSString*)aReferrer loadInBackground: (BOOL)aLoadInBG
@@ -2103,8 +2079,8 @@ static NSArray* sToolbarDefaults = nil;
- (void)getInfo:(id)sender
{
- [mSidebarBookmarksDataSource ensureBookmarks];
- [mSidebarBookmarksDataSource showBookmarkInfo:sender];
+ [mBookmarkViewController ensureBookmarks];
+ [mBookmarkViewController showBookmarkInfo:sender];
}
- (BOOL)canGetInfo
@@ -2343,7 +2319,7 @@ static NSArray* sToolbarDefaults = nil;
}
}
-- (BookmarksToolbar*) bookmarksToolbar
+- (BookmarkToolbar*) bookmarkToolbar
{
return mPersonalToolbar;
}
@@ -2553,19 +2529,15 @@ static NSArray* sToolbarDefaults = nil;
[[mBrowserView getBrowserView] setActive:newResponderIsGecko];
}
-- (NSDrawer *)sidebarDrawer
-{
- return mSidebarDrawer;
-}
- (PageProxyIcon *)proxyIconView
{
return mProxyIcon;
}
-- (BookmarksDataSource*)bookmarksDataSource
+- (BookmarkViewController *)bookmarkViewController
{
- return mSidebarBookmarksDataSource;
+ return mBookmarkViewController;
}
- (id)windowWillReturnFieldEditor:(NSWindow *)aWindow toObject:(id)anObject
@@ -2582,6 +2554,10 @@ static NSArray* sToolbarDefaults = nil;
return nil;
}
+- (NSUndoManager *)windowWillReturnUndoManager:(NSWindow *)sender
+{
+ return [[BookmarkManager sharedBookmarkManager] undoManager];
+}
- (IBAction)reloadWithNewCharset:(NSString*)charset
{
@@ -2618,10 +2594,10 @@ static NSArray* sToolbarDefaults = nil;
// cancel all pending loads. safari does this, i think we should too
[self stopAllPendingLoads];
- [mBookmarksController selectLastContainer];
+ [mBookmarkViewController selectLastContainer];
// set focus to appropriate area of bm manager
- [mBookmarksController focus];
+ [mBookmarkViewController focus];
}
else {
CHBrowserView* browserView = [mBrowserView getBrowserView];
diff --git a/camino/src/browser/BrowserWrapper.h b/camino/src/browser/BrowserWrapper.h
index 5834350d25c1..43bba2c0d97e 100644
--- a/camino/src/browser/BrowserWrapper.h
+++ b/camino/src/browser/BrowserWrapper.h
@@ -80,8 +80,6 @@ class nsISupportsArray;
BOOL mOffline;
BOOL mListenersAttached; // We hook up our click and context menu listeners lazily.
// If we never become the primary view, we don't bother creating the listeners.
- BOOL mIsBookmarksImport; // This view was created for the purpose of importing bookmarks. Upon
- // completion, we need to do the import and then destroy ourselves.
BOOL mActivateOnLoad; // If set, activate the browser view when loading starts.
}
@@ -111,8 +109,6 @@ class nsISupportsArray;
- (NSWindow*)getNativeWindow;
- (NSMenu*)getContextMenu;
-- (void)setIsBookmarksImport:(BOOL)aIsImport;
-
- (void)getTitle:(NSString **)outTitle andHref:(NSString**)outHrefString;
// CHBrowserListener messages
diff --git a/camino/src/browser/BrowserWrapper.mm b/camino/src/browser/BrowserWrapper.mm
index e015d91f11fe..6bb6eda45e3a 100644
--- a/camino/src/browser/BrowserWrapper.mm
+++ b/camino/src/browser/BrowserWrapper.mm
@@ -40,7 +40,7 @@
#import "PreferenceManager.h"
#import "BrowserWrapper.h"
#import "BrowserWindowController.h"
-#import "BookmarksService.h"
+#import "BookmarksClient.h"
#import "SiteIconProvider.h"
#import "BrowserTabViewItem.h"
#import "ToolTip.h"
@@ -92,7 +92,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged";
{
mTabItem = aTab;
mWindow = aWindow;
- mIsBookmarksImport = NO;
return [self initWithFrame: NSZeroRect];
}
@@ -388,26 +387,17 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged";
mProgress = 1.0;
mIsBusy = NO;
- // need to check succeeded here because for a charset-induced reload,
- // this can get called initially with a failure code.
- if (mIsBookmarksImport && succeeded)
- {
- nsCOMPtr domWindow;
- nsCOMPtr webBrowser = getter_AddRefs([mBrowserView getWebBrowser]);
- webBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
- if (domWindow)
- {
- nsCOMPtr domDocument;
- domWindow->GetDocument(getter_AddRefs(domDocument));
- if (domDocument)
- BookmarksService::ImportBookmarks(domDocument);
- }
- [self windowClosed];
- [self removeFromSuperview];
- }
-
if (mWindowController)
[mWindowController loadingDone];
+ // send a little love to the bookmarks
+ NSString *urlString = nil;
+ NSString *titleString = nil;
+ [self getTitle:&titleString andHref:&urlString];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:0] forKey:URLLoadSuccessKey];
+ if (urlString && ![urlString isEqualToString:@"about:blank"]) {
+ NSNotification *note = [NSNotification notificationWithName:URLLoadNotification object:urlString userInfo:userInfo];
+ [[NSNotificationQueue defaultQueue] enqueueNotification:note postingStyle:NSPostWhenIdle];
+ }
}
- (void)onProgressChange:(int)currentBytes outOf:(int)maxBytes
@@ -438,7 +428,7 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged";
// if the favicon uri has changed, fire off favicon load. When it completes, our
// imageLoadedNotification selector gets called.
if (![faviconURI isEqualToString:mSiteIconURI])
- siteIconLoadInitiated = [faviconProvider loadFavoriteIcon:self forURI:urlSpec withUserData:nil allowNetwork:YES];
+ siteIconLoadInitiated = [faviconProvider loadFavoriteIcon:self forURI:urlSpec allowNetwork:YES];
}
else
{
@@ -675,11 +665,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged";
*outTitle = [NSString stringWithString:*outHrefString];
}
--(void)setIsBookmarksImport:(BOOL)aIsImport
-{
- mIsBookmarksImport = aIsImport;
-}
-
- (void)offlineModeChanged: (NSNotification*)aNotification
{
nsCOMPtr ioService(do_GetService(ioServiceContractID));
@@ -827,10 +812,6 @@ const NSString* kOfflineNotificationName = @"offlineModeChanged";
if (resetTabIcon || ![tabItem tabIcon])
[tabItem setTabIcon:mSiteIconImage isDraggable:tabIconDraggable];
}
-
- // make sure any bookmark items that use this favicon uri are updated
- if (inSiteIcon)
- [[BookmarksManager sharedBookmarksManager] updateProxyImage:inSiteIcon forSiteIcon:inSiteIconURI];
}
- (void)registerNotificationListener
diff --git a/camino/src/browser/PageProxyIcon.mm b/camino/src/browser/PageProxyIcon.mm
index 23d5c03c2942..89437fd6596f 100644
--- a/camino/src/browser/PageProxyIcon.mm
+++ b/camino/src/browser/PageProxyIcon.mm
@@ -23,10 +23,9 @@
#import "NSString+Utils.h"
#import "NSPasteboard+Utils.h"
-
+#import "BrowserWindowController.h"
#import "PageProxyIcon.h"
-#import "BookmarksService.h"
#import "MainController.h"
#include "nsCRT.h"
diff --git a/camino/src/browser/SiteIconProvider.h b/camino/src/browser/SiteIconProvider.h
index 00add5f6bdc2..7d2868f1bb96 100644
--- a/camino/src/browser/SiteIconProvider.h
+++ b/camino/src/browser/SiteIconProvider.h
@@ -49,6 +49,7 @@ class NeckoCacheHelper;
@interface SiteIconProvider : NSObject
{
NeckoCacheHelper* mMissedIconsCacheHelper;
+ NSMutableDictionary *mRequestDict;
}
+ (SiteIconProvider*)sharedFavoriteIconProvider;
@@ -64,7 +65,7 @@ class NeckoCacheHelper;
// This method returns YES if the uri request was dispatched (i.e. if we know
// that we've looked for, and failed to find, this icon before). If it returns
// YES, then the 'SiteIconLoadNotificationName' notification will be sent out.
-// userData is not retained.
-- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI withUserData:(id)userData allowNetwork:(BOOL)inAllowNetwork;
+
+- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork;
@end
diff --git a/camino/src/browser/SiteIconProvider.mm b/camino/src/browser/SiteIconProvider.mm
index ce89f0cf405a..9748fee0a9c1 100644
--- a/camino/src/browser/SiteIconProvider.mm
+++ b/camino/src/browser/SiteIconProvider.mm
@@ -211,6 +211,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
if ((self = [super init]))
{
mMissedIconsCacheHelper = new NeckoCacheHelper("Favicon", "Missed");
+ mRequestDict = [[NSMutableDictionary alloc] initWithCapacity:5];
nsresult rv = mMissedIconsCacheHelper->Init("MissedIconsCache");
if (NS_FAILED(rv)) {
delete mMissedIconsCacheHelper;
@@ -224,6 +225,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
- (void)dealloc
{
delete mMissedIconsCacheHelper;
+ [mRequestDict release];
[super dealloc];
}
@@ -246,7 +248,7 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
return inCache;
}
-- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI withUserData:(id)userData allowNetwork:(BOOL)inAllowNetwork
+- (BOOL)loadFavoriteIcon:(id)sender forURI:(NSString *)inURI allowNetwork:(BOOL)inAllowNetwork
{
// look for a favicon
nsAutoString uriString;
@@ -264,9 +266,10 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
{
return NO;
}
-
+ // preserve requesting URI for later notification
+ [mRequestDict setObject:inURI forKey:faviconString];
RemoteDataProvider* dataProvider = [RemoteDataProvider sharedRemoteDataProvider];
- return [dataProvider loadURI:faviconString forTarget:sender withListener:self withUserData:userData allowNetworking:inAllowNetwork];
+ return [dataProvider loadURI:faviconString forTarget:sender withListener:self withUserData:nil allowNetworking:inAllowNetwork];
}
#define SITE_ICON_EXPIRATION_SECONDS (60 * 60 * 24 * 7) // 1 week
@@ -304,17 +307,23 @@ static nsresult MakeFaviconURIFromURI(const nsAString& inURIString, nsAString& o
[faviconImage setScalesWhenResized:YES];
[faviconImage setSize:NSMakeSize(16, 16)];
}
+ // figure out what URL triggered this favicon request
+
+ NSString *requestURL = [mRequestDict objectForKey:inURI];
+ if (!requestURL)
+ requestURL = [NSString string];
// we always send out the notification, so that clients know
// about failed requests
NSDictionary* notificationData = [NSDictionary dictionaryWithObjectsAndKeys:
inURI, SiteIconLoadURIKey,
faviconImage, SiteIconLoadImageKey, // may be nil
- userData, SiteIconLoadUserDataKey,
+ requestURL, SiteIconLoadUserDataKey,
nil];
-
- [[NSNotificationCenter defaultCenter] postNotificationName: SiteIconLoadNotificationName
- object:target userInfo:notificationData];
+ NSNotification *note = [NSNotification notificationWithName: SiteIconLoadNotificationName object:target userInfo:notificationData];
+ [[NSNotificationQueue defaultQueue] enqueueNotification: note postingStyle:NSPostWhenIdle];
+ // cleanup our key holder
+ [mRequestDict removeObjectForKey:inURI];
}
#pragma mark -
diff --git a/camino/src/embedding/CHBrowserListener.mm b/camino/src/embedding/CHBrowserListener.mm
index 2c2cd1b73c62..088ef209f113 100644
--- a/camino/src/embedding/CHBrowserListener.mm
+++ b/camino/src/embedding/CHBrowserListener.mm
@@ -45,15 +45,16 @@
#include "nsIURI.h"
#include "nsIDOMWindow.h"
//#include "nsIWidget.h"
-
// XPCOM and String includes
+#include "nsIRequest.h"
#include "nsCRT.h"
#include "nsString.h"
#include "nsCOMPtr.h"
#include "nsIDOMPopupBlockedEvent.h"
-
+#include "nsNetError.h"
#import "CHBrowserView.h"
-
+#import "BookmarksClient.h"
+#import "Bookmark.h"
#include "CHBrowserListener.h"
@@ -529,15 +530,36 @@ CHBrowserListener::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequ
{
NSEnumerator* enumerator = [mListeners objectEnumerator];
id obj;
-
if (aStateFlags & nsIWebProgressListener::STATE_IS_NETWORK) {
if (aStateFlags & nsIWebProgressListener::STATE_START) {
while ((obj = [enumerator nextObject]))
[obj onLoadingStarted];
}
else if (aStateFlags & nsIWebProgressListener::STATE_STOP) {
- while ((obj = [enumerator nextObject]))
+ // we need to pass along errors like this so our bookmarks know
+ // if they're still OK
+ NSNumber *errNum = nil;
+ if ((aStatus == NS_ERROR_UNKNOWN_HOST ||
+ aStatus == NS_ERROR_CONNECTION_REFUSED ||
+ aStatus == NS_ERROR_UNKNOWN_PROXY_HOST ||
+ aStatus == NS_ERROR_PROXY_CONNECTION_REFUSED))
+ errNum = [NSNumber numberWithUnsignedInt:kBookmarkServerErrorStatus];
+ else if (( aStatus == NS_ERROR_MALFORMED_URI ||
+ aStatus == NS_ERROR_UNKNOWN_PROTOCOL))
+ errNum = [NSNumber numberWithUnsignedInt:kBookmarkBrokenLinkStatus];
+ else if ((aStatus == NS_BINDING_REDIRECTED))
+ errNum = [NSNumber numberWithUnsignedInt:kBookmarkMovedLinkStatus];
+ if (errNum) {
+ nsCAutoString uriString;
+ aRequest->GetName(uriString);
+ NSString *fixedURL = [NSString stringWithCString:uriString.get()];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:errNum forKey:URLLoadSuccessKey];
+ NSNotification *note = [NSNotification notificationWithName:URLLoadNotification object:fixedURL userInfo:userInfo];
+ [[NSNotificationQueue defaultQueue] enqueueNotification:note postingStyle:NSPostWhenIdle];
+ }
+ while ((obj = [enumerator nextObject])) {
[obj onLoadingCompleted:(NS_SUCCEEDED(aStatus))];
+ }
}
}
diff --git a/camino/src/extensions/ExtendedOutlineView.h b/camino/src/extensions/ExtendedOutlineView.h
index 2b9ec4b02a91..6f3c5fb9dbab 100644
--- a/camino/src/extensions/ExtendedOutlineView.h
+++ b/camino/src/extensions/ExtendedOutlineView.h
@@ -1,26 +1,42 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 (the "License"); you may not use this file
-* except in compliance with the License. You may obtain a copy of
-* the License at http://www.mozilla.org/MPL/
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
*
-* Software distributed under the License is distributed on an "AS
-* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-* implied. See the License for the specific language governing
-* rights and limitations under the License.
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
*
-* The Original Code is the Mozilla browser.
+* The Original Code is mozilla.org code.
*
-* The Initial Developer of the Original Code is Netscape
-* Communications Corporation. Portions created by Netscape are
-* Copyright (C) 2002 Netscape Communications Corporation. All
-* Rights Reserved.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Hyatt (Original Author)
* Max Horn (Context menu & tooltip code)
-*/
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
#import
@@ -43,7 +59,8 @@
-(void)setDeleteAction: (SEL)deleteAction;
-(SEL)deleteAction;
--(void)setDelegate:(id)anObject;
+-(void)_editItem:(id)item;
+-(void)_cancelEditItem;
@end
diff --git a/camino/src/extensions/ExtendedOutlineView.mm b/camino/src/extensions/ExtendedOutlineView.mm
index 7c10847a529a..f928c05027bc 100644
--- a/camino/src/extensions/ExtendedOutlineView.mm
+++ b/camino/src/extensions/ExtendedOutlineView.mm
@@ -1,26 +1,42 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
-* The contents of this file are subject to the Mozilla Public
-* License Version 1.1 (the "License"); you may not use this file
-* except in compliance with the License. You may obtain a copy of
-* the License at http://www.mozilla.org/MPL/
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
*
-* Software distributed under the License is distributed on an "AS
-* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
-* implied. See the License for the specific language governing
-* rights and limitations under the License.
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
*
-* The Original Code is the Mozilla browser.
+* The Original Code is mozilla.org code.
*
-* The Initial Developer of the Original Code is Netscape
-* Communications Corporation. Portions created by Netscape are
-* Copyright (C) 2002 Netscape Communications Corporation. All
-* Rights Reserved.
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* David Hyatt (Original Author)
* Max Horn (Context menu, tooltip code, and editing)
-*/
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
#import "ExtendedOutlineView.h"
@@ -186,7 +202,9 @@
if ([delegate respondsToSelector:@selector(outlineView:contextMenuForItem:)])
return [delegate outlineView:self contextMenuForItem:item];
- }
+ } else
+ // no item, no context menu
+ return nil;
}
// Just return the default context menu
@@ -283,7 +301,7 @@
// Little trick: if editing was already in progress, then the field editor
// will be first responder. For our purposes this is the same as if we
// were first responder, so pretend it were so.
- if (oldEditRow >= 0)
+ if (oldEditRow > -1)
wasFirstResponder = YES;
// If we already were first responder of the main window, and the click was
diff --git a/camino/src/extensions/ExtendedTableView.h b/camino/src/extensions/ExtendedTableView.h
new file mode 100644
index 000000000000..f9ae258f398b
--- /dev/null
+++ b/camino/src/extensions/ExtendedTableView.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+
+
+@interface ExtendedTableView : NSTableView {
+ SEL mDeleteAction;
+}
+
+-(void)setDeleteAction: (SEL)deleteAction;
+-(SEL)deleteAction;
+
+@end
diff --git a/camino/src/extensions/ExtendedTableView.mm b/camino/src/extensions/ExtendedTableView.mm
new file mode 100644
index 000000000000..0318676fc030
--- /dev/null
+++ b/camino/src/extensions/ExtendedTableView.mm
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: NPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Netscape Public License
+* Version 1.1 (the "License"); you may not use this file except in
+* compliance with the License. You may obtain a copy of the License at
+* http://www.mozilla.org/NPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is mozilla.org code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the NPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the NPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "ExtendedTableView.h"
+
+
+@implementation ExtendedTableView
+
+-(void)setDeleteAction: (SEL)aDeleteAction
+{
+ mDeleteAction = aDeleteAction;
+}
+
+-(SEL)deleteAction
+{
+ return mDeleteAction;
+}
+
+-(void)keyDown:(NSEvent*)aEvent
+{
+ const unichar kForwardDeleteChar = 0xf728; // couldn't find this in any cocoa header
+
+ // check each char in the event array. it should be just 1 char, but
+ // just in case we use a loop.
+ int len = [[aEvent characters] length];
+ for ( int i = 0; i < len; ++i ) {
+ unichar c = [[aEvent characters] characterAtIndex:i];
+
+ // Check for a certain set of special keys.
+ if (c == NSDeleteCharacter || c == NSBackspaceCharacter || c == kForwardDeleteChar) {
+ // delete the bookmark
+ if (mDeleteAction)
+ [NSApp sendAction: mDeleteAction to: [self target] from: self];
+ return;
+ }
+ }
+ return [super keyDown: aEvent];
+}
+
+-(NSMenu *)menuForEvent:(NSEvent *)theEvent
+{
+ int rowIndex;
+ NSPoint point;
+ point = [self convertPoint:[theEvent locationInWindow] fromView:nil];
+ rowIndex = [self rowAtPoint:point];
+ if (rowIndex >= 0) {
+ [self abortEditing];
+ id delegate = [self delegate];
+ if (![self isRowSelected:rowIndex]) {
+ if ([delegate respondsToSelector:@selector(tableView:shouldSelectRow:)]) {
+ if (![delegate tableView:self shouldSelectRow:rowIndex])
+ return nil;
+ }
+ }
+ if ([delegate respondsToSelector:@selector(tableView:contextMenuForRow:)])
+ return [delegate tableView:self contextMenuForRow:rowIndex];
+ }
+ return nil;
+}
+
+@end
diff --git a/camino/src/extensions/NSArray+Utils.h b/camino/src/extensions/NSArray+Utils.h
new file mode 100644
index 000000000000..6ad052ecc59d
--- /dev/null
+++ b/camino/src/extensions/NSArray+Utils.h
@@ -0,0 +1,46 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is Chimera code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import
+
+
+@interface NSArray (ChimeraArrayUtils)
+// Packages pointers to BookmarkItems into NSData objects for pasteboard
++(NSArray *)dataArrayFromPointerArrayForMozBookmarkDrop:(NSArray *)dragArray;
+// Converts NSData objects on a pasteboard back into pointers to BookmarkItems.
++(NSArray *)pointerArrayFromDataArrayForMozBookmarkDrop:(NSArray *)dragArray;
+@end
diff --git a/camino/src/extensions/NSArray+Utils.mm b/camino/src/extensions/NSArray+Utils.mm
new file mode 100644
index 000000000000..f03fc4a8aefb
--- /dev/null
+++ b/camino/src/extensions/NSArray+Utils.mm
@@ -0,0 +1,66 @@
+/* ***** BEGIN LICENSE BLOCK *****
+* Version: MPL 1.1/GPL 2.0/LGPL 2.1
+*
+* The contents of this file are subject to the Mozilla Public License Version
+* 1.1 (the "License"); you may not use this file except in compliance with
+* the License. You may obtain a copy of the License at
+* http://www.mozilla.org/MPL/
+*
+* Software distributed under the License is distributed on an "AS IS" basis,
+* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+* for the specific language governing rights and limitations under the
+* License.
+*
+* The Original Code is Chimera code.
+*
+* The Initial Developer of the Original Code is
+* Netscape Communications Corporation.
+* Portions created by the Initial Developer are Copyright (C) 2002
+* the Initial Developer. All Rights Reserved.
+*
+* Contributor(s):
+* David Haas
+*
+* Alternatively, the contents of this file may be used under the terms of
+* either the GNU General Public License Version 2 or later (the "GPL"), or
+* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+* in which case the provisions of the GPL or the LGPL are applicable instead
+* of those above. If you wish to allow use of your version of this file only
+* under the terms of either the GPL or the LGPL, and not to allow others to
+* use your version of this file under the terms of the MPL, indicate your
+* decision by deleting the provisions above and replace them with the notice
+* and other provisions required by the GPL or the LGPL. If you do not delete
+* the provisions above, a recipient may use your version of this file under
+* the terms of any one of the MPL, the GPL or the LGPL.
+*
+* ***** END LICENSE BLOCK ***** */
+
+#import "NSArray+Utils.h"
+
+
+@implementation NSArray (ChimeraArrayUtils)
+
++(NSArray *)pointerArrayFromDataArrayForMozBookmarkDrop:(NSArray *)dragArray
+{
+ NSMutableArray *fixedArray = [NSMutableArray arrayWithCapacity:[dragArray count]];
+ NSEnumerator *enumerator = [dragArray objectEnumerator];
+ id aDataToPointer, aPointerToBookmark;
+ while ((aDataToPointer = [enumerator nextObject])) {
+ [aDataToPointer getBytes:&aPointerToBookmark length:(sizeof(id))];
+ [fixedArray addObject:aPointerToBookmark];
+ }
+ return [NSArray arrayWithArray:fixedArray];
+}
+
++(NSArray *)dataArrayFromPointerArrayForMozBookmarkDrop:(NSArray *)dragArray
+{
+ NSMutableArray *dataArray = [NSMutableArray arrayWithCapacity:[dragArray count]];
+ NSEnumerator *enumerator = [dragArray objectEnumerator];
+ id aBookmark;
+ while ((aBookmark = [enumerator nextObject]))
+ [dataArray addObject:[NSData dataWithBytes:&aBookmark length:(sizeof(id))]];
+ return [NSArray arrayWithArray:dataArray];
+}
+
+
+@end
diff --git a/camino/src/extensions/NSString+Utils.h b/camino/src/extensions/NSString+Utils.h
index a5b6b8afb2ad..c3808c9720eb 100644
--- a/camino/src/extensions/NSString+Utils.h
+++ b/camino/src/extensions/NSString+Utils.h
@@ -52,6 +52,7 @@ typedef enum
@interface NSString (ChimeraStringUtils)
+ (id)ellipsisString;
++ (id)escapedURLString:(NSString *)unescapedString;
+ (id)stringWithPRUnichars:(const PRUnichar*)inString;
+ (id)stringWith_nsAString:(const nsAString&)inString;
- (void)assignTo_nsAString:(nsAString&)ioString;
@@ -60,6 +61,9 @@ typedef enum
- (NSString *)stringByReplacingCharactersInSet:(NSCharacterSet*)characterSet withString:(NSString*)string;
- (NSString *)stringByTruncatingTo:(unsigned int)maxCharacters at:(ETruncationType)truncationType;
- (NSString *)stringByTrimmingWhitespace;
+-(NSString *)stringByRemovingAmpEscapes;
+-(NSString *)stringByAddingAmpEscapes;
+-(NSString *)stripWWW;
// allocate a new unicode buffer with the contents of the current string. Caller
// is responsible for freeing the buffer.
diff --git a/camino/src/extensions/NSString+Utils.mm b/camino/src/extensions/NSString+Utils.mm
index 9ebfef07142c..3b93eacb6398 100644
--- a/camino/src/extensions/NSString+Utils.mm
+++ b/camino/src/extensions/NSString+Utils.mm
@@ -20,6 +20,7 @@
*
* Contributor(s):
* Simon Fraser
+ * David Haas
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@@ -59,6 +60,12 @@
return sEllipsisString;
}
++ (id)escapedURLString:(NSString *)unescapedString
+{
+ NSString *escapedString = (NSString *) CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)unescapedString, NULL, NULL, kCFStringEncodingUTF8);
+ return [escapedString autorelease];
+}
+
+ (id)stringWithPRUnichars:(const PRUnichar*)inString
{
if (inString)
@@ -167,6 +174,91 @@
return retStr;
}
+-(NSString *)stringByRemovingAmpEscapes
+{
+ NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self];
+ //10.2 and later
+ if ([dirtyStringMutant respondsToSelector:@selector(replaceOccurrencesOfString:withString:options:range:)]){
+ [dirtyStringMutant replaceOccurrencesOfString:@"&"withString:@"&" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@"""withString:@"\"" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@"<"withString:@"<" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@">"withString:@">" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@"—"withString:@"-" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ return [dirtyStringMutant stringByRemovingCharactersInSet:[NSCharacterSet controlCharacterSet]];
+ }
+
+ // 10.1.
+ NSRange ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"&"];
+ ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@""" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"\""];
+ ampRange = [dirtyStringMutant rangeOfString:@""" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"<"];
+ ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@">"];
+ ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@"—" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"-"];
+ ampRange = [dirtyStringMutant rangeOfString:@"—" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+
+ return [dirtyStringMutant stringByRemovingCharactersInSet:[NSCharacterSet controlCharacterSet]];
+}
+
+-(NSString *)stringByAddingAmpEscapes
+{
+ NSMutableString* dirtyStringMutant = [NSMutableString stringWithString:self];
+ //10.2 & later
+ if ([dirtyStringMutant respondsToSelector:@selector(replaceOccurrencesOfString:withString:options:range:)]){
+ [dirtyStringMutant replaceOccurrencesOfString:@"&"withString:@"&" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@"\""withString:@""" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@"<"withString:@"<" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ [dirtyStringMutant replaceOccurrencesOfString:@">"withString:@">" options:NSLiteralSearch range:NSMakeRange(0,[dirtyStringMutant length])];
+ return [NSString stringWithString:dirtyStringMutant];
+ }
+ // 10.1.
+ NSRange ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"&"];
+ ampRange = [dirtyStringMutant rangeOfString:@"&" options:NSLiteralSearch range:NSMakeRange(ampRange.location+1,[dirtyStringMutant length]-ampRange.location-1)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@"\"" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"""];
+ ampRange = [dirtyStringMutant rangeOfString:@"\"" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@"<"];
+ ampRange = [dirtyStringMutant rangeOfString:@"<" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch];
+ while (ampRange.location != NSNotFound) {
+ [dirtyStringMutant replaceCharactersInRange:ampRange withString:@">"];
+ ampRange = [dirtyStringMutant rangeOfString:@">" options:NSLiteralSearch range:NSMakeRange(ampRange.location,[dirtyStringMutant length]-ampRange.location)];
+ }
+ return [NSString stringWithString:dirtyStringMutant];
+}
+
+-(NSString *)stripWWW
+{
+ if ([self hasPrefix:@"www."] && ([self length]>4))
+ return [self substringFromIndex:4];
+ return self;
+}
+
@end
diff --git a/camino/src/extensions/RDFOutlineViewDataSource.h b/camino/src/extensions/RDFOutlineViewDataSource.h
index 596fbae51a12..fb8829a14782 100644
--- a/camino/src/extensions/RDFOutlineViewDataSource.h
+++ b/camino/src/extensions/RDFOutlineViewDataSource.h
@@ -36,15 +36,14 @@
*
* ***** END LICENSE BLOCK ***** */
-#import
#import
-#import "ExtendedOutlineView.h"
class nsIRDFDataSource;
class nsIRDFContainer;
class nsIRDFContainerUtils;
class nsIRDFResource;
class nsIRDFService;
+@class ExtendedOutlineView;
// RDF Resource Wrapper to make the Outline View happy
diff --git a/camino/src/extensions/RDFOutlineViewDataSource.mm b/camino/src/extensions/RDFOutlineViewDataSource.mm
index c3dbd4e58ed5..3c7d1e72a852 100644
--- a/camino/src/extensions/RDFOutlineViewDataSource.mm
+++ b/camino/src/extensions/RDFOutlineViewDataSource.mm
@@ -40,6 +40,7 @@
#import "RDFOutlineViewDataSource.h"
#import "CHBrowserService.h"
+#import "ExtendedOutlineView.h"
#include "nsCRT.h"
#include "nsIRDFDataSource.h"
@@ -333,8 +334,12 @@
// The table column's identifier is the last part of the RDF Resource URI of the property
// being displayed in that column, e.g. "http://home.netscape.com/NC-rdf#Name"
+ // little hack inserted until history moves to new bookmark format
+ NSString *identifier = [aTableColumn identifier];
+ if ([identifier isEqualToString:@"title"])
+ identifier = [NSString stringWithString:@"Name"];
NSString* columnPropertyURI = [NSString stringWithFormat:@"http://home.netscape.com/NC-rdf#%@",
- [aTableColumn identifier]];
+ identifier];
NSString* propString = [self getPropertyString:columnPropertyURI forItem:aItem];
return [self createCellContents:propString withColumn:columnPropertyURI byItem:aItem];
diff --git a/camino/src/history/HistoryDataSource.h b/camino/src/history/HistoryDataSource.h
index 0c3180ffaac9..e40d2e4435c9 100644
--- a/camino/src/history/HistoryDataSource.h
+++ b/camino/src/history/HistoryDataSource.h
@@ -36,8 +36,6 @@
*
* ***** END LICENSE BLOCK ***** */
-#import
-
#import "RDFOutlineViewDataSource.h"
class nsAString;
diff --git a/camino/src/history/HistoryDataSource.mm b/camino/src/history/HistoryDataSource.mm
index 702f4b890821..bd6359fb8248 100644
--- a/camino/src/history/HistoryDataSource.mm
+++ b/camino/src/history/HistoryDataSource.mm
@@ -41,6 +41,7 @@
#import "BrowserWindowController.h"
#import "HistoryDataSource.h"
#import "CHBrowserView.h"
+#import "ExtendedOutlineView.h"
#include "nsIRDFService.h"
#include "nsIRDFDataSource.h"
@@ -265,7 +266,8 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
//
-(id) createCellContents:(NSString*)inValue withColumn:(NSString*)inColumn byItem:(id) inItem
{
- if ([inValue length] == 0)
+ NSString *fragment = [[NSURL URLWithString:inColumn] fragment];
+ if (([inValue length] == 0) && [fragment isEqualToString:@"Name"])
inValue = [self getPropertyString:@"http://home.netscape.com/NC-rdf#URL" forItem:inItem];
return inValue;
}
@@ -430,7 +432,7 @@ HistoryDataSourceObserver::OnChange(nsIRDFDataSource*, nsIRDFResource*,
- (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(NSCell *)inCell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
{
// set the image on the name column. the url column doesn't have an image.
- if ([[tableColumn identifier] isEqualToString: @"Name"]) {
+ if ([[tableColumn identifier] isEqualToString: @"title"]) {
if ( [outlineView isExpandable: item] )
[inCell setImage:[NSImage imageNamed:@"folder"]];
else
diff --git a/camino/src/rendezvous/NetworkServices.h b/camino/src/rendezvous/NetworkServices.h
index 9094f2f00b3d..3bde5dd3df39 100644
--- a/camino/src/rendezvous/NetworkServices.h
+++ b/camino/src/rendezvous/NetworkServices.h
@@ -45,9 +45,9 @@
@protocol NetworkServicesClient
-- (void)availableServicesChanged:(NetworkServices*)servicesProvider;
-- (void)serviceResolved:(int)serviceID withURL:(NSString*)url;
-- (void)serviceResolutionFailed:(int)serviceID;
+- (void)availableServicesChanged:(NSNotification *)note;
+- (void)serviceResolved:(NSNotification *)note;
+- (void)serviceResolutionFailed:(NSNotification *)note;
@end
@@ -60,22 +60,27 @@
NSNetServiceBrowser* mFtpBrowser;
int mCurServiceID; // unique ID for each service
- NSMutableDictionary* mNetworkServices; // services keyed by ID
+ NSMutableDictionary* mNetworkServices; // services keyed by ID
- NSMutableArray* mClients; // array of id
+ NSMutableDictionary* mClients; // dictionary of cliend id's for a request
}
++ (id)sharedNetworkServices;
++ (void)shutdownNetworkServices;
- (void)startServices;
- (void)stopServices;
-
-- (void)registerClient:(id)client;
-- (void)unregisterClient:(id)client;
-
-- (void)attemptResolveService:(int)serviceID;
+- (void)attemptResolveService:(int)serviceID forSender:(id)aSender;
- (NSString*)serviceName:(int)serviceID;
- (NSString*)serviceProtocol:(int)serviceID;
-
- (NSEnumerator*)serviceEnumerator;
+// Notifications
+ extern NSString *NetworkServicesAvailableServicesChanged;
+ extern NSString *NetworkServicesResolutionSuccess;
+ extern NSString *NetworkServicesResolutionFailure;
+ extern NSString *NetworkServicesClientKey;
+ extern NSString *NetworkServicesResolvedURLKey;
+ extern NSString *NetworkServicesServiceKey;
+
@end
diff --git a/camino/src/rendezvous/NetworkServices.mm b/camino/src/rendezvous/NetworkServices.mm
index 4c333ac4e31a..c81e1dbe2059 100644
--- a/camino/src/rendezvous/NetworkServices.mm
+++ b/camino/src/rendezvous/NetworkServices.mm
@@ -21,6 +21,7 @@
*
* Contributor(s):
* Simon Fraser
+ * David Haas
*
*
* Alternatively, the contents of this file may be used under the terms of
@@ -51,6 +52,14 @@
@class NSNetServiceBrowser;
+// client notifications
+NSString *NetworkServicesAvailableServicesChanged = @"netserv_asc";
+NSString *NetworkServicesResolutionSuccess = @"netserv_resok";
+NSString *NetworkServicesClientKey = @"netserv_clikey";
+NSString *NetworkServicesResolvedURLKey = @"netserv_urlkey";
+NSString *NetworkServicesResolutionFailure = @"netserv_resbad";
+NSString *NetworkServicesServiceKey = @"netserv_srvkey";
+
@interface NetworkServices(Private)
@@ -59,25 +68,39 @@
- (void)notifyClientsOfServicesChange;
-- (void)notifyClientsOfServiceResolution:(int)serviceID withURL:(NSString*)url;
-- (void)notifyClientsOfServiceResolutionFailure:(int)serviceID;
+- (void)notifyClientsOfServiceResolution:(NSNetService *)aService withURL:(NSString*)url;
+- (void)notifyClientsOfServiceResolutionFailure:(NSNetService *)aService;
- (void)serviceAppeared:(NSNetService*)service;
- (void)serviceDisappeared:(NSNetService*)service;
- (void)serviceResolved:(NSNetService*)service;
-- (int)getServiceID:(NSNetService*)service;
-
@end
@implementation NetworkServices
+static NetworkServices* gNetworkServices = nil;
+
++(id)sharedNetworkServices
+{
+ if (!gNetworkServices)
+ gNetworkServices = [[NetworkServices alloc] init];
+ return gNetworkServices;
+}
+
++(void)shutdownNetworkServices
+{
+ [gNetworkServices release]; //this'll put the hammer on things
+}
+
- (id)init
{
if ((self = [super init]))
{
mNetworkServices = [[NSMutableDictionary alloc] initWithCapacity:5];
+ mClients = [[NSMutableDictionary alloc] initWithCapacity:2];
mCurServiceID = 0;
+ [self setupNetworkBrowsers];
}
return self;
@@ -88,6 +111,8 @@
[self stopServices];
[mNetworkServices release];
[mClients release];
+ if (self == gNetworkServices)
+ gNetworkServices = nil;
[super dealloc];
}
@@ -120,20 +145,6 @@
}
}
-- (void)registerClient:(id)client
-{
- if (!mClients)
- mClients = [[NSMutableArray alloc] initWithCapacity:2];
-
- if ([mClients indexOfObject:client] == NSNotFound)
- [mClients addObject:client];
-}
-
-- (void)unregisterClient:(id)client
-{
- [mClients removeObject:client];
-}
-
- (NSString*)serviceName:(int)serviceID
{
NSNetService* service = [mNetworkServices objectForKey:[NSNumber numberWithInt:serviceID]];
@@ -150,11 +161,12 @@
return [service type];
}
-- (void)attemptResolveService:(int)serviceID
+- (void)attemptResolveService:(int)serviceID forSender:(id)aSender;
{
- NSNetService* service = [mNetworkServices objectForKey:[NSNumber numberWithInt:serviceID]];
+ NSNumber *serviceKey = [NSNumber numberWithInt:serviceID];
+ NSNetService* service = [mNetworkServices objectForKey:serviceKey];
if (!service) return;
-
+ [mClients setObject:aSender forKey:serviceKey];
[service resolve];
}
@@ -165,13 +177,6 @@
#pragma mark -
-- (int)getServiceID:(NSNetService*)service
-{
- NSArray* serviceKeys = [mNetworkServices allKeysForObject:service];
- // there should only ever be one key
- return [[serviceKeys objectAtIndex:0] intValue];
-}
-
- (void)setupNetworkBrowsers
{
mHttpBrowser = [[NSNetServiceBrowser alloc] init];
@@ -190,33 +195,27 @@
- (void)notifyClientsOfServicesChange
{
- for (unsigned int i = 0; i < [mClients count]; i ++)
- {
- id client = [mClients objectAtIndex:i];
- [client availableServicesChanged:self];
- }
+ NSNotification *note = [NSNotification notificationWithName:NetworkServicesAvailableServicesChanged object:self userInfo:nil];
+ NSNotificationQueue *nc = [NSNotificationQueue defaultQueue];
+ [nc enqueueNotification:note postingStyle:NSPostWhenIdle coalesceMask:NSNotificationCoalescingOnName forModes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
}
-- (void)notifyClientsOfServiceResolution:(int)serviceID withURL:(NSString*)url
+- (void)notifyClientsOfServiceResolution:(NSNetService *)aService withURL:(NSString*)url
{
- // ldeally we'd keep track of which client issued the attemptResolveService
- // request, and only give it the callback. But we don't do that yet.
- for (unsigned int i = 0; i < [mClients count]; i ++)
- {
- id client = [mClients objectAtIndex:i];
- [client serviceResolved:serviceID withURL:url];
- }
+ NSNumber *serviceKey = [[mNetworkServices allKeysForObject:aService] objectAtIndex:0];
+ id aClient = [mClients objectForKey:serviceKey];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:aClient,NetworkServicesClientKey,url,NetworkServicesResolvedURLKey,[aService name],NetworkServicesServiceKey,nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:NetworkServicesResolutionSuccess object:self userInfo:userInfo];
+ [mClients removeObjectForKey:aService];
}
-- (void)notifyClientsOfServiceResolutionFailure:(int)serviceID
+- (void)notifyClientsOfServiceResolutionFailure:(NSNetService *)aService
{
- // ldeally we'd keep track of which client issued the attemptResolveService
- // request, and only give it the callback. But we don't do that yet.
- for (unsigned int i = 0; i < [mClients count]; i ++)
- {
- id client = [mClients objectAtIndex:i];
- [client serviceResolutionFailed:serviceID];
- }
+ NSNumber *serviceKey = [[mNetworkServices allKeysForObject:aService] objectAtIndex:0];
+ id aClient = [mClients objectForKey:serviceKey];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:aClient, NetworkServicesClientKey, [aService name],NetworkServicesServiceKey,nil];
+ [[NSNotificationCenter defaultCenter] postNotificationName:NetworkServicesResolutionFailure object:self userInfo:userInfo];
+ [mClients removeObjectForKey:aService];
}
- (void)serviceAppeared:(NSNetService*)service
@@ -365,7 +364,7 @@ static inline u_int ns_get16(u_char* buffer)
else
urlString = [NSString stringWithFormat:@"%s//%s:%u%s", protocol, escapedTarget, port, pathBuffer];
- [self notifyClientsOfServiceResolution:[self getServiceID:netService] withURL:urlString];
+ [self notifyClientsOfServiceResolution:netService withURL:urlString];
return;
}
}
@@ -373,7 +372,7 @@ static inline u_int ns_get16(u_char* buffer)
}
// only get here on failure
- [self notifyClientsOfServiceResolutionFailure:[self getServiceID:netService]];
+ [self notifyClientsOfServiceResolutionFailure:netService];
}
- (void)netServiceDidResolveAddress:(NSNetService *)netService
@@ -385,7 +384,7 @@ static inline u_int ns_get16(u_char* buffer)
- (void)netService:(NSNetService *)sender didNotResolve:(NSDictionary *)errorDict
{
- [self notifyClientsOfServiceResolutionFailure:[self getServiceID:sender]];
+ [self notifyClientsOfServiceResolutionFailure:sender];
// now clear out the state of the service
[sender stop];
}
@@ -401,8 +400,7 @@ static inline u_int ns_get16(u_char* buffer)
{
[self serviceAppeared:aNetService];
[aNetService setDelegate:self];
-
- // rebuild the menu
+ // trigger notification
if (!moreComing)
[self notifyClientsOfServicesChange];
}