OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this | 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 | 2 // source code is governed by a BSD-style license that can be found in the |
3 // LICENSE file. | 3 // LICENSE file. |
4 | 4 |
5 #include "views/controls/menu/native_menu_win.h" | 5 #include "views/controls/menu/native_menu_win.h" |
6 | 6 |
| 7 #include "app/gfx/canvas.h" |
| 8 #include "app/gfx/font.h" |
7 #include "app/l10n_util.h" | 9 #include "app/l10n_util.h" |
8 #include "app/l10n_util_win.h" | 10 #include "app/l10n_util_win.h" |
9 #include "base/logging.h" | 11 #include "base/logging.h" |
10 #include "base/stl_util-inl.h" | 12 #include "base/stl_util-inl.h" |
| 13 #include "third_party/skia/include/core/SkBitmap.h" |
11 #include "views/accelerator.h" | 14 #include "views/accelerator.h" |
12 #include "views/controls/menu/menu_2.h" | 15 #include "views/controls/menu/menu_2.h" |
13 | 16 |
14 namespace views { | 17 namespace views { |
15 | 18 |
| 19 // The width of an icon, including the pixels between the icon and |
| 20 // the item label. |
| 21 static const int kIconWidth = 23; |
| 22 // Margins between the top of the item and the label. |
| 23 static const int kItemTopMargin = 3; |
| 24 // Margins between the bottom of the item and the label. |
| 25 static const int kItemBottomMargin = 4; |
| 26 // Margins between the left of the item and the icon. |
| 27 static const int kItemLeftMargin = 4; |
| 28 // Margins between the right of the item and the label. |
| 29 static const int kItemRightMargin = 10; |
| 30 // The width for displaying the sub-menu arrow. |
| 31 static const int kArrowWidth = 10; |
| 32 |
16 struct NativeMenuWin::ItemData { | 33 struct NativeMenuWin::ItemData { |
17 // The Windows API requires that whoever creates the menus must own the | 34 // 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 | 35 // strings used for labels, and keep them around for the lifetime of the |
19 // created menu. So be it. | 36 // created menu. So be it. |
20 std::wstring label; | 37 std::wstring label; |
21 | 38 |
22 // Someone needs to own submenus, it may as well be us. | 39 // Someone needs to own submenus, it may as well be us. |
23 scoped_ptr<Menu2> submenu; | 40 scoped_ptr<Menu2> submenu; |
| 41 |
| 42 // We need a pointer back to the containing menu in various circumstances. |
| 43 NativeMenuWin* native_menu_win; |
| 44 |
| 45 // The index of the item within the menu's model. |
| 46 int model_index; |
24 }; | 47 }; |
25 | 48 |
26 // A window that receives messages from Windows relevant to the native menu | 49 // A window that receives messages from Windows relevant to the native menu |
27 // structure we have constructed in NativeMenuWin. | 50 // structure we have constructed in NativeMenuWin. |
28 class NativeMenuWin::MenuHostWindow { | 51 class NativeMenuWin::MenuHostWindow { |
29 public: | 52 public: |
30 MenuHostWindow() { | 53 MenuHostWindow() { |
31 RegisterClass(); | 54 RegisterClass(); |
32 hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName, | 55 hwnd_ = CreateWindowEx(l10n_util::GetExtendedStyles(), kWindowClassName, |
33 L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); | 56 L"", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 if (mii.wID == w_param) | 106 if (mii.wID == w_param) |
84 return i; | 107 return i; |
85 } | 108 } |
86 // If we didn't find a matching command ID, this means a submenu has been | 109 // If we didn't find a matching command ID, this means a submenu has been |
87 // selected instead, and rather than passing a command ID in | 110 // selected instead, and rather than passing a command ID in |
88 // LOWORD(w_param), Windows has actually passed us a position, so we just | 111 // LOWORD(w_param), Windows has actually passed us a position, so we just |
89 // return it. | 112 // return it. |
90 return w_param; | 113 return w_param; |
91 } | 114 } |
92 | 115 |
| 116 NativeMenuWin::ItemData* GetItemData(ULONG_PTR item_data) { |
| 117 return reinterpret_cast<NativeMenuWin::ItemData*>(item_data); |
| 118 } |
| 119 |
93 // Called when the user selects a specific item. | 120 // Called when the user selects a specific item. |
94 void OnMenuCommand(int position, HMENU menu) { | 121 void OnMenuCommand(int position, HMENU menu) { |
95 NativeMenuWin* intergoat = GetNativeMenuWinFromHMENU(menu); | 122 NativeMenuWin* intergoat = GetNativeMenuWinFromHMENU(menu); |
96 Menu2Model* model = intergoat->model_; | 123 Menu2Model* model = intergoat->model_; |
97 model->ActivatedAt(position); | 124 model->ActivatedAt(position); |
98 } | 125 } |
99 | 126 |
100 // Called as the user moves their mouse or arrows through the contents of the | 127 // Called as the user moves their mouse or arrows through the contents of the |
101 // menu. | 128 // menu. |
102 void OnMenuSelect(WPARAM w_param, HMENU menu) { | 129 void OnMenuSelect(WPARAM w_param, HMENU menu) { |
103 if (!menu) | 130 if (!menu) |
104 return; // menu is null when closing on XP. | 131 return; // menu is null when closing on XP. |
105 | 132 |
106 int position = GetMenuItemIndexFromWPARAM(menu, w_param); | 133 int position = GetMenuItemIndexFromWPARAM(menu, w_param); |
107 if (position >= 0) | 134 if (position >= 0) |
108 GetNativeMenuWinFromHMENU(menu)->model_->HighlightChangedTo(position); | 135 GetNativeMenuWinFromHMENU(menu)->model_->HighlightChangedTo(position); |
109 } | 136 } |
110 | 137 |
| 138 // Called by Windows to measure the size of an owner-drawn menu item. |
| 139 void OnMeasureItem(WPARAM w_param, MEASUREITEMSTRUCT* measure_item_struct) { |
| 140 NativeMenuWin::ItemData* data = GetItemData(measure_item_struct->itemData); |
| 141 if (data) { |
| 142 gfx::Font font; |
| 143 measure_item_struct->itemWidth = font.GetStringWidth(data->label) + |
| 144 kIconWidth + kItemLeftMargin + kItemRightMargin - |
| 145 GetSystemMetrics(SM_CXMENUCHECK); |
| 146 if (data->submenu.get()) |
| 147 measure_item_struct->itemWidth += kArrowWidth; |
| 148 // If the label contains an accelerator, make room for tab. |
| 149 if (data->label.find(L'\t') != std::wstring::npos) |
| 150 measure_item_struct->itemWidth += font.GetStringWidth(L" "); |
| 151 measure_item_struct->itemHeight = |
| 152 font.height() + kItemBottomMargin + kItemTopMargin; |
| 153 } else { |
| 154 // Measure separator size. |
| 155 measure_item_struct->itemHeight = GetSystemMetrics(SM_CYMENU) / 2; |
| 156 measure_item_struct->itemWidth = 0; |
| 157 } |
| 158 } |
| 159 |
| 160 // Called by Windows to paint an owner-drawn menu item. |
| 161 void OnDrawItem(UINT w_param, DRAWITEMSTRUCT* draw_item_struct) { |
| 162 HDC dc = draw_item_struct->hDC; |
| 163 COLORREF prev_bg_color, prev_text_color; |
| 164 |
| 165 // Set background color and text color |
| 166 if (draw_item_struct->itemState & ODS_SELECTED) { |
| 167 prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_HIGHLIGHT)); |
| 168 prev_text_color = SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); |
| 169 } else { |
| 170 prev_bg_color = SetBkColor(dc, GetSysColor(COLOR_MENU)); |
| 171 if (draw_item_struct->itemState & ODS_DISABLED) |
| 172 prev_text_color = SetTextColor(dc, GetSysColor(COLOR_GRAYTEXT)); |
| 173 else |
| 174 prev_text_color = SetTextColor(dc, GetSysColor(COLOR_MENUTEXT)); |
| 175 } |
| 176 |
| 177 if (draw_item_struct->itemData) { |
| 178 NativeMenuWin::ItemData* data = GetItemData(draw_item_struct->itemData); |
| 179 // Draw the background. |
| 180 HBRUSH hbr = CreateSolidBrush(GetBkColor(dc)); |
| 181 FillRect(dc, &draw_item_struct->rcItem, hbr); |
| 182 DeleteObject(hbr); |
| 183 |
| 184 // Draw the label. |
| 185 RECT rect = draw_item_struct->rcItem; |
| 186 rect.top += kItemTopMargin; |
| 187 // Should we add kIconWidth only when icon.width() != 0 ? |
| 188 rect.left += kItemLeftMargin + kIconWidth; |
| 189 rect.right -= kItemRightMargin; |
| 190 UINT format = DT_TOP | DT_SINGLELINE; |
| 191 // Check whether the mnemonics should be underlined. |
| 192 BOOL underline_mnemonics; |
| 193 SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &underline_mnemonics, 0); |
| 194 if (!underline_mnemonics) |
| 195 format |= DT_HIDEPREFIX; |
| 196 gfx::Font font; |
| 197 HGDIOBJ old_font = static_cast<HFONT>(SelectObject(dc, font.hfont())); |
| 198 int fontsize = font.FontSize(); |
| 199 |
| 200 // If an accelerator is specified (with a tab delimiting the rest of the |
| 201 // label from the accelerator), we have to justify the fist part on the |
| 202 // left and the accelerator on the right. |
| 203 // TODO(jungshik): This will break in RTL UI. Currently, he/ar use the |
| 204 // window system UI font and will not hit here. |
| 205 std::wstring label = data->label; |
| 206 std::wstring accel; |
| 207 std::wstring::size_type tab_pos = label.find(L'\t'); |
| 208 if (tab_pos != std::wstring::npos) { |
| 209 accel = label.substr(tab_pos); |
| 210 label = label.substr(0, tab_pos); |
| 211 } |
| 212 DrawTextEx(dc, const_cast<wchar_t*>(label.data()), |
| 213 static_cast<int>(label.size()), &rect, format | DT_LEFT, NULL); |
| 214 if (!accel.empty()) |
| 215 DrawTextEx(dc, const_cast<wchar_t*>(accel.data()), |
| 216 static_cast<int>(accel.size()), &rect, |
| 217 format | DT_RIGHT, NULL); |
| 218 SelectObject(dc, old_font); |
| 219 |
| 220 // Draw the icon after the label, otherwise it would be covered |
| 221 // by the label. |
| 222 SkBitmap icon; |
| 223 if (data->native_menu_win->model_->GetIconAt(data->model_index, &icon)) { |
| 224 gfx::Canvas canvas(icon.width(), icon.height(), false); |
| 225 canvas.drawColor(SK_ColorBLACK, SkPorterDuff::kClear_Mode); |
| 226 canvas.DrawBitmapInt(icon, 0, 0); |
| 227 canvas.getTopPlatformDevice().drawToHDC(dc, |
| 228 draw_item_struct->rcItem.left + kItemLeftMargin, |
| 229 draw_item_struct->rcItem.top + (draw_item_struct->rcItem.bottom - |
| 230 draw_item_struct->rcItem.top - icon.height()) / 2, NULL); |
| 231 } |
| 232 |
| 233 } else { |
| 234 // Draw the separator |
| 235 draw_item_struct->rcItem.top += |
| 236 (draw_item_struct->rcItem.bottom - draw_item_struct->rcItem.top) / 3; |
| 237 DrawEdge(dc, &draw_item_struct->rcItem, EDGE_ETCHED, BF_TOP); |
| 238 } |
| 239 |
| 240 SetBkColor(dc, prev_bg_color); |
| 241 SetTextColor(dc, prev_text_color); |
| 242 } |
| 243 |
111 bool ProcessWindowMessage(HWND window, | 244 bool ProcessWindowMessage(HWND window, |
112 UINT message, | 245 UINT message, |
113 WPARAM w_param, | 246 WPARAM w_param, |
114 LPARAM l_param, | 247 LPARAM l_param, |
115 LRESULT* l_result) { | 248 LRESULT* l_result) { |
116 switch (message) { | 249 switch (message) { |
117 case WM_MENUCOMMAND: | 250 case WM_MENUCOMMAND: |
118 OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param)); | 251 OnMenuCommand(w_param, reinterpret_cast<HMENU>(l_param)); |
119 *l_result = 0; | 252 *l_result = 0; |
120 return true; | 253 return true; |
121 case WM_MENUSELECT: | 254 case WM_MENUSELECT: |
122 OnMenuSelect(LOWORD(w_param), reinterpret_cast<HMENU>(l_param)); | 255 OnMenuSelect(LOWORD(w_param), reinterpret_cast<HMENU>(l_param)); |
123 *l_result = 0; | 256 *l_result = 0; |
124 return true; | 257 return true; |
| 258 case WM_MEASUREITEM: |
| 259 OnMeasureItem(w_param, reinterpret_cast<MEASUREITEMSTRUCT*>(l_param)); |
| 260 *l_result = 0; |
| 261 return true; |
| 262 case WM_DRAWITEM: |
| 263 OnDrawItem(w_param, reinterpret_cast<DRAWITEMSTRUCT*>(l_param)); |
| 264 *l_result = 0; |
| 265 return true; |
125 // TODO(beng): bring over owner draw from old menu system. | 266 // TODO(beng): bring over owner draw from old menu system. |
126 } | 267 } |
127 return false; | 268 return false; |
128 } | 269 } |
129 | 270 |
130 static LRESULT CALLBACK MenuHostWindowProc(HWND window, | 271 static LRESULT CALLBACK MenuHostWindowProc(HWND window, |
131 UINT message, | 272 UINT message, |
132 WPARAM w_param, | 273 WPARAM w_param, |
133 LPARAM l_param) { | 274 LPARAM l_param) { |
134 MenuHostWindow* host = | 275 MenuHostWindow* host = |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
206 // A depth-first walk of the menu items, updating states. | 347 // A depth-first walk of the menu items, updating states. |
207 for (int menu_index = first_item_index_; | 348 for (int menu_index = first_item_index_; |
208 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) { | 349 menu_index < first_item_index_ + model_->GetItemCount(); ++menu_index) { |
209 int model_index = menu_index - first_item_index_; | 350 int model_index = menu_index - first_item_index_; |
210 SetMenuItemState(menu_index, model_->IsEnabledAt(model_index), | 351 SetMenuItemState(menu_index, model_->IsEnabledAt(model_index), |
211 model_->IsItemCheckedAt(model_index), false); | 352 model_->IsItemCheckedAt(model_index), false); |
212 if (model_->IsLabelDynamicAt(model_index)) { | 353 if (model_->IsLabelDynamicAt(model_index)) { |
213 SetMenuItemLabel(menu_index, model_index, | 354 SetMenuItemLabel(menu_index, model_index, |
214 model_->GetLabelAt(model_index)); | 355 model_->GetLabelAt(model_index)); |
215 } | 356 } |
216 Menu2* submenu = items_.at(model_index)->submenu.get(); | 357 Menu2* submenu = items_[model_index]->submenu.get(); |
217 if (submenu) | 358 if (submenu) |
218 submenu->UpdateStates(); | 359 submenu->UpdateStates(); |
219 } | 360 } |
220 } | 361 } |
221 | 362 |
222 gfx::NativeMenu NativeMenuWin::GetNativeMenu() const { | 363 gfx::NativeMenu NativeMenuWin::GetNativeMenu() const { |
223 return menu_; | 364 return menu_; |
224 } | 365 } |
225 | 366 |
226 //////////////////////////////////////////////////////////////////////////////// | 367 //////////////////////////////////////////////////////////////////////////////// |
227 // NativeMenuWin, private: | 368 // NativeMenuWin, private: |
228 | 369 |
229 bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const { | 370 bool NativeMenuWin::IsSeparatorItemAt(int menu_index) const { |
230 MENUITEMINFO mii = {0}; | 371 MENUITEMINFO mii = {0}; |
231 mii.cbSize = sizeof(mii); | 372 mii.cbSize = sizeof(mii); |
232 mii.fMask = MIIM_FTYPE; | 373 mii.fMask = MIIM_FTYPE; |
233 GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii); | 374 GetMenuItemInfo(menu_, menu_index, MF_BYPOSITION, &mii); |
234 return !!(mii.fType & MF_SEPARATOR); | 375 return !!(mii.fType & MF_SEPARATOR); |
235 } | 376 } |
236 | 377 |
237 void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) { | 378 void NativeMenuWin::AddMenuItemAt(int menu_index, int model_index) { |
238 MENUITEMINFO mii = {0}; | 379 MENUITEMINFO mii = {0}; |
239 mii.cbSize = sizeof(mii); | 380 mii.cbSize = sizeof(mii); |
240 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA; | 381 mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_DATA; |
241 if (!owner_draw_) | 382 if (!owner_draw_) |
242 mii.fType = MFT_STRING; | 383 mii.fType = MFT_STRING; |
243 else | 384 else |
244 mii.fType = MFT_OWNERDRAW; | 385 mii.fType = MFT_OWNERDRAW; |
245 mii.dwItemData = reinterpret_cast<ULONG_PTR>(this); | |
246 | 386 |
247 ItemData* item_data = new ItemData; | 387 ItemData* item_data = new ItemData; |
| 388 item_data->label = std::wstring(); |
248 Menu2Model::ItemType type = model_->GetTypeAt(model_index); | 389 Menu2Model::ItemType type = model_->GetTypeAt(model_index); |
249 if (type == Menu2Model::TYPE_SUBMENU) { | 390 if (type == Menu2Model::TYPE_SUBMENU) { |
250 item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index))); | 391 item_data->submenu.reset(new Menu2(model_->GetSubmenuModelAt(model_index))); |
251 mii.fMask |= MIIM_SUBMENU; | 392 mii.fMask |= MIIM_SUBMENU; |
252 mii.hSubMenu = item_data->submenu->GetNativeMenu(); | 393 mii.hSubMenu = item_data->submenu->GetNativeMenu(); |
253 } else { | 394 } else { |
254 if (type == Menu2Model::TYPE_RADIO) | 395 if (type == Menu2Model::TYPE_RADIO) |
255 mii.fType |= MFT_RADIOCHECK; | 396 mii.fType |= MFT_RADIOCHECK; |
256 mii.wID = model_->GetCommandIdAt(model_index); | 397 mii.wID = model_->GetCommandIdAt(model_index); |
257 } | 398 } |
| 399 item_data->native_menu_win = this; |
| 400 item_data->model_index = model_index; |
258 items_.insert(items_.begin() + model_index, item_data); | 401 items_.insert(items_.begin() + model_index, item_data); |
| 402 mii.dwItemData = reinterpret_cast<ULONG_PTR>(item_data); |
259 UpdateMenuItemInfoForString(&mii, model_index, | 403 UpdateMenuItemInfoForString(&mii, model_index, |
260 model_->GetLabelAt(model_index)); | 404 model_->GetLabelAt(model_index)); |
261 InsertMenuItem(menu_, menu_index, TRUE, &mii); | 405 InsertMenuItem(menu_, menu_index, TRUE, &mii); |
262 } | 406 } |
263 | 407 |
264 void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) { | 408 void NativeMenuWin::AddSeparatorItemAt(int menu_index, int model_index) { |
265 MENUITEMINFO mii = {0}; | 409 MENUITEMINFO mii = {0}; |
266 mii.cbSize = sizeof(mii); | 410 mii.cbSize = sizeof(mii); |
267 mii.fMask = MIIM_FTYPE; | 411 mii.fMask = MIIM_FTYPE; |
268 mii.fType = MFT_SEPARATOR; | 412 mii.fType = MFT_SEPARATOR; |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
324 | 468 |
325 // Windows only requires a pointer to the label string if it's going to be | 469 // Windows only requires a pointer to the label string if it's going to be |
326 // doing the drawing. | 470 // doing the drawing. |
327 if (!owner_draw_) { | 471 if (!owner_draw_) { |
328 mii->fMask |= MIIM_STRING; | 472 mii->fMask |= MIIM_STRING; |
329 mii->dwTypeData = | 473 mii->dwTypeData = |
330 const_cast<wchar_t*>(items_.at(model_index)->label.c_str()); | 474 const_cast<wchar_t*>(items_.at(model_index)->label.c_str()); |
331 } | 475 } |
332 } | 476 } |
333 | 477 |
334 NativeMenuWin* NativeMenuWin::GetMenuForCommandId(UINT command_id) const { | |
335 // Menus can have nested submenus. In the views Menu system, each submenu is | |
336 // wrapped in a NativeMenu instance, which may have a different model and | |
337 // delegate from the parent menu. The trouble is, RunMenuAt is called on the | |
338 // parent NativeMenuWin, and so it's not possible to assume that we can just | |
339 // dispatch the command id returned by TrackPopupMenuEx to the parent's | |
340 // delegate. For this reason, we stow a pointer on every menu item we create | |
341 // to the NativeMenuWin that most closely contains it. Fortunately, Windows | |
342 // provides GetMenuItemInfo, which can walk down the menu item tree from | |
343 // the root |menu_| to find the data for a given item even if it's in a | |
344 // submenu. | |
345 MENUITEMINFO mii = {0}; | |
346 mii.cbSize = sizeof(mii); | |
347 mii.fMask = MIIM_DATA; | |
348 GetMenuItemInfo(menu_, command_id, FALSE, &mii); | |
349 return reinterpret_cast<NativeMenuWin*>(mii.dwItemData); | |
350 } | |
351 | |
352 UINT NativeMenuWin::GetAlignmentFlags(int alignment) const { | 478 UINT NativeMenuWin::GetAlignmentFlags(int alignment) const { |
353 bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT; | 479 bool rtl = l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT; |
354 UINT alignment_flags = TPM_TOPALIGN; | 480 UINT alignment_flags = TPM_TOPALIGN; |
355 if (alignment == Menu2::ALIGN_TOPLEFT) | 481 if (alignment == Menu2::ALIGN_TOPLEFT) |
356 alignment_flags |= TPM_LEFTALIGN; | 482 alignment_flags |= TPM_LEFTALIGN; |
357 else if (alignment == Menu2::ALIGN_TOPRIGHT) | 483 else if (alignment == Menu2::ALIGN_TOPRIGHT) |
358 alignment_flags |= TPM_RIGHTALIGN; | 484 alignment_flags |= TPM_RIGHTALIGN; |
359 return alignment_flags; | 485 return alignment_flags; |
360 } | 486 } |
361 | 487 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
405 | 531 |
406 //////////////////////////////////////////////////////////////////////////////// | 532 //////////////////////////////////////////////////////////////////////////////// |
407 // MenuWrapper, public: | 533 // MenuWrapper, public: |
408 | 534 |
409 // static | 535 // static |
410 MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) { | 536 MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) { |
411 return new NativeMenuWin(menu->model(), NULL); | 537 return new NativeMenuWin(menu->model(), NULL); |
412 } | 538 } |
413 | 539 |
414 } // namespace views | 540 } // namespace views |
OLD | NEW |