| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "views/controls/text_field.h" | 5 #include "views/controls/textfield/textfield.h" |
| 6 | 6 |
| 7 #include <atlbase.h> | 7 #include <atlbase.h> |
| 8 #include <atlapp.h> | 8 #include <atlapp.h> |
| 9 #include <atlcrack.h> | 9 #include <atlcrack.h> |
| 10 #include <atlctrls.h> | 10 #include <atlctrls.h> |
| 11 #include <atlmisc.h> | 11 #include <atlmisc.h> |
| 12 #include <tom.h> // For ITextDocument, a COM interface to CRichEditCtrl | 12 #include <tom.h> // For ITextDocument, a COM interface to CRichEditCtrl |
| 13 #include <vsstyle.h> | 13 #include <vsstyle.h> |
| 14 | 14 |
| 15 #include "app/gfx/insets.h" | 15 #include "app/gfx/insets.h" |
| (...skipping 12 matching lines...) Expand all Loading... |
| 28 #include "views/focus/focus_util_win.h" | 28 #include "views/focus/focus_util_win.h" |
| 29 #include "views/views_delegate.h" | 29 #include "views/views_delegate.h" |
| 30 #include "views/widget/widget.h" | 30 #include "views/widget/widget.h" |
| 31 | 31 |
| 32 using gfx::NativeTheme; | 32 using gfx::NativeTheme; |
| 33 | 33 |
| 34 namespace views { | 34 namespace views { |
| 35 | 35 |
| 36 static const int kDefaultEditStyle = WS_CHILD | WS_VISIBLE; | 36 static const int kDefaultEditStyle = WS_CHILD | WS_VISIBLE; |
| 37 | 37 |
| 38 class TextField::Edit | 38 class Textfield::Edit |
| 39 : public CWindowImpl<TextField::Edit, CRichEditCtrl, | 39 : public CWindowImpl<Textfield::Edit, CRichEditCtrl, |
| 40 CWinTraits<kDefaultEditStyle> >, | 40 CWinTraits<kDefaultEditStyle> >, |
| 41 public CRichEditCommands<TextField::Edit>, | 41 public CRichEditCommands<Textfield::Edit>, |
| 42 public Menu::Delegate { | 42 public Menu::Delegate { |
| 43 public: | 43 public: |
| 44 DECLARE_WND_CLASS(L"ChromeViewsTextFieldEdit"); | 44 DECLARE_WND_CLASS(L"ChromeViewsTextfieldEdit"); |
| 45 | 45 |
| 46 Edit(TextField* parent, bool draw_border); | 46 Edit(Textfield* parent, bool draw_border); |
| 47 ~Edit(); | 47 ~Edit(); |
| 48 | 48 |
| 49 std::wstring GetText() const; | 49 std::wstring GetText() const; |
| 50 void SetText(const std::wstring& text); | 50 void SetText(const std::wstring& text); |
| 51 void AppendText(const std::wstring& text); | 51 void AppendText(const std::wstring& text); |
| 52 | 52 |
| 53 std::wstring GetSelectedText() const; | 53 std::wstring GetSelectedText() const; |
| 54 | 54 |
| 55 // Selects all the text in the edit. Use this in place of SetSelAll() to | 55 // Selects all the text in the edit. Use this in place of SetSelAll() to |
| 56 // avoid selecting the "phantom newline" at the end of the edit. | 56 // avoid selecting the "phantom newline" at the end of the edit. |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 179 bool can_discard_mousemove_; | 179 bool can_discard_mousemove_; |
| 180 | 180 |
| 181 // The text of this control before a possible change. | 181 // The text of this control before a possible change. |
| 182 std::wstring text_before_change_; | 182 std::wstring text_before_change_; |
| 183 | 183 |
| 184 // If true, the mouse is over the edit. | 184 // If true, the mouse is over the edit. |
| 185 bool contains_mouse_; | 185 bool contains_mouse_; |
| 186 | 186 |
| 187 static bool did_load_library_; | 187 static bool did_load_library_; |
| 188 | 188 |
| 189 TextField* parent_; | 189 Textfield* parent_; |
| 190 | 190 |
| 191 // The context menu for the edit. | 191 // The context menu for the edit. |
| 192 scoped_ptr<Menu> context_menu_; | 192 scoped_ptr<Menu> context_menu_; |
| 193 | 193 |
| 194 // Border insets. | 194 // Border insets. |
| 195 gfx::Insets content_insets_; | 195 gfx::Insets content_insets_; |
| 196 | 196 |
| 197 // Whether the border is drawn. | 197 // Whether the border is drawn. |
| 198 bool draw_border_; | 198 bool draw_border_; |
| 199 | 199 |
| 200 // This interface is useful for accessing the CRichEditCtrl at a low level. | 200 // This interface is useful for accessing the CRichEditCtrl at a low level. |
| 201 mutable CComQIPtr<ITextDocument> text_object_model_; | 201 mutable CComQIPtr<ITextDocument> text_object_model_; |
| 202 | 202 |
| 203 // The position and the length of the ongoing composition string. | 203 // The position and the length of the ongoing composition string. |
| 204 // These values are used for removing a composition string from a search | 204 // These values are used for removing a composition string from a search |
| 205 // text to emulate Firefox. | 205 // text to emulate Firefox. |
| 206 bool ime_discard_composition_; | 206 bool ime_discard_composition_; |
| 207 int ime_composition_start_; | 207 int ime_composition_start_; |
| 208 int ime_composition_length_; | 208 int ime_composition_length_; |
| 209 | 209 |
| 210 COLORREF bg_color_; | 210 COLORREF bg_color_; |
| 211 | 211 |
| 212 DISALLOW_COPY_AND_ASSIGN(Edit); | 212 DISALLOW_COPY_AND_ASSIGN(Edit); |
| 213 }; | 213 }; |
| 214 | 214 |
| 215 /////////////////////////////////////////////////////////////////////////////// | 215 /////////////////////////////////////////////////////////////////////////////// |
| 216 // Helper classes | 216 // Helper classes |
| 217 | 217 |
| 218 TextField::Edit::ScopedFreeze::ScopedFreeze(TextField::Edit* edit, | 218 Textfield::Edit::ScopedFreeze::ScopedFreeze(Textfield::Edit* edit, |
| 219 ITextDocument* text_object_model) | 219 ITextDocument* text_object_model) |
| 220 : edit_(edit), | 220 : edit_(edit), |
| 221 text_object_model_(text_object_model) { | 221 text_object_model_(text_object_model) { |
| 222 // Freeze the screen. | 222 // Freeze the screen. |
| 223 if (text_object_model_) { | 223 if (text_object_model_) { |
| 224 long count; | 224 long count; |
| 225 text_object_model_->Freeze(&count); | 225 text_object_model_->Freeze(&count); |
| 226 } | 226 } |
| 227 } | 227 } |
| 228 | 228 |
| 229 TextField::Edit::ScopedFreeze::~ScopedFreeze() { | 229 Textfield::Edit::ScopedFreeze::~ScopedFreeze() { |
| 230 // Unfreeze the screen. | 230 // Unfreeze the screen. |
| 231 if (text_object_model_) { | 231 if (text_object_model_) { |
| 232 long count; | 232 long count; |
| 233 text_object_model_->Unfreeze(&count); | 233 text_object_model_->Unfreeze(&count); |
| 234 if (count == 0) { | 234 if (count == 0) { |
| 235 // We need to UpdateWindow() here instead of InvalidateRect() because, as | 235 // We need to UpdateWindow() here instead of InvalidateRect() because, as |
| 236 // far as I can tell, the edit likes to synchronously erase its background | 236 // far as I can tell, the edit likes to synchronously erase its background |
| 237 // when unfreezing, thus requiring us to synchronously redraw if we don't | 237 // when unfreezing, thus requiring us to synchronously redraw if we don't |
| 238 // want flicker. | 238 // want flicker. |
| 239 edit_->UpdateWindow(); | 239 edit_->UpdateWindow(); |
| 240 } | 240 } |
| 241 } | 241 } |
| 242 } | 242 } |
| 243 | 243 |
| 244 /////////////////////////////////////////////////////////////////////////////// | 244 /////////////////////////////////////////////////////////////////////////////// |
| 245 // TextField::Edit | 245 // Textfield::Edit |
| 246 | 246 |
| 247 bool TextField::Edit::did_load_library_ = false; | 247 bool Textfield::Edit::did_load_library_ = false; |
| 248 | 248 |
| 249 TextField::Edit::Edit(TextField* parent, bool draw_border) | 249 Textfield::Edit::Edit(Textfield* parent, bool draw_border) |
| 250 : parent_(parent), | 250 : parent_(parent), |
| 251 tracking_double_click_(false), | 251 tracking_double_click_(false), |
| 252 double_click_time_(0), | 252 double_click_time_(0), |
| 253 can_discard_mousemove_(false), | 253 can_discard_mousemove_(false), |
| 254 contains_mouse_(false), | 254 contains_mouse_(false), |
| 255 draw_border_(draw_border), | 255 draw_border_(draw_border), |
| 256 ime_discard_composition_(false), | 256 ime_discard_composition_(false), |
| 257 ime_composition_start_(0), | 257 ime_composition_start_(0), |
| 258 ime_composition_length_(0), | 258 ime_composition_length_(0), |
| 259 bg_color_(0) { | 259 bg_color_(0) { |
| 260 if (!did_load_library_) | 260 if (!did_load_library_) |
| 261 did_load_library_ = !!LoadLibrary(L"riched20.dll"); | 261 did_load_library_ = !!LoadLibrary(L"riched20.dll"); |
| 262 | 262 |
| 263 DWORD style = kDefaultEditStyle; | 263 DWORD style = kDefaultEditStyle; |
| 264 if (parent->GetStyle() & TextField::STYLE_PASSWORD) | 264 if (parent->GetStyle() & Textfield::STYLE_PASSWORD) |
| 265 style |= ES_PASSWORD; | 265 style |= ES_PASSWORD; |
| 266 | 266 |
| 267 if (parent->read_only_) | 267 if (parent->read_only_) |
| 268 style |= ES_READONLY; | 268 style |= ES_READONLY; |
| 269 | 269 |
| 270 if (parent->GetStyle() & TextField::STYLE_MULTILINE) | 270 if (parent->GetStyle() & Textfield::STYLE_MULTILINE) |
| 271 style |= ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL; | 271 style |= ES_MULTILINE | ES_WANTRETURN | ES_AUTOVSCROLL; |
| 272 else | 272 else |
| 273 style |= ES_AUTOHSCROLL; | 273 style |= ES_AUTOHSCROLL; |
| 274 // Make sure we apply RTL related extended window styles if necessary. | 274 // Make sure we apply RTL related extended window styles if necessary. |
| 275 DWORD ex_style = l10n_util::GetExtendedStyles(); | 275 DWORD ex_style = l10n_util::GetExtendedStyles(); |
| 276 | 276 |
| 277 RECT r = {0, 0, parent_->width(), parent_->height()}; | 277 RECT r = {0, 0, parent_->width(), parent_->height()}; |
| 278 Create(parent_->GetWidget()->GetNativeView(), r, NULL, style, ex_style); | 278 Create(parent_->GetWidget()->GetNativeView(), r, NULL, style, ex_style); |
| 279 | 279 |
| 280 if (parent->GetStyle() & TextField::STYLE_LOWERCASE) { | 280 if (parent->GetStyle() & Textfield::STYLE_LOWERCASE) { |
| 281 DCHECK((parent->GetStyle() & TextField::STYLE_PASSWORD) == 0); | 281 DCHECK((parent->GetStyle() & Textfield::STYLE_PASSWORD) == 0); |
| 282 SetEditStyle(SES_LOWERCASE, SES_LOWERCASE); | 282 SetEditStyle(SES_LOWERCASE, SES_LOWERCASE); |
| 283 } | 283 } |
| 284 | 284 |
| 285 // Set up the text_object_model_. | 285 // Set up the text_object_model_. |
| 286 CComPtr<IRichEditOle> ole_interface; | 286 CComPtr<IRichEditOle> ole_interface; |
| 287 ole_interface.Attach(GetOleInterface()); | 287 ole_interface.Attach(GetOleInterface()); |
| 288 text_object_model_ = ole_interface; | 288 text_object_model_ = ole_interface; |
| 289 | 289 |
| 290 context_menu_.reset(new MenuWin(this, Menu::TOPLEFT, m_hWnd)); | 290 context_menu_.reset(new MenuWin(this, Menu::TOPLEFT, m_hWnd)); |
| 291 context_menu_->AppendMenuItemWithLabel(IDS_APP_UNDO, | 291 context_menu_->AppendMenuItemWithLabel(IDS_APP_UNDO, |
| 292 l10n_util::GetString(IDS_APP_UNDO)); | 292 l10n_util::GetString(IDS_APP_UNDO)); |
| 293 context_menu_->AppendSeparator(); | 293 context_menu_->AppendSeparator(); |
| 294 context_menu_->AppendMenuItemWithLabel(IDS_APP_CUT, | 294 context_menu_->AppendMenuItemWithLabel(IDS_APP_CUT, |
| 295 l10n_util::GetString(IDS_APP_CUT)); | 295 l10n_util::GetString(IDS_APP_CUT)); |
| 296 context_menu_->AppendMenuItemWithLabel(IDS_APP_COPY, | 296 context_menu_->AppendMenuItemWithLabel(IDS_APP_COPY, |
| 297 l10n_util::GetString(IDS_APP_COPY)); | 297 l10n_util::GetString(IDS_APP_COPY)); |
| 298 context_menu_->AppendMenuItemWithLabel(IDS_APP_PASTE, | 298 context_menu_->AppendMenuItemWithLabel(IDS_APP_PASTE, |
| 299 l10n_util::GetString(IDS_APP_PASTE)); | 299 l10n_util::GetString(IDS_APP_PASTE)); |
| 300 context_menu_->AppendSeparator(); | 300 context_menu_->AppendSeparator(); |
| 301 context_menu_->AppendMenuItemWithLabel(IDS_APP_SELECT_ALL, | 301 context_menu_->AppendMenuItemWithLabel(IDS_APP_SELECT_ALL, |
| 302 l10n_util::GetString(IDS_APP_SELECT_ALL
)); | 302 l10n_util::GetString(IDS_APP_SELECT_ALL
)); |
| 303 } | 303 } |
| 304 | 304 |
| 305 TextField::Edit::~Edit() { | 305 Textfield::Edit::~Edit() { |
| 306 } | 306 } |
| 307 | 307 |
| 308 std::wstring TextField::Edit::GetText() const { | 308 std::wstring Textfield::Edit::GetText() const { |
| 309 int len = GetTextLength() + 1; | 309 int len = GetTextLength() + 1; |
| 310 std::wstring str; | 310 std::wstring str; |
| 311 GetWindowText(WriteInto(&str, len), len); | 311 GetWindowText(WriteInto(&str, len), len); |
| 312 return str; | 312 return str; |
| 313 } | 313 } |
| 314 | 314 |
| 315 void TextField::Edit::SetText(const std::wstring& text) { | 315 void Textfield::Edit::SetText(const std::wstring& text) { |
| 316 // Adjusting the string direction before setting the text in order to make | 316 // Adjusting the string direction before setting the text in order to make |
| 317 // sure both RTL and LTR strings are displayed properly. | 317 // sure both RTL and LTR strings are displayed properly. |
| 318 std::wstring text_to_set; | 318 std::wstring text_to_set; |
| 319 if (!l10n_util::AdjustStringForLocaleDirection(text, &text_to_set)) | 319 if (!l10n_util::AdjustStringForLocaleDirection(text, &text_to_set)) |
| 320 text_to_set = text; | 320 text_to_set = text; |
| 321 if (parent_->GetStyle() & STYLE_LOWERCASE) | 321 if (parent_->GetStyle() & STYLE_LOWERCASE) |
| 322 text_to_set = l10n_util::ToLower(text_to_set); | 322 text_to_set = l10n_util::ToLower(text_to_set); |
| 323 SetWindowText(text_to_set.c_str()); | 323 SetWindowText(text_to_set.c_str()); |
| 324 } | 324 } |
| 325 | 325 |
| 326 void TextField::Edit::AppendText(const std::wstring& text) { | 326 void Textfield::Edit::AppendText(const std::wstring& text) { |
| 327 int text_length = GetWindowTextLength(); | 327 int text_length = GetWindowTextLength(); |
| 328 ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length)); | 328 ::SendMessage(m_hWnd, TBM_SETSEL, true, MAKELPARAM(text_length, text_length)); |
| 329 ::SendMessage(m_hWnd, EM_REPLACESEL, false, | 329 ::SendMessage(m_hWnd, EM_REPLACESEL, false, |
| 330 reinterpret_cast<LPARAM>(text.c_str())); | 330 reinterpret_cast<LPARAM>(text.c_str())); |
| 331 } | 331 } |
| 332 | 332 |
| 333 std::wstring TextField::Edit::GetSelectedText() const { | 333 std::wstring Textfield::Edit::GetSelectedText() const { |
| 334 // Figure out the length of the selection. | 334 // Figure out the length of the selection. |
| 335 long start; | 335 long start; |
| 336 long end; | 336 long end; |
| 337 GetSel(start, end); | 337 GetSel(start, end); |
| 338 | 338 |
| 339 // Grab the selected text. | 339 // Grab the selected text. |
| 340 std::wstring str; | 340 std::wstring str; |
| 341 GetSelText(WriteInto(&str, end - start + 1)); | 341 GetSelText(WriteInto(&str, end - start + 1)); |
| 342 | 342 |
| 343 return str; | 343 return str; |
| 344 } | 344 } |
| 345 | 345 |
| 346 void TextField::Edit::SelectAll() { | 346 void Textfield::Edit::SelectAll() { |
| 347 // Select from the end to the front so that the first part of the text is | 347 // Select from the end to the front so that the first part of the text is |
| 348 // always visible. | 348 // always visible. |
| 349 SetSel(GetTextLength(), 0); | 349 SetSel(GetTextLength(), 0); |
| 350 } | 350 } |
| 351 | 351 |
| 352 void TextField::Edit::ClearSelection() { | 352 void Textfield::Edit::ClearSelection() { |
| 353 SetSel(GetTextLength(), GetTextLength()); | 353 SetSel(GetTextLength(), GetTextLength()); |
| 354 } | 354 } |
| 355 | 355 |
| 356 void TextField::Edit::RemoveBorder() { | 356 void Textfield::Edit::RemoveBorder() { |
| 357 if (!draw_border_) | 357 if (!draw_border_) |
| 358 return; | 358 return; |
| 359 | 359 |
| 360 draw_border_ = false; | 360 draw_border_ = false; |
| 361 SetWindowPos(NULL, 0, 0, 0, 0, | 361 SetWindowPos(NULL, 0, 0, 0, 0, |
| 362 SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE | | 362 SWP_NOMOVE | SWP_FRAMECHANGED | SWP_NOACTIVATE | |
| 363 SWP_NOOWNERZORDER | SWP_NOSIZE); | 363 SWP_NOOWNERZORDER | SWP_NOSIZE); |
| 364 } | 364 } |
| 365 | 365 |
| 366 void TextField::Edit::SetEnabled(bool enabled) { | 366 void Textfield::Edit::SetEnabled(bool enabled) { |
| 367 SendMessage(parent_->GetNativeComponent(), WM_ENABLE, | 367 SendMessage(parent_->GetNativeComponent(), WM_ENABLE, |
| 368 static_cast<WPARAM>(enabled), 0); | 368 static_cast<WPARAM>(enabled), 0); |
| 369 } | 369 } |
| 370 | 370 |
| 371 // static | 371 // static |
| 372 bool TextField::IsKeystrokeEnter(const Keystroke& key) { | 372 bool Textfield::IsKeystrokeEnter(const Keystroke& key) { |
| 373 return key.key == VK_RETURN; | 373 return key.key == VK_RETURN; |
| 374 } | 374 } |
| 375 | 375 |
| 376 // static | 376 // static |
| 377 bool TextField::IsKeystrokeEscape(const Keystroke& key) { | 377 bool Textfield::IsKeystrokeEscape(const Keystroke& key) { |
| 378 return key.key == VK_ESCAPE; | 378 return key.key == VK_ESCAPE; |
| 379 } | 379 } |
| 380 | 380 |
| 381 void TextField::Edit::SetBackgroundColor(COLORREF bg_color) { | 381 void Textfield::Edit::SetBackgroundColor(COLORREF bg_color) { |
| 382 CRichEditCtrl::SetBackgroundColor(bg_color); | 382 CRichEditCtrl::SetBackgroundColor(bg_color); |
| 383 bg_color_ = bg_color; | 383 bg_color_ = bg_color; |
| 384 } | 384 } |
| 385 | 385 |
| 386 bool TextField::Edit::IsCommandEnabled(int id) const { | 386 bool Textfield::Edit::IsCommandEnabled(int id) const { |
| 387 switch (id) { | 387 switch (id) { |
| 388 case IDS_APP_UNDO: return !parent_->IsReadOnly() && !!CanUndo(); | 388 case IDS_APP_UNDO: return !parent_->IsReadOnly() && !!CanUndo(); |
| 389 case IDS_APP_CUT: return !parent_->IsReadOnly() && | 389 case IDS_APP_CUT: return !parent_->IsReadOnly() && |
| 390 !parent_->IsPassword() && !!CanCut(); | 390 !parent_->IsPassword() && !!CanCut(); |
| 391 case IDS_APP_COPY: return !!CanCopy() && !parent_->IsPassword(); | 391 case IDS_APP_COPY: return !!CanCopy() && !parent_->IsPassword(); |
| 392 case IDS_APP_PASTE: return !parent_->IsReadOnly() && !!CanPaste(); | 392 case IDS_APP_PASTE: return !parent_->IsReadOnly() && !!CanPaste(); |
| 393 case IDS_APP_SELECT_ALL: return !!CanSelectAll(); | 393 case IDS_APP_SELECT_ALL: return !!CanSelectAll(); |
| 394 default: NOTREACHED(); | 394 default: NOTREACHED(); |
| 395 return false; | 395 return false; |
| 396 } | 396 } |
| 397 } | 397 } |
| 398 | 398 |
| 399 void TextField::Edit::ExecuteCommand(int id) { | 399 void Textfield::Edit::ExecuteCommand(int id) { |
| 400 ScopedFreeze freeze(this, GetTextObjectModel()); | 400 ScopedFreeze freeze(this, GetTextObjectModel()); |
| 401 OnBeforePossibleChange(); | 401 OnBeforePossibleChange(); |
| 402 switch (id) { | 402 switch (id) { |
| 403 case IDS_APP_UNDO: Undo(); break; | 403 case IDS_APP_UNDO: Undo(); break; |
| 404 case IDS_APP_CUT: Cut(); break; | 404 case IDS_APP_CUT: Cut(); break; |
| 405 case IDS_APP_COPY: Copy(); break; | 405 case IDS_APP_COPY: Copy(); break; |
| 406 case IDS_APP_PASTE: Paste(); break; | 406 case IDS_APP_PASTE: Paste(); break; |
| 407 case IDS_APP_SELECT_ALL: SelectAll(); break; | 407 case IDS_APP_SELECT_ALL: SelectAll(); break; |
| 408 default: NOTREACHED(); break; | 408 default: NOTREACHED(); break; |
| 409 } | 409 } |
| 410 OnAfterPossibleChange(); | 410 OnAfterPossibleChange(); |
| 411 } | 411 } |
| 412 | 412 |
| 413 void TextField::Edit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { | 413 void Textfield::Edit::OnChar(TCHAR ch, UINT repeat_count, UINT flags) { |
| 414 HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags); | 414 HandleKeystroke(GetCurrentMessage()->message, ch, repeat_count, flags); |
| 415 } | 415 } |
| 416 | 416 |
| 417 void TextField::Edit::OnContextMenu(HWND window, const CPoint& point) { | 417 void Textfield::Edit::OnContextMenu(HWND window, const CPoint& point) { |
| 418 CPoint p(point); | 418 CPoint p(point); |
| 419 if (point.x == -1 || point.y == -1) { | 419 if (point.x == -1 || point.y == -1) { |
| 420 GetCaretPos(&p); | 420 GetCaretPos(&p); |
| 421 MapWindowPoints(HWND_DESKTOP, &p, 1); | 421 MapWindowPoints(HWND_DESKTOP, &p, 1); |
| 422 } | 422 } |
| 423 context_menu_->RunMenuAt(p.x, p.y); | 423 context_menu_->RunMenuAt(p.x, p.y); |
| 424 } | 424 } |
| 425 | 425 |
| 426 void TextField::Edit::OnCopy() { | 426 void Textfield::Edit::OnCopy() { |
| 427 if (parent_->IsPassword()) | 427 if (parent_->IsPassword()) |
| 428 return; | 428 return; |
| 429 | 429 |
| 430 const std::wstring text(GetSelectedText()); | 430 const std::wstring text(GetSelectedText()); |
| 431 | 431 |
| 432 if (!text.empty() && ViewsDelegate::views_delegate) { | 432 if (!text.empty() && ViewsDelegate::views_delegate) { |
| 433 ScopedClipboardWriter scw(ViewsDelegate::views_delegate->GetClipboard()); | 433 ScopedClipboardWriter scw(ViewsDelegate::views_delegate->GetClipboard()); |
| 434 scw.WriteText(text); | 434 scw.WriteText(text); |
| 435 } | 435 } |
| 436 } | 436 } |
| 437 | 437 |
| 438 void TextField::Edit::OnCut() { | 438 void Textfield::Edit::OnCut() { |
| 439 if (parent_->IsReadOnly() || parent_->IsPassword()) | 439 if (parent_->IsReadOnly() || parent_->IsPassword()) |
| 440 return; | 440 return; |
| 441 | 441 |
| 442 OnCopy(); | 442 OnCopy(); |
| 443 | 443 |
| 444 // This replace selection will have no effect (even on the undo stack) if the | 444 // This replace selection will have no effect (even on the undo stack) if the |
| 445 // current selection is empty. | 445 // current selection is empty. |
| 446 ReplaceSel(L"", true); | 446 ReplaceSel(L"", true); |
| 447 } | 447 } |
| 448 | 448 |
| 449 LRESULT TextField::Edit::OnImeChar(UINT message, WPARAM wparam, LPARAM lparam) { | 449 LRESULT Textfield::Edit::OnImeChar(UINT message, WPARAM wparam, LPARAM lparam) { |
| 450 // http://crbug.com/7707: a rich-edit control may crash when it receives a | 450 // http://crbug.com/7707: a rich-edit control may crash when it receives a |
| 451 // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message. | 451 // WM_IME_CHAR message while it is processing a WM_IME_COMPOSITION message. |
| 452 // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR | 452 // Since view controls don't need WM_IME_CHAR messages, we prevent WM_IME_CHAR |
| 453 // messages from being dispatched to view controls via the CallWindowProc() | 453 // messages from being dispatched to view controls via the CallWindowProc() |
| 454 // call. | 454 // call. |
| 455 return 0; | 455 return 0; |
| 456 } | 456 } |
| 457 | 457 |
| 458 LRESULT TextField::Edit::OnImeStartComposition(UINT message, | 458 LRESULT Textfield::Edit::OnImeStartComposition(UINT message, |
| 459 WPARAM wparam, | 459 WPARAM wparam, |
| 460 LPARAM lparam) { | 460 LPARAM lparam) { |
| 461 // Users may press alt+shift or control+shift keys to change their keyboard | 461 // Users may press alt+shift or control+shift keys to change their keyboard |
| 462 // layouts. So, we retrieve the input locale identifier everytime we start | 462 // layouts. So, we retrieve the input locale identifier everytime we start |
| 463 // an IME composition. | 463 // an IME composition. |
| 464 int language_id = PRIMARYLANGID(GetKeyboardLayout(0)); | 464 int language_id = PRIMARYLANGID(GetKeyboardLayout(0)); |
| 465 ime_discard_composition_ = | 465 ime_discard_composition_ = |
| 466 language_id == LANG_JAPANESE || language_id == LANG_CHINESE; | 466 language_id == LANG_JAPANESE || language_id == LANG_CHINESE; |
| 467 ime_composition_start_ = 0; | 467 ime_composition_start_ = 0; |
| 468 ime_composition_length_ = 0; | 468 ime_composition_length_ = 0; |
| 469 | 469 |
| 470 return DefWindowProc(message, wparam, lparam); | 470 return DefWindowProc(message, wparam, lparam); |
| 471 } | 471 } |
| 472 | 472 |
| 473 LRESULT TextField::Edit::OnImeComposition(UINT message, | 473 LRESULT Textfield::Edit::OnImeComposition(UINT message, |
| 474 WPARAM wparam, | 474 WPARAM wparam, |
| 475 LPARAM lparam) { | 475 LPARAM lparam) { |
| 476 text_before_change_.clear(); | 476 text_before_change_.clear(); |
| 477 LRESULT result = DefWindowProc(message, wparam, lparam); | 477 LRESULT result = DefWindowProc(message, wparam, lparam); |
| 478 | 478 |
| 479 ime_composition_start_ = 0; | 479 ime_composition_start_ = 0; |
| 480 ime_composition_length_ = 0; | 480 ime_composition_length_ = 0; |
| 481 if (ime_discard_composition_) { | 481 if (ime_discard_composition_) { |
| 482 // Call IMM32 functions to retrieve the position and the length of the | 482 // Call IMM32 functions to retrieve the position and the length of the |
| 483 // ongoing composition string and notify the OnAfterPossibleChange() | 483 // ongoing composition string and notify the OnAfterPossibleChange() |
| (...skipping 15 matching lines...) Expand all Loading... |
| 499 ime_composition_length_ = composition_size / sizeof(wchar_t); | 499 ime_composition_length_ = composition_size / sizeof(wchar_t); |
| 500 | 500 |
| 501 ImmReleaseContext(m_hWnd, imm_context); | 501 ImmReleaseContext(m_hWnd, imm_context); |
| 502 } | 502 } |
| 503 } | 503 } |
| 504 | 504 |
| 505 OnAfterPossibleChange(); | 505 OnAfterPossibleChange(); |
| 506 return result; | 506 return result; |
| 507 } | 507 } |
| 508 | 508 |
| 509 LRESULT TextField::Edit::OnImeEndComposition(UINT message, | 509 LRESULT Textfield::Edit::OnImeEndComposition(UINT message, |
| 510 WPARAM wparam, | 510 WPARAM wparam, |
| 511 LPARAM lparam) { | 511 LPARAM lparam) { |
| 512 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without | 512 // Bug 11863: Korean IMEs send a WM_IME_ENDCOMPOSITION message without |
| 513 // sending any WM_IME_COMPOSITION messages when a user deletes all | 513 // sending any WM_IME_COMPOSITION messages when a user deletes all |
| 514 // composition characters, i.e. a composition string becomes empty. To handle | 514 // composition characters, i.e. a composition string becomes empty. To handle |
| 515 // this case, we need to update the find results when a composition is | 515 // this case, we need to update the find results when a composition is |
| 516 // finished or canceled. | 516 // finished or canceled. |
| 517 parent_->SyncText(); | 517 parent_->SyncText(); |
| 518 if (parent_->GetController()) | 518 if (parent_->GetController()) |
| 519 parent_->GetController()->ContentsChanged(parent_, GetText()); | 519 parent_->GetController()->ContentsChanged(parent_, GetText()); |
| 520 return DefWindowProc(message, wparam, lparam); | 520 return DefWindowProc(message, wparam, lparam); |
| 521 } | 521 } |
| 522 | 522 |
| 523 void TextField::Edit::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) { | 523 void Textfield::Edit::OnKeyDown(TCHAR key, UINT repeat_count, UINT flags) { |
| 524 // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than | 524 // NOTE: Annoyingly, ctrl-alt-<key> generates WM_KEYDOWN rather than |
| 525 // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places | 525 // WM_SYSKEYDOWN, so we need to check (flags & KF_ALTDOWN) in various places |
| 526 // in this function even with a WM_SYSKEYDOWN handler. | 526 // in this function even with a WM_SYSKEYDOWN handler. |
| 527 | 527 |
| 528 switch (key) { | 528 switch (key) { |
| 529 case VK_RETURN: | 529 case VK_RETURN: |
| 530 // If we are multi-line, we want to let returns through so they start a | 530 // If we are multi-line, we want to let returns through so they start a |
| 531 // new line. | 531 // new line. |
| 532 if (parent_->IsMultiLine()) | 532 if (parent_->IsMultiLine()) |
| 533 break; | 533 break; |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 592 // We ignore this event because an IME sends WM_IME_COMPOSITION messages | 592 // We ignore this event because an IME sends WM_IME_COMPOSITION messages |
| 593 // when it updates the CRichEditCtrl text. | 593 // when it updates the CRichEditCtrl text. |
| 594 return; | 594 return; |
| 595 } | 595 } |
| 596 | 596 |
| 597 // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many | 597 // CRichEditCtrl changes its text on WM_KEYDOWN instead of WM_CHAR for many |
| 598 // different keys (backspace, ctrl-v, ...), so we call this in both cases. | 598 // different keys (backspace, ctrl-v, ...), so we call this in both cases. |
| 599 HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags); | 599 HandleKeystroke(GetCurrentMessage()->message, key, repeat_count, flags); |
| 600 } | 600 } |
| 601 | 601 |
| 602 void TextField::Edit::OnLButtonDblClk(UINT keys, const CPoint& point) { | 602 void Textfield::Edit::OnLButtonDblClk(UINT keys, const CPoint& point) { |
| 603 // Save the double click info for later triple-click detection. | 603 // Save the double click info for later triple-click detection. |
| 604 tracking_double_click_ = true; | 604 tracking_double_click_ = true; |
| 605 double_click_point_ = point; | 605 double_click_point_ = point; |
| 606 double_click_time_ = GetCurrentMessage()->time; | 606 double_click_time_ = GetCurrentMessage()->time; |
| 607 | 607 |
| 608 ScopedFreeze freeze(this, GetTextObjectModel()); | 608 ScopedFreeze freeze(this, GetTextObjectModel()); |
| 609 OnBeforePossibleChange(); | 609 OnBeforePossibleChange(); |
| 610 DefWindowProc(WM_LBUTTONDBLCLK, keys, | 610 DefWindowProc(WM_LBUTTONDBLCLK, keys, |
| 611 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); | 611 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); |
| 612 OnAfterPossibleChange(); | 612 OnAfterPossibleChange(); |
| 613 } | 613 } |
| 614 | 614 |
| 615 void TextField::Edit::OnLButtonDown(UINT keys, const CPoint& point) { | 615 void Textfield::Edit::OnLButtonDown(UINT keys, const CPoint& point) { |
| 616 // Check for triple click, then reset tracker. Should be safe to subtract | 616 // Check for triple click, then reset tracker. Should be safe to subtract |
| 617 // double_click_time_ from the current message's time even if the timer has | 617 // double_click_time_ from the current message's time even if the timer has |
| 618 // wrapped in between. | 618 // wrapped in between. |
| 619 const bool is_triple_click = tracking_double_click_ && | 619 const bool is_triple_click = tracking_double_click_ && |
| 620 win_util::IsDoubleClick(double_click_point_, point, | 620 win_util::IsDoubleClick(double_click_point_, point, |
| 621 GetCurrentMessage()->time - double_click_time_); | 621 GetCurrentMessage()->time - double_click_time_); |
| 622 tracking_double_click_ = false; | 622 tracking_double_click_ = false; |
| 623 | 623 |
| 624 ScopedFreeze freeze(this, GetTextObjectModel()); | 624 ScopedFreeze freeze(this, GetTextObjectModel()); |
| 625 OnBeforePossibleChange(); | 625 OnBeforePossibleChange(); |
| 626 DefWindowProc(WM_LBUTTONDOWN, keys, | 626 DefWindowProc(WM_LBUTTONDOWN, keys, |
| 627 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click), | 627 MAKELPARAM(ClipXCoordToVisibleText(point.x, is_triple_click), |
| 628 point.y)); | 628 point.y)); |
| 629 OnAfterPossibleChange(); | 629 OnAfterPossibleChange(); |
| 630 } | 630 } |
| 631 | 631 |
| 632 void TextField::Edit::OnLButtonUp(UINT keys, const CPoint& point) { | 632 void Textfield::Edit::OnLButtonUp(UINT keys, const CPoint& point) { |
| 633 ScopedFreeze freeze(this, GetTextObjectModel()); | 633 ScopedFreeze freeze(this, GetTextObjectModel()); |
| 634 OnBeforePossibleChange(); | 634 OnBeforePossibleChange(); |
| 635 DefWindowProc(WM_LBUTTONUP, keys, | 635 DefWindowProc(WM_LBUTTONUP, keys, |
| 636 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); | 636 MAKELPARAM(ClipXCoordToVisibleText(point.x, false), point.y)); |
| 637 OnAfterPossibleChange(); | 637 OnAfterPossibleChange(); |
| 638 } | 638 } |
| 639 | 639 |
| 640 void TextField::Edit::OnMouseLeave() { | 640 void Textfield::Edit::OnMouseLeave() { |
| 641 SetContainsMouse(false); | 641 SetContainsMouse(false); |
| 642 } | 642 } |
| 643 | 643 |
| 644 LRESULT TextField::Edit::OnMouseWheel(UINT message, | 644 LRESULT Textfield::Edit::OnMouseWheel(UINT message, |
| 645 WPARAM w_param, LPARAM l_param) { | 645 WPARAM w_param, LPARAM l_param) { |
| 646 // Reroute the mouse-wheel to the window under the mouse pointer if | 646 // Reroute the mouse-wheel to the window under the mouse pointer if |
| 647 // applicable. | 647 // applicable. |
| 648 if (views::RerouteMouseWheel(m_hWnd, w_param, l_param)) | 648 if (views::RerouteMouseWheel(m_hWnd, w_param, l_param)) |
| 649 return 0; | 649 return 0; |
| 650 return DefWindowProc(message, w_param, l_param);; | 650 return DefWindowProc(message, w_param, l_param);; |
| 651 } | 651 } |
| 652 | 652 |
| 653 void TextField::Edit::OnMouseMove(UINT keys, const CPoint& point) { | 653 void Textfield::Edit::OnMouseMove(UINT keys, const CPoint& point) { |
| 654 SetContainsMouse(true); | 654 SetContainsMouse(true); |
| 655 // Clamp the selection to the visible text so the user can't drag to select | 655 // Clamp the selection to the visible text so the user can't drag to select |
| 656 // the "phantom newline". In theory we could achieve this by clipping the X | 656 // the "phantom newline". In theory we could achieve this by clipping the X |
| 657 // coordinate, but in practice the edit seems to behave nondeterministically | 657 // coordinate, but in practice the edit seems to behave nondeterministically |
| 658 // with similar sequences of clipped input coordinates fed to it. Maybe it's | 658 // with similar sequences of clipped input coordinates fed to it. Maybe it's |
| 659 // reading the mouse cursor position directly? | 659 // reading the mouse cursor position directly? |
| 660 // | 660 // |
| 661 // This solution has a minor visual flaw, however: if there's a visible | 661 // This solution has a minor visual flaw, however: if there's a visible |
| 662 // cursor at the edge of the text (only true when there's no selection), | 662 // cursor at the edge of the text (only true when there's no selection), |
| 663 // dragging the mouse around outside that edge repaints the cursor on every | 663 // dragging the mouse around outside that edge repaints the cursor on every |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 697 // just a few pixels vertically end up selecting the "phantom newline"... | 697 // just a few pixels vertically end up selecting the "phantom newline"... |
| 698 // sometimes. | 698 // sometimes. |
| 699 RECT r; | 699 RECT r; |
| 700 GetRect(&r); | 700 GetRect(&r); |
| 701 DefWindowProc(WM_MOUSEMOVE, keys, | 701 DefWindowProc(WM_MOUSEMOVE, keys, |
| 702 MAKELPARAM(point.x, (r.bottom - r.top) / 2)); | 702 MAKELPARAM(point.x, (r.bottom - r.top) / 2)); |
| 703 OnAfterPossibleChange(); | 703 OnAfterPossibleChange(); |
| 704 } | 704 } |
| 705 } | 705 } |
| 706 | 706 |
| 707 int TextField::Edit::OnNCCalcSize(BOOL w_param, LPARAM l_param) { | 707 int Textfield::Edit::OnNCCalcSize(BOOL w_param, LPARAM l_param) { |
| 708 content_insets_.Set(0, 0, 0, 0); | 708 content_insets_.Set(0, 0, 0, 0); |
| 709 parent_->CalculateInsets(&content_insets_); | 709 parent_->CalculateInsets(&content_insets_); |
| 710 if (w_param) { | 710 if (w_param) { |
| 711 NCCALCSIZE_PARAMS* nc_params = | 711 NCCALCSIZE_PARAMS* nc_params = |
| 712 reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param); | 712 reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param); |
| 713 nc_params->rgrc[0].left += content_insets_.left(); | 713 nc_params->rgrc[0].left += content_insets_.left(); |
| 714 nc_params->rgrc[0].right -= content_insets_.right(); | 714 nc_params->rgrc[0].right -= content_insets_.right(); |
| 715 nc_params->rgrc[0].top += content_insets_.top(); | 715 nc_params->rgrc[0].top += content_insets_.top(); |
| 716 nc_params->rgrc[0].bottom -= content_insets_.bottom(); | 716 nc_params->rgrc[0].bottom -= content_insets_.bottom(); |
| 717 } else { | 717 } else { |
| 718 RECT* rect = reinterpret_cast<RECT*>(l_param); | 718 RECT* rect = reinterpret_cast<RECT*>(l_param); |
| 719 rect->left += content_insets_.left(); | 719 rect->left += content_insets_.left(); |
| 720 rect->right -= content_insets_.right(); | 720 rect->right -= content_insets_.right(); |
| 721 rect->top += content_insets_.top(); | 721 rect->top += content_insets_.top(); |
| 722 rect->bottom -= content_insets_.bottom(); | 722 rect->bottom -= content_insets_.bottom(); |
| 723 } | 723 } |
| 724 return 0; | 724 return 0; |
| 725 } | 725 } |
| 726 | 726 |
| 727 void TextField::Edit::OnNCPaint(HRGN region) { | 727 void Textfield::Edit::OnNCPaint(HRGN region) { |
| 728 if (!draw_border_) | 728 if (!draw_border_) |
| 729 return; | 729 return; |
| 730 | 730 |
| 731 HDC hdc = GetWindowDC(); | 731 HDC hdc = GetWindowDC(); |
| 732 | 732 |
| 733 CRect window_rect; | 733 CRect window_rect; |
| 734 GetWindowRect(&window_rect); | 734 GetWindowRect(&window_rect); |
| 735 // Convert to be relative to 0x0. | 735 // Convert to be relative to 0x0. |
| 736 window_rect.MoveToXY(0, 0); | 736 window_rect.MoveToXY(0, 0); |
| 737 | 737 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 779 NativeTheme::instance()->PaintTextField(hdc, part, state, classic_state, | 779 NativeTheme::instance()->PaintTextField(hdc, part, state, classic_state, |
| 780 &window_rect, bg_color_, false, | 780 &window_rect, bg_color_, false, |
| 781 true); | 781 true); |
| 782 | 782 |
| 783 // NOTE: I tried checking the transparent property of the theme and invoking | 783 // NOTE: I tried checking the transparent property of the theme and invoking |
| 784 // drawParentBackground, but it didn't seem to make a difference. | 784 // drawParentBackground, but it didn't seem to make a difference. |
| 785 | 785 |
| 786 ReleaseDC(hdc); | 786 ReleaseDC(hdc); |
| 787 } | 787 } |
| 788 | 788 |
| 789 void TextField::Edit::OnNonLButtonDown(UINT keys, const CPoint& point) { | 789 void Textfield::Edit::OnNonLButtonDown(UINT keys, const CPoint& point) { |
| 790 // Interestingly, the edit doesn't seem to cancel triple clicking when the | 790 // Interestingly, the edit doesn't seem to cancel triple clicking when the |
| 791 // x-buttons (which usually means "thumb buttons") are pressed, so we only | 791 // x-buttons (which usually means "thumb buttons") are pressed, so we only |
| 792 // call this for M and R down. | 792 // call this for M and R down. |
| 793 tracking_double_click_ = false; | 793 tracking_double_click_ = false; |
| 794 SetMsgHandled(false); | 794 SetMsgHandled(false); |
| 795 } | 795 } |
| 796 | 796 |
| 797 void TextField::Edit::OnPaste() { | 797 void Textfield::Edit::OnPaste() { |
| 798 if (parent_->IsReadOnly() || !ViewsDelegate::views_delegate) | 798 if (parent_->IsReadOnly() || !ViewsDelegate::views_delegate) |
| 799 return; | 799 return; |
| 800 | 800 |
| 801 Clipboard* clipboard = ViewsDelegate::views_delegate->GetClipboard(); | 801 Clipboard* clipboard = ViewsDelegate::views_delegate->GetClipboard(); |
| 802 | 802 |
| 803 if (!clipboard->IsFormatAvailable(Clipboard::GetPlainTextWFormatType())) | 803 if (!clipboard->IsFormatAvailable(Clipboard::GetPlainTextWFormatType())) |
| 804 return; | 804 return; |
| 805 | 805 |
| 806 std::wstring clipboard_str; | 806 std::wstring clipboard_str; |
| 807 clipboard->ReadText(&clipboard_str); | 807 clipboard->ReadText(&clipboard_str); |
| 808 if (!clipboard_str.empty()) { | 808 if (!clipboard_str.empty()) { |
| 809 std::wstring collapsed(CollapseWhitespace(clipboard_str, false)); | 809 std::wstring collapsed(CollapseWhitespace(clipboard_str, false)); |
| 810 if (parent_->GetStyle() & STYLE_LOWERCASE) | 810 if (parent_->GetStyle() & STYLE_LOWERCASE) |
| 811 collapsed = l10n_util::ToLower(collapsed); | 811 collapsed = l10n_util::ToLower(collapsed); |
| 812 // Force a Paste operation to trigger OnContentsChanged, even if identical | 812 // Force a Paste operation to trigger OnContentsChanged, even if identical |
| 813 // contents are pasted into the text box. | 813 // contents are pasted into the text box. |
| 814 text_before_change_.clear(); | 814 text_before_change_.clear(); |
| 815 ReplaceSel(collapsed.c_str(), true); | 815 ReplaceSel(collapsed.c_str(), true); |
| 816 } | 816 } |
| 817 } | 817 } |
| 818 | 818 |
| 819 void TextField::Edit::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { | 819 void Textfield::Edit::OnSysChar(TCHAR ch, UINT repeat_count, UINT flags) { |
| 820 // Nearly all alt-<xxx> combos result in beeping rather than doing something | 820 // Nearly all alt-<xxx> combos result in beeping rather than doing something |
| 821 // useful, so we discard most. Exceptions: | 821 // useful, so we discard most. Exceptions: |
| 822 // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead | 822 // * ctrl-alt-<xxx>, which is sometimes important, generates WM_CHAR instead |
| 823 // of WM_SYSCHAR, so it doesn't need to be handled here. | 823 // of WM_SYSCHAR, so it doesn't need to be handled here. |
| 824 // * alt-space gets translated by the default WM_SYSCHAR handler to a | 824 // * alt-space gets translated by the default WM_SYSCHAR handler to a |
| 825 // WM_SYSCOMMAND to open the application context menu, so we need to allow | 825 // WM_SYSCOMMAND to open the application context menu, so we need to allow |
| 826 // it through. | 826 // it through. |
| 827 if (ch == VK_SPACE) | 827 if (ch == VK_SPACE) |
| 828 SetMsgHandled(false); | 828 SetMsgHandled(false); |
| 829 } | 829 } |
| 830 | 830 |
| 831 void TextField::Edit::HandleKeystroke(UINT message, | 831 void Textfield::Edit::HandleKeystroke(UINT message, |
| 832 TCHAR key, | 832 TCHAR key, |
| 833 UINT repeat_count, | 833 UINT repeat_count, |
| 834 UINT flags) { | 834 UINT flags) { |
| 835 ScopedFreeze freeze(this, GetTextObjectModel()); | 835 ScopedFreeze freeze(this, GetTextObjectModel()); |
| 836 | 836 |
| 837 TextField::Controller* controller = parent_->GetController(); | 837 Textfield::Controller* controller = parent_->GetController(); |
| 838 bool handled = false; | 838 bool handled = false; |
| 839 if (controller) { | 839 if (controller) { |
| 840 handled = controller->HandleKeystroke(parent_, | 840 handled = controller->HandleKeystroke(parent_, |
| 841 TextField::Keystroke(message, key, repeat_count, flags)); | 841 Textfield::Keystroke(message, key, repeat_count, flags)); |
| 842 } | 842 } |
| 843 | 843 |
| 844 if (!handled) { | 844 if (!handled) { |
| 845 OnBeforePossibleChange(); | 845 OnBeforePossibleChange(); |
| 846 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); | 846 DefWindowProc(message, key, MAKELPARAM(repeat_count, flags)); |
| 847 OnAfterPossibleChange(); | 847 OnAfterPossibleChange(); |
| 848 } | 848 } |
| 849 } | 849 } |
| 850 | 850 |
| 851 void TextField::Edit::OnBeforePossibleChange() { | 851 void Textfield::Edit::OnBeforePossibleChange() { |
| 852 // Record our state. | 852 // Record our state. |
| 853 text_before_change_ = GetText(); | 853 text_before_change_ = GetText(); |
| 854 } | 854 } |
| 855 | 855 |
| 856 void TextField::Edit::OnAfterPossibleChange() { | 856 void Textfield::Edit::OnAfterPossibleChange() { |
| 857 // Prevent the user from selecting the "phantom newline" at the end of the | 857 // Prevent the user from selecting the "phantom newline" at the end of the |
| 858 // edit. If they try, we just silently move the end of the selection back to | 858 // edit. If they try, we just silently move the end of the selection back to |
| 859 // the end of the real text. | 859 // the end of the real text. |
| 860 CHARRANGE new_sel; | 860 CHARRANGE new_sel; |
| 861 GetSel(new_sel); | 861 GetSel(new_sel); |
| 862 const int length = GetTextLength(); | 862 const int length = GetTextLength(); |
| 863 if (new_sel.cpMax > length) { | 863 if (new_sel.cpMax > length) { |
| 864 new_sel.cpMax = length; | 864 new_sel.cpMax = length; |
| 865 if (new_sel.cpMin > length) | 865 if (new_sel.cpMin > length) |
| 866 new_sel.cpMin = length; | 866 new_sel.cpMin = length; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 879 ime_composition_length_ = 0; | 879 ime_composition_length_ = 0; |
| 880 if (new_text.empty()) | 880 if (new_text.empty()) |
| 881 return; | 881 return; |
| 882 } | 882 } |
| 883 parent_->SyncText(); | 883 parent_->SyncText(); |
| 884 if (parent_->GetController()) | 884 if (parent_->GetController()) |
| 885 parent_->GetController()->ContentsChanged(parent_, new_text); | 885 parent_->GetController()->ContentsChanged(parent_, new_text); |
| 886 } | 886 } |
| 887 } | 887 } |
| 888 | 888 |
| 889 LONG TextField::Edit::ClipXCoordToVisibleText(LONG x, | 889 LONG Textfield::Edit::ClipXCoordToVisibleText(LONG x, |
| 890 bool is_triple_click) const { | 890 bool is_triple_click) const { |
| 891 // Clip the X coordinate to the left edge of the text. Careful: | 891 // Clip the X coordinate to the left edge of the text. Careful: |
| 892 // PosFromChar(0) may return a negative X coordinate if the beginning of the | 892 // PosFromChar(0) may return a negative X coordinate if the beginning of the |
| 893 // text has scrolled off the edit, so don't go past the clip rect's edge. | 893 // text has scrolled off the edit, so don't go past the clip rect's edge. |
| 894 PARAFORMAT2 pf2; | 894 PARAFORMAT2 pf2; |
| 895 GetParaFormat(pf2); | 895 GetParaFormat(pf2); |
| 896 // Calculation of the clipped coordinate is more complicated if the paragraph | 896 // Calculation of the clipped coordinate is more complicated if the paragraph |
| 897 // layout is RTL layout, or if there is RTL characters inside the LTR layout | 897 // layout is RTL layout, or if there is RTL characters inside the LTR layout |
| 898 // paragraph. | 898 // paragraph. |
| 899 bool ltr_text_in_ltr_layout = true; | 899 bool ltr_text_in_ltr_layout = true; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 935 // the end of the text, but triple-click will still work. | 935 // the end of the text, but triple-click will still work. |
| 936 if (x < left_bound) { | 936 if (x < left_bound) { |
| 937 return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 : | 937 return (is_triple_click && ltr_text_in_ltr_layout) ? left_bound - 1 : |
| 938 left_bound; | 938 left_bound; |
| 939 } | 939 } |
| 940 if ((length == 0) || (x < right_bound)) | 940 if ((length == 0) || (x < right_bound)) |
| 941 return x; | 941 return x; |
| 942 return is_triple_click ? (right_bound - 1) : right_bound; | 942 return is_triple_click ? (right_bound - 1) : right_bound; |
| 943 } | 943 } |
| 944 | 944 |
| 945 void TextField::Edit::SetContainsMouse(bool contains_mouse) { | 945 void Textfield::Edit::SetContainsMouse(bool contains_mouse) { |
| 946 if (contains_mouse == contains_mouse_) | 946 if (contains_mouse == contains_mouse_) |
| 947 return; | 947 return; |
| 948 | 948 |
| 949 contains_mouse_ = contains_mouse; | 949 contains_mouse_ = contains_mouse; |
| 950 | 950 |
| 951 if (!draw_border_) | 951 if (!draw_border_) |
| 952 return; | 952 return; |
| 953 | 953 |
| 954 if (contains_mouse_) { | 954 if (contains_mouse_) { |
| 955 // Register for notification when the mouse leaves. Need to do this so | 955 // Register for notification when the mouse leaves. Need to do this so |
| 956 // that we can reset contains mouse properly. | 956 // that we can reset contains mouse properly. |
| 957 TRACKMOUSEEVENT tme; | 957 TRACKMOUSEEVENT tme; |
| 958 tme.cbSize = sizeof(tme); | 958 tme.cbSize = sizeof(tme); |
| 959 tme.dwFlags = TME_LEAVE; | 959 tme.dwFlags = TME_LEAVE; |
| 960 tme.hwndTrack = m_hWnd; | 960 tme.hwndTrack = m_hWnd; |
| 961 tme.dwHoverTime = 0; | 961 tme.dwHoverTime = 0; |
| 962 TrackMouseEvent(&tme); | 962 TrackMouseEvent(&tme); |
| 963 } | 963 } |
| 964 RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME); | 964 RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_FRAME); |
| 965 } | 965 } |
| 966 | 966 |
| 967 ITextDocument* TextField::Edit::GetTextObjectModel() const { | 967 ITextDocument* Textfield::Edit::GetTextObjectModel() const { |
| 968 if (!text_object_model_) { | 968 if (!text_object_model_) { |
| 969 CComPtr<IRichEditOle> ole_interface; | 969 CComPtr<IRichEditOle> ole_interface; |
| 970 ole_interface.Attach(GetOleInterface()); | 970 ole_interface.Attach(GetOleInterface()); |
| 971 text_object_model_ = ole_interface; | 971 text_object_model_ = ole_interface; |
| 972 } | 972 } |
| 973 return text_object_model_; | 973 return text_object_model_; |
| 974 } | 974 } |
| 975 | 975 |
| 976 ///////////////////////////////////////////////////////////////////////////// | 976 ///////////////////////////////////////////////////////////////////////////// |
| 977 // TextField | 977 // Textfield |
| 978 | 978 |
| 979 TextField::~TextField() { | 979 Textfield::~Textfield() { |
| 980 if (edit_) { | 980 if (edit_) { |
| 981 // If the edit hwnd still exists, we need to destroy it explicitly. | 981 // If the edit hwnd still exists, we need to destroy it explicitly. |
| 982 if (*edit_) | 982 if (*edit_) |
| 983 edit_->DestroyWindow(); | 983 edit_->DestroyWindow(); |
| 984 delete edit_; | 984 delete edit_; |
| 985 } | 985 } |
| 986 } | 986 } |
| 987 | 987 |
| 988 void TextField::ViewHierarchyChanged(bool is_add, View* parent, View* child) { | 988 void Textfield::ViewHierarchyChanged(bool is_add, View* parent, View* child) { |
| 989 Widget* widget; | 989 Widget* widget; |
| 990 | 990 |
| 991 if (is_add && (widget = GetWidget())) { | 991 if (is_add && (widget = GetWidget())) { |
| 992 // This notification is called from the AddChildView call below. Ignore it. | 992 // This notification is called from the AddChildView call below. Ignore it. |
| 993 if (native_view_ && !edit_) | 993 if (native_view_ && !edit_) |
| 994 return; | 994 return; |
| 995 | 995 |
| 996 if (!native_view_) { | 996 if (!native_view_) { |
| 997 native_view_ = new HWNDView(); // Deleted from our superclass destructor | 997 native_view_ = new HWNDView(); // Deleted from our superclass destructor |
| 998 AddChildView(native_view_); | 998 AddChildView(native_view_); |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1015 if (!text_.empty()) | 1015 if (!text_.empty()) |
| 1016 edit_->SetText(text_); | 1016 edit_->SetText(text_); |
| 1017 UpdateEditBackgroundColor(); | 1017 UpdateEditBackgroundColor(); |
| 1018 Layout(); | 1018 Layout(); |
| 1019 } | 1019 } |
| 1020 } else if (!is_add && edit_ && IsWindow(edit_->m_hWnd)) { | 1020 } else if (!is_add && edit_ && IsWindow(edit_->m_hWnd)) { |
| 1021 edit_->SetParent(NULL); | 1021 edit_->SetParent(NULL); |
| 1022 } | 1022 } |
| 1023 } | 1023 } |
| 1024 | 1024 |
| 1025 void TextField::Layout() { | 1025 void Textfield::Layout() { |
| 1026 if (native_view_) { | 1026 if (native_view_) { |
| 1027 native_view_->SetBounds(GetLocalBounds(true)); | 1027 native_view_->SetBounds(GetLocalBounds(true)); |
| 1028 native_view_->Layout(); | 1028 native_view_->Layout(); |
| 1029 } | 1029 } |
| 1030 } | 1030 } |
| 1031 | 1031 |
| 1032 gfx::Size TextField::GetPreferredSize() { | 1032 gfx::Size Textfield::GetPreferredSize() { |
| 1033 gfx::Insets insets; | 1033 gfx::Insets insets; |
| 1034 CalculateInsets(&insets); | 1034 CalculateInsets(&insets); |
| 1035 return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) + | 1035 return gfx::Size(font_.GetExpectedTextWidth(default_width_in_chars_) + |
| 1036 insets.width(), | 1036 insets.width(), |
| 1037 num_lines_ * font_.height() + insets.height()); | 1037 num_lines_ * font_.height() + insets.height()); |
| 1038 } | 1038 } |
| 1039 | 1039 |
| 1040 std::wstring TextField::GetText() const { | 1040 std::wstring Textfield::GetText() const { |
| 1041 return text_; | 1041 return text_; |
| 1042 } | 1042 } |
| 1043 | 1043 |
| 1044 void TextField::SetText(const std::wstring& text) { | 1044 void Textfield::SetText(const std::wstring& text) { |
| 1045 text_ = text; | 1045 text_ = text; |
| 1046 if (edit_) | 1046 if (edit_) |
| 1047 edit_->SetText(text); | 1047 edit_->SetText(text); |
| 1048 } | 1048 } |
| 1049 | 1049 |
| 1050 void TextField::AppendText(const std::wstring& text) { | 1050 void Textfield::AppendText(const std::wstring& text) { |
| 1051 text_ += text; | 1051 text_ += text; |
| 1052 if (edit_) | 1052 if (edit_) |
| 1053 edit_->AppendText(text); | 1053 edit_->AppendText(text); |
| 1054 } | 1054 } |
| 1055 | 1055 |
| 1056 void TextField::CalculateInsets(gfx::Insets* insets) { | 1056 void Textfield::CalculateInsets(gfx::Insets* insets) { |
| 1057 DCHECK(insets); | 1057 DCHECK(insets); |
| 1058 | 1058 |
| 1059 if (!draw_border_) | 1059 if (!draw_border_) |
| 1060 return; | 1060 return; |
| 1061 | 1061 |
| 1062 // NOTE: One would think GetThemeMargins would return the insets we should | 1062 // NOTE: One would think GetThemeMargins would return the insets we should |
| 1063 // use, but it doesn't. The margins returned by GetThemeMargins are always | 1063 // use, but it doesn't. The margins returned by GetThemeMargins are always |
| 1064 // 0. | 1064 // 0. |
| 1065 | 1065 |
| 1066 // This appears to be the insets used by Windows. | 1066 // This appears to be the insets used by Windows. |
| 1067 insets->Set(3, 3, 3, 3); | 1067 insets->Set(3, 3, 3, 3); |
| 1068 } | 1068 } |
| 1069 | 1069 |
| 1070 void TextField::SyncText() { | 1070 void Textfield::SyncText() { |
| 1071 if (edit_) | 1071 if (edit_) |
| 1072 text_ = edit_->GetText(); | 1072 text_ = edit_->GetText(); |
| 1073 } | 1073 } |
| 1074 | 1074 |
| 1075 void TextField::SetController(Controller* controller) { | 1075 void Textfield::SetController(Controller* controller) { |
| 1076 controller_ = controller; | 1076 controller_ = controller; |
| 1077 } | 1077 } |
| 1078 | 1078 |
| 1079 TextField::Controller* TextField::GetController() const { | 1079 Textfield::Controller* Textfield::GetController() const { |
| 1080 return controller_; | 1080 return controller_; |
| 1081 } | 1081 } |
| 1082 | 1082 |
| 1083 bool TextField::IsReadOnly() const { | 1083 bool Textfield::IsReadOnly() const { |
| 1084 return edit_ ? ((edit_->GetStyle() & ES_READONLY) != 0) : read_only_; | 1084 return edit_ ? ((edit_->GetStyle() & ES_READONLY) != 0) : read_only_; |
| 1085 } | 1085 } |
| 1086 | 1086 |
| 1087 bool TextField::IsPassword() const { | 1087 bool Textfield::IsPassword() const { |
| 1088 return GetStyle() & TextField::STYLE_PASSWORD; | 1088 return GetStyle() & Textfield::STYLE_PASSWORD; |
| 1089 } | 1089 } |
| 1090 | 1090 |
| 1091 bool TextField::IsMultiLine() const { | 1091 bool Textfield::IsMultiLine() const { |
| 1092 return (style_ & STYLE_MULTILINE) != 0; | 1092 return (style_ & STYLE_MULTILINE) != 0; |
| 1093 } | 1093 } |
| 1094 | 1094 |
| 1095 void TextField::SetReadOnly(bool read_only) { | 1095 void Textfield::SetReadOnly(bool read_only) { |
| 1096 read_only_ = read_only; | 1096 read_only_ = read_only; |
| 1097 if (edit_) { | 1097 if (edit_) { |
| 1098 edit_->SetReadOnly(read_only); | 1098 edit_->SetReadOnly(read_only); |
| 1099 UpdateEditBackgroundColor(); | 1099 UpdateEditBackgroundColor(); |
| 1100 } | 1100 } |
| 1101 } | 1101 } |
| 1102 | 1102 |
| 1103 void TextField::Focus() { | 1103 void Textfield::Focus() { |
| 1104 ::SetFocus(native_view_->GetHWND()); | 1104 ::SetFocus(native_view_->GetHWND()); |
| 1105 } | 1105 } |
| 1106 | 1106 |
| 1107 void TextField::SelectAll() { | 1107 void Textfield::SelectAll() { |
| 1108 if (edit_) | 1108 if (edit_) |
| 1109 edit_->SelectAll(); | 1109 edit_->SelectAll(); |
| 1110 } | 1110 } |
| 1111 | 1111 |
| 1112 void TextField::ClearSelection() const { | 1112 void Textfield::ClearSelection() const { |
| 1113 if (edit_) | 1113 if (edit_) |
| 1114 edit_->ClearSelection(); | 1114 edit_->ClearSelection(); |
| 1115 } | 1115 } |
| 1116 | 1116 |
| 1117 HWND TextField::GetNativeComponent() { | 1117 HWND Textfield::GetNativeComponent() { |
| 1118 return native_view_->GetHWND(); | 1118 return native_view_->GetHWND(); |
| 1119 } | 1119 } |
| 1120 | 1120 |
| 1121 void TextField::SetBackgroundColor(SkColor color) { | 1121 void Textfield::SetBackgroundColor(SkColor color) { |
| 1122 background_color_ = color; | 1122 background_color_ = color; |
| 1123 use_default_background_color_ = false; | 1123 use_default_background_color_ = false; |
| 1124 UpdateEditBackgroundColor(); | 1124 UpdateEditBackgroundColor(); |
| 1125 } | 1125 } |
| 1126 | 1126 |
| 1127 void TextField::SetDefaultBackgroundColor() { | 1127 void Textfield::SetDefaultBackgroundColor() { |
| 1128 use_default_background_color_ = true; | 1128 use_default_background_color_ = true; |
| 1129 UpdateEditBackgroundColor(); | 1129 UpdateEditBackgroundColor(); |
| 1130 } | 1130 } |
| 1131 | 1131 |
| 1132 void TextField::SetFont(const gfx::Font& font) { | 1132 void Textfield::SetFont(const gfx::Font& font) { |
| 1133 font_ = font; | 1133 font_ = font; |
| 1134 if (edit_) | 1134 if (edit_) |
| 1135 edit_->SetFont(font.hfont()); | 1135 edit_->SetFont(font.hfont()); |
| 1136 } | 1136 } |
| 1137 | 1137 |
| 1138 gfx::Font TextField::GetFont() const { | 1138 gfx::Font Textfield::GetFont() const { |
| 1139 return font_; | 1139 return font_; |
| 1140 } | 1140 } |
| 1141 | 1141 |
| 1142 bool TextField::SetHorizontalMargins(int left, int right) { | 1142 bool Textfield::SetHorizontalMargins(int left, int right) { |
| 1143 // SendMessage expects the two values to be packed into one using MAKELONG | 1143 // SendMessage expects the two values to be packed into one using MAKELONG |
| 1144 // so we truncate to 16 bits if necessary. | 1144 // so we truncate to 16 bits if necessary. |
| 1145 return ERROR_SUCCESS == SendMessage(GetNativeComponent(), | 1145 return ERROR_SUCCESS == SendMessage(GetNativeComponent(), |
| 1146 (UINT) EM_SETMARGINS, | 1146 (UINT) EM_SETMARGINS, |
| 1147 (WPARAM) EC_LEFTMARGIN | EC_RIGHTMARGIN, | 1147 (WPARAM) EC_LEFTMARGIN | EC_RIGHTMARGIN, |
| 1148 (LPARAM) MAKELONG(left & 0xFFFF, | 1148 (LPARAM) MAKELONG(left & 0xFFFF, |
| 1149 right & 0xFFFF)); | 1149 right & 0xFFFF)); |
| 1150 } | 1150 } |
| 1151 | 1151 |
| 1152 void TextField::SetHeightInLines(int num_lines) { | 1152 void Textfield::SetHeightInLines(int num_lines) { |
| 1153 DCHECK(IsMultiLine()); | 1153 DCHECK(IsMultiLine()); |
| 1154 num_lines_ = num_lines; | 1154 num_lines_ = num_lines; |
| 1155 } | 1155 } |
| 1156 | 1156 |
| 1157 void TextField::RemoveBorder() { | 1157 void Textfield::RemoveBorder() { |
| 1158 if (!draw_border_) | 1158 if (!draw_border_) |
| 1159 return; | 1159 return; |
| 1160 | 1160 |
| 1161 draw_border_ = false; | 1161 draw_border_ = false; |
| 1162 if (edit_) | 1162 if (edit_) |
| 1163 edit_->RemoveBorder(); | 1163 edit_->RemoveBorder(); |
| 1164 } | 1164 } |
| 1165 | 1165 |
| 1166 void TextField::SetEnabled(bool enabled) { | 1166 void Textfield::SetEnabled(bool enabled) { |
| 1167 View::SetEnabled(enabled); | 1167 View::SetEnabled(enabled); |
| 1168 edit_->SetEnabled(enabled); | 1168 edit_->SetEnabled(enabled); |
| 1169 } | 1169 } |
| 1170 | 1170 |
| 1171 bool TextField::IsFocusable() const { | 1171 bool Textfield::IsFocusable() const { |
| 1172 return IsEnabled() && !IsReadOnly(); | 1172 return IsEnabled() && !IsReadOnly(); |
| 1173 } | 1173 } |
| 1174 | 1174 |
| 1175 void TextField::AboutToRequestFocusFromTabTraversal(bool reverse) { | 1175 void Textfield::AboutToRequestFocusFromTabTraversal(bool reverse) { |
| 1176 SelectAll(); | 1176 SelectAll(); |
| 1177 } | 1177 } |
| 1178 | 1178 |
| 1179 bool TextField::SkipDefaultKeyEventProcessing(const KeyEvent& e) { | 1179 bool Textfield::SkipDefaultKeyEventProcessing(const KeyEvent& e) { |
| 1180 // TODO(hamaji): Figure out which keyboard combinations we need to add here, | 1180 // TODO(hamaji): Figure out which keyboard combinations we need to add here, |
| 1181 // similar to LocationBarView::SkipDefaultKeyEventProcessing. | 1181 // similar to LocationBarView::SkipDefaultKeyEventProcessing. |
| 1182 if (e.GetCharacter() == VK_BACK) | 1182 if (e.GetCharacter() == VK_BACK) |
| 1183 return true; // We'll handle BackSpace ourselves. | 1183 return true; // We'll handle BackSpace ourselves. |
| 1184 | 1184 |
| 1185 // We don't translate accelerators for ALT + NumPad digit, they are used for | 1185 // We don't translate accelerators for ALT + NumPad digit, they are used for |
| 1186 // entering special characters. | 1186 // entering special characters. |
| 1187 if (e.IsAltDown() && | 1187 if (e.IsAltDown() && |
| 1188 win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey())) | 1188 win_util::IsNumPadDigit(e.GetCharacter(), e.IsExtendedKey())) |
| 1189 return true; | 1189 return true; |
| 1190 | 1190 |
| 1191 return false; | 1191 return false; |
| 1192 } | 1192 } |
| 1193 | 1193 |
| 1194 void TextField::UpdateEditBackgroundColor() { | 1194 void Textfield::UpdateEditBackgroundColor() { |
| 1195 if (!edit_) | 1195 if (!edit_) |
| 1196 return; | 1196 return; |
| 1197 | 1197 |
| 1198 COLORREF bg_color; | 1198 COLORREF bg_color; |
| 1199 if (!use_default_background_color_) | 1199 if (!use_default_background_color_) |
| 1200 bg_color = skia::SkColorToCOLORREF(background_color_); | 1200 bg_color = skia::SkColorToCOLORREF(background_color_); |
| 1201 else | 1201 else |
| 1202 bg_color = GetSysColor(read_only_ ? COLOR_3DFACE : COLOR_WINDOW); | 1202 bg_color = GetSysColor(read_only_ ? COLOR_3DFACE : COLOR_WINDOW); |
| 1203 edit_->SetBackgroundColor(bg_color); | 1203 edit_->SetBackgroundColor(bg_color); |
| 1204 } | 1204 } |
| 1205 | 1205 |
| 1206 } // namespace views | 1206 } // namespace views |
| OLD | NEW |