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

Side by Side Diff: views/controls/menu/native_menu_gtk.cc

Issue 119237: A new menu system. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 6 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
« no previous file with comments | « views/controls/menu/native_menu_gtk.h ('k') | views/controls/menu/native_menu_win.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this
2 // source code is governed by a BSD-style license that can be found in the
3 // LICENSE file.
4
5 #include "views/controls/menu/native_menu_gtk.h"
6
7 #include "base/string_util.h"
8 #include "base/time.h"
9 #include "views/accelerator.h"
10 #include "views/controls/menu/menu_2.h"
11
12 namespace {
13 // Data passed to the UpdateStateCallback from gtk_container_foreach.
14 struct UpdateStateData {
15 // The model to retrieve state from.
16 views::Menu2Model* model;
17 // The index within said model.
18 int index;
19 };
20
21 // Data passed to the MenuPositionFunc from gtk_menu_popup
22 struct Position {
23 // The point to run the menu at.
24 gfx::Point point;
25 // The alignment of the menu at that point.
26 views::Menu2::Alignment alignment;
27 };
28
29 std::string ConvertAcceleratorsFromWindowsStyle(const std::string& label) {
30 std::string ret;
31 ret.reserve(label.length());
32 for (size_t i = 0; i < label.length(); ++i) {
33 if ('&' == label[i]) {
34 if (i + 1 < label.length() && '&' == label[i + 1]) {
35 ret.push_back(label[i]);
36 ++i;
37 } else {
38 ret.push_back('_');
39 }
40 } else {
41 ret.push_back(label[i]);
42 }
43 }
44
45 return ret;
46 }
47
48 // Returns true if the menu item type specified can be executed as a command.
49 bool MenuTypeCanExecute(views::Menu2Model::ItemType type) {
50 return type == views::Menu2Model::TYPE_COMMAND ||
51 type == views::Menu2Model::TYPE_CHECK ||
52 type == views::Menu2Model::TYPE_RADIO;
53 }
54
55 } // namespace
56
57 namespace views {
58
59 ////////////////////////////////////////////////////////////////////////////////
60 // NativeMenuGtk, public:
61
62 NativeMenuGtk::NativeMenuGtk(Menu2Model* model,
63 Menu2Delegate* delegate)
64 : model_(model),
65 delegate_(delegate),
66 menu_(NULL) {
67 }
68
69 NativeMenuGtk::~NativeMenuGtk() {
70 gtk_widget_destroy(menu_);
71 }
72
73 ////////////////////////////////////////////////////////////////////////////////
74 // NativeMenuGtk, MenuWrapper implementation:
75
76 void NativeMenuGtk::RunMenuAt(const gfx::Point& point, int alignment) {
77 Position position = { point, static_cast<Menu2::Alignment>(alignment) };
78 // TODO(beng): value of '1' will not work for context menus!
79 gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, MenuPositionFunc, &position, 1,
80 gtk_get_current_event_time());
81 }
82
83 void NativeMenuGtk::Rebuild() {
84 ResetMenu();
85
86 GtkRadioMenuItem* last_radio_item = NULL;
87 for (int i = 0; i < model_->GetItemCount(); ++i) {
88 Menu2Model::ItemType type = model_->GetTypeAt(i);
89 if (type == Menu2Model::TYPE_SEPARATOR)
90 AddSeparatorAt(i);
91 else
92 AddMenuItemAt(i, &last_radio_item);
93 }
94 }
95
96 void NativeMenuGtk::UpdateStates() {
97 UpdateStateData data = { model_, 0 };
98 gtk_container_foreach(GTK_CONTAINER(menu_), &UpdateStateCallback, &data);
99 }
100
101 gfx::NativeMenu NativeMenuGtk::GetNativeMenu() const {
102 return menu_;
103 }
104
105 ////////////////////////////////////////////////////////////////////////////////
106 // NativeMenuGtk, private:
107
108 void NativeMenuGtk::AddSeparatorAt(int index) {
109 GtkWidget* separator = gtk_separator_menu_item_new();
110 gtk_widget_show(separator);
111 gtk_menu_append(menu_, separator);
112 }
113
114 void NativeMenuGtk::AddMenuItemAt(int index,
115 GtkRadioMenuItem** last_radio_item) {
116 GtkWidget* menu_item = NULL;
117 std::string label = ConvertAcceleratorsFromWindowsStyle(WideToUTF8(
118 model_->GetLabelAt(index)));
119
120 Menu2Model::ItemType type = model_->GetTypeAt(index);
121 switch (type) {
122 case Menu2Model::TYPE_CHECK:
123 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
124 break;
125 case Menu2Model::TYPE_RADIO:
126 if (*last_radio_item) {
127 menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
128 *last_radio_item, label.c_str());
129 } else {
130 menu_item = gtk_radio_menu_item_new_with_mnemonic(NULL, label.c_str());
131 }
132 break;
133 case Menu2Model::TYPE_SUBMENU:
134 case Menu2Model::TYPE_COMMAND:
135 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
136 break;
137 default:
138 NOTREACHED();
139 break;
140 }
141
142 // TODO(beng): icons
143
144 if (type == Menu2Model::TYPE_SUBMENU) {
145 // TODO(beng): we're leaking these objects right now... consider some other
146 // arrangement.
147 Menu2* submenu = new Menu2(model_->GetSubmenuModelAt(index), delegate_);
148 g_object_set_data(G_OBJECT(menu_item), "submenu", submenu);
149 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
150 submenu->GetNativeMenu());
151 }
152
153 views::Accelerator accelerator(0, false, false, false);
154 if (model_->GetAcceleratorAt(index, &accelerator)) {
155 // TODO(beng): accelerators w/gtk_widget_add_accelerator.
156 }
157 g_object_set_data(G_OBJECT(menu_item), "position",
158 reinterpret_cast<void*>(index));
159 g_signal_connect(G_OBJECT(menu_item), "activate", G_CALLBACK(CallActivate),
160 this);
161 gtk_widget_show(menu_item);
162 gtk_menu_append(menu_, menu_item);
163 }
164
165 // static
166 void NativeMenuGtk::UpdateStateCallback(GtkWidget* menu_item, gpointer data) {
167 UpdateStateData* usd = reinterpret_cast<UpdateStateData*>(data);
168 gtk_widget_set_sensitive(menu_item, usd->model->IsEnabledAt(usd->index));
169 if (GTK_IS_CHECK_MENU_ITEM(menu_item)) {
170 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
171 usd->model->IsItemCheckedAt(usd->index));
172 }
173 // Recurse into submenus, too.
174 if (GTK_IS_MENU_ITEM(menu_item)) {
175 if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item))) {
176 Menu2* submenu =
177 reinterpret_cast<Menu2*>(g_object_get_data(G_OBJECT(menu_item),
178 "submenu"));
179 if (submenu)
180 submenu->UpdateStates();
181 }
182 }
183 ++usd->index;
184 }
185
186 void NativeMenuGtk::ResetMenu() {
187 if (menu_)
188 gtk_widget_destroy(menu_);
189 menu_ = gtk_menu_new();
190 }
191
192 // static
193 void NativeMenuGtk::MenuPositionFunc(GtkMenu* menu,
194 int* x,
195 int* y,
196 gboolean* push_in,
197 void* data) {
198 Position* position = reinterpret_cast<Position*>(data);
199 // TODO(beng): RTL
200 *x = position->point.x();
201 *y = position->point.y();
202 if (position->alignment == Menu2::ALIGN_TOPRIGHT) {
203 GtkRequisition menu_req;
204 gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);
205 *x -= menu_req.width;
206 }
207 *push_in = FALSE;
208 }
209
210 void NativeMenuGtk::OnActivate(GtkMenuItem* menu_item) {
211 int position = reinterpret_cast<int>(g_object_get_data(G_OBJECT(menu_item),
212 "position"));
213 if (model_->IsEnabledAt(position) &&
214 MenuTypeCanExecute(model_->GetTypeAt(position))) {
215 delegate_->ExecuteCommand(model_, model_->GetCommandIdAt(position));
216 }
217 }
218
219 // static
220 void NativeMenuGtk::CallActivate(GtkMenuItem* menu_item,
221 NativeMenuGtk* native_menu) {
222 native_menu->OnActivate(menu_item);
223 }
224
225 ////////////////////////////////////////////////////////////////////////////////
226 // MenuWrapper, public:
227
228 // static
229 MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
230 return new NativeMenuGtk(menu->model(), menu->delegate());
231 }
232
233 } // namespace views
OLDNEW
« no previous file with comments | « views/controls/menu/native_menu_gtk.h ('k') | views/controls/menu/native_menu_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698