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

Side by Side Diff: views/widget/native_widget_views.cc

Issue 8598024: Now that we are doing a hard-cut-over to Aura, remove a bunch of *Views based classes that are ob... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « views/widget/native_widget_views.h ('k') | views/widget/native_widget_wayland.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "views/widget/native_widget_views.h"
6
7 #include "base/bind.h"
8 #include "ui/base/hit_test.h"
9 #include "ui/gfx/compositor/compositor.h"
10 #include "ui/gfx/compositor/layer.h"
11 #include "ui/gfx/compositor/layer_animator.h"
12 #include "views/view.h"
13 #include "views/views_delegate.h"
14 #include "views/widget/native_widget_view.h"
15 #include "views/widget/root_view.h"
16 #include "views/widget/tooltip_manager_views.h"
17 #include "views/widget/window_manager.h"
18
19 #if defined(HAVE_IBUS)
20 #include "ui/views/ime/input_method_ibus.h"
21 #else
22 #include "ui/views/ime/mock_input_method.h"
23 #endif
24
25 namespace {
26
27 gfx::Rect AdjustRectOriginForParentWidget(const gfx::Rect& rect,
28 const views::Widget* parent) {
29 if (!parent)
30 return rect;
31
32 gfx::Rect adjusted = rect;
33 gfx::Rect parent_bounds = parent->GetWindowScreenBounds();
34 adjusted.Offset(-parent_bounds.x(), -parent_bounds.y());
35 return adjusted;
36 }
37
38 } // namespace
39
40 namespace views {
41
42 ////////////////////////////////////////////////////////////////////////////////
43 // NativeWidgetViews, public:
44
45 NativeWidgetViews::NativeWidgetViews(internal::NativeWidgetDelegate* delegate)
46 : delegate_(delegate),
47 parent_(NULL),
48 view_(NULL),
49 active_(false),
50 window_state_(ui::SHOW_STATE_DEFAULT),
51 always_on_top_(false),
52 ALLOW_THIS_IN_INITIALIZER_LIST(close_widget_factory_(this)),
53 ownership_(Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET),
54 delete_native_view_(true) {
55 }
56
57 NativeWidgetViews::~NativeWidgetViews() {
58 delegate_->OnNativeWidgetDestroying();
59
60 if (view_ && delete_native_view_) {
61 // We must prevent the NativeWidgetView from attempting to delete us.
62 view_->set_delete_native_widget(false);
63 delete view_;
64 }
65
66 delegate_->OnNativeWidgetDestroyed();
67 if (ownership_ == Widget::InitParams::NATIVE_WIDGET_OWNS_WIDGET)
68 delete delegate_;
69 }
70
71 View* NativeWidgetViews::GetView() {
72 return view_;
73 }
74
75 const View* NativeWidgetViews::GetView() const {
76 return view_;
77 }
78
79 void NativeWidgetViews::OnActivate(bool active) {
80 // TODO(oshima): find out if we should check toplevel here.
81 if (active_ == active)
82 return;
83 active_ = active;
84 delegate_->OnNativeWidgetActivationChanged(active);
85
86 // TODO(oshima): Focus change should be separated from window activation.
87 // This will be fixed when we have WM API.
88 Widget* widget = GetWidget();
89 if (widget->is_top_level()) {
90 InputMethod* input_method = widget->GetInputMethod();
91 if (active) {
92 input_method->OnFocus();
93 // See description of got_initial_focus_in_ for details on this.
94 widget->GetFocusManager()->RestoreFocusedView();
95 } else {
96 input_method->OnBlur();
97 widget->GetFocusManager()->StoreFocusedView();
98 }
99 }
100 view_->SchedulePaint();
101 }
102
103 bool NativeWidgetViews::OnKeyEvent(const KeyEvent& key_event) {
104 InputMethod* input_method = GetWidget()->GetInputMethod();
105 DCHECK(input_method);
106 input_method->DispatchKeyEvent(key_event);
107 return true;
108 }
109
110 void NativeWidgetViews::DispatchKeyEventPostIME(const KeyEvent& key) {
111 // TODO(oshima): GTK impl handles menu_key in special way. This may be
112 // necessary when running under NativeWidgetX.
113 if (delegate_->OnKeyEvent(key))
114 return;
115 if (key.type() == ui::ET_KEY_PRESSED && GetWidget()->GetFocusManager())
116 GetWidget()->GetFocusManager()->OnKeyEvent(key);
117 }
118
119 ////////////////////////////////////////////////////////////////////////////////
120 // NativeWidgetViews, protected:
121
122 void NativeWidgetViews::OnBoundsChanged(const gfx::Rect& new_bounds,
123 const gfx::Rect& old_bounds) {
124 delegate_->OnNativeWidgetSizeChanged(new_bounds.size());
125 }
126
127 bool NativeWidgetViews::OnMouseEvent(const MouseEvent& event) {
128 #if defined(TOUCH_UI) || defined(USE_AURA)
129 TooltipManagerViews* tooltip_manager =
130 static_cast<TooltipManagerViews*>(GetTooltipManager());
131 if (tooltip_manager)
132 tooltip_manager->UpdateForMouseEvent(event);
133 #endif
134 return HandleWindowOperation(event) ? true : delegate_->OnMouseEvent(event);
135 }
136
137 ////////////////////////////////////////////////////////////////////////////////
138 // NativeWidgetViews, NativeWidget implementation:
139
140 void NativeWidgetViews::InitNativeWidget(const Widget::InitParams& params) {
141 parent_ = params.parent_widget;
142 ownership_ = params.ownership;
143 always_on_top_ = params.keep_on_top;
144 View* parent_view = NULL;
145 if (params.parent_widget) {
146 parent_view = params.parent_widget->GetChildViewParent();
147 } else if (ViewsDelegate::views_delegate &&
148 ViewsDelegate::views_delegate->GetDefaultParentView() &&
149 !params.child) {
150 parent_view = ViewsDelegate::views_delegate->GetDefaultParentView();
151 } else if (params.parent) {
152 Widget* widget = Widget::GetWidgetForNativeView(params.parent);
153 parent_view = widget->GetChildViewParent();
154 }
155
156 gfx::Rect bounds = GetWidget()->is_top_level() ?
157 AdjustRectOriginForParentWidget(params.bounds, parent_) : params.bounds;
158 view_ = new internal::NativeWidgetView(this);
159 view_->SetBoundsRect(bounds);
160 view_->SetVisible(params.type == Widget::InitParams::TYPE_CONTROL);
161
162 // With the default NATIVE_WIDGET_OWNS_WIDGET ownership, the
163 // deletion of either of the NativeWidgetViews or NativeWidgetView
164 // (e.g. via View hierarchy destruction) will delete the other.
165 // With WIDGET_OWNS_NATIVE_WIDGET, NativeWidgetViews should only
166 // be deleted by its Widget.
167 if (ownership_ == Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET)
168 view_->set_delete_native_widget(false);
169
170 if (parent_view)
171 parent_view->AddChildView(view_);
172 view_->SetPaintToLayer(true);
173 if (View::get_use_acceleration_when_possible())
174 view_->SetFillsBoundsOpaquely(!params.transparent);
175 // TODO(beng): SetInitParams().
176 }
177
178 NonClientFrameView* NativeWidgetViews::CreateNonClientFrameView() {
179 return NULL;
180 }
181
182 void NativeWidgetViews::UpdateFrameAfterFrameChange() {
183 }
184
185 bool NativeWidgetViews::ShouldUseNativeFrame() const {
186 // NOTIMPLEMENTED();
187 return false;
188 }
189
190 void NativeWidgetViews::FrameTypeChanged() {
191 }
192
193 Widget* NativeWidgetViews::GetWidget() {
194 return delegate_->AsWidget();
195 }
196
197 const Widget* NativeWidgetViews::GetWidget() const {
198 return delegate_->AsWidget();
199 }
200
201 gfx::NativeView NativeWidgetViews::GetNativeView() const {
202 return GetParentNativeWidget() ?
203 GetParentNativeWidget()->GetNativeView() : NULL;
204 }
205
206 gfx::NativeWindow NativeWidgetViews::GetNativeWindow() const {
207 return GetParentNativeWidget() ?
208 GetParentNativeWidget()->GetNativeWindow() : NULL;
209 }
210
211 Widget* NativeWidgetViews::GetTopLevelWidget() {
212 // This can get called when this is in the process of being destroyed, and
213 // view_ has already been unset.
214 if (!view_)
215 return GetWidget();
216 if (ViewsDelegate::views_delegate &&
217 view_->parent() == ViewsDelegate::views_delegate->GetDefaultParentView())
218 return GetWidget();
219 // During Widget destruction, this function may be called after |view_| is
220 // detached from a Widget, at which point this NativeWidget's Widget becomes
221 // the effective toplevel.
222 Widget* containing_widget = view_->GetWidget();
223 return containing_widget ? containing_widget->GetTopLevelWidget()
224 : GetWidget();
225 }
226
227 const ui::Compositor* NativeWidgetViews::GetCompositor() const {
228 return view_->GetWidget() ? view_->GetWidget()->GetCompositor() : NULL;
229 }
230
231 ui::Compositor* NativeWidgetViews::GetCompositor() {
232 return view_->GetWidget() ? view_->GetWidget()->GetCompositor() : NULL;
233 }
234
235 void NativeWidgetViews::CalculateOffsetToAncestorWithLayer(
236 gfx::Point* offset,
237 ui::Layer** layer_parent) {
238 view_->CalculateOffsetToAncestorWithLayer(offset, layer_parent);
239 }
240
241 void NativeWidgetViews::ReorderLayers() {
242 view_->ReorderLayers();
243 }
244
245 void NativeWidgetViews::ViewRemoved(View* view) {
246 internal::NativeWidgetPrivate* parent = GetParentNativeWidget();
247 if (parent)
248 parent->ViewRemoved(view);
249 }
250
251 void NativeWidgetViews::SetNativeWindowProperty(const char* name, void* value) {
252 if (value)
253 props_map_[name] = value;
254 else
255 props_map_.erase(name);
256 }
257
258 void* NativeWidgetViews::GetNativeWindowProperty(const char* name) const {
259 PropsMap::const_iterator iter = props_map_.find(name);
260 return iter != props_map_.end() ? iter->second : NULL;
261 }
262
263 TooltipManager* NativeWidgetViews::GetTooltipManager() const {
264 const internal::NativeWidgetPrivate* parent = GetParentNativeWidget();
265 return parent ? parent->GetTooltipManager() : NULL;
266 }
267
268 bool NativeWidgetViews::IsScreenReaderActive() const {
269 return GetParentNativeWidget()->IsScreenReaderActive();
270 }
271
272 void NativeWidgetViews::SendNativeAccessibilityEvent(
273 View* view,
274 ui::AccessibilityTypes::Event event_type) {
275 return GetParentNativeWidget()->SendNativeAccessibilityEvent(view,
276 event_type);
277 }
278
279 void NativeWidgetViews::SetMouseCapture() {
280 WindowManager::Get()->SetMouseCapture(GetWidget());
281 }
282
283 void NativeWidgetViews::ReleaseMouseCapture() {
284 WindowManager::Get()->ReleaseMouseCapture(GetWidget());
285 }
286
287 bool NativeWidgetViews::HasMouseCapture() const {
288 return WindowManager::Get()->HasMouseCapture(GetWidget());
289 }
290
291 InputMethod* NativeWidgetViews::CreateInputMethod() {
292 #if defined(HAVE_IBUS)
293 InputMethod* input_method = new InputMethodIBus(this);
294 #else
295 InputMethod* input_method = new MockInputMethod(this);
296 #endif
297 input_method->Init(GetWidget());
298 return input_method;
299 }
300
301 void NativeWidgetViews::CenterWindow(const gfx::Size& size) {
302 const gfx::Size parent_size = GetView()->parent()->size();
303 GetView()->SetBounds((parent_size.width() - size.width())/2,
304 (parent_size.height() - size.height())/2,
305 size.width(), size.height());
306 }
307
308 void NativeWidgetViews::GetWindowPlacement(
309 gfx::Rect* bounds,
310 ui::WindowShowState* show_state) const {
311 *bounds = GetView()->bounds();
312 *show_state = ui::SHOW_STATE_NORMAL;
313 }
314
315 void NativeWidgetViews::SetWindowTitle(const string16& title) {
316 }
317
318 void NativeWidgetViews::SetWindowIcons(const SkBitmap& window_icon,
319 const SkBitmap& app_icon) {
320 }
321
322 void NativeWidgetViews::SetAccessibleName(const string16& name) {
323 }
324
325 void NativeWidgetViews::SetAccessibleRole(ui::AccessibilityTypes::Role role) {
326 }
327
328 void NativeWidgetViews::SetAccessibleState(
329 ui::AccessibilityTypes::State state) {
330 }
331
332 void NativeWidgetViews::BecomeModal() {
333 NOTIMPLEMENTED();
334 }
335
336 gfx::Rect NativeWidgetViews::GetWindowScreenBounds() const {
337 if (GetWidget() == GetWidget()->GetTopLevelWidget() &&
338 !parent_)
339 return view_->bounds();
340 gfx::Point origin = view_->bounds().origin();
341 View::ConvertPointToScreen(view_->parent(), &origin);
342 return gfx::Rect(origin.x(), origin.y(), view_->width(), view_->height());
343 }
344
345 gfx::Rect NativeWidgetViews::GetClientAreaScreenBounds() const {
346 return GetWindowScreenBounds();
347 }
348
349 gfx::Rect NativeWidgetViews::GetRestoredBounds() const {
350 return GetWindowScreenBounds();
351 }
352
353 void NativeWidgetViews::SetBounds(const gfx::Rect& bounds) {
354 // |bounds| are supplied in the coordinates of the parent.
355 if (GetWidget()->is_top_level())
356 view_->SetBoundsRect(AdjustRectOriginForParentWidget(bounds, parent_));
357 else
358 view_->SetBoundsRect(bounds);
359 }
360
361 void NativeWidgetViews::SetSize(const gfx::Size& size) {
362 view_->SetSize(size);
363 }
364
365 void NativeWidgetViews::MoveAbove(gfx::NativeView native_view) {
366 NOTIMPLEMENTED();
367 }
368
369 void NativeWidgetViews::MoveToTop() {
370 view_->parent()->ReorderChildView(view_, -1);
371 }
372
373 void NativeWidgetViews::SetShape(gfx::NativeRegion region) {
374 NOTIMPLEMENTED();
375 }
376
377 void NativeWidgetViews::Close() {
378 Hide();
379 if (!close_widget_factory_.HasWeakPtrs()) {
380 MessageLoop::current()->PostTask(
381 FROM_HERE,
382 base::Bind(&NativeWidgetViews::CloseNow,
383 close_widget_factory_.GetWeakPtr()));
384 }
385 }
386
387 void NativeWidgetViews::CloseNow() {
388 delete view_;
389 view_ = NULL;
390 }
391
392 void NativeWidgetViews::EnableClose(bool enable) {
393 }
394
395 void NativeWidgetViews::Show() {
396 if (always_on_top_)
397 MoveToTop();
398 view_->SetVisible(true);
399 GetWidget()->SetInitialFocus();
400 }
401
402 void NativeWidgetViews::Hide() {
403 view_->SetVisible(false);
404 if (HasMouseCapture())
405 ReleaseMouseCapture();
406
407 // This is necessary because views desktop's window manager doesn't
408 // set the focus back to parent.
409 if (GetWidget()->is_top_level()) {
410 Widget* parent_widget = view_->GetWidget();
411 if (parent_widget && parent_widget->GetInputMethod())
412 parent_widget->GetInputMethod()->OnFocus();
413 }
414 }
415
416 void NativeWidgetViews::ShowWithWindowState(ui::WindowShowState show_state) {
417 Show();
418 }
419
420 void NativeWidgetViews::ShowMaximizedWithBounds(
421 const gfx::Rect& restored_bounds) {
422 Show();
423 }
424
425 bool NativeWidgetViews::IsVisible() const {
426 return view_->IsVisible() && (GetWidget()->is_top_level() ||
427 GetWidget()->GetTopLevelWidget()->IsVisible());
428 }
429
430 void NativeWidgetViews::Activate() {
431 // Enable WidgetObserverTest.ActivationChange when this is implemented.
432 MoveToTop();
433 OnActivate(true);
434 }
435
436 void NativeWidgetViews::Deactivate() {
437 OnActivate(false);
438 }
439
440 bool NativeWidgetViews::IsActive() const {
441 return active_;
442 }
443
444 void NativeWidgetViews::SetAlwaysOnTop(bool on_top) {
445 always_on_top_ = on_top;
446 // This is not complete yet. At least |MoveToTop| will need to be updated to
447 // make sure a 'normal' window does not get on top of a window with
448 // |always_on_top_| set.
449 NOTIMPLEMENTED();
450 }
451
452 void NativeWidgetViews::Maximize() {
453 if (window_state_ == ui::SHOW_STATE_MAXIMIZED)
454 return;
455
456 if (window_state_ != ui::SHOW_STATE_MINIMIZED) {
457 // Remember bounds and transform to use when unmaximized.
458 restored_bounds_ = view_->bounds();
459 restored_transform_ = view_->GetTransform();
460 }
461
462 window_state_ = ui::SHOW_STATE_MAXIMIZED;
463 gfx::Size size = GetParentNativeWidget()->GetWindowScreenBounds().size();
464 SetBounds(gfx::Rect(gfx::Point(), size));
465 }
466
467 void NativeWidgetViews::Minimize() {
468 if (view_->layer() && view_->layer()->GetAnimator()->is_animating())
469 return;
470
471 gfx::Rect view_bounds = view_->bounds();
472 gfx::Rect parent_bounds = view_->parent()->bounds();
473
474 if (window_state_ != ui::SHOW_STATE_MAXIMIZED) {
475 restored_bounds_ = view_bounds;
476 restored_transform_ = view_->GetTransform();
477 }
478
479 float aspect_ratio = static_cast<float>(view_bounds.width()) /
480 static_cast<float>(view_bounds.height());
481 int target_size = 100;
482 int target_height = target_size;
483 int target_width = static_cast<int>(aspect_ratio * target_height);
484 if (target_width > target_size) {
485 target_width = target_size;
486 target_height = static_cast<int>(target_width / aspect_ratio);
487 }
488
489 int target_x = 20;
490 int target_y = parent_bounds.height() - target_size - 20;
491
492 view_->SetBounds(
493 target_x, target_y, view_bounds.width(), view_bounds.height());
494
495 ui::Transform transform;
496 transform.SetScale((float)target_width / (float)view_bounds.width(),
497 (float)target_height / (float)view_bounds.height());
498 view_->SetTransform(transform);
499
500 window_state_ = ui::SHOW_STATE_MINIMIZED;
501 }
502
503 bool NativeWidgetViews::IsMaximized() const {
504 return window_state_ == ui::SHOW_STATE_MAXIMIZED;
505 }
506
507 bool NativeWidgetViews::IsMinimized() const {
508 return window_state_ == ui::SHOW_STATE_MINIMIZED;
509 }
510
511 void NativeWidgetViews::Restore() {
512 if (view_->layer() && view_->layer()->GetAnimator()->is_animating())
513 return;
514
515 window_state_ = ui::SHOW_STATE_NORMAL;
516 view_->SetBoundsRect(restored_bounds_);
517 view_->SetTransform(restored_transform_);
518 }
519
520 void NativeWidgetViews::SetFullscreen(bool fullscreen) {
521 NOTIMPLEMENTED();
522 }
523
524 bool NativeWidgetViews::IsFullscreen() const {
525 // NOTIMPLEMENTED();
526 return false;
527 }
528
529 void NativeWidgetViews::SetOpacity(unsigned char opacity) {
530 NOTIMPLEMENTED();
531 }
532
533 void NativeWidgetViews::SetUseDragFrame(bool use_drag_frame) {
534 NOTIMPLEMENTED();
535 }
536
537 bool NativeWidgetViews::IsAccessibleWidget() const {
538 NOTIMPLEMENTED();
539 return false;
540 }
541
542 void NativeWidgetViews::RunShellDrag(View* view,
543 const ui::OSExchangeData& data,
544 int operation) {
545 GetParentNativeWidget()->RunShellDrag(view, data, operation);
546 }
547
548 void NativeWidgetViews::SchedulePaintInRect(const gfx::Rect& rect) {
549 view_->SchedulePaintInRect(rect);
550 }
551
552 void NativeWidgetViews::SetCursor(gfx::NativeCursor cursor) {
553 view_->set_cursor(cursor);
554 GetParentNativeWidget()->SetCursor(cursor);
555 }
556
557 void NativeWidgetViews::ClearNativeFocus() {
558 GetParentNativeWidget()->ClearNativeFocus();
559 }
560
561 void NativeWidgetViews::FocusNativeView(gfx::NativeView native_view) {
562 GetParentNativeWidget()->FocusNativeView(native_view);
563 }
564
565 bool NativeWidgetViews::ConvertPointFromAncestor(
566 const Widget* ancestor, gfx::Point* point) const {
567 // This method converts the point from ancestor's coordinates to
568 // this widget's coordinate using recursion as the widget hierachy
569 // is usually shallow.
570
571 if (ancestor == GetWidget())
572 return true; // no conversion necessary
573
574 const Widget* parent_widget = view_->GetWidget();
575 if (!parent_widget) // couldn't reach the ancestor.
576 return false;
577
578 if (parent_widget == ancestor ||
579 parent_widget->ConvertPointFromAncestor(ancestor, point)) {
580 View::ConvertPointToView(parent_widget->GetRootView(), GetView(), point);
581 return true;
582 }
583 return false;
584 }
585
586 gfx::Rect NativeWidgetViews::GetWorkAreaBoundsInScreen() const {
587 return GetParentNativeWidget()->GetWorkAreaBoundsInScreen();
588 }
589
590 void NativeWidgetViews::SetInactiveRenderingDisabled(bool value) {
591 }
592
593 ////////////////////////////////////////////////////////////////////////////////
594 // NativeWidgetViews, private:
595
596 internal::NativeWidgetPrivate* NativeWidgetViews::GetParentNativeWidget() {
597 Widget* containing_widget = view_ ? view_->GetWidget() : NULL;
598 return containing_widget ? static_cast<internal::NativeWidgetPrivate*>(
599 containing_widget->native_widget()) :
600 NULL;
601 }
602
603 const internal::NativeWidgetPrivate*
604 NativeWidgetViews::GetParentNativeWidget() const {
605 const Widget* containing_widget = view_ ? view_->GetWidget() : NULL;
606 return containing_widget ? static_cast<const internal::NativeWidgetPrivate*>(
607 containing_widget->native_widget()) :
608 NULL;
609 }
610
611 bool NativeWidgetViews::HandleWindowOperation(const MouseEvent& event) {
612 if (event.type() != ui::ET_MOUSE_PRESSED)
613 return false;
614
615 Widget* widget = GetWidget();
616 if (widget->non_client_view()) {
617 int hittest_code = widget->non_client_view()->NonClientHitTest(
618 event.location());
619 switch (hittest_code) {
620 case HTCAPTION: {
621 if (!event.IsOnlyRightMouseButton()) {
622 WindowManager::Get()->StartMoveDrag(widget, event.location());
623 return true;
624 }
625 break;
626 }
627 case HTBOTTOM:
628 case HTBOTTOMLEFT:
629 case HTBOTTOMRIGHT:
630 case HTGROWBOX:
631 case HTLEFT:
632 case HTRIGHT:
633 case HTTOP:
634 case HTTOPLEFT:
635 case HTTOPRIGHT: {
636 WindowManager::Get()->StartResizeDrag(
637 widget, event.location(), hittest_code);
638 return true;
639 }
640 default:
641 // Everything else falls into standard client event handling.
642 break;
643 }
644 }
645 return false;
646 }
647
648 } // namespace views
OLDNEW
« no previous file with comments | « views/widget/native_widget_views.h ('k') | views/widget/native_widget_wayland.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698