blob: 03dab34892acd282d6546a60e7d99cd2289f7866 [file] [log] [blame]
[email protected]9e790bd2011-01-10 23:48:541// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]eccda9db2010-06-23 17:27:082// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]9e790bd2011-01-10 23:48:545#include "chrome/browser/ui/views/wrench_menu.h"
[email protected]eccda9db2010-06-23 17:27:086
[email protected]730718e2011-10-07 06:57:017#include <algorithm>
[email protected]eccda9db2010-06-23 17:27:088#include <cmath>
[email protected]730718e2011-10-07 06:57:019#include <set>
[email protected]eccda9db2010-06-23 17:27:0810
[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"
[email protected]1a3aba82010-11-08 23:52:5413#include "chrome/app/chrome_command_ids.h"
[email protected]c5174282011-05-20 23:42:0614#include "chrome/browser/bookmarks/bookmark_model.h"
[email protected]8ecad5e2010-12-02 21:18:3315#include "chrome/browser/profiles/profile.h"
[email protected]f46be6e2010-11-16 03:52:3216#include "chrome/browser/ui/browser.h"
[email protected]c5174282011-05-20 23:42:0617#include "chrome/browser/ui/browser_window.h"
18#include "chrome/browser/ui/views/bookmarks/bookmark_menu_delegate.h"
[email protected]72e07fa2011-10-10 22:38:0719#include "chrome/common/chrome_notification_types.h"
[email protected]21f11682011-03-02 16:45:4220#include "content/browser/tab_contents/tab_contents.h"
[email protected]afd1e522011-04-27 23:29:5921#include "content/browser/user_metrics.h"
[email protected]f4c2a6f2011-03-08 18:28:0622#include "content/common/notification_observer.h"
23#include "content/common/notification_registrar.h"
24#include "content/common/notification_source.h"
[email protected]0d6e9bd2011-10-18 04:29:1625#include "content/public/browser/notification_types.h"
[email protected]e12e3f62010-08-06 00:44:5926#include "grit/chromium_strings.h"
[email protected]eccda9db2010-06-23 17:27:0827#include "grit/generated_resources.h"
28#include "grit/theme_resources.h"
29#include "third_party/skia/include/core/SkPaint.h"
[email protected]c051a1b2011-01-21 23:30:1730#include "ui/base/l10n/l10n_util.h"
[email protected]42ce29d2011-01-20 23:19:4631#include "ui/base/resource/resource_bundle.h"
[email protected]08397d52011-02-05 01:53:3832#include "ui/gfx/canvas.h"
33#include "ui/gfx/canvas_skia.h"
34#include "ui/gfx/skia_util.h"
[email protected]eccda9db2010-06-23 17:27:0835#include "views/background.h"
36#include "views/controls/button/image_button.h"
[email protected]a00b0322010-06-25 16:21:3237#include "views/controls/button/menu_button.h"
[email protected]eccda9db2010-06-23 17:27:0838#include "views/controls/button/text_button.h"
39#include "views/controls/label.h"
40#include "views/controls/menu/menu_config.h"
41#include "views/controls/menu/menu_item_view.h"
[email protected]2c6ba3692011-08-25 03:59:5442#include "views/controls/menu/menu_runner.h"
[email protected]eccda9db2010-06-23 17:27:0843#include "views/controls/menu/menu_scroll_view_container.h"
44#include "views/controls/menu/submenu_view.h"
[email protected]2fdd00a2011-06-13 21:56:2645#include "views/widget/widget.h"
[email protected]eccda9db2010-06-23 17:27:0846
[email protected]44cbd9e2011-01-14 15:49:4047using ui::MenuModel;
[email protected]eccda9db2010-06-23 17:27:0848using views::CustomButton;
49using views::ImageButton;
50using views::Label;
51using views::MenuConfig;
52using views::MenuItemView;
53using views::TextButton;
54using views::View;
55
56namespace {
57
58// Colors used for buttons.
59const SkColor kHotBorderColor = SkColorSetARGB(72, 0, 0, 0);
60const SkColor kBorderColor = SkColorSetARGB(36, 0, 0, 0);
61const SkColor kPushedBorderColor = SkColorSetARGB(72, 0, 0, 0);
62const SkColor kHotBackgroundColor = SkColorSetARGB(204, 255, 255, 255);
63const SkColor kBackgroundColor = SkColorSetARGB(102, 255, 255, 255);
64const SkColor kPushedBackgroundColor = SkColorSetARGB(13, 0, 0, 0);
65
66// Horizontal padding on the edges of the buttons.
67const int kHorizontalPadding = 6;
68
69// Subclass of ImageButton whose preferred size includes the size of the border.
70class FullscreenButton : public ImageButton {
71 public:
[email protected]65ecd5b2010-07-27 21:29:0672 explicit FullscreenButton(views::ButtonListener* listener)
73 : ImageButton(listener) { }
[email protected]eccda9db2010-06-23 17:27:0874
75 virtual gfx::Size GetPreferredSize() {
76 gfx::Size pref = ImageButton::GetPreferredSize();
77 gfx::Insets insets;
78 if (border())
79 border()->GetInsets(&insets);
80 pref.Enlarge(insets.width(), insets.height());
81 return pref;
82 }
83
84 private:
85 DISALLOW_COPY_AND_ASSIGN(FullscreenButton);
86};
87
88// Border for buttons contained in the menu. This is only used for getting the
89// insets, the actual painting is done in MenuButtonBackground.
90class MenuButtonBorder : public views::Border {
91 public:
92 MenuButtonBorder() {}
93
94 virtual void Paint(const View& view, gfx::Canvas* canvas) const {
95 // Painting of border is done in MenuButtonBackground.
96 }
97
98 virtual void GetInsets(gfx::Insets* insets) const {
[email protected]e57bfac2010-08-03 17:19:5499 insets->Set(MenuConfig::instance().item_top_margin,
[email protected]eccda9db2010-06-23 17:27:08100 kHorizontalPadding,
[email protected]e57bfac2010-08-03 17:19:54101 MenuConfig::instance().item_bottom_margin,
[email protected]eccda9db2010-06-23 17:27:08102 kHorizontalPadding);
103 }
104
105 private:
106 DISALLOW_COPY_AND_ASSIGN(MenuButtonBorder);
107};
108
109// Combination border/background for the buttons contained in the menu. The
110// painting of the border/background is done here as TextButton does not always
111// paint the border.
112class MenuButtonBackground : public views::Background {
113 public:
114 enum ButtonType {
115 LEFT_BUTTON,
116 CENTER_BUTTON,
117 RIGHT_BUTTON,
118 SINGLE_BUTTON,
119 };
120
121 explicit MenuButtonBackground(ButtonType type)
122 : type_(type),
123 left_button_(NULL),
124 right_button_(NULL) {}
125
126 // Used when the type is CENTER_BUTTON to determine if the left/right edge
127 // needs to be rendered selected.
128 void SetOtherButtons(CustomButton* left_button, CustomButton* right_button) {
[email protected]5214bdd2010-08-02 21:43:47129 if (base::i18n::IsRTL()) {
130 left_button_ = right_button;
131 right_button_ = left_button;
132 } else {
133 left_button_ = left_button;
134 right_button_ = right_button;
135 }
[email protected]eccda9db2010-06-23 17:27:08136 }
137
138 virtual void Paint(gfx::Canvas* canvas, View* view) const {
139 CustomButton::ButtonState state =
140 (view->GetClassName() == views::Label::kViewClassName) ?
141 CustomButton::BS_NORMAL : static_cast<CustomButton*>(view)->state();
142 int w = view->width();
143 int h = view->height();
[email protected]34344902010-07-07 20:26:26144 switch (TypeAdjustedForRTL()) {
[email protected]eccda9db2010-06-23 17:27:08145 case LEFT_BUTTON:
146 canvas->FillRectInt(background_color(state), 1, 1, w, h - 2);
147 canvas->FillRectInt(border_color(state), 2, 0, w, 1);
148 canvas->FillRectInt(border_color(state), 1, 1, 1, 1);
149 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4);
150 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1);
151 canvas->FillRectInt(border_color(state), 2, h - 1, w, 1);
152 break;
153
154 case CENTER_BUTTON: {
155 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2);
156 SkColor left_color = state != CustomButton::BS_NORMAL ?
157 border_color(state) : border_color(left_button_->state());
158 canvas->FillRectInt(left_color, 0, 0, 1, h);
159 canvas->FillRectInt(border_color(state), 1, 0, w - 2, 1);
160 canvas->FillRectInt(border_color(state), 1, h - 1, w - 2, 1);
161 SkColor right_color = state != CustomButton::BS_NORMAL ?
162 border_color(state) : border_color(right_button_->state());
163 canvas->FillRectInt(right_color, w - 1, 0, 1, h);
164 break;
165 }
166
167 case RIGHT_BUTTON:
168 canvas->FillRectInt(background_color(state), 0, 1, w - 1, h - 2);
169 canvas->FillRectInt(border_color(state), 0, 0, w - 2, 1);
170 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1);
171 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4);
172 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1);
173 canvas->FillRectInt(border_color(state), 0, h - 1, w - 2, 1);
174 break;
175
176 case SINGLE_BUTTON:
177 canvas->FillRectInt(background_color(state), 1, 1, w - 2, h - 2);
178 canvas->FillRectInt(border_color(state), 2, 0, w - 4, 1);
179 canvas->FillRectInt(border_color(state), 1, 1, 1, 1);
180 canvas->FillRectInt(border_color(state), 0, 2, 1, h - 4);
181 canvas->FillRectInt(border_color(state), 1, h - 2, 1, 1);
182 canvas->FillRectInt(border_color(state), 2, h - 1, w - 4, 1);
183 canvas->FillRectInt(border_color(state), w - 2, 1, 1, 1);
184 canvas->FillRectInt(border_color(state), w - 1, 2, 1, h - 4);
185 canvas->FillRectInt(border_color(state), w - 2, h - 2, 1, 1);
186 break;
187
188 default:
189 NOTREACHED();
190 break;
191 }
192 }
193
194 private:
195 static SkColor border_color(CustomButton::ButtonState state) {
196 switch (state) {
197 case CustomButton::BS_HOT: return kHotBorderColor;
198 case CustomButton::BS_PUSHED: return kPushedBorderColor;
199 default: return kBorderColor;
200 }
201 }
202
203 static SkColor background_color(CustomButton::ButtonState state) {
204 switch (state) {
205 case CustomButton::BS_HOT: return kHotBackgroundColor;
206 case CustomButton::BS_PUSHED: return kPushedBackgroundColor;
207 default: return kBackgroundColor;
208 }
209 }
210
[email protected]34344902010-07-07 20:26:26211 ButtonType TypeAdjustedForRTL() const {
212 if (!base::i18n::IsRTL())
213 return type_;
214
215 switch (type_) {
216 case LEFT_BUTTON: return RIGHT_BUTTON;
217 case RIGHT_BUTTON: return LEFT_BUTTON;
218 default: break;
219 }
220 return type_;
221 }
222
[email protected]eccda9db2010-06-23 17:27:08223 const ButtonType type_;
224
225 // See description above setter for details.
226 CustomButton* left_button_;
227 CustomButton* right_button_;
228
229 DISALLOW_COPY_AND_ASSIGN(MenuButtonBackground);
230};
231
232// A View subclass that forces SchedulePaint to paint all. Normally when the
233// mouse enters/exits a button the buttons invokes SchedulePaint. As part of the
234// button border (MenuButtonBackground) is rendered by the button to the
235// left/right of it SchedulePaint on the the button may not be enough, so this
236// forces a paint all.
237class ScheduleAllView : public views::View {
238 public:
239 ScheduleAllView() {}
240
[email protected]64831952011-06-03 21:19:32241 virtual void SchedulePaintInRect(const gfx::Rect& r) {
242 View::SchedulePaintInRect(gfx::Rect(0, 0, width(), height()));
243 }
244
[email protected]eccda9db2010-06-23 17:27:08245 private:
246 DISALLOW_COPY_AND_ASSIGN(ScheduleAllView);
247};
248
[email protected]6f1d18e2011-01-10 20:57:00249string16 GetAccessibleNameForWrenchMenuItem(
[email protected]2cc800b2010-10-01 18:27:34250 MenuModel* model, int item_index, int accessible_string_id) {
[email protected]6f1d18e2011-01-10 20:57:00251 string16 accessible_name = l10n_util::GetStringUTF16(accessible_string_id);
252 string16 accelerator_text;
[email protected]2cc800b2010-10-01 18:27:34253
[email protected]44cbd9e2011-01-14 15:49:40254 ui::Accelerator menu_accelerator;
[email protected]2cc800b2010-10-01 18:27:34255 if (model->GetAcceleratorAt(item_index, &menu_accelerator)) {
256 accelerator_text =
[email protected]3a37ad412011-05-20 14:46:05257 views::Accelerator(menu_accelerator.key_code(),
[email protected]2cc800b2010-10-01 18:27:34258 menu_accelerator.modifiers()).GetShortcutText();
259 }
260
261 return MenuItemView::GetAccessibleNameForMenuItem(
262 accessible_name, accelerator_text);
[email protected]eccda9db2010-06-23 17:27:08263}
264
[email protected]2cc800b2010-10-01 18:27:34265// WrenchMenuView is a view that can contain text buttons.
266class WrenchMenuView : public ScheduleAllView, public views::ButtonListener {
267 public:
268 WrenchMenuView(WrenchMenu* menu, MenuModel* menu_model)
269 : menu_(menu), menu_model_(menu_model) { }
270
271 TextButton* CreateAndConfigureButton(int string_id,
272 MenuButtonBackground::ButtonType type,
273 int index,
274 MenuButtonBackground** background) {
275 return CreateButtonWithAccName(
276 string_id, type, index, background, string_id);
277 }
278
279 TextButton* CreateButtonWithAccName(int string_id,
280 MenuButtonBackground::ButtonType type,
281 int index,
282 MenuButtonBackground** background,
283 int acc_string_id) {
284 TextButton* button =
[email protected]7c1b7b42011-01-06 21:39:37285 new TextButton(this, UTF16ToWide(l10n_util::GetStringUTF16(string_id)));
[email protected]001f08a2011-01-14 18:34:56286 button->SetAccessibleName(
287 GetAccessibleNameForWrenchMenuItem(menu_model_, index, acc_string_id));
[email protected]4d81c242011-06-01 17:59:47288 button->set_focusable(true);
[email protected]2cc800b2010-10-01 18:27:34289 button->set_request_focus_on_press(false);
290 button->set_tag(index);
291 button->SetEnabled(menu_model_->IsEnabledAt(index));
292 button->set_prefix_type(TextButton::PREFIX_HIDE);
293 MenuButtonBackground* bg = new MenuButtonBackground(type);
294 button->set_background(bg);
295 button->SetEnabledColor(MenuConfig::instance().text_color);
296 if (background)
297 *background = bg;
298 button->set_border(new MenuButtonBorder());
299 button->set_alignment(TextButton::ALIGN_CENTER);
[email protected]2cc800b2010-10-01 18:27:34300 button->SetFont(views::MenuConfig::instance().font);
301 button->ClearMaxTextSize();
302 AddChildView(button);
303 return button;
304 }
305
306 protected:
307 // Hosting WrenchMenu.
308 WrenchMenu* menu_;
309
310 // The menu model containing the increment/decrement/reset items.
311 MenuModel* menu_model_;
312
313 private:
314 DISALLOW_COPY_AND_ASSIGN(WrenchMenuView);
315};
316
[email protected]eccda9db2010-06-23 17:27:08317} // namespace
318
319// CutCopyPasteView ------------------------------------------------------------
320
321// CutCopyPasteView is the view containing the cut/copy/paste buttons.
[email protected]2cc800b2010-10-01 18:27:34322class WrenchMenu::CutCopyPasteView : public WrenchMenuView {
[email protected]eccda9db2010-06-23 17:27:08323 public:
324 CutCopyPasteView(WrenchMenu* menu,
325 MenuModel* menu_model,
326 int cut_index,
327 int copy_index,
328 int paste_index)
[email protected]2cc800b2010-10-01 18:27:34329 : WrenchMenuView(menu, menu_model) {
[email protected]eccda9db2010-06-23 17:27:08330 TextButton* cut = CreateAndConfigureButton(
[email protected]2cc800b2010-10-01 18:27:34331 IDS_CUT, MenuButtonBackground::LEFT_BUTTON, cut_index, NULL);
[email protected]eccda9db2010-06-23 17:27:08332
333 MenuButtonBackground* copy_background = NULL;
334 CreateAndConfigureButton(
[email protected]2cc800b2010-10-01 18:27:34335 IDS_COPY, MenuButtonBackground::CENTER_BUTTON, copy_index,
336 &copy_background);
[email protected]eccda9db2010-06-23 17:27:08337
338 TextButton* paste = CreateAndConfigureButton(
[email protected]2cc800b2010-10-01 18:27:34339 IDS_PASTE, MenuButtonBackground::RIGHT_BUTTON, paste_index, NULL);
[email protected]eccda9db2010-06-23 17:27:08340
341 copy_background->SetOtherButtons(cut, paste);
342 }
343
344 gfx::Size GetPreferredSize() {
345 // Returned height doesn't matter as MenuItemView forces everything to the
346 // height of the menuitemview.
[email protected]20838602011-02-09 04:50:21347 return gfx::Size(GetMaxChildViewPreferredWidth() * child_count(), 0);
[email protected]eccda9db2010-06-23 17:27:08348 }
349
350 void Layout() {
351 // All buttons are given the same width.
352 int width = GetMaxChildViewPreferredWidth();
[email protected]20838602011-02-09 04:50:21353 for (int i = 0; i < child_count(); ++i)
[email protected]fe84b912011-07-15 17:13:33354 child_at(i)->SetBounds(i * width, 0, width, height());
[email protected]eccda9db2010-06-23 17:27:08355 }
356
357 // ButtonListener
358 virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
359 menu_->CancelAndEvaluate(menu_model_, sender->tag());
360 }
361
362 private:
363 // Returns the max preferred width of all the children.
364 int GetMaxChildViewPreferredWidth() {
365 int width = 0;
[email protected]20838602011-02-09 04:50:21366 for (int i = 0; i < child_count(); ++i)
[email protected]fe84b912011-07-15 17:13:33367 width = std::max(width, child_at(i)->GetPreferredSize().width());
[email protected]eccda9db2010-06-23 17:27:08368 return width;
369 }
370
[email protected]eccda9db2010-06-23 17:27:08371 DISALLOW_COPY_AND_ASSIGN(CutCopyPasteView);
372};
373
374// ZoomView --------------------------------------------------------------------
375
376// Padding between the increment buttons and the reset button.
377static const int kZoomPadding = 6;
378
379// ZoomView contains the various zoom controls: two buttons to increase/decrease
380// the zoom, a label showing the current zoom percent, and a button to go
381// full-screen.
[email protected]2cc800b2010-10-01 18:27:34382class WrenchMenu::ZoomView : public WrenchMenuView,
[email protected]eccda9db2010-06-23 17:27:08383 public NotificationObserver {
384 public:
385 ZoomView(WrenchMenu* menu,
386 MenuModel* menu_model,
[email protected]eccda9db2010-06-23 17:27:08387 int decrement_index,
[email protected]e1a02d62010-07-16 16:27:41388 int increment_index,
[email protected]eccda9db2010-06-23 17:27:08389 int fullscreen_index)
[email protected]2cc800b2010-10-01 18:27:34390 : WrenchMenuView(menu, menu_model),
[email protected]eccda9db2010-06-23 17:27:08391 fullscreen_index_(fullscreen_index),
392 increment_button_(NULL),
393 zoom_label_(NULL),
394 decrement_button_(NULL),
395 fullscreen_button_(NULL),
396 zoom_label_width_(0) {
[email protected]2cc800b2010-10-01 18:27:34397 decrement_button_ = CreateButtonWithAccName(
398 IDS_ZOOM_MINUS2, MenuButtonBackground::LEFT_BUTTON, decrement_index,
399 NULL, IDS_ACCNAME_ZOOM_MINUS2);
[email protected]eccda9db2010-06-23 17:27:08400
[email protected]7c1b7b42011-01-06 21:39:37401 zoom_label_ = new Label(
[email protected]730718e2011-10-07 06:57:01402 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100));
[email protected]9b0e63e52011-10-12 23:08:10403 zoom_label_->SetAutoColorReadabilityEnabled(false);
404 zoom_label_->SetEnabledColor(MenuConfig::instance().text_color);
[email protected]eccda9db2010-06-23 17:27:08405 zoom_label_->SetHorizontalAlignment(Label::ALIGN_RIGHT);
406 MenuButtonBackground* center_bg =
407 new MenuButtonBackground(MenuButtonBackground::CENTER_BUTTON);
408 zoom_label_->set_background(center_bg);
409 zoom_label_->set_border(new MenuButtonBorder());
410 zoom_label_->SetFont(MenuConfig::instance().font);
411 AddChildView(zoom_label_);
[email protected]84e4f8c2010-07-15 02:18:32412 zoom_label_width_ = MaxWidthForZoomLabel();
[email protected]eccda9db2010-06-23 17:27:08413
[email protected]2cc800b2010-10-01 18:27:34414 increment_button_ = CreateButtonWithAccName(
415 IDS_ZOOM_PLUS2, MenuButtonBackground::RIGHT_BUTTON, increment_index,
416 NULL, IDS_ACCNAME_ZOOM_PLUS2);
[email protected]eccda9db2010-06-23 17:27:08417
[email protected]5214bdd2010-08-02 21:43:47418 center_bg->SetOtherButtons(decrement_button_, increment_button_);
[email protected]eccda9db2010-06-23 17:27:08419
420 fullscreen_button_ = new FullscreenButton(this);
421 fullscreen_button_->SetImage(
422 ImageButton::BS_NORMAL,
423 ResourceBundle::GetSharedInstance().GetBitmapNamed(
424 IDR_FULLSCREEN_MENU_BUTTON));
[email protected]4d81c242011-06-01 17:59:47425 fullscreen_button_->set_focusable(true);
[email protected]eccda9db2010-06-23 17:27:08426 fullscreen_button_->set_request_focus_on_press(false);
427 fullscreen_button_->set_tag(fullscreen_index);
428 fullscreen_button_->SetImageAlignment(
429 ImageButton::ALIGN_CENTER, ImageButton::ALIGN_MIDDLE);
430 fullscreen_button_->set_border(views::Border::CreateEmptyBorder(
431 0, kHorizontalPadding, 0, kHorizontalPadding));
432 fullscreen_button_->set_background(
433 new MenuButtonBackground(MenuButtonBackground::SINGLE_BUTTON));
[email protected]001f08a2011-01-14 18:34:56434 fullscreen_button_->SetAccessibleName(
[email protected]2cc800b2010-10-01 18:27:34435 GetAccessibleNameForWrenchMenuItem(
[email protected]001f08a2011-01-14 18:34:56436 menu_model, fullscreen_index, IDS_ACCNAME_FULLSCREEN));
[email protected]eccda9db2010-06-23 17:27:08437 AddChildView(fullscreen_button_);
438
439 UpdateZoomControls();
440
[email protected]89805c12011-05-23 23:53:00441 registrar_.Add(
[email protected]432115822011-07-10 15:52:27442 this, content::NOTIFICATION_ZOOM_LEVEL_CHANGED,
[email protected]89805c12011-05-23 23:53:00443 Source<HostZoomMap>(menu->browser_->profile()->GetHostZoomMap()));
[email protected]eccda9db2010-06-23 17:27:08444 }
445
446 gfx::Size GetPreferredSize() {
447 // The increment/decrement button are forced to the same width.
448 int button_width = std::max(increment_button_->GetPreferredSize().width(),
449 decrement_button_->GetPreferredSize().width());
450 int fullscreen_width = fullscreen_button_->GetPreferredSize().width();
451 // Returned height doesn't matter as MenuItemView forces everything to the
452 // height of the menuitemview.
453 return gfx::Size(button_width + zoom_label_width_ + button_width +
454 kZoomPadding + fullscreen_width, 0);
455 }
456
457 void Layout() {
458 int x = 0;
459 int button_width = std::max(increment_button_->GetPreferredSize().width(),
460 decrement_button_->GetPreferredSize().width());
461 gfx::Rect bounds(0, 0, button_width, height());
462
[email protected]97a31142011-02-08 00:09:16463 decrement_button_->SetBoundsRect(bounds);
[email protected]eccda9db2010-06-23 17:27:08464
465 x += bounds.width();
466 bounds.set_x(x);
467 bounds.set_width(zoom_label_width_);
[email protected]97a31142011-02-08 00:09:16468 zoom_label_->SetBoundsRect(bounds);
[email protected]eccda9db2010-06-23 17:27:08469
470 x += bounds.width();
471 bounds.set_x(x);
472 bounds.set_width(button_width);
[email protected]97a31142011-02-08 00:09:16473 increment_button_->SetBoundsRect(bounds);
[email protected]eccda9db2010-06-23 17:27:08474
475 x += bounds.width() + kZoomPadding;
476 bounds.set_x(x);
477 bounds.set_width(fullscreen_button_->GetPreferredSize().width());
[email protected]97a31142011-02-08 00:09:16478 fullscreen_button_->SetBoundsRect(bounds);
[email protected]eccda9db2010-06-23 17:27:08479 }
480
481 // ButtonListener:
482 virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
483 if (sender->tag() == fullscreen_index_) {
484 menu_->CancelAndEvaluate(menu_model_, sender->tag());
485 } else {
486 // Zoom buttons don't close the menu.
487 menu_model_->ActivatedAt(sender->tag());
488 }
489 }
490
491 // NotificationObserver:
[email protected]432115822011-07-10 15:52:27492 virtual void Observe(int type,
[email protected]eccda9db2010-06-23 17:27:08493 const NotificationSource& source,
494 const NotificationDetails& details) {
[email protected]432115822011-07-10 15:52:27495 DCHECK_EQ(content::NOTIFICATION_ZOOM_LEVEL_CHANGED, type);
[email protected]eccda9db2010-06-23 17:27:08496 UpdateZoomControls();
497 }
498
499 private:
500 void UpdateZoomControls() {
[email protected]b75b8292010-10-01 07:28:25501 bool enable_increment = false;
502 bool enable_decrement = false;
503 TabContents* selected_tab = menu_->browser_->GetSelectedTabContents();
504 int zoom = 100;
505 if (selected_tab)
506 zoom = selected_tab->GetZoomPercent(&enable_increment, &enable_decrement);
[email protected]eccda9db2010-06-23 17:27:08507 increment_button_->SetEnabled(enable_increment);
508 decrement_button_->SetEnabled(enable_decrement);
[email protected]730718e2011-10-07 06:57:01509 zoom_label_->SetText(
510 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, zoom));
[email protected]eccda9db2010-06-23 17:27:08511
[email protected]b75b8292010-10-01 07:28:25512 zoom_label_width_ = MaxWidthForZoomLabel();
[email protected]84e4f8c2010-07-15 02:18:32513 }
514
515 // Calculates the max width the zoom string can be.
516 int MaxWidthForZoomLabel() {
517 gfx::Font font = zoom_label_->font();
518 gfx::Insets insets;
519 if (zoom_label_->border())
520 zoom_label_->border()->GetInsets(&insets);
[email protected]b75b8292010-10-01 07:28:25521
[email protected]84e4f8c2010-07-15 02:18:32522 int max_w = 0;
[email protected]b75b8292010-10-01 07:28:25523
524 TabContents* selected_tab = menu_->browser_->GetSelectedTabContents();
525 if (selected_tab) {
526 int min_percent = selected_tab->minimum_zoom_percent();
527 int max_percent = selected_tab->maximum_zoom_percent();
528
529 int step = (max_percent - min_percent) / 10;
530 for (int i = min_percent; i <= max_percent; i += step) {
[email protected]13658c42011-01-04 20:46:14531 int w = font.GetStringWidth(
532 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, i));
[email protected]b75b8292010-10-01 07:28:25533 max_w = std::max(w, max_w);
534 }
535 } else {
[email protected]13658c42011-01-04 20:46:14536 max_w = font.GetStringWidth(
537 l10n_util::GetStringFUTF16Int(IDS_ZOOM_PERCENT, 100));
[email protected]84e4f8c2010-07-15 02:18:32538 }
[email protected]b75b8292010-10-01 07:28:25539
[email protected]84e4f8c2010-07-15 02:18:32540 return max_w + insets.width();
541 }
542
[email protected]eccda9db2010-06-23 17:27:08543 // Index of the fullscreen menu item in the model.
544 const int fullscreen_index_;
545
546 NotificationRegistrar registrar_;
547
548 // Button for incrementing the zoom.
549 TextButton* increment_button_;
550
551 // Label showing zoom as a percent.
552 Label* zoom_label_;
553
554 // Button for decrementing the zoom.
555 TextButton* decrement_button_;
556
557 ImageButton* fullscreen_button_;
558
559 // Width given to |zoom_label_|. This is the width at 100%.
560 int zoom_label_width_;
561
562 DISALLOW_COPY_AND_ASSIGN(ZoomView);
563};
564
565// WrenchMenu ------------------------------------------------------------------
566
567WrenchMenu::WrenchMenu(Browser* browser)
[email protected]2c6ba3692011-08-25 03:59:54568 : root_(NULL),
569 browser_(browser),
[email protected]eccda9db2010-06-23 17:27:08570 selected_menu_model_(NULL),
[email protected]c5174282011-05-20 23:42:06571 selected_index_(0),
572 bookmark_menu_(NULL),
573 first_bookmark_command_id_(0) {
[email protected]72e07fa2011-10-10 22:38:07574 registrar_.Add(this, chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED,
575 Source<Profile>(browser_->profile()));
[email protected]eccda9db2010-06-23 17:27:08576}
577
[email protected]2c6ba3692011-08-25 03:59:54578WrenchMenu::~WrenchMenu() {
579 if (bookmark_menu_delegate_.get()) {
580 BookmarkModel* model = browser_->profile()->GetBookmarkModel();
581 if (model)
582 model->RemoveObserver(this);
583 }
584}
585
[email protected]44cbd9e2011-01-14 15:49:40586void WrenchMenu::Init(ui::MenuModel* model) {
[email protected]2c6ba3692011-08-25 03:59:54587 DCHECK(!root_);
588 root_ = new MenuItemView(this);
[email protected]eccda9db2010-06-23 17:27:08589 root_->set_has_icons(true); // We have checks, radios and icons, set this
590 // so we get the taller menu style.
591 int next_id = 1;
[email protected]2c6ba3692011-08-25 03:59:54592 PopulateMenu(root_, model, &next_id);
[email protected]c5174282011-05-20 23:42:06593 first_bookmark_command_id_ = next_id + 1;
[email protected]2c6ba3692011-08-25 03:59:54594 menu_runner_.reset(new views::MenuRunner(root_));
[email protected]eccda9db2010-06-23 17:27:08595}
596
[email protected]a00b0322010-06-25 16:21:32597void WrenchMenu::RunMenu(views::MenuButton* host) {
[email protected]eccda9db2010-06-23 17:27:08598 gfx::Point screen_loc;
599 views::View::ConvertPointToScreen(host, &screen_loc);
[email protected]e78f12a22010-07-28 22:41:05600 gfx::Rect bounds(screen_loc, host->size());
[email protected]366069f2011-01-11 09:36:12601 UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu"));
[email protected]2c6ba3692011-08-25 03:59:54602 if (menu_runner_->RunMenuAt(host->GetWidget(), host, bounds,
603 MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) ==
604 views::MenuRunner::MENU_DELETED)
605 return;
[email protected]c5174282011-05-20 23:42:06606 if (bookmark_menu_delegate_.get()) {
607 BookmarkModel* model = browser_->profile()->GetBookmarkModel();
608 if (model)
609 model->RemoveObserver(this);
610 }
[email protected]eccda9db2010-06-23 17:27:08611 if (selected_menu_model_)
612 selected_menu_model_->ActivatedAt(selected_index_);
613}
614
[email protected]8d37b112011-09-15 18:01:01615string16 WrenchMenu::GetTooltipText(int id,
[email protected]c5174282011-05-20 23:42:06616 const gfx::Point& p) {
617 return is_bookmark_command(id) ?
[email protected]8d37b112011-09-15 18:01:01618 bookmark_menu_delegate_->GetTooltipText(id, p) : string16();
[email protected]c5174282011-05-20 23:42:06619}
620
621bool WrenchMenu::IsTriggerableEvent(views::MenuItemView* menu,
622 const views::MouseEvent& e) {
623 return is_bookmark_command(menu->GetCommand()) ?
624 bookmark_menu_delegate_->IsTriggerableEvent(menu, e) :
625 MenuDelegate::IsTriggerableEvent(menu, e);
626}
627
628bool WrenchMenu::GetDropFormats(
629 MenuItemView* menu,
630 int* formats,
631 std::set<ui::OSExchangeData::CustomFormat>* custom_formats) {
632 CreateBookmarkMenu();
633 return bookmark_menu_delegate_.get() &&
634 bookmark_menu_delegate_->GetDropFormats(menu, formats, custom_formats);
635}
636
637bool WrenchMenu::AreDropTypesRequired(MenuItemView* menu) {
638 CreateBookmarkMenu();
639 return bookmark_menu_delegate_.get() &&
640 bookmark_menu_delegate_->AreDropTypesRequired(menu);
641}
642
643bool WrenchMenu::CanDrop(MenuItemView* menu,
644 const ui::OSExchangeData& data) {
645 CreateBookmarkMenu();
646 return bookmark_menu_delegate_.get() &&
647 bookmark_menu_delegate_->CanDrop(menu, data);
648}
649
650int WrenchMenu::GetDropOperation(
651 MenuItemView* item,
652 const views::DropTargetEvent& event,
653 DropPosition* position) {
654 return is_bookmark_command(item->GetCommand()) ?
655 bookmark_menu_delegate_->GetDropOperation(item, event, position) :
656 ui::DragDropTypes::DRAG_NONE;
657}
658
659int WrenchMenu::OnPerformDrop(MenuItemView* menu,
660 DropPosition position,
661 const views::DropTargetEvent& event) {
662 if (!is_bookmark_command(menu->GetCommand()))
663 return ui::DragDropTypes::DRAG_NONE;
664
665 int result = bookmark_menu_delegate_->OnPerformDrop(menu, position, event);
666 return result;
667}
668
669bool WrenchMenu::ShowContextMenu(MenuItemView* source,
670 int id,
671 const gfx::Point& p,
672 bool is_mouse_gesture) {
673 return is_bookmark_command(id) ?
674 bookmark_menu_delegate_->ShowContextMenu(source, id, p,
675 is_mouse_gesture) :
676 false;
677}
678
679bool WrenchMenu::CanDrag(MenuItemView* menu) {
680 return is_bookmark_command(menu->GetCommand()) ?
681 bookmark_menu_delegate_->CanDrag(menu) : false;
682}
683
684void WrenchMenu::WriteDragData(MenuItemView* sender,
685 ui::OSExchangeData* data) {
686 DCHECK(is_bookmark_command(sender->GetCommand()));
687 return bookmark_menu_delegate_->WriteDragData(sender, data);
688}
689
690int WrenchMenu::GetDragOperations(MenuItemView* sender) {
691 return is_bookmark_command(sender->GetCommand()) ?
692 bookmark_menu_delegate_->GetDragOperations(sender) :
693 MenuDelegate::GetDragOperations(sender);
694}
695
696int WrenchMenu::GetMaxWidthForMenu(MenuItemView* menu) {
697 return is_bookmark_command(menu->GetCommand()) ?
698 bookmark_menu_delegate_->GetMaxWidthForMenu(menu) :
699 MenuDelegate::GetMaxWidthForMenu(menu);
700}
701
[email protected]eccda9db2010-06-23 17:27:08702bool WrenchMenu::IsItemChecked(int id) const {
[email protected]c7b6a222011-05-27 16:24:43703 if (is_bookmark_command(id))
[email protected]c5174282011-05-20 23:42:06704 return false;
705
[email protected]eccda9db2010-06-23 17:27:08706 const Entry& entry = id_to_entry_.find(id)->second;
707 return entry.first->IsItemCheckedAt(entry.second);
708}
709
710bool WrenchMenu::IsCommandEnabled(int id) const {
[email protected]c5174282011-05-20 23:42:06711 if (is_bookmark_command(id))
712 return true;
713
[email protected]eccda9db2010-06-23 17:27:08714 if (id == 0)
715 return false; // The root item.
716
717 const Entry& entry = id_to_entry_.find(id)->second;
718 int command_id = entry.first->GetCommandIdAt(entry.second);
719 // The items representing the cut (cut/copy/paste) and zoom menu
720 // (increment/decrement/reset) are always enabled. The child views of these
721 // items enabled state updates appropriately.
[email protected]e1a02d62010-07-16 16:27:41722 return command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS ||
[email protected]eccda9db2010-06-23 17:27:08723 entry.first->IsEnabledAt(entry.second);
724}
725
[email protected]c5174282011-05-20 23:42:06726void WrenchMenu::ExecuteCommand(int id, int mouse_event_flags) {
727 if (is_bookmark_command(id)) {
728 bookmark_menu_delegate_->ExecuteCommand(id, mouse_event_flags);
729 return;
730 }
731
732 // Not a bookmark
[email protected]eccda9db2010-06-23 17:27:08733 const Entry& entry = id_to_entry_.find(id)->second;
[email protected]60555ec2010-07-02 20:18:25734 int command_id = entry.first->GetCommandIdAt(entry.second);
735
[email protected]e1a02d62010-07-16 16:27:41736 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) {
[email protected]60555ec2010-07-02 20:18:25737 // These items are represented by child views. If ExecuteCommand is invoked
738 // it means the user clicked on the area around the buttons and we should
739 // not do anyting.
740 return;
741 }
742
[email protected]eccda9db2010-06-23 17:27:08743 return entry.first->ActivatedAt(entry.second);
744}
745
746bool WrenchMenu::GetAccelerator(int id, views::Accelerator* accelerator) {
[email protected]c5174282011-05-20 23:42:06747 if (is_bookmark_command(id))
748 return false;
749
[email protected]eccda9db2010-06-23 17:27:08750 const Entry& entry = id_to_entry_.find(id)->second;
751 int command_id = entry.first->GetCommandIdAt(entry.second);
[email protected]e1a02d62010-07-16 16:27:41752 if (command_id == IDC_CUT || command_id == IDC_ZOOM_MINUS) {
[email protected]eccda9db2010-06-23 17:27:08753 // These have special child views; don't show the accelerator for them.
754 return false;
755 }
756
[email protected]44cbd9e2011-01-14 15:49:40757 ui::Accelerator menu_accelerator;
[email protected]eccda9db2010-06-23 17:27:08758 if (!entry.first->GetAcceleratorAt(entry.second, &menu_accelerator))
759 return false;
760
[email protected]3a37ad412011-05-20 14:46:05761 *accelerator = views::Accelerator(menu_accelerator.key_code(),
[email protected]eccda9db2010-06-23 17:27:08762 menu_accelerator.modifiers());
763 return true;
764}
765
[email protected]c5174282011-05-20 23:42:06766void WrenchMenu::WillShowMenu(MenuItemView* menu) {
767 if (menu == bookmark_menu_)
768 CreateBookmarkMenu();
769}
770
771void WrenchMenu::BookmarkModelChanged() {
[email protected]c7b6a222011-05-27 16:24:43772 DCHECK(bookmark_menu_delegate_.get());
773 if (!bookmark_menu_delegate_->is_mutating_model())
774 root_->Cancel();
[email protected]c5174282011-05-20 23:42:06775}
776
[email protected]72e07fa2011-10-10 22:38:07777
778void WrenchMenu::Observe(int type,
779 const NotificationSource& source,
780 const NotificationDetails& details) {
781 switch (type) {
782 case chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED:
783 // A change in the global errors list can add or remove items from the
784 // menu. Close the menu to avoid have a stale menu on-screen.
785 root_->Cancel();
786 break;
787 default:
788 NOTREACHED();
789 }
790}
791
[email protected]eccda9db2010-06-23 17:27:08792void WrenchMenu::PopulateMenu(MenuItemView* parent,
793 MenuModel* model,
794 int* next_id) {
795 int index_offset = model->GetFirstItemIndex(NULL);
796 for (int i = 0, max = model->GetItemCount(); i < max; ++i) {
797 int index = i + index_offset;
798
799 MenuItemView* item =
800 AppendMenuItem(parent, model, index, model->GetTypeAt(index), next_id);
801
802 if (model->GetTypeAt(index) == MenuModel::TYPE_SUBMENU)
803 PopulateMenu(item, model->GetSubmenuModelAt(index), next_id);
804
[email protected]c5174282011-05-20 23:42:06805 switch (model->GetCommandIdAt(index)) {
806 case IDC_CUT:
807 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index));
808 DCHECK_LT(i + 2, max);
809 DCHECK_EQ(IDC_COPY, model->GetCommandIdAt(index + 1));
810 DCHECK_EQ(IDC_PASTE, model->GetCommandIdAt(index + 2));
811 item->SetTitle(UTF16ToWide(l10n_util::GetStringUTF16(IDS_EDIT2)));
812 item->AddChildView(
813 new CutCopyPasteView(this, model, index, index + 1, index + 2));
814 i += 2;
815 break;
816
817 case IDC_ZOOM_MINUS:
818 DCHECK_EQ(MenuModel::TYPE_COMMAND, model->GetTypeAt(index));
819 DCHECK_EQ(IDC_ZOOM_PLUS, model->GetCommandIdAt(index + 1));
820 DCHECK_EQ(IDC_FULLSCREEN, model->GetCommandIdAt(index + 2));
821 item->SetTitle(UTF16ToWide(l10n_util::GetStringUTF16(IDS_ZOOM_MENU2)));
822 item->AddChildView(
823 new ZoomView(this, model, index, index + 1, index + 2));
824 i += 2;
825 break;
826
827 case IDC_BOOKMARKS_MENU:
828 DCHECK(!bookmark_menu_);
829 bookmark_menu_ = item;
830 break;
831
832 default:
833 break;
[email protected]eccda9db2010-06-23 17:27:08834 }
835 }
836}
837
838MenuItemView* WrenchMenu::AppendMenuItem(MenuItemView* parent,
839 MenuModel* model,
840 int index,
841 MenuModel::ItemType menu_type,
842 int* next_id) {
843 int id = (*next_id)++;
[email protected]eccda9db2010-06-23 17:27:08844
845 id_to_entry_[id].first = model;
846 id_to_entry_[id].second = index;
847
[email protected]74434942010-12-05 04:00:59848 MenuItemView* menu_item = parent->AppendMenuItemFromModel(model, index, id);
[email protected]eccda9db2010-06-23 17:27:08849
[email protected]d4c4f252010-09-01 19:27:46850 if (menu_item)
851 menu_item->SetVisible(model->IsVisibleAt(index));
852
[email protected]eccda9db2010-06-23 17:27:08853 if (menu_type == MenuModel::TYPE_COMMAND && model->HasIcons()) {
854 SkBitmap icon;
855 if (model->GetIconAt(index, &icon))
856 menu_item->SetIcon(icon);
857 }
858
859 return menu_item;
860}
861
862void WrenchMenu::CancelAndEvaluate(MenuModel* model, int index) {
863 selected_menu_model_ = model;
864 selected_index_ = index;
865 root_->Cancel();
866}
[email protected]c5174282011-05-20 23:42:06867
868void WrenchMenu::CreateBookmarkMenu() {
869 if (bookmark_menu_delegate_.get())
870 return; // Already created the menu.
871
872 BookmarkModel* model = browser_->profile()->GetBookmarkModel();
873 if (!model->IsLoaded())
874 return;
875
876 model->AddObserver(this);
[email protected]5fc549f02011-07-26 06:00:42877
878 // TODO(oshima): Replace with views only API.
879 views::Widget* parent = views::Widget::GetWidgetForNativeWindow(
880 browser_->window()->GetNativeHandle());
[email protected]c5174282011-05-20 23:42:06881 bookmark_menu_delegate_.reset(
882 new BookmarkMenuDelegate(browser_->profile(),
883 NULL,
[email protected]5fc549f02011-07-26 06:00:42884 parent,
[email protected]c5174282011-05-20 23:42:06885 first_bookmark_command_id_));
886 bookmark_menu_delegate_->Init(
[email protected]72bdcfe2011-07-22 17:21:58887 this, bookmark_menu_, model->bookmark_bar_node(), 0,
[email protected]6ef3e8a32011-10-18 03:25:49888 BookmarkMenuDelegate::SHOW_OTHER_FOLDER,
889 bookmark_utils::LAUNCH_WRENCH_MENU);
[email protected]c5174282011-05-20 23:42:06890}