OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.h" | 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.h" |
6 | 6 |
7 #include "base/mac/bundle_locations.h" | 7 #include "base/mac/bundle_locations.h" |
8 #include "base/strings/sys_string_conversions.h" | 8 #include "base/strings/sys_string_conversions.h" |
| 9 #include "chrome/browser/ui/bookmarks/bookmark_bubble_observer.h" |
9 #include "chrome/browser/ui/browser.h" | 10 #include "chrome/browser/ui/browser.h" |
10 #include "chrome/browser/ui/browser_finder.h" | 11 #include "chrome/browser/ui/browser_finder.h" |
11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" | 12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" |
12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.h" | 13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_sync_promo_controller.h" |
13 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 14 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
14 #import "chrome/browser/ui/cocoa/info_bubble_view.h" | 15 #import "chrome/browser/ui/cocoa/info_bubble_view.h" |
15 #include "chrome/browser/ui/sync/sync_promo_ui.h" | 16 #include "chrome/browser/ui/sync/sync_promo_ui.h" |
16 #include "chrome/grit/generated_resources.h" | 17 #include "chrome/grit/generated_resources.h" |
17 #include "components/bookmarks/browser/bookmark_model.h" | 18 #include "components/bookmarks/browser/bookmark_model.h" |
18 #include "components/bookmarks/browser/bookmark_utils.h" | 19 #include "components/bookmarks/browser/bookmark_utils.h" |
(...skipping 28 matching lines...) Expand all Loading... |
47 // Singleton object to act as a representedObject for the "choose another | 48 // Singleton object to act as a representedObject for the "choose another |
48 // folder" item in the pop up. | 49 // folder" item in the pop up. |
49 static ChooseAnotherFolder* object = nil; | 50 static ChooseAnotherFolder* object = nil; |
50 if (!object) { | 51 if (!object) { |
51 object = [[ChooseAnotherFolder alloc] init]; | 52 object = [[ChooseAnotherFolder alloc] init]; |
52 } | 53 } |
53 return object; | 54 return object; |
54 } | 55 } |
55 | 56 |
56 - (id)initWithParentWindow:(NSWindow*)parentWindow | 57 - (id)initWithParentWindow:(NSWindow*)parentWindow |
| 58 bubbleObserver:(bookmarks::BookmarkBubbleObserver*)bubbleObserver |
57 managed:(bookmarks::ManagedBookmarkService*)managed | 59 managed:(bookmarks::ManagedBookmarkService*)managed |
58 model:(BookmarkModel*)model | 60 model:(BookmarkModel*)model |
59 node:(const BookmarkNode*)node | 61 node:(const BookmarkNode*)node |
60 alreadyBookmarked:(BOOL)alreadyBookmarked { | 62 alreadyBookmarked:(BOOL)alreadyBookmarked { |
61 DCHECK(managed); | 63 DCHECK(managed); |
62 DCHECK(node); | 64 DCHECK(node); |
63 if ((self = [super initWithWindowNibPath:@"BookmarkBubble" | 65 if ((self = [super initWithWindowNibPath:@"BookmarkBubble" |
64 parentWindow:parentWindow | 66 parentWindow:parentWindow |
65 anchoredAt:NSZeroPoint])) { | 67 anchoredAt:NSZeroPoint])) { |
| 68 bookmarkBubbleObserver_ = bubbleObserver; |
66 managedBookmarkService_ = managed; | 69 managedBookmarkService_ = managed; |
67 model_ = model; | 70 model_ = model; |
68 node_ = node; | 71 node_ = node; |
69 alreadyBookmarked_ = alreadyBookmarked; | 72 alreadyBookmarked_ = alreadyBookmarked; |
70 } | 73 } |
71 return self; | 74 return self; |
72 } | 75 } |
73 | 76 |
74 - (void)awakeFromNib { | 77 - (void)awakeFromNib { |
75 [super awakeFromNib]; | 78 [super awakeFromNib]; |
(...skipping 18 matching lines...) Expand all Loading... |
94 // Adjust the height of the bubble so that the sync promo fits in it, | 97 // Adjust the height of the bubble so that the sync promo fits in it, |
95 // except for its bottom border. The xib file hides the left and right | 98 // except for its bottom border. The xib file hides the left and right |
96 // borders of the sync promo. | 99 // borders of the sync promo. |
97 NSRect bubbleFrame = [[self window] frame]; | 100 NSRect bubbleFrame = [[self window] frame]; |
98 bubbleFrame.size.height += | 101 bubbleFrame.size.height += |
99 syncPromoHeight - [syncPromoController_ borderWidth]; | 102 syncPromoHeight - [syncPromoController_ borderWidth]; |
100 [[self window] setFrame:bubbleFrame display:YES]; | 103 [[self window] setFrame:bubbleFrame display:YES]; |
101 } | 104 } |
102 } | 105 } |
103 | 106 |
104 // If this is a new bookmark somewhere visible (e.g. on the bookmark | 107 - (void)notifyBubbleClosed { |
105 // bar), pulse it. Else, call ourself recursively with our parent | 108 if (!bookmarkBubbleObserver_) |
106 // until we find something visible to pulse. | 109 return; |
107 - (void)startPulsingBookmarkButton:(const BookmarkNode*)node { | |
108 while (node) { | |
109 if ((node->parent() == model_->bookmark_bar_node()) || | |
110 (node->parent() == | |
111 managedBookmarkService_->managed_node()) || | |
112 (node->parent() == managedBookmarkService_->supervised_node()) || | |
113 (node == model_->other_node())) { | |
114 pulsingBookmarkNode_ = node; | |
115 bookmarkObserver_->StartObservingNode(pulsingBookmarkNode_); | |
116 NSValue *value = [NSValue valueWithPointer:node]; | |
117 NSDictionary *dict = [NSDictionary | |
118 dictionaryWithObjectsAndKeys:value, | |
119 bookmark_button::kBookmarkKey, | |
120 [NSNumber numberWithBool:YES], | |
121 bookmark_button::kBookmarkPulseFlagKey, | |
122 nil]; | |
123 [[NSNotificationCenter defaultCenter] | |
124 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification | |
125 object:self | |
126 userInfo:dict]; | |
127 return; | |
128 } | |
129 node = node->parent(); | |
130 } | |
131 } | |
132 | 110 |
133 - (void)stopPulsingBookmarkButton { | 111 bookmarkBubbleObserver_->OnBookmarkBubbleHidden(); |
134 if (!pulsingBookmarkNode_) | 112 bookmarkBubbleObserver_ = nullptr; |
135 return; | |
136 NSValue *value = [NSValue valueWithPointer:pulsingBookmarkNode_]; | |
137 if (bookmarkObserver_) | |
138 bookmarkObserver_->StopObservingNode(pulsingBookmarkNode_); | |
139 pulsingBookmarkNode_ = NULL; | |
140 NSDictionary *dict = [NSDictionary | |
141 dictionaryWithObjectsAndKeys:value, | |
142 bookmark_button::kBookmarkKey, | |
143 [NSNumber numberWithBool:NO], | |
144 bookmark_button::kBookmarkPulseFlagKey, | |
145 nil]; | |
146 [[NSNotificationCenter defaultCenter] | |
147 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification | |
148 object:self | |
149 userInfo:dict]; | |
150 } | 113 } |
151 | 114 |
152 // Close the bookmark bubble without changing anything. Unlike a | 115 // Close the bookmark bubble without changing anything. Unlike a |
153 // typical dialog's OK/Cancel, where Cancel is "do nothing", all | 116 // typical dialog's OK/Cancel, where Cancel is "do nothing", all |
154 // buttons on the bubble have the capacity to change the bookmark | 117 // buttons on the bubble have the capacity to change the bookmark |
155 // model. This is an IBOutlet-looking entry point to remove the | 118 // model. This is an IBOutlet-looking entry point to remove the |
156 // dialog without touching the model. | 119 // dialog without touching the model. |
157 - (void)dismissWithoutEditing:(id)sender { | 120 - (void)dismissWithoutEditing:(id)sender { |
158 [self close]; | 121 [self close]; |
159 } | 122 } |
160 | 123 |
161 - (void)windowWillClose:(NSNotification*)notification { | 124 - (void)windowWillClose:(NSNotification*)notification { |
162 // We caught a close so we don't need to watch for the parent closing. | 125 // We caught a close so we don't need to watch for the parent closing. |
163 bookmarkObserver_.reset(); | 126 bookmarkObserver_.reset(); |
164 [self stopPulsingBookmarkButton]; | 127 [self notifyBubbleClosed]; |
165 [super windowWillClose:notification]; | 128 [super windowWillClose:notification]; |
166 } | 129 } |
167 | 130 |
168 // Override -[BaseBubbleController showWindow:] to tweak bubble location and | 131 // Override -[BaseBubbleController showWindow:] to tweak bubble location and |
169 // set up UI elements. | 132 // set up UI elements. |
170 - (void)showWindow:(id)sender { | 133 - (void)showWindow:(id)sender { |
171 NSWindow* window = [self window]; // Force load the NIB. | 134 NSWindow* window = [self window]; // Force load the NIB. |
172 NSWindow* parentWindow = self.parentWindow; | 135 NSWindow* parentWindow = self.parentWindow; |
173 BrowserWindowController* bwc = | 136 BrowserWindowController* bwc = |
174 [BrowserWindowController browserWindowControllerForWindow:parentWindow]; | 137 [BrowserWindowController browserWindowControllerForWindow:parentWindow]; |
175 [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; | |
176 | 138 |
177 InfoBubbleView* bubble = self.bubble; | 139 InfoBubbleView* bubble = self.bubble; |
178 [bubble setArrowLocation:info_bubble::kTopRight]; | 140 [bubble setArrowLocation:info_bubble::kTopRight]; |
179 | 141 |
180 // Insure decent positioning even in the absence of a browser controller, | 142 // Insure decent positioning even in the absence of a browser controller, |
181 // which will occur for some unit tests. | 143 // which will occur for some unit tests. |
182 NSPoint arrowTip = bwc ? [bwc bookmarkBubblePoint] : | 144 NSPoint arrowTip = bwc ? [bwc bookmarkBubblePoint] : |
183 NSMakePoint([window frame].size.width, [window frame].size.height); | 145 NSMakePoint([window frame].size.width, [window frame].size.height); |
184 arrowTip = [parentWindow convertBaseToScreen:arrowTip]; | 146 arrowTip = [parentWindow convertBaseToScreen:arrowTip]; |
185 NSPoint bubbleArrowTip = [bubble arrowTip]; | 147 NSPoint bubbleArrowTip = [bubble arrowTip]; |
186 bubbleArrowTip = [bubble convertPoint:bubbleArrowTip toView:nil]; | 148 bubbleArrowTip = [bubble convertPoint:bubbleArrowTip toView:nil]; |
187 arrowTip.y -= bubbleArrowTip.y; | 149 arrowTip.y -= bubbleArrowTip.y; |
188 arrowTip.x -= bubbleArrowTip.x; | 150 arrowTip.x -= bubbleArrowTip.x; |
189 [window setFrameOrigin:arrowTip]; | 151 [window setFrameOrigin:arrowTip]; |
190 | 152 |
191 // Default is IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK; "Bookmark". | 153 // Default is IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK; "Bookmark". |
192 // If adding for the 1st time the string becomes "Bookmark Added!" | 154 // If adding for the 1st time the string becomes "Bookmark Added!" |
193 if (!alreadyBookmarked_) { | 155 if (!alreadyBookmarked_) { |
194 NSString* title = | 156 NSString* title = |
195 l10n_util::GetNSString(IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED); | 157 l10n_util::GetNSString(IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED); |
196 [bigTitle_ setStringValue:title]; | 158 [bigTitle_ setStringValue:title]; |
197 } | 159 } |
198 | 160 |
199 [self fillInFolderList]; | 161 [self fillInFolderList]; |
200 | 162 |
201 // Ping me when things change out from under us. Unlike a normal | 163 // Ping me when things change out from under us. Unlike a normal |
202 // dialog, the bookmark bubble's cancel: means "don't add this as a | 164 // dialog, the bookmark bubble's cancel: means "don't add this as a |
203 // bookmark", not "cancel editing". We must take extra care to not | 165 // bookmark", not "cancel editing". We must take extra care to not |
204 // touch the bookmark in this selector. | 166 // touch the bookmark in this selector. |
205 bookmarkObserver_.reset( | 167 bookmarkObserver_.reset(new BookmarkModelObserverForCocoa(model_, ^() { |
206 new BookmarkModelObserverForCocoa(model_, ^(BOOL nodeWasDeleted) { | 168 [self dismissWithoutEditing:nil]; |
207 // If a watched node was deleted, the pointer to the pulsing button | 169 })); |
208 // is likely stale. | |
209 if (nodeWasDeleted) | |
210 pulsingBookmarkNode_ = NULL; | |
211 [self dismissWithoutEditing:nil]; | |
212 })); | |
213 bookmarkObserver_->StartObservingNode(node_); | 170 bookmarkObserver_->StartObservingNode(node_); |
214 | 171 |
215 // Pulse something interesting on the bookmark bar. | |
216 [self startPulsingBookmarkButton:node_]; | |
217 | |
218 [parentWindow addChildWindow:window ordered:NSWindowAbove]; | 172 [parentWindow addChildWindow:window ordered:NSWindowAbove]; |
219 [window makeKeyAndOrderFront:self]; | 173 [window makeKeyAndOrderFront:self]; |
220 [self registerKeyStateEventTap]; | 174 [self registerKeyStateEventTap]; |
| 175 |
| 176 bookmarkBubbleObserver_->OnBookmarkBubbleShown(node_); |
221 } | 177 } |
222 | 178 |
223 - (void)close { | 179 - (void)close { |
224 [[BrowserWindowController browserWindowControllerForWindow:self.parentWindow] | 180 [[BrowserWindowController browserWindowControllerForWindow:self.parentWindow] |
225 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; | 181 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; |
226 | 182 |
227 [super close]; | 183 [super close]; |
228 } | 184 } |
229 | 185 |
230 // Shows the bookmark editor sheet for more advanced editing. | 186 // Shows the bookmark editor sheet for more advanced editing. |
231 - (void)showEditor { | 187 - (void)showEditor { |
232 [self ok:self]; | 188 [self ok:self]; |
233 // Send the action up through the responder chain. | 189 // Send the action up through the responder chain. |
234 [NSApp sendAction:@selector(editBookmarkNode:) to:nil from:self]; | 190 [NSApp sendAction:@selector(editBookmarkNode:) to:nil from:self]; |
235 } | 191 } |
236 | 192 |
237 - (IBAction)edit:(id)sender { | 193 - (IBAction)edit:(id)sender { |
238 content::RecordAction(UserMetricsAction("BookmarkBubble_Edit")); | 194 content::RecordAction(UserMetricsAction("BookmarkBubble_Edit")); |
239 [self showEditor]; | 195 [self showEditor]; |
240 } | 196 } |
241 | 197 |
242 - (IBAction)ok:(id)sender { | 198 - (IBAction)ok:(id)sender { |
243 [self stopPulsingBookmarkButton]; // before parent changes | |
244 [self updateBookmarkNode]; | 199 [self updateBookmarkNode]; |
245 [self close]; | 200 [self close]; |
246 } | 201 } |
247 | 202 |
248 // By implementing this, ESC causes the window to go away. If clicking the | 203 // By implementing this, ESC causes the window to go away. If clicking the |
249 // star was what prompted this bubble to appear (i.e., not already bookmarked), | 204 // star was what prompted this bubble to appear (i.e., not already bookmarked), |
250 // remove the bookmark. | 205 // remove the bookmark. |
251 - (IBAction)cancel:(id)sender { | 206 - (IBAction)cancel:(id)sender { |
252 if (!alreadyBookmarked_) { | 207 if (!alreadyBookmarked_) { |
253 // |-remove:| calls |-close| so don't do it. | 208 // |-remove:| calls |-close| so don't do it. |
254 [self remove:sender]; | 209 [self remove:sender]; |
255 } else { | 210 } else { |
256 [self stopPulsingBookmarkButton]; | |
257 [self dismissWithoutEditing:nil]; | 211 [self dismissWithoutEditing:nil]; |
258 } | 212 } |
259 } | 213 } |
260 | 214 |
261 - (IBAction)remove:(id)sender { | 215 - (IBAction)remove:(id)sender { |
262 [self stopPulsingBookmarkButton]; | |
263 bookmarks::RemoveAllBookmarks(model_, node_->url()); | 216 bookmarks::RemoveAllBookmarks(model_, node_->url()); |
264 content::RecordAction(UserMetricsAction("BookmarkBubble_Unstar")); | 217 content::RecordAction(UserMetricsAction("BookmarkBubble_Unstar")); |
265 node_ = NULL; // no longer valid | 218 node_ = NULL; // no longer valid |
266 [self ok:sender]; | 219 [self ok:sender]; |
267 } | 220 } |
268 | 221 |
269 // The controller is the target of the pop up button box action so it can | 222 // The controller is the target of the pop up button box action so it can |
270 // handle when "choose another folder" was picked. | 223 // handle when "choose another folder" was picked. |
271 - (IBAction)folderChanged:(id)sender { | 224 - (IBAction)folderChanged:(id)sender { |
272 DCHECK([sender isEqual:folderPopUpButton_]); | 225 DCHECK([sender isEqual:folderPopUpButton_]); |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
351 | 304 |
352 @end // BookmarkBubbleController | 305 @end // BookmarkBubbleController |
353 | 306 |
354 | 307 |
355 @implementation BookmarkBubbleController (ExposedForUnitTesting) | 308 @implementation BookmarkBubbleController (ExposedForUnitTesting) |
356 | 309 |
357 - (NSView*)syncPromoPlaceholder { | 310 - (NSView*)syncPromoPlaceholder { |
358 return syncPromoPlaceholder_; | 311 return syncPromoPlaceholder_; |
359 } | 312 } |
360 | 313 |
| 314 - (bookmarks::BookmarkBubbleObserver*)bookmarkBubbleObserver { |
| 315 return bookmarkBubbleObserver_; |
| 316 } |
| 317 |
361 + (NSString*)chooseAnotherFolderString { | 318 + (NSString*)chooseAnotherFolderString { |
362 return l10n_util::GetNSStringWithFixup( | 319 return l10n_util::GetNSStringWithFixup( |
363 IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); | 320 IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); |
364 } | 321 } |
365 | 322 |
366 // For the given folder node, walk the tree and add folder names to | 323 // For the given folder node, walk the tree and add folder names to |
367 // the given pop up button. | 324 // the given pop up button. |
368 - (void)addFolderNodes:(const BookmarkNode*)parent | 325 - (void)addFolderNodes:(const BookmarkNode*)parent |
369 toPopUpButton:(NSPopUpButton*)button | 326 toPopUpButton:(NSPopUpButton*)button |
370 indentation:(int)indentation { | 327 indentation:(int)indentation { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; | 361 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; |
405 DCHECK(idx != -1); | 362 DCHECK(idx != -1); |
406 [folderPopUpButton_ selectItemAtIndex:idx]; | 363 [folderPopUpButton_ selectItemAtIndex:idx]; |
407 } | 364 } |
408 | 365 |
409 - (NSPopUpButton*)folderPopUpButton { | 366 - (NSPopUpButton*)folderPopUpButton { |
410 return folderPopUpButton_; | 367 return folderPopUpButton_; |
411 } | 368 } |
412 | 369 |
413 @end // implementation BookmarkBubbleController(ExposedForUnitTesting) | 370 @end // implementation BookmarkBubbleController(ExposedForUnitTesting) |
OLD | NEW |