Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(73)

Unified Diff: ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..6c0e9ce2b9bc59b20e444f5c303899b441a1bb74
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
@@ -0,0 +1,1345 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.h"
+
+#include <memory>
+
+#include "base/ios/block_types.h"
+#import "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/strings/grit/components_strings.h"
+#include "google_apis/gaia/google_service_auth_error.h"
+#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
+#include "ios/chrome/browser/bookmarks/bookmarks_utils.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#import "ios/chrome/browser/metrics/new_tab_page_uma.h"
+#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
+#import "ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.h"
+#import "ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_all_collection_view.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_primary_view.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_waiting_view.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_item.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_view.h"
+#include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
+#import "ios/chrome/browser/ui/rtl_geometry.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#import "ios/chrome/browser/ui/url_loader.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/referrer.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+#include "ui/base/page_transition_types.h"
+
+using bookmarks::BookmarkNode;
+
+namespace {
+// The width of the bookmark menu, displaying the different sections.
+const CGFloat kMenuWidth = 264.0;
+// The margin on top to the navigation bar.
+const CGFloat kNavigationBarTopMargin = 8.0;
+} // namespace
+
+// A simple UIView subclass to pass on relayout information to its delegate.
+@protocol ContentViewDelegate<NSObject>
+- (void)willLayoutSubviews;
+@end
+
+@interface ContentView : UIView
+@property(nonatomic, assign) id<ContentViewDelegate> delegate;
+@end
+
+@implementation ContentView
+@synthesize delegate = _delegate;
+
+- (void)layoutSubviews {
+ [self.delegate willLayoutSubviews];
+ [super layoutSubviews];
+}
+@end
+
+@interface BookmarkHomeTabletNTPController ()<
+ BookmarkEditViewControllerDelegate,
+ BookmarkFolderCollectionViewDelegate,
+ BookmarkFolderEditorViewControllerDelegate,
+ BookmarkFolderViewControllerDelegate,
+ BookmarkMenuViewDelegate,
+ BookmarkModelBridgeObserver,
+ BookmarkPromoControllerDelegate,
+ ContentViewDelegate> {
+ // Bridge to register for bookmark changes.
+ std::unique_ptr<bookmarks::BookmarkModelBridge> _bridge;
+ ios::ChromeBrowserState* _browserState; // Weak.
+ id<UrlLoader> _loader; // Weak.
+
+ // The following 2 ivars both represent the set of nodes being edited.
+ // The set is for fast lookup.
+ // The vector maintains the order that edit nodes were added.
+ // Use the relevant instance methods to modify these two ivars in tandem.
+ // DO NOT modify these two ivars directly.
+ std::set<const BookmarkNode*> _editNodes;
+ std::vector<const BookmarkNode*> _editNodesOrdered;
+
+ base::mac::ObjCPropertyReleaser
+ _propertyReleaser_BookmarkHomeTabletNTPController;
+}
+
+@property(nonatomic, retain) BookmarkPanelView* panelView;
+
+#pragma mark - Properties and methods akin to BookmarkHomeViewController
+
+// Whether the view controller is in editing mode.
+@property(nonatomic, assign) BOOL editing;
+// The set of edited index paths.
+@property(nonatomic, retain) NSMutableArray* editIndexPaths;
+// The bookmark model used.
+@property(nonatomic, assign, readonly) bookmarks::BookmarkModel* bookmarks;
+// The user's browser state model used.
+@property(nonatomic, assign, readonly)
+ ios::ChromeBrowserState* browserState; // from superclass.
+
+// Replaces |_editNodes| and |_editNodesOrdered| with new container objects.
+- (void)resetEditNodes;
+// Adds |node| corresponding to a |cell| if it isn't already present.
+- (void)insertEditNode:(const BookmarkNode*)node
+ atIndexPath:(NSIndexPath*)indexPath;
+// Removes |node| corresponding to a |cell| if it's present.
+- (void)removeEditNode:(const BookmarkNode*)node
+ atIndexPath:(NSIndexPath*)indexPath;
+// This method updates the property, and resets the edit nodes.
+- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
+
+#pragma mark - Properties and methods akin to BookmarkHomeHandsetViewController
+
+// This views holds the primary content of this controller. At any point in
+// time, it contains exactly one of the BookmarkCollectionView subclasses.
+@property(nonatomic, retain) ContentView* contentView;
+// The possible views that can be shown from the menu.
+@property(nonatomic, retain) BookmarkAllCollectionView* allItemsView;
+@property(nonatomic, retain) BookmarkFolderCollectionView* folderView;
+// This view is created and used if the model is not fully loaded yet by the
+// time this controller starts.
+@property(nonatomic, retain) BookmarkHomeWaitingView* waitForModelView;
+
+// The menu with all the folders and special entries.
+@property(nonatomic, retain) BookmarkMenuView* menuView;
+// At any point in time, there is exactly one collection view whose view is part
+// of the view hierarchy. This property determines which collection view is
+// visible. Not by accident, this property also reflects the selected menu item
+// in the BookmarkMenuView.
+@property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem;
+// When the view is first shown on the screen, this property represents the
+// cached value of the y of the content offset of the primary view. This
+// property is set to nil after it is used.
+@property(nonatomic, retain)
+ NSNumber* cachedContentPosition; // FIXME: INACTIVE
+
+// The navigation bar sits on top of the main content.
+@property(nonatomic, retain) BookmarkNavigationBar* navigationBar;
+// The editing bar present when items are selected.
+@property(nonatomic, retain) BookmarkEditingBar* editingBar;
+
+// The action sheet coordinator used when trying to edit a single bookmark.
+@property(nonatomic, retain) ActionSheetCoordinator* actionSheetCoordinator;
+// The view controller used to view and edit a single bookmark.
+@property(nonatomic, retain) BookmarkEditViewController* editViewController;
+// The view controller used to pick a folder in which to move the selected
+// bookmarks.
+@property(nonatomic, retain) BookmarkFolderViewController* folderSelector;
+// The view controller to present when editing the current folder.
+@property(nonatomic, retain)
+ BookmarkFolderEditorViewController* folderEditor; // FIX
+// The controller managing the display of the promo cell and the promo view
+// controller.
+@property(nonatomic, retain) BookmarkPromoController* bookmarkPromoController;
+
+#pragma mark Specific to this class.
+
+// Either the menu or the primaryView can scrollToTop.
+@property(nonatomic, assign) BOOL scrollToTop;
+
+// Opens the url.
+- (void)loadURL:(const GURL&)url;
+#pragma mark View loading, laying out, and switching.
+// This method is called if the view needs to be loaded and the model is not
+// ready yet.
+- (void)loadWaitingView;
+// This method should be called at most once in the life-cycle of the class.
+// It should be called at the soonest possible time after the view has been
+// loaded, and the bookmark model is loaded.
+- (void)loadBookmarkViews;
+// If the view doesn't exist, create it.
+- (void)ensureAllViewExists;
+- (void)ensureFolderViewExists;
+// Updates the property 'primaryMenuItem'.
+// Updates the UI to reflect the new state of 'primaryMenuItem'.
+- (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem
+ animated:(BOOL)animated;
+// The active collection view that corresponds to primaryMenuItem.
+- (UIView<BookmarkHomePrimaryView>*)primaryView;
+// Returns whether the menu should be in a side panel that slides in.
+- (BOOL)shouldPresentMenuInSlideInPanel;
+// Returns the width of the menu.
+- (CGFloat)menuWidth;
+// Returns the leading margin of the primary view.
+- (CGFloat)primaryViewLeadingMargin;
+// Moves the menu and primary view to their correct parent views depending on
+// the layout.
+- (void)moveMenuAndPrimaryViewToAdequateParent;
+// Updates the frame of the primary view.
+- (void)refreshFrameOfPrimaryView;
+// Returns the frame of the primary view.
+- (CGRect)frameForPrimaryView;
+
+#pragma mark Editing related methods
+// This method statelessly updates the editing top bar from |_editNodes| and
+// |editing|.
+- (void)updateEditingStateAnimated:(BOOL)animated;
+// Shows or hides the editing bar.
+- (void)showEditingBarAnimated:(BOOL)animated;
+- (void)hideEditingBarAnimated:(BOOL)animated;
+// Instaneously updates the shadow of the edit bar.
+// This method should be called anytime:
+// (1)|editing| property changes.
+// (2)The primary view changes.
+// (3)The primary view's collection view is scrolled.
+// When |editing| is NO, the shadow is never shown.
+- (void)updateEditBarShadow;
+
+#pragma mark Editing bar callbacks
+// The cancel button was tapped on the editing bar.
+- (void)editingBarCancel;
+// The move button was tapped on the editing bar.
+- (void)editingBarMove;
+// The delete button was tapped on the editing bar.
+- (void)editingBarDelete;
+// The edit button was tapped on the editing bar.
+- (void)editingBarEdit;
+// The menu button is pressed on the editing bar.
+- (void)toggleMenuAnimated;
+
+#pragma mark Action sheet callbacks
+// Enters into edit mode by selecting the given node corresponding to the
+// given cell.
+- (void)selectFirstNode:(const BookmarkNode*)node
+ withCell:(UICollectionViewCell*)cell;
+// Opens the editor on the given node and cell.
+- (void)editNode:(const BookmarkNode*)node withCell:(UICollectionViewCell*)cell;
+// Opens the folder move editor for the given node.
+- (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes;
+// Deletes the current node.
+- (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes;
+
+#pragma mark private utility methods
+// Deletes the nodes, and presents a toast with an undo button.
+- (void)deleteSelectedNodes;
+
+#pragma mark Navigation bar
+// Updates the UI of the navigation bar with the primaryMenuItem.
+// This method should be called anytime:
+// (1)The primary view changes.
+// (2)The primary view has type folder, and the relevant folder has changed.
+// (3)The interface orientation changes.
+// (4)viewWillAppear, as the interface orientation may have changed.
+- (void)updateNavigationBarAnimated:(BOOL)animated
+ orientation:(UIInterfaceOrientation)orientation;
+- (void)updateNavigationBarWithDuration:(CGFloat)duration
+ orientation:(UIInterfaceOrientation)orientation;
+// Whether the edit button on the navigation bar should be shown.
+- (BOOL)shouldShowEditButton;
+// Whether the back button on the navigation bar should be shown.
+- (BOOL)shouldShowBackButton;
+// Called when the edit button is pressed on the navigation bar.
+- (void)navigationBarWantsEditing:(id)sender;
+// Called when the back button is pressed on the navigation bar.
+- (void)navigationBarBack:(id)sender;
+
+// TODO(crbug.com/450646): This should not be needed but require refactoring of
+// the BookmarkCollectionViewDelegate.
+- (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell;
+
+@end
+
+@implementation BookmarkHomeTabletNTPController
+@synthesize editing = _editing;
+@synthesize editIndexPaths = _editIndexPaths;
+@synthesize bookmarks = _bookmarks;
+
+@synthesize contentView = _contentView;
+@synthesize allItemsView = _allItemsView;
+@synthesize folderView = _folderView;
+@synthesize waitForModelView = _waitForModelView;
+
+@synthesize menuView = _menuView;
+@synthesize primaryMenuItem = _primaryMenuItem;
+@synthesize cachedContentPosition = _cachedContentPosition;
+@synthesize navigationBar = _navigationBar;
+@synthesize editingBar = _editingBar;
+@synthesize panelView = _panelView;
+
+@synthesize actionSheetCoordinator = _actionSheetCoordinator;
+@synthesize editViewController = _editViewController;
+@synthesize folderSelector = _folderSelector;
+@synthesize folderEditor = _folderEditor;
+@synthesize bookmarkPromoController = _bookmarkPromoController;
+
+@synthesize scrollToTop = _scrollToTop;
+
+// Property declared in NewTabPagePanelProtocol.
+@synthesize delegate = _delegate;
+
+- (id)initWithLoader:(id<UrlLoader>)loader
+ browserState:(ios::ChromeBrowserState*)browserState {
+ self = [super init];
+ if (self) {
+ DCHECK(browserState);
+ _browserState = browserState->GetOriginalChromeBrowserState();
+ _loader = loader;
+ _propertyReleaser_BookmarkHomeTabletNTPController.Init(
+ self, [BookmarkHomeTabletNTPController class]);
+
+ _bookmarks = ios::BookmarkModelFactory::GetForBrowserState(_browserState);
+ _bridge.reset(new bookmarks::BookmarkModelBridge(self, _bookmarks));
+ _editIndexPaths = [[NSMutableArray alloc] init];
+ // It is important to initialize the promo controller with the browser state
+ // passed in, as it could be incognito.
+ _bookmarkPromoController =
+ [[BookmarkPromoController alloc] initWithBrowserState:browserState
+ delegate:self];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ _contentView.delegate = nil;
+
+ _allItemsView.delegate = nil;
+ _folderView.delegate = nil;
+
+ _menuView.delegate = nil;
+
+ _editViewController.delegate = nil;
+ _folderSelector.delegate = nil;
+
+ [super dealloc];
+}
+
+- (ios::ChromeBrowserState*)browserState {
+ return _browserState;
+}
+
+#pragma mark - ContentViewDelegate method.
+
+- (void)willLayoutSubviews {
+ if (![self primaryView] && ![self primaryMenuItem] &&
+ self.bookmarks->loaded()) {
+ BookmarkMenuItem* item = nil;
+ CGFloat position = 0;
+ BOOL found =
+ bookmark_utils_ios::GetPositionCache(self.bookmarks, &item, &position);
+ if (!found)
+ item = [self.menuView defaultMenuItem];
+
+ [self updatePrimaryMenuItem:item animated:NO];
+ }
+
+ [self moveMenuAndPrimaryViewToAdequateParent];
+ CGFloat leadingMargin = [self primaryViewLeadingMargin];
+
+ // Prevent the panelView from hijacking the gestures so that the
+ // NTPController's scrollview can still scroll with the gestures.
+ [self.panelView enableSideSwiping:NO];
+
+ CGFloat width = self.contentView.bounds.size.width;
+ LayoutRect navBarLayout =
+ LayoutRectMake(leadingMargin, width, 0, width - leadingMargin,
+ CGRectGetHeight([self navigationBarFrame]));
+ self.navigationBar.frame = LayoutRectGetRect(navBarLayout);
+ [self.editingBar setFrame:[self editingBarFrame]];
+
+ UIInterfaceOrientation orient = GetInterfaceOrientation();
+ [self refreshFrameOfPrimaryView];
+ [[self primaryView] changeOrientation:orient];
+ [self updateNavigationBarWithDuration:0 orientation:orient];
+ if (![self shouldPresentMenuInSlideInPanel])
+ [self updateMenuViewLayout];
+}
+
+#pragma mark HomeViewController simili methods.
+
+- (void)resetEditNodes {
+ _editNodes = std::set<const BookmarkNode*>();
+ _editNodesOrdered = std::vector<const BookmarkNode*>();
+ [self.editIndexPaths removeAllObjects];
+}
+
+- (void)insertEditNode:(const BookmarkNode*)node
+ atIndexPath:(NSIndexPath*)indexPath {
+ if (_editNodes.find(node) != _editNodes.end())
+ return;
+ _editNodes.insert(node);
+ _editNodesOrdered.push_back(node);
+ if (indexPath) {
+ [self.editIndexPaths addObject:indexPath];
+ } else {
+ // Insert null to keep the index valid.
+ [self.editIndexPaths addObject:[NSNull null]];
+ }
+}
+
+- (void)removeEditNode:(const BookmarkNode*)node
+ atIndexPath:(NSIndexPath*)indexPath {
+ if (_editNodes.find(node) == _editNodes.end())
+ return;
+ _editNodes.erase(node);
+ std::vector<const BookmarkNode*>::iterator it =
+ std::find(_editNodesOrdered.begin(), _editNodesOrdered.end(), node);
+ DCHECK(it != _editNodesOrdered.end());
+ _editNodesOrdered.erase(it);
+ if (indexPath) {
+ [self.editIndexPaths removeObject:indexPath];
+ } else {
+ // If we don't have the cell, we remove it by using its index.
+ const NSUInteger index = std::distance(_editNodesOrdered.begin(), it);
+ if (index < self.editIndexPaths.count) {
+ [self.editIndexPaths removeObjectAtIndex:index];
+ }
+ }
+
+ if (_editNodes.size() == 0)
+ [self setEditing:NO animated:YES];
+ else
+ [self updateEditingStateAnimated:YES];
+}
+
+#pragma mark - private methods
+
+- (void)loadURL:(const GURL&)url {
+ if (url == GURL() || url.SchemeIs(url::kJavaScriptScheme))
+ return;
+
+ new_tab_page_uma::RecordAction(self.browserState,
+ new_tab_page_uma::ACTION_OPENED_BOOKMARK);
+ base::RecordAction(base::UserMetricsAction("MobileNTPBookmark"));
+ [_loader loadURL:url
+ referrer:web::Referrer()
+ transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
+ rendererInitiated:NO];
+}
+
+#pragma mark - Views
+
+- (void)loadWaitingView {
+ DCHECK(!self.waitForModelView);
+ DCHECK(self.contentView);
+
+ // Present a waiting view.
+ base::scoped_nsobject<BookmarkHomeWaitingView> waitingView(
+ [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds]);
+ self.waitForModelView = waitingView;
+ [self.view addSubview:self.waitForModelView];
+ [self.waitForModelView startWaiting];
+}
+
+- (void)updateMenuViewLayout {
+ LayoutRect menuLayout =
+ LayoutRectMake(0, self.view.bounds.size.width, 0, self.menuWidth,
+ self.view.bounds.size.height);
+ self.menuView.frame = LayoutRectGetRect(menuLayout);
+}
+
+- (void)loadBookmarkViews {
+ DCHECK(self.bookmarks->loaded());
+
+ // Create the menu.
+ LayoutRect menuLayout =
+ LayoutRectMake(0, self.view.bounds.size.width, 0, self.menuWidth,
+ self.view.bounds.size.height);
+ self.menuView =
+ base::scoped_nsobject<BookmarkMenuView>([[BookmarkMenuView alloc]
+ initWithBrowserState:self.browserState
+ frame:LayoutRectGetRect(menuLayout)]);
+ self.menuView.delegate = self;
+ self.menuView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
+
+ [self moveMenuAndPrimaryViewToAdequateParent];
+
+ // Load the last primary menu item which the user had active.
+ BookmarkMenuItem* item = nil;
+ CGFloat position = 0;
+ BOOL found =
+ bookmark_utils_ios::GetPositionCache(self.bookmarks, &item, &position);
+ if (!found)
+ item = [self.menuView defaultMenuItem];
+
+ [self updatePrimaryMenuItem:item animated:NO];
+
+ [[self primaryView] applyContentPosition:position];
+
+ if (found) {
+ // If the view has already been laid out, then immediately apply the content
+ // position.
+ if (self.view.window) {
+ [[self primaryView] applyContentPosition:position];
+ } else {
+ // Otherwise, save the position to be applied once the view has been laid
+ // out.
+ self.cachedContentPosition = [NSNumber numberWithFloat:position];
+ }
+ }
+}
+
+- (void)ensureAllViewExists {
+ if (self.allItemsView)
+ return;
+
+ base::scoped_nsobject<BookmarkAllCollectionView> view(
+ [[BookmarkAllCollectionView alloc] initWithBrowserState:self.browserState
+ frame:CGRectZero]);
+ self.allItemsView = view;
+ self.allItemsView.delegate = self;
+ [self.allItemsView setEditing:self.editing animated:NO];
+ self.allItemsView.autoresizingMask =
+ UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+}
+
+- (void)ensureFolderViewExists {
+ if (self.folderView)
+ return;
+
+ base::scoped_nsobject<BookmarkFolderCollectionView> view(
+ [[BookmarkFolderCollectionView alloc]
+ initWithBrowserState:self.browserState
+ frame:CGRectZero]);
+ self.folderView = view;
+ self.folderView.delegate = self;
+ [self.folderView setEditing:self.editing animated:NO];
+ self.folderView.autoresizingMask =
+ UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+}
+
+- (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem
+ animated:(BOOL)animated {
+ if ([self.primaryMenuItem isEqual:menuItem])
+ return;
+
+ if (![self.contentView superview])
+ return;
+
+ [[self primaryView] removeFromSuperview];
+ self.primaryMenuItem = menuItem;
+
+ switch (self.primaryMenuItem.type) {
+ case bookmarks::MenuItemAll:
+ [self ensureAllViewExists];
+ break;
+ case bookmarks::MenuItemFolder:
+ [self ensureFolderViewExists];
+ [self.folderView resetFolder:self.primaryMenuItem.folder];
+ [self.folderView promoStateChangedAnimated:NO];
+ break;
+ case bookmarks::MenuItemDivider:
+ case bookmarks::MenuItemSectionHeader:
+ NOTREACHED();
+ break;
+ }
+
+ [[self primaryView] changeOrientation:GetInterfaceOrientation()];
+ [[self primaryView] setScrollsToTop:self.scrollToTop];
+
+ [self moveMenuAndPrimaryViewToAdequateParent];
+
+ // [self.contentView sendSubviewToBack:primaryView];
+ [self refreshFrameOfPrimaryView];
+
+ self.navigationBar.hidden = NO;
+ [self updateNavigationBarAnimated:animated
+ orientation:GetInterfaceOrientation()];
+
+ [self.menuView updatePrimaryMenuItem:self.primaryMenuItem];
+ [self updateEditBarShadow];
+}
+
+- (UIView<BookmarkHomePrimaryView>*)primaryView {
+ if (!self.primaryMenuItem)
+ return nil;
+ DCHECK([self contentView]);
+
+ switch (self.primaryMenuItem.type) {
+ case bookmarks::MenuItemAll:
+ return self.allItemsView;
+ case bookmarks::MenuItemFolder:
+ return self.folderView;
+ case bookmarks::MenuItemDivider:
+ case bookmarks::MenuItemSectionHeader:
+ NOTREACHED();
+ return nil;
+ }
+}
+
+- (BOOL)shouldPresentMenuInSlideInPanel {
+ return IsCompactTablet();
+}
+
+- (CGFloat)menuWidth {
+ return kMenuWidth;
+}
+
+- (CGFloat)primaryViewLeadingMargin {
+ if ([self shouldPresentMenuInSlideInPanel])
+ return 0;
+ return [self menuWidth];
+}
+
+- (void)moveMenuAndPrimaryViewToAdequateParent {
+ // Remove the menuView, panelView, and primaryView from the view hierarchy.
+ if ([self.menuView superview])
+ [self.menuView removeFromSuperview];
+ if ([self.panelView superview])
+ [self.panelView removeFromSuperview];
+ UIView* primaryView = [self primaryView];
+ if ([primaryView superview])
+ [primaryView removeFromSuperview];
+
+ if ([self shouldPresentMenuInSlideInPanel]) {
+ // Create (if needed), and add the panelView to the view hierarchy.
+ if (!self.panelView) {
+ self.panelView = base::scoped_nsobject<BookmarkPanelView>(
+ [[BookmarkPanelView alloc] initWithFrame:CGRectZero
+ menuViewWidth:[self menuWidth]]);
+ }
+ [self.view addSubview:self.panelView];
+ CGSize size = self.contentView.bounds.size;
+ CGFloat navBarHeight = CGRectGetHeight([self navigationBarFrame]);
+ LayoutRect panelLayout = LayoutRectMake(
+ 0, size.width, navBarHeight, size.width, size.height - navBarHeight);
+
+ // Initialize the panelView with the menuView and the primaryView.
+ [self.panelView setFrame:LayoutRectGetRect(panelLayout)];
+ [self.panelView.menuView addSubview:self.menuView];
+ self.menuView.frame = self.panelView.menuView.bounds;
+ [self.panelView.contentView addSubview:primaryView];
+ } else {
+ [self.view addSubview:self.menuView];
+ [self.view addSubview:primaryView];
+ }
+
+ // Make sure the navigation bar is the frontmost subview.
+ [self.view bringSubviewToFront:self.navigationBar];
+}
+
+- (void)refreshFrameOfPrimaryView {
+ [self primaryView].frame = [self frameForPrimaryView];
+}
+
+- (CGRect)frameForPrimaryView {
+ CGFloat topInset = 0;
+ if (!IsCompactTablet())
+ topInset = CGRectGetHeight([self navigationBarFrame]);
+
+ CGFloat leadingMargin = [self primaryViewLeadingMargin];
+ CGSize size = self.view.bounds.size;
+ LayoutRect primaryViewLayout =
+ LayoutRectMake(leadingMargin, size.width, topInset,
+ size.width - leadingMargin, size.height - topInset);
+ return LayoutRectGetRect(primaryViewLayout);
+}
+
+#pragma mark - Editing bar methods.
+
+- (CGRect)editingBarFrame {
+ return CGRectInset(self.navigationBar.frame, 24.0, 0);
+}
+
+- (void)updateEditingStateAnimated:(BOOL)animated {
+ if (!self.editing) {
+ [self hideEditingBarAnimated:animated];
+ [self updateEditBarShadow];
+ return;
+ }
+
+ if (!self.editingBar) {
+ self.editingBar = base::scoped_nsobject<BookmarkEditingBar>(
+ [[BookmarkEditingBar alloc] initWithFrame:[self editingBarFrame]]);
+ [self.editingBar setCancelTarget:self action:@selector(editingBarCancel)];
+ [self.editingBar setDeleteTarget:self action:@selector(editingBarDelete)];
+ [self.editingBar setMoveTarget:self action:@selector(editingBarMove)];
+ [self.editingBar setEditTarget:self action:@selector(editingBarEdit)];
+
+ [self.view addSubview:self.editingBar];
+ self.editingBar.hidden = YES;
+ }
+
+ int bookmarkCount = 0;
+ int folderCount = 0;
+ for (auto node : _editNodes) {
+ if (node->is_url())
+ ++bookmarkCount;
+ else
+ ++folderCount;
+ }
+ [self.editingBar updateUIWithBookmarkCount:bookmarkCount
+ folderCount:folderCount];
+
+ [self showEditingBarAnimated:animated];
+ [self updateEditBarShadow];
+}
+
+- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
+ if (_editing == editing)
+ return;
+
+ _editing = editing;
+
+ if (editing) {
+ self.bookmarkPromoController.promoState = NO;
+ } else {
+ // Only reset the editing state when leaving edit mode. This allows
+ // subclasses to add nodes for editing before entering edit mode.
+ [self resetEditNodes];
+ [self.bookmarkPromoController updatePromoState];
+ }
+
+ [self updateEditingStateAnimated:animated];
+ if ([[self primaryMenuItem] supportsEditing])
+ [[self primaryView] setEditing:editing animated:animated];
+}
+
+- (void)showEditingBarAnimated:(BOOL)animated {
+ CGRect endFrame = [self editingBarFrame];
+ if (self.editingBar.hidden) {
+ CGRect startFrame = endFrame;
+ startFrame.origin.y = -CGRectGetHeight(startFrame);
+ self.editingBar.frame = startFrame;
+ }
+ self.editingBar.hidden = NO;
+ [UIView animateWithDuration:animated ? 0.2 : 0
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ self.editingBar.frame = endFrame;
+ }
+ completion:^(BOOL finished) {
+ if (finished)
+ self.navigationBar.hidden = YES;
+ }];
+}
+
+- (void)hideEditingBarAnimated:(BOOL)animated {
+ CGRect frame = [self editingBarFrame];
+ if (!self.editingBar.hidden) {
+ frame.origin.y = -CGRectGetHeight(frame);
+ }
+ self.navigationBar.hidden = NO;
+ [UIView animateWithDuration:animated ? 0.2 : 0
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ self.editingBar.frame = frame;
+ }
+ completion:^(BOOL finished) {
+ if (finished)
+ self.editingBar.hidden = YES;
+ }];
+}
+
+- (void)updateEditBarShadow {
+ [self.editingBar showShadow:self.editing];
+}
+
+#pragma mark Editing Bar Callbacks
+
+- (void)editingBarCancel {
+ [self setEditing:NO animated:YES];
+}
+
+- (void)editingBarMove {
+ [self moveNodes:_editNodes];
+}
+
+- (void)editingBarDelete {
+ [self deleteSelectedNodes];
+ [self setEditing:NO animated:YES];
+}
+
+- (void)editingBarEdit {
+ DCHECK_EQ(_editNodes.size(), 1u);
+ const BookmarkNode* node = *(_editNodes.begin());
+ BookmarkItemCell* itemCell = nil;
+ if (self.editIndexPaths.count > 0) {
+ NSIndexPath* indexPath = [self.editIndexPaths firstObject];
+ if (indexPath && ![indexPath isKindOfClass:[NSNull class]]) {
+ UICollectionViewCell* cell =
+ [[self primaryView].collectionView cellForItemAtIndexPath:indexPath];
+ if ([cell isKindOfClass:[BookmarkItemCell class]]) {
+ itemCell = static_cast<BookmarkItemCell*>(cell);
+ }
+ }
+ }
+ [self editNode:node withCell:itemCell];
+}
+
+- (void)toggleMenuAnimated {
+ if ([self.panelView userDrivenAnimationInProgress])
+ return;
+
+ if (self.panelView.showingMenu) {
+ [self.panelView hideMenuAnimated:YES];
+ } else {
+ [self.panelView showMenuAnimated:YES];
+ }
+}
+
+#pragma mark - BookmarkMenuViewDelegate
+
+- (void)bookmarkMenuView:(BookmarkMenuView*)view
+ selectedMenuItem:(BookmarkMenuItem*)menuItem {
+ BOOL menuItemChanged = ![[self primaryMenuItem] isEqual:menuItem];
+ [self toggleMenuAnimated];
+ if (menuItemChanged) {
+ [self setEditing:NO animated:YES];
+ [self updatePrimaryMenuItem:menuItem animated:YES];
+ }
+}
+
+#pragma mark - BookmarkCollectionViewDelegate
+// This class owns multiple views that have a delegate that conforms to
+// BookmarkCollectionViewDelegate, or a subprotocol of
+// BookmarkCollectionViewDelegate.
+- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
+ cell:(UICollectionViewCell*)cell
+ addNodeForEditing:(const BookmarkNode*)node {
+ [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]];
+ [self updateEditingStateAnimated:YES];
+}
+
+- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
+ cell:(UICollectionViewCell*)cell
+ removeNodeForEditing:(const BookmarkNode*)node {
+ [self removeEditNode:node atIndexPath:[self indexPathForCell:cell]];
+}
+
+- (const std::set<const BookmarkNode*>&)nodesBeingEdited {
+ return _editNodes;
+}
+
+- (void)bookmarkCollectionViewDidScroll:(BookmarkCollectionView*)view {
+ [self updateEditBarShadow];
+}
+
+- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
+ selectedUrlForNavigation:(const GURL&)url {
+ [self cachePosition];
+ // Before passing the URL to the block, make sure the block has a copy of the
+ // URL and not just a reference.
+ const GURL localUrl(url);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self loadURL:localUrl];
+ });
+}
+
+- (void)bookmarkCollectionView:(BookmarkCollectionView*)collectionView
+ wantsMenuForBookmark:(const BookmarkNode*)node
+ onView:(UIView*)view
+ forCell:(BookmarkItemCell*)cell {
+ DCHECK(!self.editViewController);
+ DCHECK(!self.actionSheetCoordinator);
+ self.actionSheetCoordinator = [[[ActionSheetCoordinator alloc]
+ initWithBaseViewController:self.view.window.rootViewController
+ title:nil
+ message:nil
+ rect:view.bounds
+ view:view] autorelease];
+ base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
+
+ // Select action.
+ [self.actionSheetCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_SELECT)
+ action:^{
+ [weakSelf selectFirstNode:node withCell:cell];
+ weakSelf.get().actionSheetCoordinator = nil;
+ }
+ style:UIAlertActionStyleDefault];
+
+ // Edit action.
+ [self.actionSheetCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_EDIT)
+ action:^{
+ BookmarkItemCell* itemCell = nil;
+ if ([cell isKindOfClass:[BookmarkItemCell class]])
+ itemCell = static_cast<BookmarkItemCell*>(cell);
+ [weakSelf editNode:node withCell:itemCell];
+ weakSelf.get().actionSheetCoordinator = nil;
+ }
+ style:UIAlertActionStyleDefault];
+
+ // Move action.
+ [self.actionSheetCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_MOVE)
+ action:^{
+ std::set<const BookmarkNode*> nodes;
+ nodes.insert(node);
+ [weakSelf moveNodes:nodes];
+ weakSelf.get().actionSheetCoordinator = nil;
+ }
+ style:UIAlertActionStyleDefault];
+
+ // Delete action.
+ [self.actionSheetCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_DELETE)
+ action:^{
+ std::set<const BookmarkNode*> nodes;
+ nodes.insert(node);
+ [weakSelf deleteNodes:nodes];
+ weakSelf.get().actionSheetCoordinator = nil;
+ }
+ style:UIAlertActionStyleDestructive];
+
+ // Cancel action.
+ [self.actionSheetCoordinator
+ addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
+ action:^{
+ weakSelf.get().actionSheetCoordinator = nil;
+ }
+ style:UIAlertActionStyleCancel];
+
+ [self.actionSheetCoordinator start];
+}
+
+- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
+ didLongPressCell:(UICollectionViewCell*)cell
+ forBookmark:(const BookmarkNode*)node {
+ DCHECK(!self.editing);
+ [self selectFirstNode:node withCell:cell];
+}
+
+- (BOOL)bookmarkCollectionViewShouldShowPromoCell:
+ (BookmarkCollectionView*)collectionView {
+ return self.bookmarkPromoController.promoState;
+}
+
+- (void)bookmarkCollectionViewShowSignIn:(BookmarkCollectionView*)view {
+ [self.bookmarkPromoController showSignIn];
+}
+
+- (void)bookmarkCollectionViewDismissPromo:(BookmarkCollectionView*)view {
+ [self.bookmarkPromoController hidePromoCell];
+}
+
+#pragma mark Action Sheet Callbacks
+
+- (void)selectFirstNode:(const BookmarkNode*)node
+ withCell:(UICollectionViewCell*)cell {
+ DCHECK(!self.editing);
+ [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]];
+ [self setEditing:YES animated:YES];
+}
+
+- (void)editNode:(const BookmarkNode*)node withCell:(BookmarkItemCell*)cell {
+ DCHECK(!self.editViewController);
+ DCHECK(!self.folderEditor);
+ UIViewController* editorController = nil;
+ if (node->is_folder()) {
+ BookmarkFolderEditorViewController* folderEditor =
+ [BookmarkFolderEditorViewController
+ folderEditorWithBookmarkModel:self.bookmarks
+ folder:node
+ browserState:self.browserState];
+ folderEditor.delegate = self;
+ self.folderEditor = folderEditor;
+ editorController = folderEditor;
+ } else {
+ base::scoped_nsobject<BookmarkEditViewController> controller([
+ [BookmarkEditViewController alloc] initWithBookmark:node
+ browserState:self.browserState]);
+ self.editViewController = controller;
+ self.editViewController.delegate = self;
+ editorController = self.editViewController;
+ }
+ DCHECK(editorController);
+ base::scoped_nsobject<UINavigationController> navController(
+ [[BookmarkNavigationController alloc]
+ initWithRootViewController:editorController]);
+ navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
+ [self.view.window.rootViewController presentViewController:navController
+ animated:YES
+ completion:NULL];
+}
+
+- (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes {
+ DCHECK(!self.folderSelector);
+ DCHECK(nodes.size() > 0);
+ const BookmarkNode* editedNode = *(nodes.begin());
+ const BookmarkNode* selectedFolder = editedNode->parent();
+ self.folderSelector = base::scoped_nsobject<BookmarkFolderViewController>(
+ [[BookmarkFolderViewController alloc]
+ initWithBookmarkModel:self.bookmarks
+ allowsNewFolders:YES
+ editedNodes:nodes
+ allowsCancel:YES
+ selectedFolder:selectedFolder]);
+ self.folderSelector.delegate = self;
+ base::scoped_nsobject<UINavigationController> controller(
+ [[BookmarkNavigationController alloc]
+ initWithRootViewController:self.folderSelector]);
+ controller.get().modalPresentationStyle = UIModalPresentationFormSheet;
+ [self.view.window.rootViewController presentViewController:controller
+ animated:YES
+ completion:NULL];
+}
+
+- (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes {
+ DCHECK_GE(nodes.size(), 1u);
+ bookmark_utils_ios::DeleteBookmarksWithUndoToast(nodes, self.bookmarks,
+ self.browserState);
+}
+
+#pragma mark - BookmarkFolderCollectionViewDelegate
+
+- (void)bookmarkFolderCollectionView:(BookmarkFolderCollectionView*)view
+ selectedFolderForNavigation:(const BookmarkNode*)folder {
+ BookmarkMenuItem* menuItem = nil;
+ if (view == self.folderView) {
+ const BookmarkNode* parent = RootLevelFolderForNode(folder, self.bookmarks);
+ menuItem =
+ [BookmarkMenuItem folderMenuItemForNode:folder rootAncestor:parent];
+ } else {
+ NOTREACHED();
+ return;
+ }
+ [self updatePrimaryMenuItem:menuItem animated:YES];
+}
+
+#pragma mark - BookmarkEditViewControllerDelegate
+
+- (BOOL)bookmarkEditor:(BookmarkEditViewController*)controller
+ shoudDeleteAllOccurencesOfBookmark:(const BookmarkNode*)bookmark {
+ return NO;
+}
+
+- (void)bookmarkEditorWantsDismissal:(BookmarkEditViewController*)controller {
+ self.editViewController.delegate = nil;
+ self.editViewController = nil;
+ [controller dismissViewControllerAnimated:YES completion:NULL];
+
+ // The editViewController can be displayed from the menu button, or from the
+ // edit button in edit mode. Either way, after it's dismissed, edit mode
+ // should be off.
+ [self setEditing:NO animated:NO];
+}
+
+#pragma mark - BookmarkFolderViewControllerDelegate
+
+- (void)folderPicker:(BookmarkFolderViewController*)folderPicker
+ didFinishWithFolder:(const BookmarkNode*)folder {
+ DCHECK(folder);
+ DCHECK(!folder->is_url());
+ DCHECK_GE(folderPicker.editedNodes.size(), 1u);
+
+ bookmark_utils_ios::MoveBookmarksWithUndoToast(
+ folderPicker.editedNodes, self.bookmarks, folder, self.browserState);
+
+ [self setEditing:NO animated:NO];
+ [[folderPicker presentingViewController] dismissViewControllerAnimated:YES
+ completion:nil];
+ self.folderSelector.delegate = nil;
+ self.folderSelector = nil;
+}
+
+- (void)folderPickerDidCancel:(BookmarkFolderViewController*)folderPicker {
+ [self setEditing:NO animated:NO];
+ [[folderPicker presentingViewController] dismissViewControllerAnimated:YES
+ completion:nil];
+ self.folderSelector.delegate = nil;
+ self.folderSelector = nil;
+}
+
+#pragma mark - BookmarkFolderEditorViewControllerDelegate
+
+- (void)bookmarkFolderEditor:(BookmarkFolderEditorViewController*)folderEditor
+ didFinishEditingFolder:(const BookmarkNode*)folder {
+ DCHECK(folder);
+ [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
+ completion:nil];
+ self.folderEditor.delegate = nil;
+ self.folderEditor = nil;
+}
+
+- (void)bookmarkFolderEditorDidDeleteEditedFolder:
+ (BookmarkFolderEditorViewController*)folderEditor {
+ [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
+ completion:nil];
+ self.folderEditor.delegate = nil;
+ self.folderEditor = nil;
+}
+
+- (void)bookmarkFolderEditorDidCancel:
+ (BookmarkFolderEditorViewController*)folderEditor {
+ [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
+ completion:nil];
+ self.folderEditor.delegate = nil;
+ self.folderEditor = nil;
+}
+
+#pragma mark - Internal Utility Methods
+
+- (void)deleteSelectedNodes {
+ [self deleteNodes:_editNodes];
+}
+
+- (void)moveEditingNodesToFolder:(const BookmarkNode*)folder {
+ // The UI only supports moving nodes when there are at least one selected.
+ DCHECK_GE(_editNodes.size(), 1u);
+
+ bookmark_utils_ios::MoveBookmarksWithUndoToast(_editNodes, self.bookmarks,
+ folder, self.browserState);
+}
+
+- (void)cachePosition {
+ if ([self primaryView]) {
+ bookmark_utils_ios::CachePosition(
+ [[self primaryView] contentPositionInPortraitOrientation],
+ [self primaryMenuItem]);
+ }
+}
+
+#pragma mark - Navigation bar
+
+- (CGRect)navigationBarFrame {
+ return CGRectMake(0, 0, CGRectGetWidth(self.view.bounds),
+ [BookmarkNavigationBar expectedContentViewHeight] +
+ kNavigationBarTopMargin);
+}
+
+- (void)updateNavigationBarAnimated:(BOOL)animated
+ orientation:(UIInterfaceOrientation)orientation {
+ CGFloat duration = animated ? bookmark_utils_ios::menuAnimationDuration : 0;
+ [self updateNavigationBarWithDuration:duration orientation:orientation];
+}
+
+- (void)updateNavigationBarWithDuration:(CGFloat)duration
+ orientation:(UIInterfaceOrientation)orientation {
+ [self.navigationBar setTitle:[self.primaryMenuItem titleForNavigationBar]];
+ if ([self shouldShowEditButton])
+ [self.navigationBar showEditButtonWithAnimationDuration:duration];
+ else
+ [self.navigationBar hideEditButtonWithAnimationDuration:duration];
+
+ if ([self shouldShowBackButton])
+ [self.navigationBar showBackButtonInsteadOfMenuButton:duration];
+ else
+ [self.navigationBar showMenuButtonInsteadOfBackButton:duration];
+}
+
+- (BOOL)shouldShowEditButton {
+ if (self.primaryMenuItem.type != bookmarks::MenuItemFolder)
+ return NO;
+ // The type is MenuItemFolder, so it is safe to access |folder|.
+ return !self.bookmarks->is_permanent_node(self.primaryMenuItem.folder);
+}
+
+- (BOOL)shouldShowBackButton {
+ if (self.primaryMenuItem.type != bookmarks::MenuItemFolder)
+ return NO;
+ // The type is MenuItemFolder, so it is safe to access |folder|.
+ const BookmarkNode* folder = self.primaryMenuItem.folder;
+ // Show the back button iff the folder or its immediate parent is a permanent
+ // primary folder.
+ BOOL isTopFolder = IsPrimaryPermanentNode(folder, self.bookmarks) ||
+ IsPrimaryPermanentNode(folder->parent(), self.bookmarks);
+ return !isTopFolder;
+}
+
+#pragma mark Navigation Bar Callbacks
+
+- (void)navigationBarWantsEditing:(id)sender {
+ DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder);
+ const BookmarkNode* folder = self.primaryMenuItem.folder;
+ BookmarkFolderEditorViewController* folderEditor =
+ [BookmarkFolderEditorViewController
+ folderEditorWithBookmarkModel:self.bookmarks
+ folder:folder
+ browserState:self.browserState];
+ folderEditor.delegate = self;
+ self.folderEditor = folderEditor;
+
+ base::scoped_nsobject<BookmarkNavigationController> navController(
+ [[BookmarkNavigationController alloc]
+ initWithRootViewController:self.folderEditor]);
+ navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
+ [self.view.window.rootViewController presentViewController:navController
+ animated:YES
+ completion:NULL];
+}
+
+- (void)navigationBarBack:(id)sender {
+ DCHECK([self shouldShowBackButton]);
+
+ // Go to the parent folder.
+ DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder);
+ const BookmarkNode* parentFolder = self.primaryMenuItem.folder->parent();
+ const BookmarkNode* rootAncestor =
+ RootLevelFolderForNode(parentFolder, self.bookmarks);
+ BookmarkMenuItem* menuItem =
+ [BookmarkMenuItem folderMenuItemForNode:parentFolder
+ rootAncestor:rootAncestor];
+ [self updatePrimaryMenuItem:menuItem animated:YES];
+}
+
+#pragma mark - NewTabPagePanelProtocol
+
+- (void)reload {
+}
+
+- (void)wasShown {
+}
+
+- (void)wasHidden {
+ [self cachePosition];
+}
+
+- (void)dismissModals {
+ [self.actionSheetCoordinator stop];
+ self.actionSheetCoordinator = nil;
+ [self.editViewController dismiss];
+}
+
+- (void)dismissKeyboard {
+ // Uses self.contentView directly instead of going throught self.view to
+ // avoid creating the view hierarchy unnecessarily.
+ [self.contentView endEditing:YES];
+}
+
+- (void)setScrollsToTop:(BOOL)enabled {
+ self.scrollToTop = enabled;
+ [[self primaryView] setScrollsToTop:self.scrollToTop];
+}
+
+- (UIView*)view {
+ if (!self.contentView) {
+ _contentView = [[ContentView alloc] initWithFrame:CGRectZero];
+ _contentView.delegate = self;
+ self.contentView.backgroundColor =
+ bookmark_utils_ios::mainBackgroundColor();
+ base::scoped_nsobject<BookmarkNavigationBar> bar(
+ [[BookmarkNavigationBar alloc] initWithFrame:CGRectZero]);
+ self.navigationBar = bar;
+ self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+
+ [self.navigationBar setEditTarget:self
+ action:@selector(navigationBarWantsEditing:)];
+ [self.navigationBar setBackTarget:self
+ action:@selector(navigationBarBack:)];
+
+ [self.navigationBar setMenuTarget:self
+ action:@selector(toggleMenuAnimated)];
+
+ [self.view addSubview:self.navigationBar];
+
+ if (self.bookmarks->loaded())
+ [self loadBookmarkViews];
+ else
+ [self loadWaitingView];
+ }
+ return self.contentView;
+}
+
+- (CGFloat)alphaForBottomShadow {
+ return 0;
+}
+
+#pragma mark - BookmarkModelBridgeObserver
+
+- (void)bookmarkModelLoaded {
+ if (!self.contentView)
+ return;
+
+ DCHECK(self.waitForModelView);
+ base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
+ [self.waitForModelView stopWaitingWithCompletion:^{
+ base::scoped_nsobject<BookmarkHomeTabletNTPController> strongSelf(
+ [weakSelf retain]);
+ // Early return if the controller has been deallocated.
+ if (!strongSelf)
+ return;
+ [UIView animateWithDuration:0.2
+ animations:^{
+ strongSelf.get().waitForModelView.alpha = 0.0;
+ }
+ completion:^(BOOL finished) {
+ [strongSelf.get().waitForModelView removeFromSuperview];
+ strongSelf.get().waitForModelView = nil;
+ }];
+ [strongSelf loadBookmarkViews];
+ }];
+}
+
+- (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
+ // The title of the folder may have changed.
+ if (self.primaryMenuItem.type == bookmarks::MenuItemFolder &&
+ self.primaryMenuItem.folder == bookmarkNode) {
+ UIInterfaceOrientation orient = GetInterfaceOrientation();
+ [self updateNavigationBarAnimated:NO orientation:orient];
+ }
+}
+
+- (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
+ // The node has not changed, but the ordering and existence of its children
+ // have changed.
+}
+
+- (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
+ movedFromParent:(const BookmarkNode*)oldParent
+ toParent:(const BookmarkNode*)newParent {
+ // The node has moved to a new parent folder.
+}
+
+- (void)bookmarkNodeDeleted:(const BookmarkNode*)node
+ fromFolder:(const BookmarkNode*)folder {
+ [self removeEditNode:node atIndexPath:nil];
+}
+
+- (void)bookmarkModelRemovedAllNodes {
+ // All non-permanent nodes have been removed.
+ [self setEditing:NO animated:YES];
+}
+
+#pragma mark - BookmarkPromoControllerDelegate
+
+- (void)promoStateChanged:(BOOL)promoEnabled {
+ [self.allItemsView.collectionView reloadData];
+ // This is required to workaround a crash seen once on iOS 7.1 when
+ // the collection gets two reloadData without getting a call to layout
+ // subviews, the collection view will reuse some cached data for the perfoming
+ // the layout which are invalid after the second call to reloadData.
+ // Forcing the layout invalidation after each reloadData seems to fix the
+ // issue.
+ [self.allItemsView.collectionView.collectionViewLayout invalidateLayout];
+ [self.folderView
+ promoStateChangedAnimated:self.folderView == [self primaryView]];
+}
+
+- (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell {
+ DCHECK([self primaryView].collectionView);
+ NSIndexPath* indexPath =
+ [[self primaryView].collectionView indexPathForCell:cell];
+ return indexPath;
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698