Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / bookmarks / bookmark_editor_base_controller.mm
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stack>
6
7 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_base_controller.h"
8
9 #include "base/auto_reset.h"
10 #include "base/logging.h"
11 #include "base/mac/bundle_locations.h"
12 #include "base/mac/mac_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/bookmarks/chrome_bookmark_client.h"
16 #include "chrome/browser/bookmarks/chrome_bookmark_client_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_all_tabs_controller.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h"
20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h"
21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_tree_browser_cell.h"
22 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
23 #include "chrome/grit/generated_resources.h"
24 #include "components/bookmarks/browser/bookmark_model.h"
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/l10n/l10n_util_mac.h"
27
28 using bookmarks::BookmarkExpandedStateTracker;
29
30 @interface BookmarkEditorBaseController ()
31
32 // Return the folder tree object for the given path.
33 - (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)path;
34
35 // (Re)build the folder tree from the BookmarkModel's current state.
36 - (void)buildFolderTree;
37
38 // Notifies the controller that the bookmark model has changed.
39 // |selection| specifies if the current selection should be
40 // maintained (usually YES).
41 - (void)modelChangedPreserveSelection:(BOOL)preserve;
42
43 // Notifies the controller that a node has been removed.
44 - (void)nodeRemoved:(const BookmarkNode*)node
45          fromParent:(const BookmarkNode*)parent;
46
47 // Given a folder node, collect an array containing BookmarkFolderInfos
48 // describing its subchildren which are also folders.
49 - (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node;
50
51 // Scan the folder tree stemming from the given tree folder and create
52 // any newly added folders.  Pass down info for the folder which was
53 // selected before we began creating folders.
54 - (void)createNewFoldersForFolder:(BookmarkFolderInfo*)treeFolder
55                selectedFolderInfo:(BookmarkFolderInfo*)selectedFolderInfo;
56
57 // Scan the folder tree looking for the given bookmark node and return
58 // the selection path thereto.
59 - (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)node;
60
61 // Implementation of getExpandedNodes. See description in header for details.
62 - (void)getExpandedNodes:(BookmarkExpandedStateTracker::Nodes*)nodes
63                   folder:(BookmarkFolderInfo*)info
64                     path:(std::vector<NSUInteger>*)path
65                     root:(id)root;
66 @end
67
68 // static; implemented for each platform.  Update this function for new
69 // classes derived from BookmarkEditorBaseController.
70 void BookmarkEditor::Show(gfx::NativeWindow parent_window,
71                           Profile* profile,
72                           const EditDetails& details,
73                           Configuration configuration) {
74   if (details.type == EditDetails::EXISTING_NODE &&
75       details.existing_node->is_folder()) {
76     BookmarkNameFolderController* controller =
77         [[BookmarkNameFolderController alloc]
78             initWithParentWindow:parent_window
79                          profile:profile
80                             node:details.existing_node];
81     [controller runAsModalSheet];
82     return;
83   }
84
85   if (details.type == EditDetails::NEW_FOLDER && details.urls.empty()) {
86     BookmarkNameFolderController* controller =
87         [[BookmarkNameFolderController alloc]
88              initWithParentWindow:parent_window
89                           profile:profile
90                            parent:details.parent_node
91                          newIndex:details.index];
92      [controller runAsModalSheet];
93      return;
94   }
95
96   BookmarkEditorBaseController* controller = nil;
97   if (details.type == EditDetails::NEW_FOLDER) {
98     controller = [[BookmarkAllTabsController alloc]
99                   initWithParentWindow:parent_window
100                                profile:profile
101                                 parent:details.parent_node
102                                    url:details.url
103                                  title:details.title
104                          configuration:configuration];
105   } else {
106     controller = [[BookmarkEditorController alloc]
107                   initWithParentWindow:parent_window
108                                profile:profile
109                                 parent:details.parent_node
110                                   node:details.existing_node
111                                    url:details.url
112                                  title:details.title
113                          configuration:configuration];
114   }
115   [controller runAsModalSheet];
116 }
117
118 // Adapter to tell BookmarkEditorBaseController when bookmarks change.
119 class BookmarkEditorBaseControllerBridge : public BookmarkModelObserver {
120  public:
121   BookmarkEditorBaseControllerBridge(BookmarkEditorBaseController* controller)
122       : controller_(controller),
123         importing_(false)
124   { }
125
126   void BookmarkModelLoaded(BookmarkModel* model, bool ids_reassigned) override {
127     [controller_ modelChangedPreserveSelection:YES];
128   }
129
130   void BookmarkNodeMoved(BookmarkModel* model,
131                          const BookmarkNode* old_parent,
132                          int old_index,
133                          const BookmarkNode* new_parent,
134                          int new_index) override {
135     if (!importing_ && new_parent->GetChild(new_index)->is_folder())
136       [controller_ modelChangedPreserveSelection:YES];
137   }
138
139   void BookmarkNodeAdded(BookmarkModel* model,
140                          const BookmarkNode* parent,
141                          int index) override {
142     if (!importing_ && parent->GetChild(index)->is_folder())
143       [controller_ modelChangedPreserveSelection:YES];
144   }
145
146   void BookmarkNodeRemoved(BookmarkModel* model,
147                            const BookmarkNode* parent,
148                            int old_index,
149                            const BookmarkNode* node,
150                            const std::set<GURL>& removed_urls) override {
151     [controller_ nodeRemoved:node fromParent:parent];
152     if (node->is_folder())
153       [controller_ modelChangedPreserveSelection:NO];
154   }
155
156   void BookmarkAllUserNodesRemoved(
157       BookmarkModel* model,
158       const std::set<GURL>& removed_urls) override {
159     [controller_ modelChangedPreserveSelection:NO];
160   }
161
162   void BookmarkNodeChanged(BookmarkModel* model,
163                            const BookmarkNode* node) override {
164     if (!importing_ && node->is_folder())
165       [controller_ modelChangedPreserveSelection:YES];
166   }
167
168   void BookmarkNodeChildrenReordered(BookmarkModel* model,
169                                      const BookmarkNode* node) override {
170     if (!importing_)
171       [controller_ modelChangedPreserveSelection:YES];
172   }
173
174   void BookmarkNodeFaviconChanged(BookmarkModel* model,
175                                   const BookmarkNode* node) override {
176     // I care nothing for these 'favicons': I only show folders.
177   }
178
179   void ExtensiveBookmarkChangesBeginning(BookmarkModel* model) override {
180     importing_ = true;
181   }
182
183   // Invoked after a batch import finishes.  This tells observers to update
184   // themselves if they were waiting for the update to finish.
185   void ExtensiveBookmarkChangesEnded(BookmarkModel* model) override {
186     importing_ = false;
187     [controller_ modelChangedPreserveSelection:YES];
188   }
189
190  private:
191   BookmarkEditorBaseController* controller_;  // weak
192   bool importing_;
193 };
194
195
196 #pragma mark -
197
198 @implementation BookmarkEditorBaseController
199
200 @synthesize initialName = initialName_;
201 @synthesize displayName = displayName_;
202
203 - (id)initWithParentWindow:(NSWindow*)parentWindow
204                    nibName:(NSString*)nibName
205                    profile:(Profile*)profile
206                     parent:(const BookmarkNode*)parent
207                        url:(const GURL&)url
208                      title:(const base::string16&)title
209              configuration:(BookmarkEditor::Configuration)configuration {
210   NSString* nibpath = [base::mac::FrameworkBundle()
211                         pathForResource:nibName
212                                  ofType:@"nib"];
213   if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
214     parentWindow_ = parentWindow;
215     profile_ = profile;
216     parentNode_ = parent;
217     url_ = url;
218     title_ = title;
219     configuration_ = configuration;
220     initialName_ = [@"" retain];
221     observer_.reset(new BookmarkEditorBaseControllerBridge(self));
222     [self bookmarkModel]->AddObserver(observer_.get());
223   }
224   return self;
225 }
226
227 - (void)dealloc {
228   [self bookmarkModel]->RemoveObserver(observer_.get());
229   [initialName_ release];
230   [displayName_ release];
231   [super dealloc];
232 }
233
234 - (void)awakeFromNib {
235   [self setDisplayName:[self initialName]];
236
237   if (configuration_ != BookmarkEditor::SHOW_TREE) {
238     // Remember the tree view's height; we will shrink our frame by that much.
239     NSRect frame = [[self window] frame];
240     CGFloat browserHeight = [folderTreeView_ frame].size.height;
241     frame.size.height -= browserHeight;
242     frame.origin.y += browserHeight;
243     // Remove the folder tree and "new folder" button.
244     [folderTreeView_ removeFromSuperview];
245     [newFolderButton_ removeFromSuperview];
246     // Finally, commit the size change.
247     [[self window] setFrame:frame display:YES];
248   }
249
250   // Build up a tree of the current folder configuration.
251   [self buildFolderTree];
252 }
253
254 - (void)windowDidLoad {
255   if (configuration_ == BookmarkEditor::SHOW_TREE) {
256     [self selectNodeInBrowser:parentNode_];
257   }
258 }
259
260 /* TODO(jrg):
261 // Implementing this informal protocol allows us to open the sheet
262 // somewhere other than at the top of the window.  NOTE: this means
263 // that I, the controller, am also the window's delegate.
264 - (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet
265         usingRect:(NSRect)rect {
266   // adjust rect.origin.y to be the bottom of the toolbar
267   return rect;
268 }
269 */
270
271 // TODO(jrg): consider NSModalSession.
272 - (void)runAsModalSheet {
273   // Lock down floating bar when in full-screen mode.  Don't animate
274   // otherwise the pane will be misplaced.
275   [[BrowserWindowController browserWindowControllerForWindow:parentWindow_]
276    lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
277   [NSApp beginSheet:[self window]
278      modalForWindow:parentWindow_
279       modalDelegate:self
280      didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
281         contextInfo:nil];
282 }
283
284 // This constant has to match the name of the method after it.
285 NSString* const kOkEnabledName = @"okEnabled";
286 - (BOOL)okEnabled {
287   return YES;
288 }
289
290 - (IBAction)ok:(id)sender {
291   NSWindow* window = [self window];
292   [window makeFirstResponder:window];
293   // At least one of these two functions should be provided by derived classes.
294   BOOL hasWillCommit = [self respondsToSelector:@selector(willCommit)];
295   BOOL hasDidCommit = [self respondsToSelector:@selector(didCommit)];
296   DCHECK(hasWillCommit || hasDidCommit);
297   BOOL shouldContinue = YES;
298   if (hasWillCommit) {
299     NSNumber* hasWillContinue = [self performSelector:@selector(willCommit)];
300     if (hasWillContinue && [hasWillContinue isKindOfClass:[NSNumber class]])
301       shouldContinue = [hasWillContinue boolValue];
302   }
303   if (shouldContinue)
304     [self createNewFolders];
305   if (hasDidCommit) {
306     NSNumber* hasDidContinue = [self performSelector:@selector(didCommit)];
307     if (hasDidContinue && [hasDidContinue isKindOfClass:[NSNumber class]])
308       shouldContinue = [hasDidContinue boolValue];
309   }
310   if (shouldContinue)
311     [NSApp endSheet:window];
312 }
313
314 - (IBAction)cancel:(id)sender {
315   [NSApp endSheet:[self window]];
316 }
317
318 - (void)didEndSheet:(NSWindow*)sheet
319          returnCode:(int)returnCode
320         contextInfo:(void*)contextInfo {
321   [sheet close];
322   [[BrowserWindowController browserWindowControllerForWindow:parentWindow_]
323    releaseBarVisibilityForOwner:self withAnimation:YES delay:NO];
324 }
325
326 - (void)windowWillClose:(NSNotification*)notification {
327   [self autorelease];
328 }
329
330 #pragma mark Folder Tree Management
331
332 - (BookmarkModel*)bookmarkModel {
333   return BookmarkModelFactory::GetForProfile(profile_);
334 }
335
336 - (Profile*)profile {
337   return profile_;
338 }
339
340 - (const BookmarkNode*)parentNode {
341   return parentNode_;
342 }
343
344 - (const GURL&)url {
345   return url_;
346 }
347
348 - (const base::string16&)title{
349   return title_;
350 }
351
352 - (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)indexPath {
353   NSUInteger pathCount = [indexPath length];
354   BookmarkFolderInfo* item = nil;
355   NSArray* treeNode = [self folderTreeArray];
356   for (NSUInteger i = 0; i < pathCount; ++i) {
357     item = [treeNode objectAtIndex:[indexPath indexAtPosition:i]];
358     treeNode = [item children];
359   }
360   return item;
361 }
362
363 - (NSIndexPath*)selectedIndexPath {
364   NSIndexPath* selectedIndexPath = nil;
365   NSArray* selections = [self tableSelectionPaths];
366   if ([selections count]) {
367     DCHECK([selections count] == 1);  // Should be exactly one selection.
368     selectedIndexPath = [selections objectAtIndex:0];
369   }
370   return selectedIndexPath;
371 }
372
373 - (BookmarkFolderInfo*)selectedFolder {
374   BookmarkFolderInfo* item = nil;
375   NSIndexPath* selectedIndexPath = [self selectedIndexPath];
376   if (selectedIndexPath) {
377     item = [self folderForIndexPath:selectedIndexPath];
378   }
379   return item;
380 }
381
382 - (const BookmarkNode*)selectedNode {
383   const BookmarkNode* selectedNode = NULL;
384   // Determine a new parent node only if the browser is showing.
385   if (configuration_ == BookmarkEditor::SHOW_TREE) {
386     BookmarkFolderInfo* folderInfo = [self selectedFolder];
387     if (folderInfo)
388       selectedNode = [folderInfo folderNode];
389   } else {
390     // If the tree is not showing then we use the original parent.
391     selectedNode = parentNode_;
392   }
393   return selectedNode;
394 }
395
396 - (void)expandNodes:(const BookmarkExpandedStateTracker::Nodes&)nodes {
397   id treeControllerRoot = [folderTreeController_ arrangedObjects];
398   for (BookmarkExpandedStateTracker::Nodes::const_iterator i = nodes.begin();
399        i != nodes.end(); ++i) {
400     NSIndexPath* path = [self selectionPathForNode:*i];
401     id folderNode = [treeControllerRoot descendantNodeAtIndexPath:path];
402     [folderTreeView_ expandItem:folderNode];
403   }
404 }
405
406 - (BookmarkExpandedStateTracker::Nodes)getExpandedNodes {
407   BookmarkExpandedStateTracker::Nodes nodes;
408   std::vector<NSUInteger> path;
409   NSArray* folderNodes = [self folderTreeArray];
410   NSUInteger i = 0;
411   for (BookmarkFolderInfo* folder in folderNodes) {
412     path.push_back(i);
413     [self getExpandedNodes:&nodes
414                     folder:folder
415                       path:&path
416                       root:[folderTreeController_ arrangedObjects]];
417     path.clear();
418     ++i;
419   }
420   return nodes;
421 }
422
423 - (void)getExpandedNodes:(BookmarkExpandedStateTracker::Nodes*)nodes
424                   folder:(BookmarkFolderInfo*)folder
425                     path:(std::vector<NSUInteger>*)path
426                     root:(id)root {
427   NSIndexPath* indexPath = [NSIndexPath indexPathWithIndexes:&(path->front())
428                                                       length:path->size()];
429   id node = [root descendantNodeAtIndexPath:indexPath];
430   if (![folderTreeView_ isItemExpanded:node])
431     return;
432   nodes->insert([folder folderNode]);
433   NSArray* children = [folder children];
434   NSUInteger i = 0;
435   for (BookmarkFolderInfo* childFolder in children) {
436     path->push_back(i);
437     [self getExpandedNodes:nodes folder:childFolder path:path root:root];
438     path->pop_back();
439     ++i;
440   }
441 }
442
443 - (NSArray*)folderTreeArray {
444   return folderTreeArray_.get();
445 }
446
447 - (NSArray*)tableSelectionPaths {
448   return tableSelectionPaths_.get();
449 }
450
451 - (void)setTableSelectionPath:(NSIndexPath*)tableSelectionPath {
452   [self setTableSelectionPaths:[NSArray arrayWithObject:tableSelectionPath]];
453 }
454
455 - (void)setTableSelectionPaths:(NSArray*)tableSelectionPaths {
456   tableSelectionPaths_.reset([tableSelectionPaths retain]);
457 }
458
459 - (void)selectNodeInBrowser:(const BookmarkNode*)node {
460   DCHECK(configuration_ == BookmarkEditor::SHOW_TREE);
461   NSIndexPath* selectionPath = [self selectionPathForNode:node];
462   [self willChangeValueForKey:kOkEnabledName];
463   [self setTableSelectionPath:selectionPath];
464   [self didChangeValueForKey:kOkEnabledName];
465 }
466
467 - (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)desiredNode {
468   // Back up the parent chaing for desiredNode, building up a stack
469   // of ancestor nodes.  Then crawl down the folderTreeArray looking
470   // for each ancestor in order while building up the selectionPath.
471   std::stack<const BookmarkNode*> nodeStack;
472   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
473   const BookmarkNode* rootNode = model->root_node();
474   const BookmarkNode* node = desiredNode;
475   while (node != rootNode) {
476     DCHECK(node);
477     nodeStack.push(node);
478     node = node->parent();
479   }
480   NSUInteger stackSize = nodeStack.size();
481
482   NSIndexPath* path = nil;
483   NSArray* folders = [self folderTreeArray];
484   while (!nodeStack.empty()) {
485     node = nodeStack.top();
486     nodeStack.pop();
487     // Find node in the current folders array.
488     NSUInteger i = 0;
489     for (BookmarkFolderInfo *folderInfo in folders) {
490       const BookmarkNode* testNode = [folderInfo folderNode];
491       if (testNode == node) {
492         path = path ? [path indexPathByAddingIndex:i] :
493         [NSIndexPath indexPathWithIndex:i];
494         folders = [folderInfo children];
495         break;
496       }
497       ++i;
498     }
499   }
500   DCHECK([path length] == stackSize);
501   return path;
502 }
503
504 - (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node {
505   ChromeBookmarkClient* client =
506       ChromeBookmarkClientFactory::GetForProfile(profile_);
507   NSMutableArray* childFolders = nil;
508   int childCount = node->child_count();
509   for (int i = 0; i < childCount; ++i) {
510     const BookmarkNode* childNode = node->GetChild(i);
511     if (childNode->is_folder() && childNode->IsVisible() &&
512         client->CanBeEditedByUser(childNode)) {
513       NSString* childName = base::SysUTF16ToNSString(childNode->GetTitle());
514       NSMutableArray* children = [self addChildFoldersFromNode:childNode];
515       BookmarkFolderInfo* folderInfo =
516           [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:childName
517                                                     folderNode:childNode
518                                                       children:children];
519       if (!childFolders)
520         childFolders = [NSMutableArray arrayWithObject:folderInfo];
521       else
522         [childFolders addObject:folderInfo];
523     }
524   }
525   return childFolders;
526 }
527
528 - (void)buildFolderTree {
529   // Build up a tree of the current folder configuration.
530   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile_);
531   const BookmarkNode* rootNode = model->root_node();
532   NSMutableArray* baseArray = [self addChildFoldersFromNode:rootNode];
533   DCHECK(baseArray);
534   [self willChangeValueForKey:@"folderTreeArray"];
535   folderTreeArray_.reset([baseArray retain]);
536   [self didChangeValueForKey:@"folderTreeArray"];
537 }
538
539 - (void)modelChangedPreserveSelection:(BOOL)preserve {
540   if (creatingNewFolders_)
541     return;
542   const BookmarkNode* selectedNode = [self selectedNode];
543   [self buildFolderTree];
544   if (preserve &&
545       selectedNode &&
546       configuration_ == BookmarkEditor::SHOW_TREE)
547     [self selectNodeInBrowser:selectedNode];
548 }
549
550 - (void)nodeRemoved:(const BookmarkNode*)node
551          fromParent:(const BookmarkNode*)parent {
552   if (node->is_folder()) {
553     if (parentNode_ == node || parentNode_->HasAncestor(node)) {
554       parentNode_ = [self bookmarkModel]->bookmark_bar_node();
555       if (configuration_ != BookmarkEditor::SHOW_TREE) {
556         // The user can't select a different folder, so just close up shop.
557         [self cancel:self];
558         return;
559       }
560     }
561
562     if (configuration_ == BookmarkEditor::SHOW_TREE) {
563       // For safety's sake, in case deleted node was an ancestor of selection,
564       // go back to a known safe place.
565       [self selectNodeInBrowser:parentNode_];
566     }
567   }
568 }
569
570 #pragma mark New Folder Handler
571
572 - (void)createNewFoldersForFolder:(BookmarkFolderInfo*)folderInfo
573                selectedFolderInfo:(BookmarkFolderInfo*)selectedFolderInfo {
574   NSArray* subfolders = [folderInfo children];
575   const BookmarkNode* parentNode = [folderInfo folderNode];
576   DCHECK(parentNode);
577   NSUInteger i = 0;
578   for (BookmarkFolderInfo* subFolderInfo in subfolders) {
579     if ([subFolderInfo newFolder]) {
580       BookmarkModel* model = [self bookmarkModel];
581       const BookmarkNode* newFolder =
582           model->AddFolder(parentNode, i,
583               base::SysNSStringToUTF16([subFolderInfo folderName]));
584       // Update our dictionary with the actual folder node just created.
585       [subFolderInfo setFolderNode:newFolder];
586       [subFolderInfo setNewFolder:NO];
587     }
588     [self createNewFoldersForFolder:subFolderInfo
589                  selectedFolderInfo:selectedFolderInfo];
590     ++i;
591   }
592 }
593
594 - (IBAction)newFolder:(id)sender {
595   // Create a new folder off of the selected folder node.
596   BookmarkFolderInfo* parentInfo = [self selectedFolder];
597   if (parentInfo) {
598     NSIndexPath* selection = [self selectedIndexPath];
599     NSString* newFolderName =
600         l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_EDITOR_NEW_FOLDER_NAME);
601     BookmarkFolderInfo* folderInfo =
602         [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:newFolderName];
603     [self willChangeValueForKey:@"folderTreeArray"];
604     NSMutableArray* children = [parentInfo children];
605     if (children) {
606       [children addObject:folderInfo];
607     } else {
608       children = [NSMutableArray arrayWithObject:folderInfo];
609       [parentInfo setChildren:children];
610     }
611     [self didChangeValueForKey:@"folderTreeArray"];
612
613     // Expose the parent folder children.
614     [folderTreeView_ expandItem:parentInfo];
615
616     // Select the new folder node and put the folder name into edit mode.
617     selection = [selection indexPathByAddingIndex:[children count] - 1];
618     [self setTableSelectionPath:selection];
619     NSInteger row = [folderTreeView_ selectedRow];
620     DCHECK(row >= 0);
621
622     // Put the cell into single-line mode before putting it into edit mode.
623     // TODO(kushi.p): Remove this when the project hits a 10.6+ only state.
624     NSCell* folderCell = [folderTreeView_ preparedCellAtColumn:0 row:row];
625     if ([folderCell
626           respondsToSelector:@selector(setUsesSingleLineMode:)]) {
627       [folderCell setUsesSingleLineMode:YES];
628     }
629
630     [folderTreeView_ editColumn:0 row:row withEvent:nil select:YES];
631   }
632 }
633
634 - (void)createNewFolders {
635   base::AutoReset<BOOL> creatingNewFoldersSetter(&creatingNewFolders_, YES);
636   // Scan the tree looking for nodes marked 'newFolder' and create those nodes.
637   NSArray* folderTreeArray = [self folderTreeArray];
638   for (BookmarkFolderInfo *folderInfo in folderTreeArray) {
639     [self createNewFoldersForFolder:folderInfo
640                  selectedFolderInfo:[self selectedFolder]];
641   }
642 }
643
644 #pragma mark For Unit Test Use Only
645
646 - (BOOL)okButtonEnabled {
647   return [okButton_ isEnabled];
648 }
649
650 - (void)selectTestNodeInBrowser:(const BookmarkNode*)node {
651   [self selectNodeInBrowser:node];
652 }
653
654 @end  // BookmarkEditorBaseController
655
656 @implementation BookmarkFolderInfo
657
658 @synthesize folderName = folderName_;
659 @synthesize folderNode = folderNode_;
660 @synthesize children = children_;
661 @synthesize newFolder = newFolder_;
662
663 + (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName
664                             folderNode:(const BookmarkNode*)folderNode
665                               children:(NSMutableArray*)children {
666   return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName
667                                               folderNode:folderNode
668                                                 children:children
669                                                newFolder:NO]
670           autorelease];
671 }
672
673 + (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName {
674   return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName
675                                               folderNode:NULL
676                                                 children:nil
677                                                newFolder:YES]
678           autorelease];
679 }
680
681 - (id)initWithFolderName:(NSString*)folderName
682               folderNode:(const BookmarkNode*)folderNode
683                 children:(NSMutableArray*)children
684                newFolder:(BOOL)newFolder {
685   if ((self = [super init])) {
686     // A folderName is always required, and if newFolder is NO then there
687     // should be a folderNode.  Children is optional.
688     DCHECK(folderName && (newFolder || folderNode));
689     if (folderName && (newFolder || folderNode)) {
690       folderName_ = [folderName copy];
691       folderNode_ = folderNode;
692       children_ = [children retain];
693       newFolder_ = newFolder;
694     } else {
695       NOTREACHED();  // Invalid init.
696       [self release];
697       self = nil;
698     }
699   }
700   return self;
701 }
702
703 - (id)init {
704   NOTREACHED();  // Should never be called.
705   return [self initWithFolderName:nil folderNode:nil children:nil newFolder:NO];
706 }
707
708 - (void)dealloc {
709   [folderName_ release];
710   [children_ release];
711   [super dealloc];
712 }
713
714 // Implementing isEqual: allows the NSTreeController to preserve the selection
715 // and open/shut state of outline items when the data changes.
716 - (BOOL)isEqual:(id)other {
717   return [other isKindOfClass:[BookmarkFolderInfo class]] &&
718       folderNode_ == [(BookmarkFolderInfo*)other folderNode];
719 }
720
721 @end