| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stack> |
| 6 |
| 5 #import "chrome/browser/cocoa/bookmark_editor_base_controller.h" | 7 #import "chrome/browser/cocoa/bookmark_editor_base_controller.h" |
| 6 #include "app/l10n_util.h" | 8 #include "app/l10n_util.h" |
| 9 #include "app/l10n_util_mac.h" |
| 7 #include "base/logging.h" | 10 #include "base/logging.h" |
| 8 #include "base/mac_util.h" | 11 #include "base/mac_util.h" |
| 9 #include "base/sys_string_conversions.h" | 12 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/browser/bookmarks/bookmark_model.h" | 13 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 11 #import "chrome/browser/cocoa/bookmark_all_tabs_controller.h" | 14 #import "chrome/browser/cocoa/bookmark_all_tabs_controller.h" |
| 12 #import "chrome/browser/cocoa/bookmark_editor_controller.h" | 15 #import "chrome/browser/cocoa/bookmark_editor_controller.h" |
| 13 #import "chrome/browser/cocoa/bookmark_tree_browser_cell.h" | 16 #import "chrome/browser/cocoa/bookmark_tree_browser_cell.h" |
| 14 #include "chrome/browser/profile.h" | 17 #include "chrome/browser/profile.h" |
| 15 #include "grit/generated_resources.h" | 18 #include "grit/generated_resources.h" |
| 16 | 19 |
| 17 @interface BookmarkEditorBaseController (Private) | 20 @interface BookmarkEditorBaseController (Private) |
| 18 | 21 |
| 19 // Given a cell in the folder browser, make that cell editable so that the | 22 @property (retain, readwrite) NSArray* folderTreeArray; |
| 20 // bookmark folder name can be modified by the user. | |
| 21 - (void)editFolderNameInCell:(BookmarkTreeBrowserCell*)cell; | |
| 22 | 23 |
| 23 // The action called by the bookmark folder name cell being edited by | 24 // Return the folder tree object for the given path. |
| 24 // the user when editing has been completed (such as by pressing <return>). | 25 - (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)path; |
| 25 - (void)cellEditingCompleted:(id)sender; | |
| 26 | 26 |
| 27 // Update the folder name from the current edit in the given cell | 27 // Given a folder node, collect an array containing BookmarkFolderInfos |
| 28 // and return the focus to the folder tree browser. | 28 // describing its subchildren which are also folders. |
| 29 - (void)saveFolderNameForCell:(BookmarkTreeBrowserCell*)cell; | 29 - (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node; |
| 30 | 30 |
| 31 // A custom action handler called by the bookmark folder browser when the | 31 // Scan the folder tree stemming from the given tree folder and create |
| 32 // user has double-clicked on a folder name. | 32 // any newly added folders. |
| 33 - (void)browserDoubleClicked:(id)sender; | 33 - (void)createNewFoldersForFolder:(BookmarkFolderInfo*)treeFolder; |
| 34 |
| 35 // Scan the folder tree looking for the given bookmark node and return |
| 36 // the selection path thereto. |
| 37 - (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)node; |
| 34 | 38 |
| 35 @end | 39 @end |
| 36 | 40 |
| 37 // static; implemented for each platform. | 41 // static; implemented for each platform. Update this function for new |
| 42 // classes derived from BookmarkEditorBaseController. |
| 38 void BookmarkEditor::Show(gfx::NativeWindow parent_hwnd, | 43 void BookmarkEditor::Show(gfx::NativeWindow parent_hwnd, |
| 39 Profile* profile, | 44 Profile* profile, |
| 40 const BookmarkNode* parent, | 45 const BookmarkNode* parent, |
| 41 const EditDetails& details, | 46 const EditDetails& details, |
| 42 Configuration configuration, | 47 Configuration configuration, |
| 43 Handler* handler) { | 48 Handler* handler) { |
| 44 BookmarkEditorBaseController* controller = nil; | 49 BookmarkEditorBaseController* controller = nil; |
| 45 if (details.type == EditDetails::NEW_FOLDER) { | 50 if (details.type == EditDetails::NEW_FOLDER) { |
| 46 controller = [[BookmarkAllTabsController alloc] | 51 controller = [[BookmarkAllTabsController alloc] |
| 47 initWithParentWindow:parent_hwnd | 52 initWithParentWindow:parent_hwnd |
| 48 profile:profile | 53 profile:profile |
| 49 parent:parent | 54 parent:parent |
| 50 configuration:configuration | 55 configuration:configuration |
| 51 handler:handler]; | 56 handler:handler]; |
| 52 } else { | 57 } else { |
| 53 controller = [[BookmarkEditorController alloc] | 58 controller = [[BookmarkEditorController alloc] |
| 54 initWithParentWindow:parent_hwnd | 59 initWithParentWindow:parent_hwnd |
| 55 profile:profile | 60 profile:profile |
| 56 parent:parent | 61 parent:parent |
| 57 node:details.existing_node | 62 node:details.existing_node |
| 58 configuration:configuration | 63 configuration:configuration |
| 59 handler:handler]; | 64 handler:handler]; |
| 60 } | 65 } |
| 61 [controller runAsModalSheet]; | 66 [controller runAsModalSheet]; |
| 62 } | 67 } |
| 63 | 68 |
| 64 #pragma mark Bookmark TreeNode Helpers | |
| 65 | |
| 66 namespace { | |
| 67 | |
| 68 // Find the index'th folder child of a parent, ignoring bookmarks (leafs). | |
| 69 const BookmarkNode* GetFolderChildForParent(const BookmarkNode* parent_node, | |
| 70 NSInteger folder_index) { | |
| 71 const BookmarkNode* child_node = nil; | |
| 72 int i = 0; | |
| 73 int child_count = parent_node->GetChildCount(); | |
| 74 do { | |
| 75 child_node = parent_node->GetChild(i); | |
| 76 if (child_node->type() != BookmarkNode::URL) | |
| 77 --folder_index; | |
| 78 ++i; | |
| 79 } while (folder_index >= 0 && i < child_count); | |
| 80 return child_node; | |
| 81 } | |
| 82 | |
| 83 // Determine the index of a child within its parent ignoring | |
| 84 // bookmarks (leafs). | |
| 85 int IndexOfFolderChild(const BookmarkNode* child_node) { | |
| 86 const BookmarkNode* node_parent = child_node->GetParent(); | |
| 87 int child_index = node_parent->IndexOfChild(child_node); | |
| 88 for (int i = child_index - 1; i >= 0; --i) { | |
| 89 const BookmarkNode* sibling = node_parent->GetChild(i); | |
| 90 if (sibling->type() == BookmarkNode::URL) | |
| 91 --child_index; | |
| 92 } | |
| 93 return child_index; | |
| 94 } | |
| 95 | |
| 96 } // namespace | |
| 97 | |
| 98 @implementation BookmarkEditorBaseController | 69 @implementation BookmarkEditorBaseController |
| 99 | 70 |
| 100 @synthesize initialName = initialName_; | 71 @synthesize initialName = initialName_; |
| 101 @synthesize displayName = displayName_; | 72 @synthesize displayName = displayName_; |
| 102 @synthesize okEnabled = okEnabled_; | 73 @synthesize okEnabled = okEnabled_; |
| 103 | 74 |
| 104 - (id)initWithParentWindow:(NSWindow*)parentWindow | 75 - (id)initWithParentWindow:(NSWindow*)parentWindow |
| 105 nibName:(NSString*)nibName | 76 nibName:(NSString*)nibName |
| 106 profile:(Profile*)profile | 77 profile:(Profile*)profile |
| 107 parent:(const BookmarkNode*)parent | 78 parent:(const BookmarkNode*)parent |
| (...skipping 16 matching lines...) Expand all Loading... |
| 124 - (void)dealloc { | 95 - (void)dealloc { |
| 125 [initialName_ release]; | 96 [initialName_ release]; |
| 126 [displayName_ release]; | 97 [displayName_ release]; |
| 127 [super dealloc]; | 98 [super dealloc]; |
| 128 } | 99 } |
| 129 | 100 |
| 130 - (void)awakeFromNib { | 101 - (void)awakeFromNib { |
| 131 [self setDisplayName:[self initialName]]; | 102 [self setDisplayName:[self initialName]]; |
| 132 | 103 |
| 133 if (configuration_ != BookmarkEditor::SHOW_TREE) { | 104 if (configuration_ != BookmarkEditor::SHOW_TREE) { |
| 134 // Remember the NSBrowser's height; we will shrink our frame by that | 105 // Remember the tree view's height; we will shrink our frame by that much. |
| 135 // much. | |
| 136 NSRect frame = [[self window] frame]; | 106 NSRect frame = [[self window] frame]; |
| 137 CGFloat browserHeight = [folderBrowser_ frame].size.height; | 107 CGFloat browserHeight = [folderTreeView_ frame].size.height; |
| 138 frame.size.height -= browserHeight; | 108 frame.size.height -= browserHeight; |
| 139 frame.origin.y += browserHeight; | 109 frame.origin.y += browserHeight; |
| 140 // Remove the NSBrowser and "new folder" button. | 110 // Remove the folder tree and "new folder" button. |
| 141 [folderBrowser_ removeFromSuperview]; | 111 [folderTreeView_ removeFromSuperview]; |
| 142 [newFolderButton_ removeFromSuperview]; | 112 [newFolderButton_ removeFromSuperview]; |
| 143 // Finally, commit the size change. | 113 // Finally, commit the size change. |
| 144 [[self window] setFrame:frame display:YES]; | 114 [[self window] setFrame:frame display:YES]; |
| 145 } | 115 } |
| 146 | 116 |
| 147 [folderBrowser_ setCellClass:[BookmarkTreeBrowserCell class]]; | 117 // Build up a tree of the current folder configuration. |
| 148 [folderBrowser_ setDoubleAction:@selector(browserDoubleClicked:)]; | 118 BookmarkModel* model = profile_->GetBookmarkModel(); |
| 119 const BookmarkNode* rootNode = model->root_node(); |
| 120 NSMutableArray* baseArray = [self addChildFoldersFromNode:rootNode]; |
| 121 DCHECK(baseArray); |
| 122 [self setFolderTreeArray:baseArray]; |
| 149 } | 123 } |
| 150 | 124 |
| 151 - (void)windowDidLoad { | 125 - (void)windowDidLoad { |
| 152 if (configuration_ == BookmarkEditor::SHOW_TREE) { | 126 if (configuration_ == BookmarkEditor::SHOW_TREE) { |
| 153 [self selectNodeInBrowser:parentNode_]; | 127 [self selectNodeInBrowser:parentNode_]; |
| 154 } | 128 } |
| 155 } | 129 } |
| 156 | 130 |
| 157 - (void)windowWillClose:(NSNotification *)notification { | |
| 158 // If a folder name cell is being edited then force it to end editing | |
| 159 // so that any changes are recorded. | |
| 160 [[self window] makeFirstResponder:nil]; | |
| 161 [self autorelease]; | |
| 162 } | |
| 163 | |
| 164 /* TODO(jrg): | 131 /* TODO(jrg): |
| 165 // Implementing this informal protocol allows us to open the sheet | 132 // Implementing this informal protocol allows us to open the sheet |
| 166 // somewhere other than at the top of the window. NOTE: this means | 133 // somewhere other than at the top of the window. NOTE: this means |
| 167 // that I, the controller, am also the window's delegate. | 134 // that I, the controller, am also the window's delegate. |
| 168 - (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet | 135 - (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet |
| 169 usingRect:(NSRect)rect { | 136 usingRect:(NSRect)rect { |
| 170 // adjust rect.origin.y to be the bottom of the toolbar | 137 // adjust rect.origin.y to be the bottom of the toolbar |
| 171 return rect; | 138 return rect; |
| 172 } | 139 } |
| 173 */ | 140 */ |
| 174 | 141 |
| 175 // TODO(jrg): consider NSModalSession. | 142 // TODO(jrg): consider NSModalSession. |
| 176 - (void)runAsModalSheet { | 143 - (void)runAsModalSheet { |
| 177 [NSApp beginSheet:[self window] | 144 [NSApp beginSheet:[self window] |
| 178 modalForWindow:parentWindow_ | 145 modalForWindow:parentWindow_ |
| 179 modalDelegate:self | 146 modalDelegate:self |
| 180 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) | 147 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) |
| 181 contextInfo:nil]; | 148 contextInfo:nil]; |
| 182 } | 149 } |
| 183 | 150 |
| 184 - (void)selectNodeInBrowser:(const BookmarkNode*)node { | 151 - (BOOL)okEnabled { |
| 185 DCHECK(configuration_ == BookmarkEditor::SHOW_TREE); | 152 return YES; |
| 186 std::deque<NSInteger> rowsToSelect; | 153 } |
| 187 const BookmarkNode* nodeParent = nil; | 154 |
| 188 if (node) { | 155 - (IBAction)ok:(id)sender { |
| 189 nodeParent = node->GetParent(); | 156 // At least one of these two functions should be provided by derived classes. |
| 190 // There should always be a parent node. | 157 BOOL hasWillCommit = [self respondsToSelector:@selector(willCommit)]; |
| 191 DCHECK(nodeParent); | 158 BOOL hasDidCommit = [self respondsToSelector:@selector(didCommit)]; |
| 192 while (nodeParent) { | 159 DCHECK(hasWillCommit || hasDidCommit); |
| 193 int nodeRow = IndexOfFolderChild(node); | 160 BOOL shouldContinue = YES; |
| 194 rowsToSelect.push_front(nodeRow); | 161 if (hasWillCommit) { |
| 195 node = nodeParent; | 162 NSNumber* hasWillContinue = [self performSelector:@selector(willCommit)]; |
| 196 nodeParent = nodeParent->GetParent(); | 163 if (hasWillContinue && [hasWillContinue isKindOfClass:[NSNumber class]]) |
| 197 } | 164 shouldContinue = [hasWillContinue boolValue]; |
| 198 } else { | |
| 199 BookmarkModel* model = profile_->GetBookmarkModel(); | |
| 200 nodeParent = model->GetBookmarkBarNode(); | |
| 201 rowsToSelect.push_front(0); | |
| 202 } | 165 } |
| 203 for (std::deque<NSInteger>::size_type column = 0; | 166 if (shouldContinue) |
| 204 column < rowsToSelect.size(); | 167 [self createNewFolders]; |
| 205 ++column) { | 168 if (hasDidCommit) { |
| 206 [folderBrowser_ selectRow:rowsToSelect[column] inColumn:column]; | 169 NSNumber* hasDidContinue = [self performSelector:@selector(didCommit)]; |
| 170 if (hasDidContinue && [hasDidContinue isKindOfClass:[NSNumber class]]) |
| 171 shouldContinue = [hasDidContinue boolValue]; |
| 207 } | 172 } |
| 173 if (shouldContinue) |
| 174 [NSApp endSheet:[self window]]; |
| 175 } |
| 208 | 176 |
| 209 // Force the OK button state to be re-evaluated. | 177 - (IBAction)cancel:(id)sender { |
| 210 [self willChangeValueForKey:@"okEnabled"]; | 178 [NSApp endSheet:[self window]]; |
| 211 [self didChangeValueForKey:@"okEnabled"]; | 179 } |
| 180 |
| 181 - (void)didEndSheet:(NSWindow*)sheet |
| 182 returnCode:(int)returnCode |
| 183 contextInfo:(void*)contextInfo { |
| 184 [sheet close]; |
| 185 } |
| 186 |
| 187 - (void)windowWillClose:(NSNotification*)notification { |
| 188 [self autorelease]; |
| 189 } |
| 190 |
| 191 #pragma mark Folder Tree Management |
| 192 |
| 193 - (BookmarkModel*)bookmarkModel { |
| 194 return profile_->GetBookmarkModel(); |
| 195 } |
| 196 |
| 197 - (const BookmarkNode*)parentNode { |
| 198 return parentNode_; |
| 199 } |
| 200 |
| 201 - (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)indexPath { |
| 202 NSUInteger pathCount = [indexPath length]; |
| 203 BookmarkFolderInfo* item = nil; |
| 204 NSArray* treeNode = [self folderTreeArray]; |
| 205 for (NSUInteger i = 0; i < pathCount; ++i) { |
| 206 item = [treeNode objectAtIndex:[indexPath indexAtPosition:i]]; |
| 207 treeNode = [item children]; |
| 208 } |
| 209 return item; |
| 210 } |
| 211 |
| 212 - (NSIndexPath*)selectedIndexPath { |
| 213 NSIndexPath* selectedIndexPath = nil; |
| 214 NSArray* selections = [self tableSelectionPaths]; |
| 215 if ([selections count]) { |
| 216 DCHECK([selections count] == 1); // Should be exactly one selection. |
| 217 selectedIndexPath = [selections objectAtIndex:0]; |
| 218 } |
| 219 return selectedIndexPath; |
| 220 } |
| 221 |
| 222 - (BookmarkFolderInfo*)selectedFolder { |
| 223 BookmarkFolderInfo* item = nil; |
| 224 NSIndexPath* selectedIndexPath = [self selectedIndexPath]; |
| 225 if (selectedIndexPath) { |
| 226 item = [self folderForIndexPath:selectedIndexPath]; |
| 227 } |
| 228 return item; |
| 212 } | 229 } |
| 213 | 230 |
| 214 - (const BookmarkNode*)selectedNode { | 231 - (const BookmarkNode*)selectedNode { |
| 215 BookmarkModel* model = profile_->GetBookmarkModel(); | |
| 216 const BookmarkNode* selectedNode = NULL; | 232 const BookmarkNode* selectedNode = NULL; |
| 217 // Determine a new parent node only if the browser is showing. | 233 // Determine a new parent node only if the browser is showing. |
| 218 if (configuration_ == BookmarkEditor::SHOW_TREE) { | 234 if (configuration_ == BookmarkEditor::SHOW_TREE) { |
| 219 selectedNode = model->root_node(); | 235 BookmarkFolderInfo* folderInfo = [self selectedFolder]; |
| 220 NSInteger column = 0; | 236 if (folderInfo) |
| 221 NSInteger selectedRow = [folderBrowser_ selectedRowInColumn:column]; | 237 selectedNode = [folderInfo folderNode]; |
| 222 while (selectedRow >= 0) { | |
| 223 selectedNode = GetFolderChildForParent(selectedNode, | |
| 224 selectedRow); | |
| 225 ++column; | |
| 226 selectedRow = [folderBrowser_ selectedRowInColumn:column]; | |
| 227 } | |
| 228 } else { | 238 } else { |
| 229 // If the tree is not showing then we use the original parent. | 239 // If the tree is not showing then we use the original parent. |
| 230 selectedNode = parentNode_; | 240 selectedNode = parentNode_; |
| 231 } | 241 } |
| 232 return selectedNode; | 242 return selectedNode; |
| 233 } | 243 } |
| 234 | 244 |
| 235 - (void)NotifyHandlerCreatedNode:(const BookmarkNode*)node { | 245 - (void)notifyHandlerCreatedNode:(const BookmarkNode*)node { |
| 236 if (handler_.get()) | 246 if (handler_.get()) |
| 237 handler_->NodeCreated(node); | 247 handler_->NodeCreated(node); |
| 238 } | 248 } |
| 239 | 249 |
| 240 #pragma mark New Folder Handler & Folder Cell Editing | 250 - (NSArray*)folderTreeArray { |
| 241 | 251 return folderTreeArray_.get(); |
| 242 - (void)editFolderNameInCell:(BookmarkTreeBrowserCell*)cell { | |
| 243 DCHECK([cell isKindOfClass:[BookmarkTreeBrowserCell class]]); | |
| 244 [cell setEditable:YES]; | |
| 245 [cell setTarget:self]; | |
| 246 [cell setAction:@selector(cellEditingCompleted:)]; | |
| 247 [cell setSendsActionOnEndEditing:YES]; | |
| 248 NSMatrix* matrix = [cell matrix]; | |
| 249 // Set the delegate so that we get called when editing completes. | |
| 250 [matrix setDelegate:self]; | |
| 251 [matrix selectText:self]; | |
| 252 } | 252 } |
| 253 | 253 |
| 254 - (void)cellEditingCompleted:(id)sender { | 254 - (void)setFolderTreeArray:(NSArray*)folderTreeArray { |
| 255 DCHECK([sender isKindOfClass:[NSMatrix class]]); | 255 folderTreeArray_.reset([folderTreeArray retain]); |
| 256 BookmarkTreeBrowserCell* cell = [sender selectedCell]; | |
| 257 DCHECK([cell isKindOfClass:[BookmarkTreeBrowserCell class]]); | |
| 258 [self saveFolderNameForCell:cell]; | |
| 259 } | 256 } |
| 260 | 257 |
| 261 - (void)saveFolderNameForCell:(BookmarkTreeBrowserCell*)cell { | 258 - (NSArray*)tableSelectionPaths { |
| 262 DCHECK([cell isKindOfClass:[BookmarkTreeBrowserCell class]]); | 259 return tableSelectionPaths_.get(); |
| 263 // It's possible that the cell can get reused so clean things up | |
| 264 // to prevent inadvertant notifications. | |
| 265 [cell setTarget:nil]; | |
| 266 [cell setAction:nil]; | |
| 267 [cell setEditable:NO]; | |
| 268 [cell setSendsActionOnEndEditing:NO]; | |
| 269 // Force a responder change here to force the editing of the cell's text | |
| 270 // to complete otherwise the call to -[cell title] could return stale text. | |
| 271 // The focus does not automatically get reset to the browser when the | |
| 272 // cell gives up focus. | |
| 273 [[folderBrowser_ window] makeFirstResponder:folderBrowser_]; | |
| 274 const BookmarkNode* bookmarkNode = [cell bookmarkNode]; | |
| 275 BookmarkModel* model = profile_->GetBookmarkModel(); | |
| 276 NSString* newTitle = [cell title]; | |
| 277 model->SetTitle(bookmarkNode, base::SysNSStringToWide(newTitle)); | |
| 278 } | 260 } |
| 279 | 261 |
| 280 - (void)browserDoubleClicked:(id)sender { | 262 - (void)setTableSelectionPath:(NSIndexPath*)tableSelectionPath { |
| 281 BookmarkTreeBrowserCell* cell = [folderBrowser_ selectedCell]; | 263 [self setTableSelectionPaths:[NSArray arrayWithObject:tableSelectionPath]]; |
| 282 DCHECK([cell isKindOfClass:[BookmarkTreeBrowserCell class]]); | 264 } |
| 283 [self editFolderNameInCell:cell]; | 265 |
| 266 - (void)setTableSelectionPaths:(NSArray*)tableSelectionPaths { |
| 267 tableSelectionPaths_.reset([tableSelectionPaths retain]); |
| 268 } |
| 269 |
| 270 - (void)selectNodeInBrowser:(const BookmarkNode*)node { |
| 271 DCHECK(configuration_ == BookmarkEditor::SHOW_TREE); |
| 272 NSIndexPath* selectionPath = [self selectionPathForNode:node]; |
| 273 [self willChangeValueForKey:@"okEnabled"]; |
| 274 [self setTableSelectionPath:selectionPath]; |
| 275 [self didChangeValueForKey:@"okEnabled"]; |
| 276 } |
| 277 |
| 278 - (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)desiredNode { |
| 279 // Back up the parent chaing for desiredNode, building up a stack |
| 280 // of ancestor nodes. Then crawl down the folderTreeArray looking |
| 281 // for each ancestor in order while building up the selectionPath. |
| 282 std::stack<const BookmarkNode*> nodeStack; |
| 283 BookmarkModel* model = profile_->GetBookmarkModel(); |
| 284 const BookmarkNode* rootNode = model->root_node(); |
| 285 const BookmarkNode* node = desiredNode; |
| 286 while (node != rootNode) { |
| 287 nodeStack.push(node); |
| 288 node = node->GetParent(); |
| 289 } |
| 290 NSUInteger stackSize = nodeStack.size(); |
| 291 |
| 292 NSIndexPath* path = nil; |
| 293 NSArray* folders = [self folderTreeArray]; |
| 294 while (!nodeStack.empty()) { |
| 295 node = nodeStack.top(); |
| 296 nodeStack.pop(); |
| 297 // Find node in the current folders array. |
| 298 NSUInteger i = 0; |
| 299 for (BookmarkFolderInfo *folderInfo in folders) { |
| 300 const BookmarkNode* testNode = [folderInfo folderNode]; |
| 301 if (testNode == node) { |
| 302 path = path ? [path indexPathByAddingIndex:i] : |
| 303 [NSIndexPath indexPathWithIndex:i]; |
| 304 folders = [folderInfo children]; |
| 305 break; |
| 306 } |
| 307 ++i; |
| 308 } |
| 309 } |
| 310 DCHECK([path length] == stackSize); |
| 311 return path; |
| 312 } |
| 313 |
| 314 - (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node { |
| 315 NSMutableArray* childFolders = nil; |
| 316 int childCount = node->GetChildCount(); |
| 317 for (int i = 0; i < childCount; ++i) { |
| 318 const BookmarkNode* childNode = node->GetChild(i); |
| 319 if (childNode->type() != BookmarkNode::URL) { |
| 320 NSString* childName = base::SysWideToNSString(childNode->GetTitle()); |
| 321 NSMutableArray* children = [self addChildFoldersFromNode:childNode]; |
| 322 BookmarkFolderInfo* folderInfo = |
| 323 [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:childName |
| 324 folderNode:childNode |
| 325 children:children]; |
| 326 if (!childFolders) |
| 327 childFolders = [NSMutableArray arrayWithObject:folderInfo]; |
| 328 else |
| 329 [childFolders addObject:folderInfo]; |
| 330 } |
| 331 } |
| 332 return childFolders; |
| 333 } |
| 334 |
| 335 #pragma mark New Folder Handler |
| 336 |
| 337 - (void)createNewFoldersForFolder:(BookmarkFolderInfo*)folderInfo { |
| 338 NSArray* subfolders = [folderInfo children]; |
| 339 const BookmarkNode* parentNode = [folderInfo folderNode]; |
| 340 DCHECK(parentNode); |
| 341 NSUInteger i = 0; |
| 342 for (BookmarkFolderInfo *subFolderInfo in subfolders) { |
| 343 if ([subFolderInfo newFolder]) { |
| 344 BookmarkModel* model = [self bookmarkModel]; |
| 345 const BookmarkNode* newFolder = |
| 346 model->AddGroup(parentNode, i, |
| 347 base::SysNSStringToWide([subFolderInfo folderName])); |
| 348 [self notifyHandlerCreatedNode:newFolder]; |
| 349 // Update our dictionary with the actual folder node just created. |
| 350 [subFolderInfo setFolderNode:newFolder]; |
| 351 [subFolderInfo setNewFolder:NO]; |
| 352 } |
| 353 [self createNewFoldersForFolder:subFolderInfo]; |
| 354 ++i; |
| 355 } |
| 284 } | 356 } |
| 285 | 357 |
| 286 - (IBAction)newFolder:(id)sender { | 358 - (IBAction)newFolder:(id)sender { |
| 287 BookmarkModel* model = profile_->GetBookmarkModel(); | 359 // Create a new folder off of the selected folder node. |
| 288 const BookmarkNode* newParentNode = [self selectedNode]; | 360 BookmarkFolderInfo* parentInfo = [self selectedFolder]; |
| 289 int newIndex = newParentNode->GetChildCount(); | 361 if (parentInfo) { |
| 290 std::wstring newFolderString = | 362 NSIndexPath* selection = [self selectedIndexPath]; |
| 291 l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); | 363 NSString* newFolderName = |
| 292 const BookmarkNode* newFolder = model->AddGroup(newParentNode, newIndex, | 364 l10n_util::GetNSStringWithFixup(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); |
| 293 newFolderString); | 365 BookmarkFolderInfo* folderInfo = |
| 294 [self selectNodeInBrowser:newFolder]; | 366 [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:newFolderName]; |
| 295 BookmarkTreeBrowserCell* cell = [folderBrowser_ selectedCell]; | 367 [self willChangeValueForKey:@"folderTreeArray"]; |
| 296 [self editFolderNameInCell:cell]; | 368 NSMutableArray* children = [parentInfo children]; |
| 369 if (children) { |
| 370 [children addObject:folderInfo]; |
| 371 } else { |
| 372 children = [NSMutableArray arrayWithObject:folderInfo]; |
| 373 [parentInfo setChildren:children]; |
| 374 } |
| 375 [self didChangeValueForKey:@"folderTreeArray"]; |
| 376 |
| 377 // Expose the parent folder children. |
| 378 [folderTreeView_ expandItem:parentInfo]; |
| 379 |
| 380 // Select the new folder node and put the folder name into edit mode. |
| 381 selection = [selection indexPathByAddingIndex:[children count] - 1]; |
| 382 [self setTableSelectionPath:selection]; |
| 383 NSInteger row = [folderTreeView_ selectedRow]; |
| 384 DCHECK(row >= 0); |
| 385 [folderTreeView_ editColumn:0 row:row withEvent:nil select:YES]; |
| 386 } |
| 297 } | 387 } |
| 298 | 388 |
| 299 - (BOOL)okEnabled { | 389 - (void)createNewFolders { |
| 300 return YES; | 390 // Scan the tree looking for nodes marked 'newFolder' and create those nodes. |
| 301 } | 391 NSArray* folderTreeArray = [self folderTreeArray]; |
| 302 | 392 for (BookmarkFolderInfo *folderInfo in folderTreeArray) { |
| 303 - (IBAction)ok:(id)sender { | 393 [self createNewFoldersForFolder:folderInfo]; |
| 304 [NSApp endSheet:[self window]]; | 394 } |
| 305 } | |
| 306 | |
| 307 - (IBAction)cancel:(id)sender { | |
| 308 [NSApp endSheet:[self window]]; | |
| 309 } | |
| 310 | |
| 311 - (void)didEndSheet:(NSWindow*)sheet | |
| 312 returnCode:(int)returnCode | |
| 313 contextInfo:(void*)contextInfo { | |
| 314 [sheet close]; | |
| 315 } | |
| 316 | |
| 317 - (BookmarkModel*)bookmarkModel { | |
| 318 return profile_->GetBookmarkModel(); | |
| 319 } | |
| 320 | |
| 321 - (const BookmarkNode*)parentNode { | |
| 322 return parentNode_; | |
| 323 } | 395 } |
| 324 | 396 |
| 325 #pragma mark For Unit Test Use Only | 397 #pragma mark For Unit Test Use Only |
| 326 | 398 |
| 327 - (BOOL)okButtonEnabled { | 399 - (BOOL)okButtonEnabled { |
| 328 return [okButton_ isEnabled]; | 400 return [okButton_ isEnabled]; |
| 329 } | 401 } |
| 330 | 402 |
| 331 - (void)selectTestNodeInBrowser:(const BookmarkNode*)node { | 403 - (void)selectTestNodeInBrowser:(const BookmarkNode*)node { |
| 332 [self selectNodeInBrowser:node]; | 404 [self selectNodeInBrowser:node]; |
| 333 } | 405 } |
| 334 | 406 |
| 335 + (const BookmarkNode*)folderChildForParent:(const BookmarkNode*)parent | |
| 336 withFolderIndex:(NSInteger)index { | |
| 337 return GetFolderChildForParent(parent, index); | |
| 338 } | |
| 339 | |
| 340 + (int)indexOfFolderChild:(const BookmarkNode*)child { | |
| 341 return IndexOfFolderChild(child); | |
| 342 } | |
| 343 | |
| 344 | |
| 345 #pragma mark NSBrowser delegate methods | |
| 346 | |
| 347 // Given a column number, determine the parent bookmark folder node for the | |
| 348 // bookmarks being shown in that column. This is done by scanning forward | |
| 349 // from column zero and following the selected row for each column up | |
| 350 // to the parent of the desired column. | |
| 351 - (const BookmarkNode*)parentNodeForColumn:(NSInteger)column { | |
| 352 DCHECK(column >= 0); | |
| 353 BookmarkModel* model = profile_->GetBookmarkModel(); | |
| 354 const BookmarkNode* node = model->root_node(); | |
| 355 for (NSInteger i = 0; i < column; ++i) { | |
| 356 NSInteger selectedRowInColumn = [folderBrowser_ selectedRowInColumn:i]; | |
| 357 node = GetFolderChildForParent(node, selectedRowInColumn); | |
| 358 } | |
| 359 return node; | |
| 360 } | |
| 361 | |
| 362 // This implementation returns the number of folders contained in the parent | |
| 363 // folder node for this column. | |
| 364 // TOTO(mrossetti): Decide if bookmark (i.e. non-folder) nodes should be | |
| 365 // shown, perhaps in gray. | |
| 366 - (NSInteger)browser:(NSBrowser*)sender numberOfRowsInColumn:(NSInteger)col { | |
| 367 NSInteger rowCount = 0; | |
| 368 const BookmarkNode* parentNode = [self parentNodeForColumn:col]; | |
| 369 if (parentNode) { | |
| 370 int childCount = parentNode->GetChildCount(); | |
| 371 for (int i = 0; i < childCount; ++i) { | |
| 372 const BookmarkNode* childNode = parentNode->GetChild(i); | |
| 373 if (childNode->type() != BookmarkNode::URL) | |
| 374 ++rowCount; | |
| 375 } | |
| 376 } | |
| 377 return rowCount; | |
| 378 } | |
| 379 | |
| 380 - (void)browser:(NSBrowser*)sender | |
| 381 willDisplayCell:(NSBrowserCell*)cell | |
| 382 atRow:(NSInteger)row | |
| 383 column:(NSInteger)column { | |
| 384 DCHECK(row >= 0); // Trust AppKit, but verify. | |
| 385 DCHECK(column >= 0); | |
| 386 DCHECK([cell isKindOfClass:[BookmarkTreeBrowserCell class]]); | |
| 387 const BookmarkNode* parentNode = [self parentNodeForColumn:column]; | |
| 388 const BookmarkNode* childNode = GetFolderChildForParent(parentNode, row); | |
| 389 DCHECK(childNode); | |
| 390 BookmarkTreeBrowserCell* browserCell = | |
| 391 static_cast<BookmarkTreeBrowserCell*>(cell); | |
| 392 [browserCell setTitle:base::SysWideToNSString(childNode->GetTitle())]; | |
| 393 [browserCell setBookmarkNode:childNode]; | |
| 394 [browserCell setMatrix:[folderBrowser_ matrixInColumn:column]]; | |
| 395 } | |
| 396 | |
| 397 @end // BookmarkEditorBaseController | 407 @end // BookmarkEditorBaseController |
| 398 | 408 |
| 409 @implementation BookmarkFolderInfo |
| 410 |
| 411 @synthesize folderName = folderName_; |
| 412 @synthesize folderNode = folderNode_; |
| 413 @synthesize children = children_; |
| 414 @synthesize newFolder = newFolder_; |
| 415 |
| 416 + (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName |
| 417 folderNode:(const BookmarkNode*)folderNode |
| 418 children:(NSMutableArray*)children { |
| 419 return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName |
| 420 folderNode:folderNode |
| 421 children:children |
| 422 newFolder:NO] |
| 423 autorelease]; |
| 424 } |
| 425 |
| 426 + (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName { |
| 427 return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName |
| 428 folderNode:NULL |
| 429 children:nil |
| 430 newFolder:YES] |
| 431 autorelease]; |
| 432 } |
| 433 |
| 434 - (id)initWithFolderName:(NSString*)folderName |
| 435 folderNode:(const BookmarkNode*)folderNode |
| 436 children:(NSMutableArray*)children |
| 437 newFolder:(BOOL)newFolder { |
| 438 if ((self = [super init])) { |
| 439 // A folderName is always required, and if newFolder is NO then there |
| 440 // should be a folderNode. Children is optional. |
| 441 DCHECK(folderName && (newFolder || folderNode)); |
| 442 if (folderName && (newFolder || folderNode)) { |
| 443 folderName_ = [folderName copy]; |
| 444 folderNode_ = folderNode; |
| 445 children_ = [children retain]; |
| 446 newFolder_ = newFolder; |
| 447 } else { |
| 448 NOTREACHED(); // Invalid init. |
| 449 [self release]; |
| 450 self = nil; |
| 451 } |
| 452 } |
| 453 return self; |
| 454 } |
| 455 |
| 456 - (id)init { |
| 457 NOTREACHED(); // Should never be called. |
| 458 return [self initWithFolderName:nil folderNode:nil children:nil newFolder:NO]; |
| 459 } |
| 460 |
| 461 - (void)dealloc { |
| 462 [folderName_ release]; |
| 463 [children_ release]; |
| 464 [super dealloc]; |
| 465 } |
| 466 |
| 467 @end |
| OLD | NEW |