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

Side by Side Diff: chrome/browser/ui/cocoa/keyword_editor_cocoa_controller.mm

Issue 6339002: [Mac] Consolidate all files relating to preferences in a subdir of c/b/ui/coc... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2009 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 #import <Cocoa/Cocoa.h>
6
7 #import "chrome/browser/ui/cocoa/keyword_editor_cocoa_controller.h"
8
9 #import "base/mac/mac_util.h"
10 #include "base/lazy_instance.h"
11 #include "base/sys_string_conversions.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/prefs/pref_service.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/search_engines/template_url_model.h"
16 #include "chrome/browser/search_engines/template_url_table_model.h"
17 #import "chrome/browser/ui/cocoa/edit_search_engine_cocoa_controller.h"
18 #import "chrome/browser/ui/cocoa/window_size_autosaver.h"
19 #include "chrome/common/pref_names.h"
20 #include "grit/generated_resources.h"
21 #include "skia/ext/skia_utils_mac.h"
22 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h"
23 #include "third_party/skia/include/core/SkBitmap.h"
24
25 namespace {
26
27 const CGFloat kButtonBarHeight = 35.0;
28
29 } // namespace
30
31 @interface KeywordEditorCocoaController (Private)
32 - (void)adjustEditingButtons;
33 - (void)editKeyword:(id)sender;
34 - (int)indexInModelForRow:(NSUInteger)row;
35 @end
36
37 // KeywordEditorModelObserver -------------------------------------------------
38
39 KeywordEditorModelObserver::KeywordEditorModelObserver(
40 KeywordEditorCocoaController* controller)
41 : controller_(controller),
42 icon_cache_(this) {
43 }
44
45 KeywordEditorModelObserver::~KeywordEditorModelObserver() {
46 }
47
48 // Notification that the template url model has changed in some way.
49 void KeywordEditorModelObserver::OnTemplateURLModelChanged() {
50 [controller_ modelChanged];
51 }
52
53 void KeywordEditorModelObserver::OnEditedKeyword(
54 const TemplateURL* template_url,
55 const string16& title,
56 const string16& keyword,
57 const std::string& url) {
58 KeywordEditorController* controller = [controller_ controller];
59 if (template_url) {
60 controller->ModifyTemplateURL(template_url, title, keyword, url);
61 } else {
62 controller->AddTemplateURL(title, keyword, url);
63 }
64 }
65
66 void KeywordEditorModelObserver::OnModelChanged() {
67 icon_cache_.OnModelChanged();
68 [controller_ modelChanged];
69 }
70
71 void KeywordEditorModelObserver::OnItemsChanged(int start, int length) {
72 icon_cache_.OnItemsChanged(start, length);
73 [controller_ modelChanged];
74 }
75
76 void KeywordEditorModelObserver::OnItemsAdded(int start, int length) {
77 icon_cache_.OnItemsAdded(start, length);
78 [controller_ modelChanged];
79 }
80
81 void KeywordEditorModelObserver::OnItemsRemoved(int start, int length) {
82 icon_cache_.OnItemsRemoved(start, length);
83 [controller_ modelChanged];
84 }
85
86 int KeywordEditorModelObserver::RowCount() const {
87 return [controller_ controller]->table_model()->RowCount();
88 }
89
90 SkBitmap KeywordEditorModelObserver::GetIcon(int row) const {
91 return [controller_ controller]->table_model()->GetIcon(row);
92 }
93
94 NSImage* KeywordEditorModelObserver::GetImageForRow(int row) {
95 return icon_cache_.GetImageForRow(row);
96 }
97
98 // KeywordEditorCocoaController -----------------------------------------------
99
100 namespace {
101
102 typedef std::map<Profile*,KeywordEditorCocoaController*> ProfileControllerMap;
103
104 static base::LazyInstance<ProfileControllerMap> g_profile_controller_map(
105 base::LINKER_INITIALIZED);
106
107 } // namespace
108
109 @implementation KeywordEditorCocoaController
110
111 + (KeywordEditorCocoaController*)sharedInstanceForProfile:(Profile*)profile {
112 ProfileControllerMap* map = g_profile_controller_map.Pointer();
113 DCHECK(map != NULL);
114 ProfileControllerMap::iterator it = map->find(profile);
115 if (it != map->end()) {
116 return it->second;
117 }
118 return nil;
119 }
120
121 // TODO(shess): The Windows code watches a single global window which
122 // is not distinguished by profile. This code could distinguish by
123 // profile by checking the controller's class and profile.
124 + (void)showKeywordEditor:(Profile*)profile {
125 // http://crbug.com/23359 describes a case where this panel is
126 // opened from an incognito window, which can leave the panel
127 // holding onto a stale profile. Since the same panel is used
128 // either way, arrange to use the original profile instead.
129 profile = profile->GetOriginalProfile();
130
131 ProfileControllerMap* map = g_profile_controller_map.Pointer();
132 DCHECK(map != NULL);
133 ProfileControllerMap::iterator it = map->find(profile);
134 if (it == map->end()) {
135 // Since we don't currently support multiple profiles, this class
136 // has not been tested against them, so document that assumption.
137 DCHECK_EQ(map->size(), 0U);
138
139 KeywordEditorCocoaController* controller =
140 [[self alloc] initWithProfile:profile];
141 it = map->insert(std::make_pair(profile, controller)).first;
142 }
143
144 [it->second showWindow:nil];
145 }
146
147 - (id)initWithProfile:(Profile*)profile {
148 DCHECK(profile);
149 NSString* nibpath = [base::mac::MainAppBundle()
150 pathForResource:@"KeywordEditor"
151 ofType:@"nib"];
152 if ((self = [super initWithWindowNibPath:nibpath owner:self])) {
153 profile_ = profile;
154 controller_.reset(new KeywordEditorController(profile_));
155 observer_.reset(new KeywordEditorModelObserver(self));
156 controller_->table_model()->SetObserver(observer_.get());
157 controller_->url_model()->AddObserver(observer_.get());
158 groupCell_.reset([[NSTextFieldCell alloc] init]);
159
160 if (g_browser_process && g_browser_process->local_state()) {
161 sizeSaver_.reset([[WindowSizeAutosaver alloc]
162 initWithWindow:[self window]
163 prefService:g_browser_process->local_state()
164 path:prefs::kKeywordEditorWindowPlacement]);
165 }
166 }
167 return self;
168 }
169
170 - (void)dealloc {
171 controller_->table_model()->SetObserver(NULL);
172 controller_->url_model()->RemoveObserver(observer_.get());
173 [tableView_ setDataSource:nil];
174 observer_.reset();
175 [super dealloc];
176 }
177
178 - (void)awakeFromNib {
179 // Make sure the button fits its label, but keep it the same height as the
180 // other two buttons.
181 [GTMUILocalizerAndLayoutTweaker sizeToFitView:makeDefaultButton_];
182 NSSize size = [makeDefaultButton_ frame].size;
183 size.height = NSHeight([addButton_ frame]);
184 [makeDefaultButton_ setFrameSize:size];
185
186 [[self window] setAutorecalculatesContentBorderThickness:NO
187 forEdge:NSMinYEdge];
188 [[self window] setContentBorderThickness:kButtonBarHeight
189 forEdge:NSMinYEdge];
190
191 [self adjustEditingButtons];
192 [tableView_ setDoubleAction:@selector(editKeyword:)];
193 [tableView_ setTarget:self];
194 }
195
196 // When the window closes, clean ourselves up.
197 - (void)windowWillClose:(NSNotification*)notif {
198 [self autorelease];
199
200 ProfileControllerMap* map = g_profile_controller_map.Pointer();
201 ProfileControllerMap::iterator it = map->find(profile_);
202 // It should not be possible for this to be missing.
203 // TODO(shess): Except that the unit test reaches in directly.
204 // Consider circling around and refactoring that.
205 //DCHECK(it != map->end());
206 if (it != map->end()) {
207 map->erase(it);
208 }
209 }
210
211 - (void)modelChanged {
212 [tableView_ reloadData];
213 [self adjustEditingButtons];
214 }
215
216 - (KeywordEditorController*)controller {
217 return controller_.get();
218 }
219
220 - (void)sheetDidEnd:(NSWindow*)sheet
221 returnCode:(NSInteger)code
222 context:(void*)context {
223 [sheet orderOut:self];
224 }
225
226 - (IBAction)addKeyword:(id)sender {
227 // The controller will release itself when the window closes.
228 EditSearchEngineCocoaController* editor =
229 [[EditSearchEngineCocoaController alloc] initWithProfile:profile_
230 delegate:observer_.get()
231 templateURL:NULL];
232 [NSApp beginSheet:[editor window]
233 modalForWindow:[self window]
234 modalDelegate:self
235 didEndSelector:@selector(sheetDidEnd:returnCode:context:)
236 contextInfo:NULL];
237 }
238
239 - (void)editKeyword:(id)sender {
240 const NSInteger clickedRow = [tableView_ clickedRow];
241 if (clickedRow < 0 || [self tableView:tableView_ isGroupRow:clickedRow])
242 return;
243 const TemplateURL* url = controller_->GetTemplateURL(
244 [self indexInModelForRow:clickedRow]);
245 // The controller will release itself when the window closes.
246 EditSearchEngineCocoaController* editor =
247 [[EditSearchEngineCocoaController alloc] initWithProfile:profile_
248 delegate:observer_.get()
249 templateURL:url];
250 [NSApp beginSheet:[editor window]
251 modalForWindow:[self window]
252 modalDelegate:self
253 didEndSelector:@selector(sheetDidEnd:returnCode:context:)
254 contextInfo:NULL];
255 }
256
257 - (IBAction)deleteKeyword:(id)sender {
258 NSIndexSet* selection = [tableView_ selectedRowIndexes];
259 DCHECK_GT([selection count], 0U);
260 NSUInteger index = [selection lastIndex];
261 while (index != NSNotFound) {
262 controller_->RemoveTemplateURL([self indexInModelForRow:index]);
263 index = [selection indexLessThanIndex:index];
264 }
265 }
266
267 - (IBAction)makeDefault:(id)sender {
268 NSIndexSet* selection = [tableView_ selectedRowIndexes];
269 DCHECK_EQ([selection count], 1U);
270 int row = [self indexInModelForRow:[selection firstIndex]];
271 controller_->MakeDefaultTemplateURL(row);
272 }
273
274 // Called when the user hits the escape key. Closes the window.
275 - (void)cancel:(id)sender {
276 [[self window] performClose:self];
277 }
278
279 // Table View Data Source -----------------------------------------------------
280
281 - (NSInteger)numberOfRowsInTableView:(NSTableView*)table {
282 int rowCount = controller_->table_model()->RowCount();
283 int numGroups = controller_->table_model()->GetGroups().size();
284 if ([self tableView:table isGroupRow:rowCount + numGroups - 1]) {
285 // Don't show a group header with no rows underneath it.
286 --numGroups;
287 }
288 return rowCount + numGroups;
289 }
290
291 - (id)tableView:(NSTableView*)tv
292 objectValueForTableColumn:(NSTableColumn*)tableColumn
293 row:(NSInteger)row {
294 if ([self tableView:tv isGroupRow:row]) {
295 DCHECK(!tableColumn);
296 ui::TableModel::Groups groups = controller_->table_model()->GetGroups();
297 if (row == 0) {
298 return base::SysUTF16ToNSString(groups[0].title);
299 } else {
300 return base::SysUTF16ToNSString(groups[1].title);
301 }
302 }
303
304 NSString* identifier = [tableColumn identifier];
305 if ([identifier isEqualToString:@"name"]) {
306 // The name column is an NSButtonCell so we can have text and image in the
307 // same cell. As such, the "object value" for a button cell is either on
308 // or off, so we always return off so we don't act like a button.
309 return [NSNumber numberWithInt:NSOffState];
310 }
311 if ([identifier isEqualToString:@"keyword"]) {
312 // The keyword object value is a normal string.
313 int index = [self indexInModelForRow:row];
314 int columnID = IDS_SEARCH_ENGINES_EDITOR_KEYWORD_COLUMN;
315 string16 text = controller_->table_model()->GetText(index, columnID);
316 return base::SysUTF16ToNSString(text);
317 }
318
319 // And we shouldn't have any other columns...
320 NOTREACHED();
321 return nil;
322 }
323
324 // Table View Delegate --------------------------------------------------------
325
326 // When the selection in the table view changes, we need to adjust buttons.
327 - (void)tableViewSelectionDidChange:(NSNotification*)aNotification {
328 [self adjustEditingButtons];
329 }
330
331 // Disallow selection of the group header rows.
332 - (BOOL)tableView:(NSTableView*)table shouldSelectRow:(NSInteger)row {
333 return ![self tableView:table isGroupRow:row];
334 }
335
336 - (BOOL)tableView:(NSTableView*)table isGroupRow:(NSInteger)row {
337 int otherGroupRow =
338 controller_->table_model()->last_search_engine_index() + 1;
339 return (row == 0 || row == otherGroupRow);
340 }
341
342 - (NSCell*)tableView:(NSTableView*)tableView
343 dataCellForTableColumn:(NSTableColumn*)tableColumn
344 row:(NSInteger)row {
345 static const CGFloat kCellFontSize = 12.0;
346
347 // Check to see if we are a grouped row.
348 if ([self tableView:tableView isGroupRow:row]) {
349 DCHECK(!tableColumn); // This would violate the group row contract.
350 return groupCell_.get();
351 }
352
353 NSCell* cell = [tableColumn dataCellForRow:row];
354 int offsetRow = [self indexInModelForRow:row];
355
356 // Set the favicon and title for the search engine in the name column.
357 if ([[tableColumn identifier] isEqualToString:@"name"]) {
358 DCHECK([cell isKindOfClass:[NSButtonCell class]]);
359 NSButtonCell* buttonCell = static_cast<NSButtonCell*>(cell);
360 string16 title = controller_->table_model()->GetText(offsetRow,
361 IDS_SEARCH_ENGINES_EDITOR_DESCRIPTION_COLUMN);
362 [buttonCell setTitle:base::SysUTF16ToNSString(title)];
363 [buttonCell setImage:observer_->GetImageForRow(offsetRow)];
364 [buttonCell setRefusesFirstResponder:YES]; // Don't push in like a button.
365 [buttonCell setHighlightsBy:NSNoCellMask];
366 }
367
368 // The default search engine should be in bold font.
369 const TemplateURL* defaultEngine =
370 controller_->url_model()->GetDefaultSearchProvider();
371 int rowIndex = controller_->table_model()->IndexOfTemplateURL(defaultEngine);
372 if (rowIndex == offsetRow) {
373 [cell setFont:[NSFont boldSystemFontOfSize:kCellFontSize]];
374 } else {
375 [cell setFont:[NSFont systemFontOfSize:kCellFontSize]];
376 }
377 return cell;
378 }
379
380 // Private --------------------------------------------------------------------
381
382 // This function appropriately sets the enabled states on the table's editing
383 // buttons.
384 - (void)adjustEditingButtons {
385 NSIndexSet* selection = [tableView_ selectedRowIndexes];
386 BOOL canRemove = ([selection count] > 0);
387 NSUInteger index = [selection firstIndex];
388
389 // Delete button.
390 while (canRemove && index != NSNotFound) {
391 int modelIndex = [self indexInModelForRow:index];
392 const TemplateURL& url =
393 controller_->table_model()->GetTemplateURL(modelIndex);
394 if (!controller_->CanRemove(&url))
395 canRemove = NO;
396 index = [selection indexGreaterThanIndex:index];
397 }
398 [removeButton_ setEnabled:canRemove];
399
400 // Make default button.
401 if ([selection count] != 1) {
402 [makeDefaultButton_ setEnabled:NO];
403 } else {
404 int row = [self indexInModelForRow:[selection firstIndex]];
405 const TemplateURL& url =
406 controller_->table_model()->GetTemplateURL(row);
407 [makeDefaultButton_ setEnabled:controller_->CanMakeDefault(&url)];
408 }
409 }
410
411 // This converts a row index in our table view to an index in the model by
412 // computing the group offsets.
413 - (int)indexInModelForRow:(NSUInteger)row {
414 DCHECK_GT(row, 0U);
415 unsigned otherGroupId =
416 controller_->table_model()->last_search_engine_index() + 1;
417 DCHECK_NE(row, otherGroupId);
418 if (row >= otherGroupId) {
419 return row - 2; // Other group.
420 } else {
421 return row - 1; // Default group.
422 }
423 }
424
425 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698