blob: c191ac787bcb21c0494e8b46190be51bb9af2c0a [file] [log] [blame]
[email protected]eccda9db2010-06-23 17:27:081// Copyright (c) 2010 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#include "chrome/browser/views/wrench_menu.h"
6
7#include <cmath>
8
9#include "app/l10n_util.h"
10#include "app/resource_bundle.h"
[email protected]e83326f2010-07-31 17:29:2511#include "base/string_number_conversions.h"
[email protected]eccda9db2010-06-23 17:27:0812#include "base/utf_string_conversions.h"
13#include "chrome/app/chrome_dll_resource.h"
14#include "chrome/browser/browser.h"
15#include "chrome/browser/host_zoom_map.h"
16#include "chrome/browser/profile.h"
17#include "chrome/browser/tab_contents/tab_contents.h"
18#include "chrome/common/notification_observer.h"
19#include "chrome/common/notification_registrar.h"
20#include "chrome/common/notification_source.h"
21#include "chrome/common/notification_type.h"
22#include "gfx/canvas.h"
[email protected]84e4f8c2010-07-15 02:18:3223#include "gfx/canvas_skia.h"
[email protected]eccda9db2010-06-23 17:27:0824#include "gfx/skia_util.h"
25#include "grit/generated_resources.h"
26#include "grit/theme_resources.h"
27#include "third_party/skia/include/core/SkPaint.h"
28#include "views/background.h"
29#include "views/controls/button/image_button.h"
[email protected]a00b0322010-06-25 16:21:3230#include "views/controls/button/menu_button.h"
[email protected]eccda9db2010-06-23 17:27:0831#include "views/controls/button/text_button.h"
32#include "views/controls/label.h"
33#include "views/controls/menu/menu_config.h"
34#include "views/controls/menu/menu_item_view.h"
35#include "views/controls/menu/menu_scroll_view_container.h"
36#include "views/controls/menu/submenu_view.h"
37#include "views/window/window.h"
38
39using menus::MenuModel;
40using views::CustomButton;
41using views::ImageButton;
42using views::Label;
43using views::MenuConfig;
44using views::MenuItemView;
45using views::TextButton;
46using views::View;
47
48namespace {
49
50// Colors used for buttons.
51const SkColor kHotBorderColor = SkColorSetARGB(72, 0, 0, 0);
52const SkColor kBorderColor = SkColorSetARGB(36, 0, 0, 0);
53const SkColor kPushedBorderColor = SkColorSetARGB(72, 0, 0, 0);
54const SkColor kHotBackgroundColor = SkColorSetARGB(204, 255, 255, 255);
55const SkColor kBackgroundColor = SkColorSetARGB(102, 255, 255, 255);
56const SkColor kPushedBackgroundColor = SkColorSetARGB(13, 0, 0, 0);
57
58// Horizontal padding on the edges of the buttons.
59const int kHorizontalPadding = 6;
60
61// Subclass of ImageButton whose preferred size includes the size of the border.
62class FullscreenButton : public ImageButton {
63 public:
[email protected]65ecd5b2010-07-27 21:29:0664 explicit FullscreenButton(views::ButtonListener* listener)
65 : ImageButton(listener) { }
[email protected]eccda9db2010-06-23 17:27:0866
67 virtual gfx::Size GetPreferredSize() {
68 gfx::Size pref = ImageButton::GetPreferredSize();
69 gfx::Insets insets;
70 if (border())
71 border()->GetInsets(&insets);
72 pref.Enlarge(insets.width(), insets.height());
73 return pref;
74 }
75
76 private:
77 DISALLOW_COPY_AND_ASSIGN(FullscreenButton);
78};
79
80// Border for buttons contained in the menu. This is only used for getting the
81// insets, the actual painting is done in MenuButtonBackground.
82class MenuButtonBorder : public views::Border {
83 public:
84 MenuButtonBorder() {}
85
86 virtual void Paint(const View& view, gfx::Canvas* canvas) const {
87 // Painting of border is done in MenuButtonBackground.
88 }
89
90 virtual void GetInsets(gfx::Insets* insets) const {
91 insets->Set(MenuConfig::instance().item_no_icon_top_margin,
92 kHorizontalPadding,
93 MenuConfig::instance().item_no_icon_bottom_margin,
94 kHorizontalPadding);
95 }
96
97 private:
98 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder);
99};
100
101// Combination border/background for the buttons contained in the menu. The
102// painting of the border/background is done here as TextButton does not always
103// paint the border.
104class MenuButtonBackground : public views::Background {
105 public:
106 enum ButtonType {
107 LEFT_BUTTON,
108 CENTER_BUTTON,
109 RIGHT_BUTTON,
110 SINGLE_BUTTON,
111 };
112
113 explicit MenuButtonBackground(ButtonType type)
114 : type_(type),
115 left_button_(NULL),
116 right_button_(NULL) {}
117
118 // Used when the type is CENTER_BUTTON to determine if the left/right edge
119 // needs to be rendered selected.
120 void SetOtherButtons(CustomButton* left_button, CustomButton* right_button) {
[email protected]5214bdd2010-08-02 21:43:47121 if (base::i18n::IsRTL()) {
122 left_button_ = right_button;
123 right_button_ = left_button;
124 } else {
125 left_button_ = left_button;
126 right_button_ = right_button;
127 }
[email protected]eccda9db2010-06-23 17:27:08128 }
129
130 virtual void Paint(gfx::Canvas* canvas, View* view) const {
131 CustomButton::ButtonState state =
132 (view->GetClassName() == views::Label::kViewClassName) ?
133 CustomButton::BS_NORMAL : static_cast<CustomButton*>(view)->state();
134 int w = view->width();
135 int h = view->height();
[email protected]34344902010-07-07 20:26:26136 switch (TypeAdjustedForRTL()) {
[email protected]eccda9db2010-06-23 17:27:08137 case LEFT_BUTTON:
138 canvas->FillRectInt(background_color(state), 1, 1, w, h - 2);
139 canvas->FillRectInt(border_color(state), 2, 0, w, 1);
140 canvas->FillRectInt(border_color(state), 1, 1, 1, 1);
141 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4);
142 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1);
143 canvas->FillRectInt(border_color(state), 2, h - 1, w, 1);
144 break;
145
146 case CENTER_BUTTON: {
147 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2);
148 SkColor left_color = state != CustomButton::BS_NORMAL ?
149 border_color(state) : border_color(left_button_->state());
150 canvas->FillRectInt(left_color, 0, 0, 1, h);
151 canvas->FillRectInt(border_color(state), 1, 0, w - 2, 1);
152 canvas->FillRectInt(border_color(state), 1, h - 1, w - 2, 1);
153 SkColor right_color = state != CustomButton::BS_NORMAL ?
154 border_color(state) : border_color(right_button_->state());
155 canvas->FillRectInt(right_color, w - 1, 0, 1, h);
156 break;
157 }
158
159 case RIGHT_BUTTON:
160 canvas->FillRectInt(background_color(state), 0, 1, w - 1, h - 2);
161 canvas->FillRectInt(border_color(state), 0, 0, w - 2, 1);
162 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1);
163 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4);
164 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1);
165 canvas->FillRectInt(border_color(state), 0, h - 1, w - 2, 1);
166 break;
167
168 case SINGLE_BUTTON:
169 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2);
170 canvas->FillRectInt(border_color(state), 2, 0, w - 4, 1);
171 canvas->FillRectInt(border_color(state), 1, 1, 1, 1);
172 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4);
173 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1);
174 canvas->FillRectInt(border_color(state), 2, h - 1, w - 4, 1);
175 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1);
176 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4);
177 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1);
178 break;
179
180 default:
181 NOTREACHED();
182 break;
183 }
184 }
185
186 private:
187 static SkColor border_color(CustomButton::ButtonState state) {
188 switch (state) {
189 case CustomButton::BS_HOT: return kHotBorderColor;
190 case CustomButton::BS_PUSHED: return kPushedBorderColor;
191 default: return kBorderColor;
192 }
193 }
194
195 static SkColor background_color(CustomButton::ButtonState state) {
196 switch (state) {
197 case CustomButton::BS_HOT: return kHotBackgroundColor;
198 case CustomButton::BS_PUSHED: return kPushedBackgroundColor;
199 default: return kBackgroundColor;
200 }
201 }
202
[email protected]34344902010-07-07 20:26:26203 ButtonType TypeAdjustedForRTL() const {
204 if (!base::i18n::IsRTL())
205 return type_;
206
207 switch (type_) {
208 case LEFT_BUTTON: return RIGHT_BUTTON;
209 case RIGHT_BUTTON: return LEFT_BUTTON;
210 default: break;
211 }
212 return type_;
213 }
214
[email protected]eccda9db2010-06-23 17:27:08215 const ButtonType type_;
216
217 // See description above setter for details.
218 CustomButton* left_button_;
219 CustomButton* right_button_;
220
221 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground);
222};
223
224// A View subclass that forces SchedulePaint to paint all. Normally when the
225// mouse enters/exits a button the buttons invokes SchedulePaint. As part of the
226// button border (MenuButtonBackground) is rendered by the button to the
227// left/right of it SchedulePaint on the the button may not be enough, so this
228// forces a paint all.
229class ScheduleAllView : public views::View {
230 public:
231 ScheduleAllView() {}
232
233 virtual void SchedulePaint(const gfx::Rect& r, bool urgent) {
234 if (!IsVisible())
235 return;
236
[email protected]34344902010-07-07 20:26:26237 if (GetParent()) {
238 GetParent()->SchedulePaint(GetBounds(APPLY_MIRRORING_TRANSFORMATION),
239 urgent);
240 }
[email protected]eccda9db2010-06-23 17:27:08241 }
242
243 private:
244 DISALLOW_COPY_AND_ASSIGN(ScheduleAllView);
245};
246
247TextButton* CreateAndConfigureButton(View* parent,
248 views::ButtonListener* listener,
249 int string_id,
250 MenuButtonBackground::ButtonType type,
251 MenuModel* model,
252 int index,
253 MenuButtonBackground** background) {
254 TextButton* button =
255 new TextButton(listener, l10n_util::GetString(string_id));
256 button->SetFocusable(true);
257 button->set_request_focus_on_press(false);
258 button->set_tag(index);
259 button->SetEnabled(model->IsEnabledAt(index));
260 button->set_prefix_type(TextButton::PREFIX_HIDE);
261 MenuButtonBackground* bg = new MenuButtonBackground(type);
262 button->set_background(bg);
263 button->SetEnabledColor(MenuConfig::instance().text_color);
264 if (background)
265 *background = bg;
266 button->set_border(new MenuButtonBorder());
267 button->set_alignment(TextButton::ALIGN_CENTER);
[email protected]eccda9db2010-06-23 17:27:08268 button->SetNormalHasBorder(true);
269 button->SetFont(views::MenuConfig::instance().font);
270 button->ClearMaxTextSize();
271 parent->AddChildView(button);
272 return button;
273}
274
275} // namespace
276
277// CutCopyPasteView ------------------------------------------------------------
278
279// CutCopyPasteView is the view containing the cut/copy/paste buttons.
280class WrenchMenu::CutCopyPasteView : public ScheduleAllView,
281 public views::ButtonListener {
282 public:
283 CutCopyPasteView(WrenchMenu* menu,
284 MenuModel* menu_model,
285 int cut_index,
286 int copy_index,
287 int paste_index)
288 : menu_(menu),
289 menu_model_(menu_model) {
290 TextButton* cut = CreateAndConfigureButton(
291 this, this, IDS_CUT, MenuButtonBackground::LEFT_BUTTON, menu_model,
292 cut_index, NULL);
293
294 MenuButtonBackground* copy_background = NULL;
295 CreateAndConfigureButton(
296 this, this, IDS_COPY, MenuButtonBackground::CENTER_BUTTON, menu_model,
297 copy_index, &copy_background);
298
299 TextButton* paste = CreateAndConfigureButton(
300 this, this, IDS_PASTE, MenuButtonBackground::RIGHT_BUTTON, menu_model,
301 paste_index, NULL);
302
303 copy_background->SetOtherButtons(cut, paste);
304 }
305
306 gfx::Size GetPreferredSize() {
307 // Returned height doesn't matter as MenuItemView forces everything to the
308 // height of the menuitemview.
309 return gfx::Size(GetMaxChildViewPreferredWidth() * GetChildViewCount(), 0);
310 }
311
312 void Layout() {
313 // All buttons are given the same width.
314 int width = GetMaxChildViewPreferredWidth();
315 for (int i = 0; i < GetChildViewCount(); ++i)
316 GetChildViewAt(i)->SetBounds(i * width, 0, width, height());
317 }
318
319 // ButtonListener
320 virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
321 menu_->CancelAndEvaluate(menu_model_, sender->tag());
322 }
323
324 private:
325 // Returns the max preferred width of all the children.
326 int GetMaxChildViewPreferredWidth() {
327 int width = 0;
328 for (int i = 0; i < GetChildViewCount(); ++i)
329 width = std::max(width, GetChildViewAt(i)->GetPreferredSize().width());
330 return width;
331 }
332
333 WrenchMenu* menu_;
334 MenuModel* menu_model_;
335
336 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView);
337};
338
339// ZoomView --------------------------------------------------------------------
340
341// Padding between the increment buttons and the reset button.
342static const int kZoomPadding = 6;
343
344// ZoomView contains the various zoom controls: two buttons to increase/decrease
345// the zoom, a label showing the current zoom percent, and a button to go
346// full-screen.
347class WrenchMenu::ZoomView : public ScheduleAllView,
348 public views::ButtonListener,
349 public NotificationObserver {
350 public:
351 ZoomView(WrenchMenu* menu,
352 MenuModel* menu_model,
[email protected]eccda9db2010-06-23 17:27:08353 int decrement_index,
[email protected]e1a02d62010-07-16 16:27:41354 int increment_index,
[email protected]eccda9db2010-06-23 17:27:08355 int fullscreen_index)
356 : menu_(menu),
357 menu_model_(menu_model),
358 fullscreen_index_(fullscreen_index),
359 increment_button_(NULL),
360 zoom_label_(NULL),
361 decrement_button_(NULL),
362 fullscreen_button_(NULL),
363 zoom_label_width_(0) {
[email protected]e1a02d62010-07-16 16:27:41364 decrement_button_ = CreateAndConfigureButton(
365 this, this, IDS_ZOOM_MINUS2, MenuButtonBackground::LEFT_BUTTON,
366 menu_model, decrement_index, NULL);
[email protected]eccda9db2010-06-23 17:27:08367
368 zoom_label_ = new Label(l10n_util::GetStringF(IDS_ZOOM_PERCENT, L"100"));
369 zoom_label_->SetColor(MenuConfig::instance().text_color);
370 zoom_label_->SetHorizontalAlignment(Label::ALIGN_RIGHT);
371 MenuButtonBackground* center_bg =
372 new MenuButtonBackground(MenuButtonBackground::CENTER_BUTTON);
373 zoom_label_->set_background(center_bg);
374 zoom_label_->set_border(new MenuButtonBorder());
375 zoom_label_->SetFont(MenuConfig::instance().font);
376 AddChildView(zoom_label_);
[email protected]84e4f8c2010-07-15 02:18:32377 zoom_label_width_ = MaxWidthForZoomLabel();
[email protected]eccda9db2010-06-23 17:27:08378
[email protected]e1a02d62010-07-16 16:27:41379 increment_button_ = CreateAndConfigureButton(
380 this, this, IDS_ZOOM_PLUS2, MenuButtonBackground::RIGHT_BUTTON,
381 menu_model, increment_index, NULL);
[email protected]eccda9db2010-06-23 17:27:08382
[email protected]5214bdd2010-08-02 21:43:47383 center_bg->SetOtherButtons(decrement_button_, increment_button_);
[email protected]eccda9db2010-06-23 17:27:08384
385 fullscreen_button_ = new FullscreenButton(this);
386 fullscreen_button_->SetImage(
387 ImageButton::BS_NORMAL,
388 ResourceBundle::GetSharedInstance().GetBitmapNamed(
389 IDR_FULLSCREEN_MENU_BUTTON));
390 fullscreen_button_->SetFocusable(true);
391 fullscreen_button_->set_request_focus_on_press(false);
392 fullscreen_button_->set_tag(fullscreen_index);
393 fullscreen_button_->SetImageAlignment(
394 ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE);
395 fullscreen_button_->set_border(views::Border::CreateEmptyBorder(
396 0, kHorizontalPadding, 0, kHorizontalPadding));
397 fullscreen_button_->set_background(
398 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON));
399 AddChildView(fullscreen_button_);
400
401 UpdateZoomControls();
402
403 registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
404 Source<Profile>(menu->browser_->profile()));
405 }
406
407 gfx::Size GetPreferredSize() {
408 // The increment/decrement button are forced to the same width.
409 int button_width = std::max(increment_button_->GetPreferredSize().width(),
410 decrement_button_->GetPreferredSize().width());
411 int fullscreen_width = fullscreen_button_->GetPreferredSize().width();
412 // Returned height doesn't matter as MenuItemView forces everything to the
413 // height of the menuitemview.
414 return gfx::Size(button_width + zoom_label_width_ + button_width +
415 kZoomPadding + fullscreen_width, 0);
416 }
417
418 void Layout() {
419 int x = 0;
420 int button_width = std::max(increment_button_->GetPreferredSize().width(),
421 decrement_button_->GetPreferredSize().width());
422 gfx::Rect bounds(0, 0, button_width, height());
423
[email protected]e1a02d62010-07-16 16:27:41424 decrement_button_->SetBounds(bounds);
[email protected]eccda9db2010-06-23 17:27:08425
426 x += bounds.width();
427 bounds.set_x(x);
428 bounds.set_width(zoom_label_width_);
429 zoom_label_->SetBounds(bounds);
430
431 x += bounds.width();
432 bounds.set_x(x);
433 bounds.set_width(button_width);
[email protected]e1a02d62010-07-16 16:27:41434 increment_button_->SetBounds(bounds);
[email protected]eccda9db2010-06-23 17:27:08435
436 x += bounds.width() + kZoomPadding;
437 bounds.set_x(x);
438 bounds.set_width(fullscreen_button_->GetPreferredSize().width());
439 fullscreen_button_->SetBounds(bounds);
440 }
441
442 // ButtonListener:
443 virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
444 if (sender->tag() == fullscreen_index_) {
445 menu_->CancelAndEvaluate(menu_model_, sender->tag());
446 } else {
447 // Zoom buttons don't close the menu.
448 menu_model_->ActivatedAt(sender->tag());
449 }
450 }
451
452 // NotificationObserver:
453 virtual void Observe(NotificationType type,
454 const NotificationSource& source,
455 const NotificationDetails& details) {
456 DCHECK_EQ(NotificationType::ZOOM_LEVEL_CHANGED, type.value);
457 UpdateZoomControls();
458 }
459
460 private:
461 void UpdateZoomControls() {
462 bool enable_increment, enable_decrement;
463 int zoom_percent =
[email protected]84e4f8c2010-07-15 02:18:32464 static_cast<int>(GetZoom(&enable_increment, &enable_decrement));
[email protected]7e1f2dec2010-07-21 18:31:43465 enable_increment = enable_increment &&
466 menu_model_->IsEnabledAt(increment_button_->tag());
467 enable_decrement = enable_decrement &&
468 menu_model_->IsEnabledAt(decrement_button_->tag());
[email protected]eccda9db2010-06-23 17:27:08469 increment_button_->SetEnabled(enable_increment);
470 decrement_button_->SetEnabled(enable_decrement);
[email protected]7e1f2dec2010-07-21 18:31:43471 zoom_label_->SetText(l10n_util::GetStringF(
[email protected]e83326f2010-07-31 17:29:25472 IDS_ZOOM_PERCENT,
473 UTF8ToWide(base::IntToString(zoom_percent))));
[email protected]7e1f2dec2010-07-21 18:31:43474 // If both increment and decrement are disabled, then we disable the zoom
475 // label too.
476 zoom_label_->SetEnabled(enable_increment || enable_decrement);
[email protected]eccda9db2010-06-23 17:27:08477 }
478
479 double GetZoom(bool* enable_increment, bool* enable_decrement) {
480 // TODO: move this somewhere it can be shared.
481 TabContents* selected_tab = menu_->browser_->GetSelectedTabContents();
482 *enable_decrement = *enable_increment = false;
483 if (!selected_tab)
484 return 1;
485
486 HostZoomMap* zoom_map = selected_tab->profile()->GetHostZoomMap();
487 if (!zoom_map)
488 return 1;
489
490 int zoom_level = zoom_map->GetZoomLevel(selected_tab->GetURL());
[email protected]84e4f8c2010-07-15 02:18:32491 double value = ZoomPercentFromZoomLevel(zoom_level);
492 *enable_decrement = (value != 50);
493 *enable_increment = (value != 300);
[email protected]eccda9db2010-06-23 17:27:08494 return value;
495 }
496
[email protected]84e4f8c2010-07-15 02:18:32497 double ZoomPercentFromZoomLevel(int level) {
498 return static_cast<double>(
499 std::max(std::min(std::pow(1.2, level), 3.0), .5)) * 100;
500 }
501
502 // Calculates the max width the zoom string can be.
503 int MaxWidthForZoomLabel() {
504 gfx::Font font = zoom_label_->font();
505 gfx::Insets insets;
506 if (zoom_label_->border())
507 zoom_label_->border()->GetInsets(&insets);
508 int max_w = 0;
509 for (int i = -4; i <= 7; ++i) {
510 int zoom_percent = static_cast<int>(ZoomPercentFromZoomLevel(i));
511 int w = font.GetStringWidth(
512 l10n_util::GetStringF(IDS_ZOOM_PERCENT, zoom_percent));
513 max_w = std::max(w, max_w);
514 }
515 return max_w + insets.width();
516 }
517
[email protected]eccda9db2010-06-23 17:27:08518 // Hosting WrenchMenu.
519 WrenchMenu* menu_;
520
521 // The menu model containing the increment/decrement/reset items.
522 MenuModel* menu_model_;
523
524 // Index of the fullscreen menu item in the model.
525 const int fullscreen_index_;
526
527 NotificationRegistrar registrar_;
528
529 // Button for incrementing the zoom.
530 TextButton* increment_button_;
531
532 // Label showing zoom as a percent.
533 Label* zoom_label_;
534
535 // Button for decrementing the zoom.
536 TextButton* decrement_button_;
537
538 ImageButton* fullscreen_button_;
539
540 // Width given to |zoom_label_|. This is the width at 100%.
541 int zoom_label_width_;
542
543 DISALLOW_COPY_AND_ASSIGN(ZoomView);
544};
545
546// WrenchMenu ------------------------------------------------------------------
547
548WrenchMenu::WrenchMenu(Browser* browser)
549 : browser_(browser),
550 selected_menu_model_(NULL),
551 selected_index_(0) {
552}
553
554WrenchMenu::~WrenchMenu() {
555}
556
557void WrenchMenu::Init(menus::MenuModel* model) {
558 DCHECK(!root_.get());
559 root_.reset(new MenuItemView(this));
560 root_->set_has_icons(true); // We have checks, radios and icons, set this
561 // so we get the taller menu style.
562 int next_id = 1;
563 PopulateMenu(root_.get(), model, &next_id);
564}
565
[email protected]a00b0322010-06-25 16:21:32566void WrenchMenu::RunMenu(views::MenuButton* host) {
[email protected]eccda9db2010-06-23 17:27:08567 gfx::Point screen_loc;
568 views::View::ConvertPointToScreen(host, &screen_loc);
[email protected]e78f12a22010-07-28 22:41:05569 gfx::Rect bounds(screen_loc, host->size());
[email protected]a00b0322010-06-25 16:21:32570 root_->RunMenuAt(host->GetWindow()->GetNativeWindow(), host, bounds,
[email protected]627da3f2010-07-27 21:19:01571 base::i18n::IsRTL() ? MenuItemView::TOPLEFT : MenuItemView::TOPRIGHT,
572 true);
[email protected]eccda9db2010-06-23 17:27:08573 if (selected_menu_model_)
574 selected_menu_model_->ActivatedAt(selected_index_);
575}
576
577bool WrenchMenu::IsItemChecked(int id) const {
578 const Entry& entry = id_to_entry_.find(id)->second;
579 return entry.first->IsItemCheckedAt(entry.second);
580}
581
582bool WrenchMenu::IsCommandEnabled(int id) const {
583 if (id == 0)
584 return false; // The root item.
585
586 const Entry& entry = id_to_entry_.find(id)->second;
587 int command_id = entry.first->GetCommandIdAt(entry.second);
588 // The items representing the cut (cut/copy/paste) and zoom menu
589 // (increment/decrement/reset) are always enabled. The child views of these
590 // items enabled state updates appropriately.
[email protected]e1a02d62010-07-16 16:27:41591 return command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS ||
[email protected]eccda9db2010-06-23 17:27:08592 entry.first->IsEnabledAt(entry.second);
593}
594
595void WrenchMenu::ExecuteCommand(int id) {
596 const Entry& entry = id_to_entry_.find(id)->second;
[email protected]60555ec2010-07-02 20:18:25597 int command_id = entry.first->GetCommandIdAt(entry.second);
598
[email protected]e1a02d62010-07-16 16:27:41599 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) {
[email protected]60555ec2010-07-02 20:18:25600 // These items are represented by child views. If ExecuteCommand is invoked
601 // it means the user clicked on the area around the buttons and we should
602 // not do anyting.
603 return;
604 }
605
[email protected]eccda9db2010-06-23 17:27:08606 return entry.first->ActivatedAt(entry.second);
607}
608
609bool WrenchMenu::GetAccelerator(int id, views::Accelerator* accelerator) {
610 const Entry& entry = id_to_entry_.find(id)->second;
611 int command_id = entry.first->GetCommandIdAt(entry.second);
[email protected]e1a02d62010-07-16 16:27:41612 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) {
[email protected]eccda9db2010-06-23 17:27:08613 // These have special child views; don't show the accelerator for them.
614 return false;
615 }
616
617 menus::Accelerator menu_accelerator;
618 if (!entry.first->GetAcceleratorAt(entry.second, &menu_accelerator))
619 return false;
620
621 *accelerator = views::Accelerator(menu_accelerator.GetKeyCode(),
622 menu_accelerator.modifiers());
623 return true;
624}
625
626void WrenchMenu::PopulateMenu(MenuItemView* parent,
627 MenuModel* model,
628 int* next_id) {
629 int index_offset = model->GetFirstItemIndex(NULL);
630 for (int i = 0, max = model->GetItemCount(); i < max; ++i) {
631 int index = i + index_offset;
632
633 MenuItemView* item =
634 AppendMenuItem(parent, model, index, model->GetTypeAt(index), next_id);
635
636 if (model->GetTypeAt(index) == MenuModel::TYPE_SUBMENU)
637 PopulateMenu(item, model->GetSubmenuModelAt(index), next_id);
638
639 if (model->GetCommandIdAt(index) == IDC_CUT) {
640 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index));
641 DCHECK_LT(i + 2, max);
642 DCHECK_EQ(IDC_COPY, model->GetCommandIdAt(index + 1));
643 DCHECK_EQ(IDC_PASTE, model->GetCommandIdAt(index + 2));
644 item->SetTitle(l10n_util::GetString(IDS_EDIT2));
645 item->AddChildView(
646 new CutCopyPasteView(this, model, index, index + 1, index + 2));
647 i += 2;
[email protected]e1a02d62010-07-16 16:27:41648 } else if (model->GetCommandIdAt(index) == IDC_ZOOM_MINUS) {
[email protected]eccda9db2010-06-23 17:27:08649 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index));
[email protected]e1a02d62010-07-16 16:27:41650 DCHECK_EQ(IDC_ZOOM_PLUS, model->GetCommandIdAt(index + 1));
[email protected]eccda9db2010-06-23 17:27:08651 DCHECK_EQ(IDC_FULLSCREEN, model->GetCommandIdAt(index + 2));
652 item->SetTitle(l10n_util::GetString(IDS_ZOOM_MENU2));
653 item->AddChildView(
654 new ZoomView(this, model, index, index + 1, index + 2));
655 i += 2;
656 }
657 }
658}
659
660MenuItemView* WrenchMenu::AppendMenuItem(MenuItemView* parent,
661 MenuModel* model,
662 int index,
663 MenuModel::ItemType menu_type,
664 int* next_id) {
665 int id = (*next_id)++;
666 SkBitmap icon;
667 std::wstring label;
668 MenuItemView::Type type;
669 switch (menu_type) {
670 case MenuModel::TYPE_COMMAND:
671 model->GetIconAt(index, &icon);
672 type = MenuItemView::NORMAL;
673 label = UTF16ToWide(model->GetLabelAt(index));
674 break;
675 case MenuModel::TYPE_CHECK:
676 type = MenuItemView::CHECKBOX;
677 label = UTF16ToWide(model->GetLabelAt(index));
678 break;
679 case MenuModel::TYPE_RADIO:
680 type = MenuItemView::RADIO;
681 label = UTF16ToWide(model->GetLabelAt(index));
682 break;
683 case MenuModel::TYPE_SEPARATOR:
684 type = MenuItemView::SEPARATOR;
685 break;
686 case MenuModel::TYPE_SUBMENU:
687 type = MenuItemView::SUBMENU;
688 label = UTF16ToWide(model->GetLabelAt(index));
689 break;
690 default:
691 NOTREACHED();
692 type = MenuItemView::NORMAL;
693 break;
694 }
695
696 id_to_entry_[id].first = model;
697 id_to_entry_[id].second = index;
698
699 MenuItemView* menu_item = parent->AppendMenuItemImpl(id, label, icon, type);
700
701 if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) {
702 SkBitmap icon;
703 if (model->GetIconAt(index, &icon))
704 menu_item->SetIcon(icon);
705 }
706
707 return menu_item;
708}
709
710void WrenchMenu::CancelAndEvaluate(MenuModel* model, int index) {
711 selected_menu_model_ = model;
712 selected_index_ = index;
713 root_->Cancel();
714}