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

Side by Side Diff: views/controls/menu/native_menu_win.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_win.h ('k') | views/controls/menu/simple_menu_model.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_win.h"
6
7 #include "app/l10n_util.h"
8 #include "app/l10n_util_win.h"
9 #include "base/logging.h"
10 #include "base/stl_util-inl.h"
11 #include "views/accelerator.h"
12 #include "views/controls/menu/menu_2.h"
13
14 namespace views {
15
16 struct NativeMenuWin::ItemData {
17 // The Windows API requires that whoever creates the menus must own the
18 // strings used for labels, and keep them around for the lifetime of the
19 // created menu. So be it.
20 std::wstring label;
21
22 // Someone needs to own submenus, it may as well be us.
23 scoped_ptr<Menu2> submenu;
24 };
25
26 // TODO(beng): bring over owner draw from old menu system.
27 class NativeMenuWin::MenuHostWindow {
28 public:
29 MenuHostWindow() {
30 RegisterClass();
31 hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName,
32 L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
33 SetProp(hwnd_, kMenuHostWindowKey, this);
34 }
35
36 ~MenuHostWindow() {
37 DestroyWindow(hwnd_);
38 }
39
40 HWND hwnd() const { return hwnd_; }
41
42 private:
43 static const wchar_t* kMenuHostWindowKey;
44 static const wchar_t* kWindowClassName;
45
46 void RegisterClass() {
47 static bool registered = false;
48 if (registered)
49 return;
50
51 WNDCLASSEX wcex = {0};
52 wcex.cbSize = sizeof(WNDCLASSEX);
53 wcex.style = CS_DBLCLKS;
54 wcex.lpfnWndProc = &MenuHostWindowProc;
55 wcex.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW+1);
56 wcex.lpszClassName = kWindowClassName;
57 ATOM clazz = RegisterClassEx(&wcex);
58 DCHECK(clazz);
59 registered = true;
60 }
61
62 bool ProcessWindowMessage(HWND window,
63 UINT message,
64 WPARAM w_param,
65 LPARAM l_param,
66 LRESULT* l_result) {
67 return false;
68 }
69
70 static LRESULT CALLBACK MenuHostWindowProc(HWND window,
71 UINT message,
72 WPARAM w_param,
73 LPARAM l_param) {
74 MenuHostWindow* host =
75 reinterpret_cast<MenuHostWindow*>(GetProp(window, kMenuHostWindowKey));
76 LRESULT l_result = 0;
77 if (!host || !host->ProcessWindowMessage(window, message, w_param, l_param,
78 &l_result)) {
79 return DefWindowProc(window, message, w_param, l_param);
80 }
81 return l_result;
82 }
83
84 HWND hwnd_;
85
86 DISALLOW_COPY_AND_ASSIGN(MenuHostWindow);
87 };
88
89 // static
90 const wchar_t* NativeMenuWin::MenuHostWindow::kWindowClassName =
91 L"ViewsMenuHostWindow";
92
93 const wchar_t* NativeMenuWin::MenuHostWindow::kMenuHostWindowKey =
94 L"__MENU_HOST_WINDOW__";
95
96
97 ////////////////////////////////////////////////////////////////////////////////
98 // NativeMenuWin, public:
99
100 NativeMenuWin::NativeMenuWin(Menu2Model* model,
101 Menu2Delegate* delegate,
102 HWND system_menu_for)
103 : model_(model),
104 delegate_(delegate),
105 menu_(NULL),
106 owner_draw_(false),
107 system_menu_for_(system_menu_for),
108 first_item_index_(0) {
109 }
110
111 NativeMenuWin::~NativeMenuWin() {
112 STLDeleteContainerPointers(items_.begin(), items_.end());
113 }
114
115 ////////////////////////////////////////////////////////////////////////////////
116 // NativeMenuWin, MenuWrapper implementation:
117
118 void NativeMenuWin::RunMenuAt(const gfx::Point& point, int alignment) {
119 CreateHostWindow();
120 UpdateStates();
121 UINT flags = TPM_LEFTBUTTON | TPM_RETURNCMD | TPM_RECURSE;
122 flags |= GetAlignmentFlags(alignment);
123 UINT selected_command_id = TrackPopupMenuEx(menu_, flags, point.x(),
124 point.y(), host_window_->hwnd(),
125 NULL);
126 if (selected_command_id > 0) {
127 // Locate the correct delegate and model to notify about the selection.
128 // See comment in GetMenuForCommandId for details.
129 NativeMenuWin* menu = GetMenuForCommandId(selected_command_id);
130 menu->delegate_->ExecuteCommand(menu->model_, selected_command_id);
131 }
132 }
133
134 void NativeMenuWin::Rebuild() {
135 ResetNativeMenu();
136 owner_draw_ = model_->HasIcons();
137 first_item_index_ = model_->GetFirstItemIndex(GetNativeMenu());
138 for (int menu_index = first_item_index_;
139 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) {
140 int model_index = menu_index - first_item_index_;
141 if (model_->GetTypeAt(model_index) == Menu2Model::TYPE_SEPARATOR)
142 AddSeparatorItemAt(menu_index, model_index);
143 else
144 AddMenuItemAt(menu_index, model_index);
145 }
146 }
147
148 void NativeMenuWin::UpdateStates() {
149 // A depth-first walk of the menu items, updating states.
150 for (int menu_index = first_item_index_;
151 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) {
152 int model_index = menu_index - first_item_index_;
153 SetMenuItemState(menu_index, model_->IsEnabledAt(model_index),
154 model_->IsItemCheckedAt(model_index), false);
155 if (model_->IsLabelDynamicAt(model_index)) {
156 SetMenuItemLabel(menu_index, model_index,
157 model_->GetLabelAt(model_index));
158 }
159 Menu2* submenu = items_.at(model_index)->submenu.get();
160 if (submenu)
161 submenu->UpdateStates();
162 }
163 }
164
165 gfx::NativeMenu NativeMenuWin::GetNativeMenu() const {
166 return menu_;
167 }
168
169 ////////////////////////////////////////////////////////////////////////////////
170 // NativeMenuWin, private:
171
172 bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const {
173 MENUITEMINFO mii = {0};
174 mii.cbSize = sizeof(mii);
175 mii.fMask = MIIM_FTYPE;
176 GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
177 return !!(mii.fType & MF_SEPARATOR);
178 }
179
180 void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) {
181 MENUITEMINFO mii = {0};
182 mii.cbSize = sizeof(mii);
183 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA;
184 if (!owner_draw_)
185 mii.fType = MFT_STRING;
186 else
187 mii.fType = MFT_OWNERDRAW;
188 mii.dwItemData = reinterpret_cast<ULONG_PTR>(this);
189
190 ItemData* item_data = new ItemData;
191 Menu2Model::ItemType type = model_->GetTypeAt(model_index);
192 if (type == Menu2Model::TYPE_SUBMENU) {
193 item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index),
194 delegate_));
195 mii.fMask |= MIIM_SUBMENU;
196 mii.hSubMenu = item_data->submenu->GetNativeMenu();
197 } else {
198 if (type == Menu2Model::TYPE_RADIO)
199 mii.fType |= MFT_RADIOCHECK;
200 mii.wID = model_->GetCommandIdAt(model_index);
201 }
202 items_.insert(items_.begin() + model_index, item_data);
203 UpdateMenuItemInfoForString(&mii, model_index,
204 model_->GetLabelAt(model_index));
205 InsertMenuItem(menu_, menu_index, TRUE, &mii);
206 }
207
208 void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) {
209 MENUITEMINFO mii = {0};
210 mii.cbSize = sizeof(mii);
211 mii.fMask = MIIM_FTYPE;
212 mii.fType = MFT_SEPARATOR;
213 // Insert a dummy entry into our label list so we can index directly into it
214 // using item indices if need be.
215 items_.insert(items_.begin() + model_index, new ItemData);
216 InsertMenuItem(menu_, menu_index, TRUE, &mii);
217 }
218
219 void NativeMenuWin::SetMenuItemState(int menu_index, bool enabled, bool checked,
220 bool is_default) {
221 if (IsSeparatorItemAt(menu_index))
222 return;
223
224 UINT state = enabled ? MFS_ENABLED : MFS_DISABLED;
225 if (checked)
226 state |= MFS_CHECKED;
227 if (is_default)
228 state |= MFS_DEFAULT;
229
230 MENUITEMINFO mii = {0};
231 mii.cbSize = sizeof(mii);
232 mii.fMask = MIIM_STATE;
233 mii.fState = state;
234 SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
235 }
236
237 void NativeMenuWin::SetMenuItemLabel(int menu_index,
238 int model_index,
239 const std::wstring& label) {
240 if (IsSeparatorItemAt(menu_index))
241 return;
242
243 MENUITEMINFO mii = {0};
244 mii.cbSize = sizeof(mii);
245 UpdateMenuItemInfoForString(&mii, model_index, label);
246 if (!owner_draw_)
247 SetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii);
248 }
249
250 void NativeMenuWin::UpdateMenuItemInfoForString(
251 MENUITEMINFO* mii,
252 int model_index,
253 const std::wstring& label) {
254 std::wstring formatted = label;
255 Menu2Model::ItemType type = model_->GetTypeAt(model_index);
256 if (type != Menu2Model::TYPE_SUBMENU) {
257 // Add accelerator details to the label if provided.
258 views::Accelerator accelerator(0, false, false, false);
259 if (model_->GetAcceleratorAt(model_index, &accelerator)) {
260 formatted += L"\t";
261 formatted += accelerator.GetShortcutText();
262 }
263 }
264
265 // Update the owned string, since Windows will want us to keep this new
266 // version around.
267 items_[model_index]->label = formatted;
268
269 // Windows only requires a pointer to the label string if it's going to be
270 // doing the drawing.
271 if (!owner_draw_) {
272 mii->fMask |= MIIM_STRING;
273 mii->dwTypeData =
274 const_cast<wchar_t*>(items_.at(model_index)->label.c_str());
275 }
276 }
277
278 NativeMenuWin* NativeMenuWin::GetMenuForCommandId(UINT command_id) const {
279 // Menus can have nested submenus. In the views Menu system, each submenu is
280 // wrapped in a NativeMenu instance, which may have a different model and
281 // delegate from the parent menu. The trouble is, RunMenuAt is called on the
282 // parent NativeMenuWin, and so it's not possible to assume that we can just
283 // dispatch the command id returned by TrackPopupMenuEx to the parent's
284 // delegate. For this reason, we stow a pointer on every menu item we create
285 // to the NativeMenuWin that most closely contains it. Fortunately, Windows
286 // provides GetMenuItemInfo, which can walk down the menu item tree from
287 // the root |menu_| to find the data for a given item even if it's in a
288 // submenu.
289 MENUITEMINFO mii = {0};
290 mii.cbSize = sizeof(mii);
291 mii.fMask = MIIM_DATA;
292 GetMenuItemInfo(menu_, command_id, FALSE, &mii);
293 return reinterpret_cast<NativeMenuWin*>(mii.dwItemData);
294 }
295
296 UINT NativeMenuWin::GetAlignmentFlags(int alignment) const {
297 bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT;
298 UINT alignment_flags = TPM_TOPALIGN;
299 if (alignment == Menu2::ALIGN_TOPLEFT)
300 alignment_flags |= TPM_LEFTALIGN;
301 else if (alignment == Menu2::ALIGN_TOPRIGHT)
302 alignment_flags |= TPM_RIGHTALIGN;
303 return alignment_flags;
304 }
305
306 void NativeMenuWin::ResetNativeMenu() {
307 if (IsWindow(system_menu_for_)) {
308 if (menu_)
309 GetSystemMenu(system_menu_for_, TRUE);
310 menu_ = GetSystemMenu(system_menu_for_, FALSE);
311 } else {
312 if (menu_)
313 DestroyMenu(menu_);
314 menu_ = CreatePopupMenu();
315 }
316 }
317
318 void NativeMenuWin::CreateHostWindow() {
319 if (!host_window_.get())
320 host_window_.reset(new MenuHostWindow());
321 }
322
323 ////////////////////////////////////////////////////////////////////////////////
324 // SystemMenuModel:
325
326 SystemMenuModel::SystemMenuModel(SimpleMenuModel::Delegate* delegate)
327 : SimpleMenuModel(delegate) {
328 }
329
330 SystemMenuModel::~SystemMenuModel() {
331 }
332
333 int SystemMenuModel::GetFirstItemIndex(gfx::NativeMenu native_menu) const {
334 // We allow insertions before last item (Close).
335 return std::max(0, GetMenuItemCount(native_menu) - 1);
336 }
337
338 ////////////////////////////////////////////////////////////////////////////////
339 // MenuWrapper, public:
340
341 // static
342 MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
343 return new NativeMenuWin(menu->model(), menu->delegate(), NULL);
344 }
345
346 } // namespace views
OLDNEW
« no previous file with comments | « views/controls/menu/native_menu_win.h ('k') | views/controls/menu/simple_menu_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698