1// Copyright (c) 2011 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#ifndef CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_
6#define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_
7#pragma once
8
9#import <Cocoa/Cocoa.h>
10
11#include "base/memory/scoped_nsobject.h"
12#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
13#import "chrome/browser/ui/cocoa/tracking_area.h"
14
15@class BookmarkBarController;
16@class BookmarkBarFolderView;
17@class BookmarkFolderTarget;
18@class BookmarkBarFolderHoverState;
19@class BookmarkBarFolderWindow;
20@class BookmarkBarFolderWindowContentView;
21
22// A controller for the pop-up windows from bookmark folder buttons
23// which look sort of like menus.
24@interface BookmarkBarFolderController :
25    NSWindowController<BookmarkButtonDelegate,
26                       BookmarkButtonControllerProtocol,
27                       NSUserInterfaceValidations> {
28 @private
29  // The button whose click opened us.
30  scoped_nsobject<BookmarkButton> parentButton_;
31
32  // Bookmark bar folder controller chains are torn down in two ways:
33  // 1. Clicking "outside" the folder (see use of
34  // CrApplicationEventHookProtocol in the bookmark bar controller).
35  // 2. Engaging a different folder (via hover over or explicit click).
36  //
37  // In either case, the BookmarkButtonControllerProtocol method
38  // closeAllBookmarkFolders gets called.  For bookmark bar folder
39  // controllers, this is passed up the chain so we begin with a top
40  // level "close".
41  // When any bookmark folder window closes, it necessarily tells
42  // subcontroller windows to close (down the chain), and autoreleases
43  // the controller.  (Must autorelease since the controller can still
44  // get delegate events such as windowDidClose).
45  //
46  // Bookmark bar folder controllers own their buttons.  When doing
47  // drag and drop of a button from one sub-sub-folder to a different
48  // sub-sub-folder, we need to make sure the button's pointers stay
49  // valid until we've dropped (or cancelled).  Note that such a drag
50  // causes the source sub-sub-folder (previous parent window) to go
51  // away (windows close, controllers autoreleased) since you're
52  // hovering over a different folder chain for dropping.  To keep
53  // things valid (like the button's target, its delegate, the parent
54  // cotroller that we have a pointer to below [below], etc), we heep
55  // strong pointers to our owning controller, so the entire chain
56  // stays owned.
57
58  // Our parent controller, if we are a nested folder, otherwise nil.
59  // Strong to insure the object lives as long as we need it.
60  scoped_nsobject<BookmarkBarFolderController> parentController_;
61
62  // The main bar controller from whence we or a parent sprang.
63  BookmarkBarController* barController_;  // WEAK: It owns us.
64
65  // Our buttons.  We do not have buttons for nested folders.
66  scoped_nsobject<NSMutableArray> buttons_;
67
68  // The scroll view that contains our main button view (below).
69  IBOutlet NSScrollView* scrollView_;
70
71  // The view defining the visible area in which we draw our content.
72  IBOutlet BookmarkBarFolderWindowContentView* visibleView_;
73
74  // The main view of this window (where the buttons go) within the scroller.
75  IBOutlet BookmarkBarFolderView* folderView_;
76
77  // A window used to show the shadow behind the main window when it is
78  // scrollable. (A 'shadow' window is needed because the main window, when
79  // scrollable in either or both directions, will reach completely to the
80  // top and/or bottom edge of the screen in order to support mouse tracking
81  // during scrolling operations. In that case, though, the 'visible'
82  // window must be inset a bit from the edge of the screen for aesthetics;
83  // it will also be inset much more from the bottom of the screen when the
84  // Dock is showing. When scrollable, the main window would show a shadow
85  // incorrectly positioned, hence the 'shadow' window.)
86  IBOutlet BookmarkBarFolderWindow* shadowWindow_;
87
88  // The up and down scroll arrow views. These arrows are hidden and shown
89  // as necessary (when scrolling is possible) and are contained in the nib
90  // as siblings to the scroll view.
91  IBOutlet NSView* scrollDownArrowView_;  // Positioned at the top.
92  IBOutlet NSView* scrollUpArrowView_;  // Positioned at the bottom.
93
94  // YES if subfolders should grow to the right (the default).
95  // Direction switches if we'd grow off the screen.
96  BOOL subFolderGrowthToRight_;
97
98  // Weak; we keep track to work around a
99  // setShowsBorderOnlyWhileMouseInside bug.
100  BookmarkButton* buttonThatMouseIsIn_;
101
102  // The context menu for a bookmark button which represents an URL.
103  IBOutlet NSMenu* buttonMenu_;
104
105  // The context menu for a bookmark button which represents a folder.
106  IBOutlet NSMenu* folderMenu_;
107
108  // We model hover state as a state machine with specific allowable
109  // transitions.  |hoverState_| is the state of this machine at any
110  // given time.
111  scoped_nsobject<BookmarkBarFolderHoverState> hoverState_;
112
113  // Logic for dealing with a click on a bookmark folder button.
114  scoped_nsobject<BookmarkFolderTarget> folderTarget_;
115
116  // A controller for a pop-up bookmark folder window (custom menu).
117  // We (self) are the parentController_ for our folderController_.
118  // This is not a scoped_nsobject because it owns itself (when its
119  // window closes the controller gets autoreleased).
120  BookmarkBarFolderController* folderController_;
121
122  // Implement basic menu scrolling through this tracking area.
123  ScopedCrTrackingArea scrollTrackingArea_;
124
125  // Timer to continue scrolling as needed.  We own the timer but
126  // don't release it when done (we invalidate it).
127  NSTimer* scrollTimer_;
128
129  // Precalculated sum of left and right edge padding of buttons in a
130  // folder menu window. This is calculated from the widths of the main
131  // folder menu window and the scroll view within.
132  CGFloat padding_;
133
134  // Amount to scroll by on each timer fire.  Can be + or -.
135  CGFloat verticalScrollDelta_;
136
137  // We need to know the size of the vertical scrolling arrows so we
138  // can obscure/unobscure them.
139  CGFloat verticalScrollArrowHeight_;
140
141  // Set to YES to prevent any node animations. Useful for unit testing so that
142  // incomplete animations do not cause valgrind complaints.
143  BOOL ignoreAnimations_;
144
145  int selectedIndex_;
146  NSString* typedPrefix_;
147}
148
149// Designated initializer.
150- (id)initWithParentButton:(BookmarkButton*)button
151          parentController:(BookmarkBarFolderController*)parentController
152             barController:(BookmarkBarController*)barController;
153
154// Return the parent button that owns the bookmark folder we represent.
155- (BookmarkButton*)parentButton;
156
157// Text typed by user, for type-select and arrow key support.
158// Returns YES if the menu should be closed now.
159- (BOOL)handleInputText:(NSString*)newText;
160
161// If you wanted to clear the type-select buffer. Currently only used
162// internally.
163- (void)clearInputText;
164
165// Gets notified when a fav icon asynchronously loads, so we can now use the
166// real icon instead of a generic placeholder.
167- (void)faviconLoadedForNode:(const BookmarkNode*)node;
168
169- (void)setSelectedButtonByIndex:(int)index;
170
171// Offset our folder menu window. This is usually needed in response to a
172// parent folder menu window or the bookmark bar changing position due to
173// the dragging of a bookmark node from the parent into this folder menu.
174- (void)offsetFolderMenuWindow:(NSSize)offset;
175
176// Re-layout the window menu in case some buttons were added or removed,
177// specifically as a result of the bookmark bar changing configuration
178// and altering the contents of the off-the-side folder.
179- (void)reconfigureMenu;
180
181// Actions from a context menu over a button or folder.
182- (IBAction)cutBookmark:(id)sender;
183- (IBAction)copyBookmark:(id)sender;
184- (IBAction)pasteBookmark:(id)sender;
185- (IBAction)deleteBookmark:(id)sender;
186
187// Passed up by a child view to tell us of a desire to scroll.
188- (void)scrollWheel:(NSEvent *)theEvent;
189
190- (void)mouseDragged:(NSEvent*)theEvent;
191
192
193// Forwarded to the associated BookmarkBarController.
194- (IBAction)addFolder:(id)sender;
195- (IBAction)addPage:(id)sender;
196- (IBAction)editBookmark:(id)sender;
197- (IBAction)openBookmark:(id)sender;
198- (IBAction)openAllBookmarks:(id)sender;
199- (IBAction)openAllBookmarksIncognitoWindow:(id)sender;
200- (IBAction)openAllBookmarksNewWindow:(id)sender;
201- (IBAction)openBookmarkInIncognitoWindow:(id)sender;
202- (IBAction)openBookmarkInNewForegroundTab:(id)sender;
203- (IBAction)openBookmarkInNewWindow:(id)sender;
204
205@property(assign, nonatomic) BOOL subFolderGrowthToRight;
206
207@end
208
209@interface BookmarkBarFolderController(TestingAPI)
210- (NSPoint)windowTopLeftForWidth:(int)windowWidth
211                          height:(int)windowHeight;
212- (NSArray*)buttons;
213- (BookmarkBarFolderController*)folderController;
214- (id)folderTarget;
215- (void)configureWindowLevel;
216- (void)performOneScroll:(CGFloat)delta;
217- (BookmarkButton*)buttonThatMouseIsIn;
218// Set to YES in order to prevent animations.
219- (void)setIgnoreAnimations:(BOOL)ignore;
220
221// Return YES if the scroll-up or scroll-down arrows are showing.
222- (BOOL)canScrollUp;
223- (BOOL)canScrollDown;
224- (CGFloat)verticalScrollArrowHeight;
225- (NSView*)visibleView;
226- (NSScrollView*)scrollView;
227- (NSView*)folderView;
228
229- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point;
230@end
231
232#endif  // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_CONTROLLER_H_
233