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

Side by Side Diff: ui/views/controls/textfield/textfield.h

Issue 2408623002: Views: Extract text selection code from Textfield. (Closed)
Patch Set: Rename [Starting/Ending]MouseAction. Created 4 years, 2 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 #ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_ 5 #ifndef UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_
6 #define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_ 6 #define UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_
7 7
8 #include <stddef.h> 8 #include <stddef.h>
9 #include <stdint.h> 9 #include <stdint.h>
10 10
(...skipping 11 matching lines...) Expand all
22 #include "ui/base/models/simple_menu_model.h" 22 #include "ui/base/models/simple_menu_model.h"
23 #include "ui/base/touch/touch_editing_controller.h" 23 #include "ui/base/touch/touch_editing_controller.h"
24 #include "ui/events/keycodes/keyboard_codes.h" 24 #include "ui/events/keycodes/keyboard_codes.h"
25 #include "ui/gfx/font_list.h" 25 #include "ui/gfx/font_list.h"
26 #include "ui/gfx/range/range.h" 26 #include "ui/gfx/range/range.h"
27 #include "ui/gfx/selection_model.h" 27 #include "ui/gfx/selection_model.h"
28 #include "ui/gfx/text_constants.h" 28 #include "ui/gfx/text_constants.h"
29 #include "ui/views/context_menu_controller.h" 29 #include "ui/views/context_menu_controller.h"
30 #include "ui/views/controls/textfield/textfield_model.h" 30 #include "ui/views/controls/textfield/textfield_model.h"
31 #include "ui/views/drag_controller.h" 31 #include "ui/views/drag_controller.h"
32 #include "ui/views/selection_controller_host.h"
32 #include "ui/views/view.h" 33 #include "ui/views/view.h"
33 34
34 namespace views { 35 namespace views {
35 36
36 class MenuRunner; 37 class MenuRunner;
37 class Painter; 38 class Painter;
38 class TextfieldController; 39 class TextfieldController;
40 class SelectionController;
39 41
40 // A views/skia textfield implementation. No platform-specific code is used. 42 // A views/skia textfield implementation. No platform-specific code is used.
41 class VIEWS_EXPORT Textfield : public View, 43 class VIEWS_EXPORT Textfield : public View,
42 public TextfieldModel::Delegate, 44 public TextfieldModel::Delegate,
43 public ContextMenuController, 45 public ContextMenuController,
44 public DragController, 46 public DragController,
47 public SelectionControllerHost,
45 public ui::TouchEditable, 48 public ui::TouchEditable,
46 public ui::TextInputClient { 49 public ui::TextInputClient {
47 public: 50 public:
48 // The textfield's class name. 51 // The textfield's class name.
49 static const char kViewClassName[]; 52 static const char kViewClassName[];
50 53
51 // The preferred size of the padding to be used around textfield text. 54 // The preferred size of the padding to be used around textfield text.
52 static const int kTextPadding; 55 static const int kTextPadding;
53 56
54 // Returns the text cursor blink time in milliseconds, or 0 for no blinking. 57 // Returns the text cursor blink time in milliseconds, or 0 for no blinking.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
86 // Appends the given string to the previously-existing text in the field. 89 // Appends the given string to the previously-existing text in the field.
87 void AppendText(const base::string16& new_text); 90 void AppendText(const base::string16& new_text);
88 91
89 // Inserts |new_text| at the cursor position, replacing any selected text. 92 // Inserts |new_text| at the cursor position, replacing any selected text.
90 void InsertOrReplaceText(const base::string16& new_text); 93 void InsertOrReplaceText(const base::string16& new_text);
91 94
92 // Returns the text that is currently selected. Call sites should take care to 95 // Returns the text that is currently selected. Call sites should take care to
93 // not reveal the text for a password textfield. 96 // not reveal the text for a password textfield.
94 base::string16 GetSelectedText() const; 97 base::string16 GetSelectedText() const;
95 98
96 // Select the entire text range. If |reversed| is true, the range will end at
97 // the logical beginning of the text; this generally shows the leading portion
98 // of text that overflows its display area.
99 void SelectAll(bool reversed);
100
101 // A convenience method to select the word closest to |point|.
102 void SelectWordAt(const gfx::Point& point);
103
104 // Clears the selection within the edit field and sets the caret to the end.
105 void ClearSelection();
106
107 // Checks if there is any selected text. 99 // Checks if there is any selected text.
108 bool HasSelection() const; 100 bool HasSelection() const;
109 101
110 // Gets/sets the text color to be used when painting the Textfield. 102 // Gets/sets the text color to be used when painting the Textfield.
111 // Call UseDefaultTextColor() to restore the default system color. 103 // Call UseDefaultTextColor() to restore the default system color.
112 SkColor GetTextColor() const; 104 SkColor GetTextColor() const;
113 void SetTextColor(SkColor color); 105 void SetTextColor(SkColor color);
114 void UseDefaultTextColor(); 106 void UseDefaultTextColor();
115 107
116 // Gets/sets the background color to be used when painting the Textfield. 108 // Gets/sets the background color to be used when painting the Textfield.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 157
166 // Displays a virtual keyboard or alternate input view if enabled. 158 // Displays a virtual keyboard or alternate input view if enabled.
167 void ShowImeIfNeeded(); 159 void ShowImeIfNeeded();
168 160
169 // Returns whether or not an IME is composing text. 161 // Returns whether or not an IME is composing text.
170 bool IsIMEComposing() const; 162 bool IsIMEComposing() const;
171 163
172 // Gets the selected logical text range. 164 // Gets the selected logical text range.
173 const gfx::Range& GetSelectedRange() const; 165 const gfx::Range& GetSelectedRange() const;
174 166
175 // Selects the specified logical text range.
176 void SelectRange(const gfx::Range& range);
177
178 // Gets the text selection model. 167 // Gets the text selection model.
179 const gfx::SelectionModel& GetSelectionModel() const; 168 const gfx::SelectionModel& GetSelectionModel() const;
180 169
181 // Sets the specified text selection model. 170 // Sets the specified text selection model.
182 void SelectSelectionModel(const gfx::SelectionModel& sel); 171 void SelectSelectionModel(const gfx::SelectionModel& sel);
183 172
184 // Returns the current cursor position. 173 // Returns the current cursor position.
185 size_t GetCursorPosition() const; 174 size_t GetCursorPosition() const;
186 175
187 // Set the text color over the entire text or a logical character range. 176 // Set the text color over the entire text or a logical character range.
188 // Empty and invalid ranges are ignored. 177 // Empty and invalid ranges are ignored.
189 void SetColor(SkColor value); 178 void SetColor(SkColor value);
190 void ApplyColor(SkColor value, const gfx::Range& range); 179 void ApplyColor(SkColor value, const gfx::Range& range);
191 180
192 // Set various text styles over the entire text or a logical character range. 181 // Set various text styles over the entire text or a logical character range.
193 // The respective |style| is applied if |value| is true, or removed if false. 182 // The respective |style| is applied if |value| is true, or removed if false.
194 // Empty and invalid ranges are ignored. 183 // Empty and invalid ranges are ignored.
195 void SetStyle(gfx::TextStyle style, bool value); 184 void SetStyle(gfx::TextStyle style, bool value);
196 void ApplyStyle(gfx::TextStyle style, bool value, const gfx::Range& range); 185 void ApplyStyle(gfx::TextStyle style, bool value, const gfx::Range& range);
197 186
198 // Clears Edit history. 187 // Clears Edit history.
199 void ClearEditHistory(); 188 void ClearEditHistory();
200 189
201 // Set the accessible name of the text field. 190 // Set the accessible name of the text field.
202 void SetAccessibleName(const base::string16& name); 191 void SetAccessibleName(const base::string16& name);
203 192
204 // Returns whether there is a drag operation originating from the textfield.
205 bool HasTextBeingDragged();
206
207 // View overrides: 193 // View overrides:
208 gfx::Insets GetInsets() const override; 194 gfx::Insets GetInsets() const override;
209 int GetBaseline() const override; 195 int GetBaseline() const override;
210 gfx::Size GetPreferredSize() const override; 196 gfx::Size GetPreferredSize() const override;
211 const char* GetClassName() const override; 197 const char* GetClassName() const override;
212 gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; 198 gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override;
213 bool OnMousePressed(const ui::MouseEvent& event) override; 199 bool OnMousePressed(const ui::MouseEvent& event) override;
214 bool OnMouseDragged(const ui::MouseEvent& event) override; 200 bool OnMouseDragged(const ui::MouseEvent& event) override;
215 void OnMouseReleased(const ui::MouseEvent& event) override; 201 void OnMouseReleased(const ui::MouseEvent& event) override;
216 void OnGestureEvent(ui::GestureEvent* event) override; 202 void OnGestureEvent(ui::GestureEvent* event) override;
(...skipping 30 matching lines...) Expand all
247 233
248 // DragController overrides: 234 // DragController overrides:
249 void WriteDragDataForView(View* sender, 235 void WriteDragDataForView(View* sender,
250 const gfx::Point& press_pt, 236 const gfx::Point& press_pt,
251 ui::OSExchangeData* data) override; 237 ui::OSExchangeData* data) override;
252 int GetDragOperationsForView(View* sender, const gfx::Point& p) override; 238 int GetDragOperationsForView(View* sender, const gfx::Point& p) override;
253 bool CanStartDragForView(View* sender, 239 bool CanStartDragForView(View* sender,
254 const gfx::Point& press_pt, 240 const gfx::Point& press_pt,
255 const gfx::Point& p) override; 241 const gfx::Point& p) override;
256 242
243 // SelectionControllerHost overrides:
244 bool HasTextBeingDragged() const override;
245 void SelectAll(bool reversed) override;
karandeepb 2016/10/11 03:43:56 I think it's a bit problematic that these overridd
tapted 2016/10/11 04:42:09 I think it's reasonable for classes outside to be
karandeepb 2016/10/11 05:01:30 Yeah we definitely need these on the public interf
246 void SelectRange(const gfx::Range& range) override;
247 void ClearSelection() override;
248
257 // ui::TouchEditable overrides: 249 // ui::TouchEditable overrides:
258 void SelectRect(const gfx::Point& start, const gfx::Point& end) override; 250 void SelectRect(const gfx::Point& start, const gfx::Point& end) override;
259 void MoveCaretTo(const gfx::Point& point) override; 251 void MoveCaretTo(const gfx::Point& point) override;
260 void GetSelectionEndPoints(gfx::SelectionBound* anchor, 252 void GetSelectionEndPoints(gfx::SelectionBound* anchor,
261 gfx::SelectionBound* focus) override; 253 gfx::SelectionBound* focus) override;
262 gfx::Rect GetBounds() override; 254 gfx::Rect GetBounds() override;
263 gfx::NativeView GetNativeView() const override; 255 gfx::NativeView GetNativeView() const override;
264 void ConvertPointToScreen(gfx::Point* point) override; 256 void ConvertPointToScreen(gfx::Point* point) override;
265 void ConvertPointFromScreen(gfx::Point* point) override; 257 void ConvertPointFromScreen(gfx::Point* point) override;
266 bool DrawsHandles() override; 258 bool DrawsHandles() override;
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
304 bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override; 296 bool IsTextEditCommandEnabled(ui::TextEditCommand command) const override;
305 void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override; 297 void SetTextEditCommandForNextKeyEvent(ui::TextEditCommand command) override;
306 298
307 protected: 299 protected:
308 // Inserts or appends a character in response to an IME operation. 300 // Inserts or appends a character in response to an IME operation.
309 virtual void DoInsertChar(base::char16 ch); 301 virtual void DoInsertChar(base::char16 ch);
310 302
311 // Returns the TextfieldModel's text/cursor/selection rendering model. 303 // Returns the TextfieldModel's text/cursor/selection rendering model.
312 gfx::RenderText* GetRenderText() const; 304 gfx::RenderText* GetRenderText() const;
313 305
314 gfx::Point last_click_location() const { return last_click_location_; } 306 gfx::Point last_click_location() const;
315 307
316 // Get the text from the selection clipboard. 308 // Get the text from the selection clipboard.
317 virtual base::string16 GetSelectionClipboardText() const; 309 virtual base::string16 GetSelectionClipboardText() const;
318 310
319 // Executes the given |command|. 311 // Executes the given |command|.
320 virtual void ExecuteTextEditCommand(ui::TextEditCommand command); 312 virtual void ExecuteTextEditCommand(ui::TextEditCommand command);
321 313
322 private: 314 private:
323 friend class TextfieldTestApi; 315 friend class TextfieldTestApi;
324 316
325 // View overrides: 317 // View overrides:
326 // Declared final since overriding by subclasses would interfere with the 318 // Declared final since overriding by subclasses would interfere with the
327 // accounting related to the scheduled text edit command. Subclasses should 319 // accounting related to the scheduled text edit command. Subclasses should
328 // use TextfieldController::HandleKeyEvent, to intercept the key event. 320 // use TextfieldController::HandleKeyEvent, to intercept the key event.
329 bool OnKeyPressed(const ui::KeyEvent& event) final; 321 bool OnKeyPressed(const ui::KeyEvent& event) final;
330 bool OnKeyReleased(const ui::KeyEvent& event) final; 322 bool OnKeyReleased(const ui::KeyEvent& event) final;
331 323
324 // SelectionControllerHost overrides:
325 gfx::RenderText* GetRenderTextForSelection() override;
326 bool IsReadOnly() const override;
327 void SetTextBeingDragged(bool value) override;
328 int GetViewHeight() const override;
329 int GetViewWidth() const override;
330 int GetDragSelectionDelay() const override;
331 void OnBeforeMouseAction() override;
332 void OnAfterMouseAction(bool text_changed, bool selection_changed) override;
333 void SelectWordAt(const gfx::Point& point) override;
334 void SelectWord() override;
335 void MoveCursorTo(const gfx::Point& point, bool select) override;
336 void SelectTillEdge(gfx::VisualCursorDirection direction) override;
337 void PasteSelectionClipboard() override;
338 void UpdateSelectionClipboard() override;
339
332 // Handles a request to change the value of this text field from software 340 // Handles a request to change the value of this text field from software
333 // using an accessibility API (typically automation software, screen readers 341 // using an accessibility API (typically automation software, screen readers
334 // don't normally use this). Sets the value and clears the selection. 342 // don't normally use this). Sets the value and clears the selection.
335 void AccessibilitySetValue(const base::string16& new_value); 343 void AccessibilitySetValue(const base::string16& new_value);
336 344
337 // Updates the painted background color. 345 // Updates the painted background color.
338 void UpdateBackgroundColor(); 346 void UpdateBackgroundColor();
339 347
340 // Does necessary updates when the text and/or cursor position changes. 348 // Does necessary updates when the text and/or cursor position changes. NO-OP
349 // if |performing_mouse_action_| is true to ensure updates are only made when
350 // the mouse action completes i.e. on the call to OnAfterMouseAction.
341 void UpdateAfterChange(bool text_changed, bool cursor_changed); 351 void UpdateAfterChange(bool text_changed, bool cursor_changed);
342 352
343 // A callback function to periodically update the cursor state. 353 // A callback function to periodically update the cursor state.
344 void UpdateCursor(); 354 void UpdateCursor();
345 355
346 // Repaint the cursor. 356 // Repaint the cursor.
347 void RepaintCursor(); 357 void RepaintCursor();
348 358
349 void PaintTextAndCursor(gfx::Canvas* canvas); 359 void PaintTextAndCursor(gfx::Canvas* canvas);
350 360
351 // Helper function to call MoveCursorTo on the TextfieldModel.
352 void MoveCursorTo(const gfx::Point& point, bool select);
353
354 // Helper function to update the selection on a mouse drag.
355 void SelectThroughLastDragLocation();
356
357 // Convenience method to notify the InputMethod and TouchSelectionController. 361 // Convenience method to notify the InputMethod and TouchSelectionController.
358 void OnCaretBoundsChanged(); 362 void OnCaretBoundsChanged();
359 363
360 // Convenience method to call TextfieldController::OnBeforeUserAction(); 364 // Convenience method to call TextfieldController::OnBeforeUserAction();
361 void OnBeforeUserAction(); 365 void OnBeforeUserAction();
362 366
363 // Convenience method to call TextfieldController::OnAfterUserAction(); 367 // Convenience method to call TextfieldController::OnAfterUserAction();
364 void OnAfterUserAction(); 368 void OnAfterUserAction();
365 369
366 // Calls |model_->Cut()| and notifies TextfieldController on success. 370 // Calls |model_->Cut()| and notifies TextfieldController on success.
367 bool Cut(); 371 bool Cut();
368 372
369 // Calls |model_->Copy()| and notifies TextfieldController on success. 373 // Calls |model_->Copy()| and notifies TextfieldController on success.
370 bool Copy(); 374 bool Copy();
371 375
372 // Calls |model_->Paste()| and calls TextfieldController::ContentsChanged() 376 // Calls |model_->Paste()| and calls TextfieldController::ContentsChanged()
373 // explicitly if paste succeeded. 377 // explicitly if paste succeeded.
374 bool Paste(); 378 bool Paste();
375 379
376 // Utility function to prepare the context menu. 380 // Utility function to prepare the context menu.
377 void UpdateContextMenu(); 381 void UpdateContextMenu();
378 382
379 // Tracks the mouse clicks for single/double/triple clicks.
380 void TrackMouseClicks(const ui::MouseEvent& event);
381
382 // Returns true if the current text input type allows access by the IME. 383 // Returns true if the current text input type allows access by the IME.
383 bool ImeEditingAllowed() const; 384 bool ImeEditingAllowed() const;
384 385
385 // Reveals the password character at |index| for a set duration. 386 // Reveals the password character at |index| for a set duration.
386 // If |index| is -1, the existing revealed character will be reset. 387 // If |index| is -1, the existing revealed character will be reset.
387 void RevealPasswordChar(int index); 388 void RevealPasswordChar(int index);
388 389
389 void CreateTouchSelectionControllerAndNotifyIt(); 390 void CreateTouchSelectionControllerAndNotifyIt();
390 391
391 // Updates the selection clipboard to any non-empty text selection for a non-
392 // password textfield.
393 void UpdateSelectionClipboard() const;
394
395 // Pastes the selection clipboard for the specified mouse event.
396 void PasteSelectionClipboard(const ui::MouseEvent& event);
397
398 // Called whenever a keypress is unhandled for any reason, including failing 392 // Called whenever a keypress is unhandled for any reason, including failing
399 // to insert text into a readonly text field. 393 // to insert text into a readonly text field.
400 void OnKeypressUnhandled(); 394 void OnKeypressUnhandled();
401 395
402 // Returns true if an insertion cursor should be visible (a vertical bar, 396 // Returns true if an insertion cursor should be visible (a vertical bar,
403 // placed at the point new text will be inserted). 397 // placed at the point new text will be inserted).
404 bool ShouldShowCursor() const; 398 bool ShouldShowCursor() const;
405 399
406 // Returns true if an insertion cursor should be visible and blinking. 400 // Returns true if an insertion cursor should be visible and blinking.
407 bool ShouldBlinkCursor() const; 401 bool ShouldBlinkCursor() const;
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
462 // The input flags of this text field. 456 // The input flags of this text field.
463 int text_input_flags_; 457 int text_input_flags_;
464 458
465 // The timer to reveal the last typed password character. 459 // The timer to reveal the last typed password character.
466 base::OneShotTimer password_reveal_timer_; 460 base::OneShotTimer password_reveal_timer_;
467 461
468 // Tracks whether a user action is being performed; i.e. OnBeforeUserAction() 462 // Tracks whether a user action is being performed; i.e. OnBeforeUserAction()
469 // has been called, but OnAfterUserAction() has not yet been called. 463 // has been called, but OnAfterUserAction() has not yet been called.
470 bool performing_user_action_; 464 bool performing_user_action_;
471 465
466 // Tracks whether a mouse action is being performed i.e. OnBeforeMouseAction()
467 // has been called, but OnAfterMouseAction() has not yet been called.
468 bool performing_mouse_action_;
469
472 // True if InputMethod::CancelComposition() should not be called. 470 // True if InputMethod::CancelComposition() should not be called.
473 bool skip_input_method_cancel_composition_; 471 bool skip_input_method_cancel_composition_;
474 472
475 // Insertion cursor repaint timer and visibility. 473 // Insertion cursor repaint timer and visibility.
476 base::RepeatingTimer cursor_blink_timer_; 474 base::RepeatingTimer cursor_blink_timer_;
477 475
478 // The drop cursor is a visual cue for where dragged text will be dropped. 476 // The drop cursor is a visual cue for where dragged text will be dropped.
479 bool drop_cursor_visible_; 477 bool drop_cursor_visible_;
480 gfx::SelectionModel drop_cursor_position_; 478 gfx::SelectionModel drop_cursor_position_;
481 479
482 // Is the user potentially dragging and dropping from this view? 480 // Is the user potentially dragging and dropping from this view?
483 bool initiating_drag_; 481 bool initiating_drag_;
484 482
485 // A timer and point used to modify the selection when dragging.
486 base::RepeatingTimer drag_selection_timer_;
487 gfx::Point last_drag_location_;
488
489 // State variables used to track double and triple clicks.
490 size_t aggregated_clicks_;
491 base::TimeTicks last_click_time_;
492 gfx::Point last_click_location_;
493 gfx::Range double_click_word_;
494
495 std::unique_ptr<ui::TouchEditingControllerDeprecated> 483 std::unique_ptr<ui::TouchEditingControllerDeprecated>
496 touch_selection_controller_; 484 touch_selection_controller_;
497 485
486 std::unique_ptr<SelectionController> selection_controller_;
487
498 // Used to track touch drag starting location and offset to enable touch 488 // Used to track touch drag starting location and offset to enable touch
499 // scrolling. 489 // scrolling.
500 gfx::Point drag_start_location_; 490 gfx::Point drag_start_location_;
501 int drag_start_display_offset_; 491 int drag_start_display_offset_;
502 492
503 // Tracks if touch editing handles are hidden because user has started 493 // Tracks if touch editing handles are hidden because user has started
504 // scrolling. If |true|, handles are shown after scrolling ends. 494 // scrolling. If |true|, handles are shown after scrolling ends.
505 bool touch_handles_hidden_due_to_scroll_; 495 bool touch_handles_hidden_due_to_scroll_;
506 496
507 // Context menu related members. 497 // Context menu related members.
508 std::unique_ptr<ui::SimpleMenuModel> context_menu_contents_; 498 std::unique_ptr<ui::SimpleMenuModel> context_menu_contents_;
509 std::unique_ptr<views::MenuRunner> context_menu_runner_; 499 std::unique_ptr<views::MenuRunner> context_menu_runner_;
510 500
511 // Used to bind callback functions to this object. 501 // Used to bind callback functions to this object.
512 base::WeakPtrFactory<Textfield> weak_ptr_factory_; 502 base::WeakPtrFactory<Textfield> weak_ptr_factory_;
513 503
514 DISALLOW_COPY_AND_ASSIGN(Textfield); 504 DISALLOW_COPY_AND_ASSIGN(Textfield);
515 }; 505 };
516 506
517 } // namespace views 507 } // namespace views
518 508
519 #endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_ 509 #endif // UI_VIEWS_CONTROLS_TEXTFIELD_TEXTFIELD_H_
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698