OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 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/mac/mac_util.h" | 8 #include "base/mac/mac_util.h" |
9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
10 #include "chrome/browser/bookmarks/bookmark_model.h" | 10 #include "chrome/browser/bookmarks/bookmark_model.h" |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 // An object to represent the ChooseAnotherFolder item in the pop up. | 63 // An object to represent the ChooseAnotherFolder item in the pop up. |
64 @interface ChooseAnotherFolder : NSObject | 64 @interface ChooseAnotherFolder : NSObject |
65 @end | 65 @end |
66 | 66 |
67 @implementation ChooseAnotherFolder | 67 @implementation ChooseAnotherFolder |
68 @end | 68 @end |
69 | 69 |
70 @interface BookmarkBubbleController (PrivateAPI) | 70 @interface BookmarkBubbleController (PrivateAPI) |
71 - (void)updateBookmarkNode; | 71 - (void)updateBookmarkNode; |
72 - (void)fillInFolderList; | 72 - (void)fillInFolderList; |
73 - (void)parentWindowWillClose:(NSNotification*)notification; | |
74 @end | 73 @end |
75 | 74 |
76 @implementation BookmarkBubbleController | 75 @implementation BookmarkBubbleController |
77 | 76 |
78 @synthesize node = node_; | 77 @synthesize node = node_; |
79 | 78 |
80 + (id)chooseAnotherFolderObject { | 79 + (id)chooseAnotherFolderObject { |
81 // Singleton object to act as a representedObject for the "choose another | 80 // Singleton object to act as a representedObject for the "choose another |
82 // folder" item in the pop up. | 81 // folder" item in the pop up. |
83 static ChooseAnotherFolder* object = nil; | 82 static ChooseAnotherFolder* object = nil; |
84 if (!object) { | 83 if (!object) { |
85 object = [[ChooseAnotherFolder alloc] init]; | 84 object = [[ChooseAnotherFolder alloc] init]; |
86 } | 85 } |
87 return object; | 86 return object; |
88 } | 87 } |
89 | 88 |
90 - (id)initWithParentWindow:(NSWindow*)parentWindow | 89 - (id)initWithParentWindow:(NSWindow*)parentWindow |
91 model:(BookmarkModel*)model | 90 model:(BookmarkModel*)model |
92 node:(const BookmarkNode*)node | 91 node:(const BookmarkNode*)node |
93 alreadyBookmarked:(BOOL)alreadyBookmarked { | 92 alreadyBookmarked:(BOOL)alreadyBookmarked { |
94 NSString* nibPath = | 93 if ((self = [super initWithWindowNibPath:@"BookmarkBubble" |
95 [base::mac::FrameworkBundle() pathForResource:@"BookmarkBubble" | 94 parentWindow:parentWindow |
96 ofType:@"nib"]; | 95 anchoredAt:NSZeroPoint])) { |
97 if ((self = [super initWithWindowNibPath:nibPath owner:self])) { | |
98 parentWindow_ = parentWindow; | |
99 model_ = model; | 96 model_ = model; |
100 node_ = node; | 97 node_ = node; |
101 alreadyBookmarked_ = alreadyBookmarked; | 98 alreadyBookmarked_ = alreadyBookmarked; |
102 | |
103 // Watch to see if the parent window closes, and if so, close this one. | |
104 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; | |
105 [center addObserver:self | |
106 selector:@selector(parentWindowWillClose:) | |
107 name:NSWindowWillCloseNotification | |
108 object:parentWindow_]; | |
109 } | 99 } |
110 return self; | 100 return self; |
111 } | 101 } |
112 | 102 |
113 - (void)awakeFromNib { | 103 - (void)awakeFromNib { |
| 104 [super awakeFromNib]; |
| 105 |
114 // Check if NSTextFieldCell supports the method. This check is in place as | 106 // Check if NSTextFieldCell supports the method. This check is in place as |
115 // only 10.6 and greater support the setUsesSingleLineMode method. | 107 // only 10.6 and greater support the setUsesSingleLineMode method. |
116 // TODO(kushi.p): Remove this when the project hits a 10.6+ only state. | 108 // TODO(kushi.p): Remove this when the project hits a 10.6+ only state. |
117 NSTextFieldCell* nameFieldCell_ = [nameTextField_ cell]; | 109 NSTextFieldCell* nameFieldCell_ = [nameTextField_ cell]; |
118 if ([nameFieldCell_ | 110 if ([nameFieldCell_ |
119 respondsToSelector:@selector(setUsesSingleLineMode:)]) { | 111 respondsToSelector:@selector(setUsesSingleLineMode:)]) { |
120 [nameFieldCell_ setUsesSingleLineMode:YES]; | 112 [nameFieldCell_ setUsesSingleLineMode:YES]; |
121 } | 113 } |
122 } | 114 } |
123 | 115 |
124 - (void)dealloc { | |
125 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
126 [super dealloc]; | |
127 } | |
128 | |
129 // If this is a new bookmark somewhere visible (e.g. on the bookmark | 116 // If this is a new bookmark somewhere visible (e.g. on the bookmark |
130 // bar), pulse it. Else, call ourself recursively with our parent | 117 // bar), pulse it. Else, call ourself recursively with our parent |
131 // until we find something visible to pulse. | 118 // until we find something visible to pulse. |
132 - (void)startPulsingBookmarkButton:(const BookmarkNode*)node { | 119 - (void)startPulsingBookmarkButton:(const BookmarkNode*)node { |
133 while (node) { | 120 while (node) { |
134 if ((node->parent() == model_->bookmark_bar_node()) || | 121 if ((node->parent() == model_->bookmark_bar_node()) || |
135 (node == model_->other_node())) { | 122 (node == model_->other_node())) { |
136 pulsingBookmarkNode_ = node; | 123 pulsingBookmarkNode_ = node; |
137 NSValue *value = [NSValue valueWithPointer:node]; | 124 NSValue *value = [NSValue valueWithPointer:node]; |
138 NSDictionary *dict = [NSDictionary | 125 NSDictionary *dict = [NSDictionary |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 | 157 |
171 // Close the bookmark bubble without changing anything. Unlike a | 158 // Close the bookmark bubble without changing anything. Unlike a |
172 // typical dialog's OK/Cancel, where Cancel is "do nothing", all | 159 // typical dialog's OK/Cancel, where Cancel is "do nothing", all |
173 // buttons on the bubble have the capacity to change the bookmark | 160 // buttons on the bubble have the capacity to change the bookmark |
174 // model. This is an IBOutlet-looking entry point to remove the | 161 // model. This is an IBOutlet-looking entry point to remove the |
175 // dialog without touching the model. | 162 // dialog without touching the model. |
176 - (void)dismissWithoutEditing:(id)sender { | 163 - (void)dismissWithoutEditing:(id)sender { |
177 [self close]; | 164 [self close]; |
178 } | 165 } |
179 | 166 |
180 - (void)parentWindowWillClose:(NSNotification*)notification { | |
181 [self close]; | |
182 } | |
183 | |
184 - (void)windowWillClose:(NSNotification*)notification { | 167 - (void)windowWillClose:(NSNotification*)notification { |
185 // We caught a close so we don't need to watch for the parent closing. | 168 // We caught a close so we don't need to watch for the parent closing. |
186 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
187 bookmark_observer_.reset(NULL); | 169 bookmark_observer_.reset(NULL); |
188 chrome_observer_.reset(NULL); | 170 chrome_observer_.reset(NULL); |
189 [self stopPulsingBookmarkButton]; | 171 [self stopPulsingBookmarkButton]; |
190 [self autorelease]; | 172 [super windowWillClose:notification]; |
191 } | 173 } |
192 | 174 |
193 // We want this to be a child of a browser window. addChildWindow: | 175 // Override -[BaseBubbleController showWindow:] to tweak bubble location and |
194 // (called from this function) will bring the window on-screen; | 176 // set up UI elements. |
195 // unfortunately, [NSWindowController showWindow:] will also bring it | |
196 // on-screen (but will cause unexpected changes to the window's | |
197 // position). We cannot have an addChildWindow: and a subsequent | |
198 // showWindow:. Thus, we have our own version. | |
199 - (void)showWindow:(id)sender { | 177 - (void)showWindow:(id)sender { |
| 178 NSWindow* window = [self window]; // Force load the NIB. |
| 179 NSWindow* parentWindow = self.parentWindow; |
200 BrowserWindowController* bwc = | 180 BrowserWindowController* bwc = |
201 [BrowserWindowController browserWindowControllerForWindow:parentWindow_]; | 181 [BrowserWindowController browserWindowControllerForWindow:parentWindow]; |
202 [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; | 182 [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; |
203 NSWindow* window = [self window]; // completes nib load | 183 |
204 [bubble_ setArrowLocation:info_bubble::kTopRight]; | 184 InfoBubbleView* bubble = self.bubble; |
| 185 [bubble setArrowLocation:info_bubble::kTopRight]; |
| 186 |
205 // Insure decent positioning even in the absence of a browser controller, | 187 // Insure decent positioning even in the absence of a browser controller, |
206 // which will occur for some unit tests. | 188 // which will occur for some unit tests. |
207 NSPoint arrowtip = bwc ? [bwc bookmarkBubblePoint] : | 189 NSPoint arrowTip = bwc ? [bwc bookmarkBubblePoint] : |
208 NSMakePoint([window frame].size.width, [window frame].size.height); | 190 NSMakePoint([window frame].size.width, [window frame].size.height); |
209 NSPoint origin = [parentWindow_ convertBaseToScreen:arrowtip]; | 191 arrowTip = [parentWindow convertBaseToScreen:arrowTip]; |
210 NSPoint bubbleArrowtip = [bubble_ arrowTip]; | 192 NSPoint bubbleArrowTip = [bubble arrowTip]; |
211 bubbleArrowtip = [bubble_ convertPoint:bubbleArrowtip toView:nil]; | 193 bubbleArrowTip = [bubble convertPoint:bubbleArrowTip toView:nil]; |
212 origin.y -= bubbleArrowtip.y; | 194 arrowTip.y -= bubbleArrowTip.y; |
213 origin.x -= bubbleArrowtip.x; | 195 arrowTip.x -= bubbleArrowTip.x; |
214 [window setFrameOrigin:origin]; | 196 [window setFrameOrigin:arrowTip]; |
215 [parentWindow_ addChildWindow:window ordered:NSWindowAbove]; | 197 |
216 // Default is IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK; "Bookmark". | 198 // Default is IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARK; "Bookmark". |
217 // If adding for the 1st time the string becomes "Bookmark Added!" | 199 // If adding for the 1st time the string becomes "Bookmark Added!" |
218 if (!alreadyBookmarked_) { | 200 if (!alreadyBookmarked_) { |
219 NSString* title = | 201 NSString* title = |
220 l10n_util::GetNSString(IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED); | 202 l10n_util::GetNSString(IDS_BOOKMARK_BUBBLE_PAGE_BOOKMARKED); |
221 [bigTitle_ setStringValue:title]; | 203 [bigTitle_ setStringValue:title]; |
222 } | 204 } |
223 | 205 |
224 [self fillInFolderList]; | 206 [self fillInFolderList]; |
225 | 207 |
226 // Ping me when things change out from under us. Unlike a normal | 208 // Ping me when things change out from under us. Unlike a normal |
227 // dialog, the bookmark bubble's cancel: means "don't add this as a | 209 // dialog, the bookmark bubble's cancel: means "don't add this as a |
228 // bookmark", not "cancel editing". We must take extra care to not | 210 // bookmark", not "cancel editing". We must take extra care to not |
229 // touch the bookmark in this selector. | 211 // touch the bookmark in this selector. |
230 bookmark_observer_.reset(new BookmarkModelObserverForCocoa( | 212 bookmark_observer_.reset(new BookmarkModelObserverForCocoa( |
231 node_, model_, | 213 node_, model_, |
232 self, | 214 self, |
233 @selector(dismissWithoutEditing:))); | 215 @selector(dismissWithoutEditing:))); |
234 chrome_observer_.reset(new BookmarkBubbleNotificationBridge( | 216 chrome_observer_.reset(new BookmarkBubbleNotificationBridge( |
235 self, @selector(dismissWithoutEditing:))); | 217 self, @selector(dismissWithoutEditing:))); |
236 | 218 |
237 // Pulse something interesting on the bookmark bar. | 219 // Pulse something interesting on the bookmark bar. |
238 [self startPulsingBookmarkButton:node_]; | 220 [self startPulsingBookmarkButton:node_]; |
239 | 221 |
| 222 [parentWindow addChildWindow:window ordered:NSWindowAbove]; |
240 [window makeKeyAndOrderFront:self]; | 223 [window makeKeyAndOrderFront:self]; |
241 } | 224 } |
242 | 225 |
243 - (void)close { | 226 - (void)close { |
244 [[BrowserWindowController browserWindowControllerForWindow:parentWindow_] | 227 [[BrowserWindowController browserWindowControllerForWindow:self.parentWindow] |
245 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; | 228 releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; |
246 [parentWindow_ removeChildWindow:[self window]]; | |
247 | |
248 // If you quit while the bubble is open, sometimes we get a | |
249 // DidResignKey before we get our parent's WindowWillClose and | |
250 // sometimes not. We protect against a multiple close (or reference | |
251 // to parentWindow_ at a bad time) by clearing it out once we're | |
252 // done, and by removing ourself from future notifications. | |
253 [[NSNotificationCenter defaultCenter] | |
254 removeObserver:self | |
255 name:NSWindowWillCloseNotification | |
256 object:parentWindow_]; | |
257 parentWindow_ = nil; | |
258 | 229 |
259 [super close]; | 230 [super close]; |
260 } | 231 } |
261 | 232 |
262 // Shows the bookmark editor sheet for more advanced editing. | 233 // Shows the bookmark editor sheet for more advanced editing. |
263 - (void)showEditor { | 234 - (void)showEditor { |
264 [self ok:self]; | 235 [self ok:self]; |
265 // Send the action up through the responder chain. | 236 // Send the action up through the responder chain. |
266 [NSApp sendAction:@selector(editBookmarkNode:) to:nil from:self]; | 237 [NSApp sendAction:@selector(editBookmarkNode:) to:nil from:self]; |
267 } | 238 } |
(...skipping 29 matching lines...) Expand all Loading... |
297 [self ok:sender]; | 268 [self ok:sender]; |
298 } | 269 } |
299 | 270 |
300 // The controller is the target of the pop up button box action so it can | 271 // The controller is the target of the pop up button box action so it can |
301 // handle when "choose another folder" was picked. | 272 // handle when "choose another folder" was picked. |
302 - (IBAction)folderChanged:(id)sender { | 273 - (IBAction)folderChanged:(id)sender { |
303 DCHECK([sender isEqual:folderPopUpButton_]); | 274 DCHECK([sender isEqual:folderPopUpButton_]); |
304 // It is possible that due to model change our parent window has been closed | 275 // It is possible that due to model change our parent window has been closed |
305 // but the popup is still showing and able to notify the controller of a | 276 // but the popup is still showing and able to notify the controller of a |
306 // folder change. We ignore the sender in this case. | 277 // folder change. We ignore the sender in this case. |
307 if (!parentWindow_) | 278 if (!self.parentWindow) |
308 return; | 279 return; |
309 NSMenuItem* selected = [folderPopUpButton_ selectedItem]; | 280 NSMenuItem* selected = [folderPopUpButton_ selectedItem]; |
310 ChooseAnotherFolder* chooseItem = [[self class] chooseAnotherFolderObject]; | 281 ChooseAnotherFolder* chooseItem = [[self class] chooseAnotherFolderObject]; |
311 if ([[selected representedObject] isEqual:chooseItem]) { | 282 if ([[selected representedObject] isEqual:chooseItem]) { |
312 content::RecordAction( | 283 content::RecordAction( |
313 UserMetricsAction("BookmarkBubble_EditFromCombobox")); | 284 UserMetricsAction("BookmarkBubble_EditFromCombobox")); |
314 [self showEditor]; | 285 [self showEditor]; |
315 } | 286 } |
316 } | 287 } |
317 | 288 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
375 [item setRepresentedObject:obj]; | 346 [item setRepresentedObject:obj]; |
376 // Finally, select the current parent. | 347 // Finally, select the current parent. |
377 NSValue* parentValue = [NSValue valueWithPointer:node_->parent()]; | 348 NSValue* parentValue = [NSValue valueWithPointer:node_->parent()]; |
378 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; | 349 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; |
379 [folderPopUpButton_ selectItemAtIndex:idx]; | 350 [folderPopUpButton_ selectItemAtIndex:idx]; |
380 } | 351 } |
381 | 352 |
382 @end // BookmarkBubbleController | 353 @end // BookmarkBubbleController |
383 | 354 |
384 | 355 |
385 @implementation BookmarkBubbleController(ExposedForUnitTesting) | 356 @implementation BookmarkBubbleController (ExposedForUnitTesting) |
386 | 357 |
387 + (NSString*)chooseAnotherFolderString { | 358 + (NSString*)chooseAnotherFolderString { |
388 return l10n_util::GetNSStringWithFixup( | 359 return l10n_util::GetNSStringWithFixup( |
389 IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); | 360 IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); |
390 } | 361 } |
391 | 362 |
392 // For the given folder node, walk the tree and add folder names to | 363 // For the given folder node, walk the tree and add folder names to |
393 // the given pop up button. | 364 // the given pop up button. |
394 - (void)addFolderNodes:(const BookmarkNode*)parent | 365 - (void)addFolderNodes:(const BookmarkNode*)parent |
395 toPopUpButton:(NSPopUpButton*)button | 366 toPopUpButton:(NSPopUpButton*)button |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
428 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; | 399 NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; |
429 DCHECK(idx != -1); | 400 DCHECK(idx != -1); |
430 [folderPopUpButton_ selectItemAtIndex:idx]; | 401 [folderPopUpButton_ selectItemAtIndex:idx]; |
431 } | 402 } |
432 | 403 |
433 - (NSPopUpButton*)folderPopUpButton { | 404 - (NSPopUpButton*)folderPopUpButton { |
434 return folderPopUpButton_; | 405 return folderPopUpButton_; |
435 } | 406 } |
436 | 407 |
437 @end // implementation BookmarkBubbleController(ExposedForUnitTesting) | 408 @end // implementation BookmarkBubbleController(ExposedForUnitTesting) |
OLD | NEW |