diff --git a/camino/src/bookmarks/BookmarkImportDlgController.h b/camino/src/bookmarks/BookmarkImportDlgController.h index 4527a9bb73d..ccfb4ffe12a 100644 --- a/camino/src/bookmarks/BookmarkImportDlgController.h +++ b/camino/src/bookmarks/BookmarkImportDlgController.h @@ -43,6 +43,9 @@ IBOutlet NSPopUpButton* mBrowserListButton; IBOutlet NSButton* mCancelButton; IBOutlet NSButton* mImportButton; + IBOutlet NSProgressIndicator* mImportProgressBar; + IBOutlet NSView* mImportView; + IBOutlet NSView* mProgressView; } -(void) buildAvailableFileList; @@ -51,5 +54,6 @@ -(IBAction) loadOpenPanel:(id)aSender; -(IBAction) nullAction:(id)aSender; -(void) alertSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo; +-(void) finishThreadedImport:(BOOL)success fromFile:(NSString *)aFile; @end diff --git a/camino/src/bookmarks/BookmarkImportDlgController.mm b/camino/src/bookmarks/BookmarkImportDlgController.mm index 904d392939e..f595b2bbddf 100644 --- a/camino/src/bookmarks/BookmarkImportDlgController.mm +++ b/camino/src/bookmarks/BookmarkImportDlgController.mm @@ -47,11 +47,14 @@ -(void) tryAddImportFromBrowser: (NSString *) aBrowserName withBookmarkPath: (NSString *) aPath; -(void) tryOmniWeb5Import; --(void) buildButtonForBrowser:(NSString *) aBrowserName withObject:(id)anObject; +-(void) buildButtonForBrowser:(NSString *) aBrowserName withPathArray:(NSArray *)anArray; -(NSString *) getSaltedBookmarkPathForProfile: (NSString *) aPath; --(void) beginImportFrom:(NSString *) aPath intoFolder:(BookmarkFolder *) aFolder; --(void) beginOmniWeb5ImportFrom:(NSArray *)anArray intoFolder:(BookmarkFolder *)aFolder; --(void) finishImport:(BOOL)success fromFile:(NSString *)aFile intoFolder:(BookmarkFolder*)aFolder; +-(void) beginImportFrom:(NSArray *)aPath withTitles:(NSArray *)anArray; +-(void) beginOmniWeb5ImportFrom:(NSArray *)anArray; +-(void) finishImport:(BOOL)success fromFiles:(NSArray *)anArray; +-(void) finishThreadedImport:(BOOL)success fromFiles:(NSArray *)anArray; +-(void) showProgressView; +-(void) showImportView; @end @@ -61,8 +64,8 @@ -(void) windowDidLoad { + [self showImportView]; [self buildAvailableFileList]; - [[self window] center]; } // Check for common webbrower bookmark files and, if they exist, add import buttons. @@ -107,7 +110,7 @@ NSFileManager *fm = [NSFileManager defaultManager]; NSString *fullPathString = [aPath stringByStandardizingPath]; if ([fm fileExistsAtPath:fullPathString]) { - [self buildButtonForBrowser:aBrowserName withObject:fullPathString]; + [self buildButtonForBrowser:aBrowserName withPathArray:[NSArray arrayWithObject:fullPathString]]; } } @@ -131,7 +134,7 @@ } if ([haveFiles count] > 0) { - [self buildButtonForBrowser:@"OmniWeb 5" withObject:haveFiles]; + [self buildButtonForBrowser:@"OmniWeb 5" withPathArray:haveFiles]; } } @@ -153,13 +156,13 @@ return nil; } --(void) buildButtonForBrowser:(NSString *) aBrowserName withObject:(id)aThingy +-(void) buildButtonForBrowser:(NSString *) aBrowserName withPathArray:(NSArray *)anArray { [mBrowserListButton insertItemWithTitle:aBrowserName atIndex:0]; NSMenuItem *browserItem = [mBrowserListButton itemAtIndex:0]; [browserItem setTarget:self]; [browserItem setAction:@selector(nullAction:)]; - [browserItem setRepresentedObject:aThingy]; + [browserItem setRepresentedObject:anArray]; } // keeps browsers turned on @@ -175,22 +178,18 @@ -(IBAction) import:(id)aSender { - // XXX show a spinner, disable the button, postpone to next event loop (to give UI time to update) 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", @"")]; + titleString = [NSString stringWithString:NSLocalizedString(@"Imported IE Favorites", @"")]; else - titleString = [[NSString alloc] initWithFormat:NSLocalizedString(@"Imported %@ Bookmarks", @""), [selectedItem title]]; - [importFolder setTitle:titleString]; - [titleString release]; + titleString = [NSString stringWithFormat:NSLocalizedString(@"Imported %@ Bookmarks", @""), [selectedItem title]]; // Stupid OmniWeb 5 gets its own import function if ( [[selectedItem title] isEqualToString:@"OmniWeb 5"] ) { - [self beginOmniWeb5ImportFrom:[selectedItem representedObject] intoFolder:importFolder]; + [self beginOmniWeb5ImportFrom:[selectedItem representedObject]]; } else { - [self beginImportFrom:[selectedItem representedObject] intoFolder:importFolder]; + [self beginImportFrom:[selectedItem representedObject] withTitles:[NSArray arrayWithObject:titleString]]; } } @@ -207,75 +206,56 @@ types: array]; if (result == NSOKButton) { NSString *pathToFile = [[openPanel filenames] objectAtIndex:0]; - BookmarkFolder *importFolder = [[[BookmarkManager sharedBookmarkManager] rootBookmarks] addBookmarkFolder]; - [importFolder setTitle:NSLocalizedString(@"Imported Bookmarks",@"Imported Bookmarks")]; - [self beginImportFrom:pathToFile intoFolder:importFolder]; + [self beginImportFrom:[NSArray arrayWithObject:pathToFile] + withTitles:[NSArray arrayWithObject:NSLocalizedString(@"Imported Bookmarks",@"Imported Bookmarks")]]; } } --(void) beginOmniWeb5ImportFrom:(NSArray *)anArray intoFolder:(BookmarkFolder *) aFolder +-(void) beginOmniWeb5ImportFrom:(NSArray *)anArray { - [mCancelButton setEnabled:NO]; - [mImportButton setEnabled:NO]; - NSEnumerator *enumerator = [anArray objectEnumerator]; - + NSMutableArray *titleArray= [NSMutableArray array]; NSString* curFilename = nil; - - BOOL success = TRUE; - NSString *curPath; - while ( success && (curPath = [enumerator nextObject] ) ) + NSString *curPath = nil; + while ( curPath = [enumerator nextObject] ) { curFilename = [curPath lastPathComponent]; // What folder we import into depends on what OmniWeb file we're importing. if ([curFilename isEqualToString:@"Bookmarks.html"] ) { - success = [[BookmarkManager sharedBookmarkManager] importBookmarks:curPath intoFolder:aFolder]; + [titleArray addObject:NSLocalizedString(@"Imported OmniWeb 5 Bookmarks", @"Imported OmniWeb 5 Bookmarks")]; } else if ([curFilename isEqualToString:@"Favorites.html"] ) { - BookmarkFolder *favoriteFolder = [aFolder addBookmarkFolder]; - [favoriteFolder setTitle:NSLocalizedString(@"OmniWeb Favorites", @"OmniWeb Favorites")]; - success = [[BookmarkManager sharedBookmarkManager] importBookmarks:curPath intoFolder:favoriteFolder]; + [titleArray addObject:NSLocalizedString(@"OmniWeb Favorites", @"OmniWeb Favorites")]; } else if ([curFilename isEqualToString:@"Published.html"]) { - BookmarkFolder *publishedFolder = [aFolder addBookmarkFolder]; - [publishedFolder setTitle:NSLocalizedString(@"OmniWeb Published", @"OmniWeb Published")]; - success = [[BookmarkManager sharedBookmarkManager] importBookmarks:curPath intoFolder:publishedFolder]; + [titleArray addObject:NSLocalizedString(@"OmniWeb Published", @"OmniWeb Published")]; } } - - [mCancelButton setEnabled:YES]; - [mImportButton setEnabled:YES]; - - // Non-rigorous reporting of errors - [self finishImport:success fromFile:curFilename intoFolder:aFolder]; + [self beginImportFrom:anArray withTitles:titleArray]; } --(void) beginImportFrom:(NSString *) aPath intoFolder:(BookmarkFolder *) aFolder +-(void) beginImportFrom:(NSArray *) aPathArray withTitles:(NSArray *)aTitleArray { - [mCancelButton setEnabled:NO]; - [mImportButton setEnabled:NO]; - - BOOL success = [[BookmarkManager sharedBookmarkManager] importBookmarks:aPath intoFolder:aFolder]; - - [mCancelButton setEnabled:YES]; - [mImportButton setEnabled:YES]; - - [self finishImport:success fromFile:[aPath lastPathComponent] intoFolder:aFolder]; - + [self showProgressView]; + NSDictionary *aDict = [NSDictionary dictionaryWithObjectsAndKeys: aPathArray, kBookmarkImportPathIndentifier, + aTitleArray, kBookmarkImportNewFolderNameIdentifier, nil]; + [NSThread detachNewThreadSelector:@selector(importBookmarksThreadEntry:) + toTarget:[BookmarkManager sharedBookmarkManager] + withObject:aDict]; } --(void) finishImport:(BOOL)success fromFile:(NSString *)aFile intoFolder:(BookmarkFolder*)aFolder +-(void) finishThreadedImport:(BOOL)success fromFile:(NSString *)aFile { - //show them the imported bookmarks if import succeeded if (success) { - [[self window] orderOut:self]; BrowserWindowController* windowController = [(MainController *)[NSApp delegate] openBrowserWindowWithURL:@"about:bookmarks" andReferrer:nil behind:nil allowPopups:NO]; BookmarkViewController* bmController = [windowController bookmarkViewController]; - [bmController setItemToRevealOnLoad:aFolder]; + BookmarkFolder *rootFolder = [[BookmarkManager sharedBookmarkManager] rootBookmarks]; + BookmarkFolder *newFolder = [rootFolder objectAtIndex:([rootFolder count] - 1)]; + [bmController setItemToRevealOnLoad:newFolder]; } else { @@ -291,6 +271,8 @@ [NSString stringWithFormat:NSLocalizedString(@"ImportFailureMessage", @"The file '%@' is not a supported bookmark file type."), aFile] ); } + [[self window] orderOut:self]; + [self showImportView]; } -(void) alertSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo @@ -298,4 +280,23 @@ [[self window] orderOut:self]; } +-(void) showProgressView +{ + NSSize viewSize = [mProgressView frame].size; + [[self window] setContentView:mProgressView]; + [[self window] setContentSize:viewSize]; + [[self window] center]; + [mImportProgressBar setUsesThreadedAnimation:YES]; + [mImportProgressBar startAnimation:self]; +} + +-(void) showImportView +{ + [mImportProgressBar stopAnimation:self]; + NSSize viewSize = [mImportView frame].size; + [[self window] setContentView:mImportView]; + [[self window] setContentSize:viewSize]; + [[self window] center]; +} + @end diff --git a/camino/src/bookmarks/BookmarkManager.h b/camino/src/bookmarks/BookmarkManager.h index fe670d1b786..5d18dd8b42d 100644 --- a/camino/src/bookmarks/BookmarkManager.h +++ b/camino/src/bookmarks/BookmarkManager.h @@ -47,10 +47,12 @@ @class KindaSmartFolderManager; extern NSString* const kBookmarkManagerStartedNotification; - extern NSString* const kBookmarksToolbarFolderIdentifier; extern NSString* const kBookmarksMenuFolderIdentifier; +extern NSString* const kBookmarkImportPathIndentifier; +extern NSString* const kBookmarkImportNewFolderNameIdentifier; + const int kBookmarksContextMenuArrangeSeparatorTag = 100; @@ -142,7 +144,7 @@ const int kBookmarksContextMenuArrangeSeparatorTag = 100; // Importing bookmarks - (void)startImportBookmarks; -- (BOOL)importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +- (void)importBookmarksThreadEntry:(NSDictionary *)aDict; // Writing bookmark files - (void)writeHTMLFile:(NSString *)pathToFile; diff --git a/camino/src/bookmarks/BookmarkManager.mm b/camino/src/bookmarks/BookmarkManager.mm index d0968f08725..a5a043deb58 100644 --- a/camino/src/bookmarks/BookmarkManager.mm +++ b/camino/src/bookmarks/BookmarkManager.mm @@ -67,13 +67,20 @@ NSString* const kBookmarkManagerStartedNotification = @"BookmarkManagerStartedNotification"; // root bookmark folder identifiers (must be unique!) -NSString* const kBookmarksToolbarFolderIdentifier = @"BookmarksToolbarFolder"; -NSString* const kBookmarksMenuFolderIdentifier = @"BookmarksMenuFolder"; +NSString* const kBookmarksToolbarFolderIdentifier = @"BookmarksToolbarFolder"; +NSString* const kBookmarksMenuFolderIdentifier = @"BookmarksMenuFolder"; -static NSString* const kTop10BookmarksFolderIdentifier = @"Top10BookmarksFolder"; -static NSString* const kRendezvousFolderIdentifier = @"RendezvousFolder"; // aka Bonjour -static NSString* const kAddressBookFolderIdentifier = @"AddressBookFolder"; -static NSString* const kHistoryFolderIdentifier = @"HistoryFolder"; +static NSString* const kTop10BookmarksFolderIdentifier = @"Top10BookmarksFolder"; +static NSString* const kRendezvousFolderIdentifier = @"RendezvousFolder"; // aka Bonjour +static NSString* const kAddressBookFolderIdentifier = @"AddressBookFolder"; +static NSString* const kHistoryFolderIdentifier = @"HistoryFolder"; + +NSString* const kBookmarkImportPathIndentifier = @"path"; +NSString* const kBookmarkImportNewFolderNameIdentifier = @"title"; + +static NSString* const kBookmarkImportStatusIndentifier = @"flag"; +static NSString* const kBookmarkImportNewFolderIdentifier = @"folder"; +static NSString* const kBookmarkImportNewFolderIndexIdentifier = @"index"; // these are suggested indices; we only use them to order the root folders, not to access them. enum { @@ -111,6 +118,7 @@ enum { - (BOOL)importHTMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; - (BOOL)importCaminoXMLFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder settingToolbarFolder:(BOOL)setToolbarFolder; - (BOOL)importPropertyListFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; +- (void)importBookmarksThreadReturn:(NSDictionary *)aDict; - (BOOL)readOperaFile:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder; @@ -601,9 +609,14 @@ static BookmarkManager* gBookmarkManager = nil; return [mRootBookmarks itemWithUUID:uuid]; } +// only the main thread can get the undo manager. +// imports (on a background thread) get nothing, which is ok. +// this keeps things nice and thread safe -(NSUndoManager *) undoManager { - return mUndoManager; + if ([NSThread inMainThread]) + return mUndoManager; + return nil; } - (void)setPathToBookmarkFile:(NSString *)aString @@ -1466,39 +1479,104 @@ static BookmarkManager* gBookmarkManager = nil; [mImportDlgController showWindow:nil]; } -- (BOOL)importBookmarks:(NSString *)pathToFile intoFolder:(BookmarkFolder *)aFolder +- (void)importBookmarksThreadEntry:(NSDictionary *)aDict { - // I feel dirty doing it this way. But we'll check file extension - // to figure out how to handle this. - NSUndoManager *undoManager = [self undoManager]; - [undoManager beginUndoGrouping]; - BOOL success = NO; - NSString *extension =[pathToFile pathExtension]; - if ([extension isEqualToString:@""]) // we'll go out on a limb here - success = [self readOperaFile:pathToFile intoFolder:aFolder]; - else if ([extension isEqualToString:@"html"] || [extension isEqualToString:@"htm"]) - success = [self importHTMLFile:pathToFile intoFolder:aFolder]; - else if ([extension isEqualToString:@"xml"]) - success = [self importCaminoXMLFile:pathToFile intoFolder:aFolder settingToolbarFolder:NO]; - else if ([extension isEqualToString:@"plist"] || !success) - success = [self importPropertyListFile: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 readOperaFile:pathToFile intoFolder:aFolder]; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + BOOL success = TRUE; + int currentFile = 0; + NSArray *pathArray = [aDict objectForKey:kBookmarkImportPathIndentifier]; + NSArray *titleArray = [aDict objectForKey:kBookmarkImportNewFolderNameIdentifier]; + NSString *pathToFile; + NSString *aTitle; + BookmarkFolder *topImportFolder = [[BookmarkFolder alloc] init]; + BookmarkFolder *importFolder = topImportFolder; + + NSEnumerator *pathEnumerator = [pathArray objectEnumerator]; + NSEnumerator *titleEnumerator = [titleArray objectEnumerator]; + + [self startSuppressingChangeNotifications]; + + while (success && (pathToFile = [pathEnumerator nextObject]) ) + { + if (!importFolder) + { + importFolder = [[BookmarkFolder alloc] init]; + } + + NSString *extension =[pathToFile pathExtension]; + if ([extension isEqualToString:@""]) // we'll go out on a limb here + success = [self readOperaFile:pathToFile intoFolder:importFolder]; + else if ([extension isEqualToString:@"html"] || [extension isEqualToString:@"htm"]) + success = [self importHTMLFile:pathToFile intoFolder:importFolder]; + else if ([extension isEqualToString:@"xml"]) + success = [self importCaminoXMLFile:pathToFile intoFolder:importFolder settingToolbarFolder:NO]; + else if ([extension isEqualToString:@"plist"] || !success) + success = [self importPropertyListFile:pathToFile intoFolder:importFolder]; + // 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 importHTMLFile:pathToFile intoFolder:aFolder]; + success = [self readOperaFile:pathToFile intoFolder:importFolder]; if (!success) { - success = [self importCaminoXMLFile:pathToFile intoFolder:aFolder settingToolbarFolder:NO]; + success = [self importHTMLFile:pathToFile intoFolder:importFolder]; + if (!success) { + success = [self importCaminoXMLFile:pathToFile intoFolder:importFolder settingToolbarFolder:NO]; + } } } + + aTitle = [titleEnumerator nextObject]; + + if (!aTitle) + aTitle = NSLocalizedString(@"Imported Bookmarks",@"Imported Bookmarks"); + + [importFolder setTitle:aTitle]; + + if (importFolder != topImportFolder) + { + [topImportFolder appendChild:importFolder]; + [importFolder release]; + } + + importFolder = nil; + currentFile++; } - [[undoManager prepareWithInvocationTarget:[self rootBookmarks]] deleteChild:aFolder]; - [undoManager endUndoGrouping]; - [undoManager setActionName:NSLocalizedString(@"Import Bookmarks", @"")]; - return success; + + [self stopSuppressingChangeNotifications]; + + NSDictionary *returnDict = [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:success],kBookmarkImportStatusIndentifier, + [NSNumber numberWithInt:currentFile], kBookmarkImportNewFolderIndexIdentifier, + pathArray, kBookmarkImportPathIndentifier, + topImportFolder, kBookmarkImportNewFolderIdentifier, + nil]; + + [self performSelectorOnMainThread:@selector(importBookmarksThreadReturn:) + withObject:returnDict + waitUntilDone:YES]; + // release the top-level import folder we allocated - somebody else retains it by now if still needed. + [topImportFolder release]; + + [pool release]; } +-(void)importBookmarksThreadReturn:(NSDictionary *)aDict +{ + BOOL success = [[aDict objectForKey:kBookmarkImportStatusIndentifier] boolValue]; + NSArray *fileArray = [aDict objectForKey:kBookmarkImportPathIndentifier]; + int currentIndex = [[aDict objectForKey:kBookmarkImportNewFolderIndexIdentifier] intValue]; + BookmarkFolder *rootFolder = [self rootBookmarks]; + BookmarkFolder *importFolder = [aDict objectForKey:kBookmarkImportNewFolderIdentifier]; + if (success || ((currentIndex - [fileArray count]) > 0) ) + { + NSUndoManager *undoManager = [self undoManager]; + [rootFolder appendChild:importFolder]; + [undoManager setActionName:NSLocalizedString(@"Import Bookmarks", @"")]; + } + [mImportDlgController finishThreadedImport:success + fromFile:[[fileArray objectAtIndex:(--currentIndex)] lastPathComponent] ]; +} + + // spits out text file as NSString with "proper" encoding based on pretty shitty detection - (NSString *)decodedTextFile:(NSString *)pathToFile {