OLD | NEW |
---|---|
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 #include "ash/wm/custom_frame_view_ash.h" | 5 #include "ash/wm/custom_frame_view_ash.h" |
6 | 6 |
7 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" | 7 #include "ash/wm/caption_buttons/frame_caption_button_container_view.h" |
8 #include "ash/wm/caption_buttons/frame_maximize_button.h" | |
9 #include "ash/wm/caption_buttons/frame_maximize_button_observer.h" | |
8 #include "ash/wm/frame_border_hit_test_controller.h" | 10 #include "ash/wm/frame_border_hit_test_controller.h" |
9 #include "ash/wm/header_painter.h" | 11 #include "ash/wm/header_painter.h" |
12 #include "ash/wm/immersive_fullscreen_controller.h" | |
10 #include "grit/ash_resources.h" | 13 #include "grit/ash_resources.h" |
11 #include "ui/gfx/canvas.h" | 14 #include "ui/gfx/canvas.h" |
12 #include "ui/gfx/font.h" | 15 #include "ui/gfx/font.h" |
13 #include "ui/gfx/rect.h" | 16 #include "ui/gfx/rect.h" |
14 #include "ui/gfx/size.h" | 17 #include "ui/gfx/size.h" |
18 #include "ui/views/view.h" | |
15 #include "ui/views/widget/native_widget_aura.h" | 19 #include "ui/views/widget/native_widget_aura.h" |
16 #include "ui/views/widget/widget.h" | 20 #include "ui/views/widget/widget.h" |
17 #include "ui/views/widget/widget_delegate.h" | 21 #include "ui/views/widget/widget_delegate.h" |
22 #include "ui/views/widget/widget_deletion_observer.h" | |
23 #include "ui/views/window/non_client_view.h" | |
18 | 24 |
19 namespace { | 25 namespace { |
20 | 26 |
21 const gfx::Font& GetTitleFont() { | 27 const gfx::Font& GetTitleFont() { |
22 static gfx::Font* title_font = NULL; | 28 static gfx::Font* title_font = NULL; |
23 if (!title_font) | 29 if (!title_font) |
24 title_font = new gfx::Font(views::NativeWidgetAura::GetWindowTitleFont()); | 30 title_font = new gfx::Font(views::NativeWidgetAura::GetWindowTitleFont()); |
25 return *title_font; | 31 return *title_font; |
26 } | 32 } |
27 | 33 |
28 } // namespace | 34 } // namespace |
29 | 35 |
30 namespace ash { | 36 namespace ash { |
31 | 37 |
32 // static | 38 /////////////////////////////////////////////////////////////////////////////// |
33 const char CustomFrameViewAsh::kViewClassName[] = "CustomFrameViewAsh"; | 39 // CustomFrameViewAsh::HeaderView |
34 | 40 |
35 //////////////////////////////////////////////////////////////////////////////// | 41 // View which paints the header. It slides off and on screen in immersive |
36 // CustomFrameViewAsh, public: | 42 // fullscreen. |
37 CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame) | 43 class CustomFrameViewAsh::HeaderView |
44 : public views::View, | |
45 public ImmersiveFullscreenController::Delegate, | |
46 public FrameMaximizeButtonObserver { | |
47 public: | |
48 // |frame| is the widget that the caption buttons act on. | |
49 HeaderView(views::Widget* frame); | |
oshima
2013/11/07 18:56:13
explicit
| |
50 virtual ~HeaderView(); | |
51 | |
52 // Schedules a repaint for the entire title. | |
53 void SchedulePaintForTitle(); | |
54 | |
55 // Tells the window controls to reset themselves to the normal state. | |
56 void ResetWindowControls(); | |
57 | |
58 // Returns the amount of the view's pixels which should be on screen. | |
59 int GetPreferredOnScreenHeight() const; | |
60 | |
61 // Returns the view's preferred height. | |
62 int GetPreferredHeight() const; | |
63 | |
64 // Returns the view's minimum width. | |
65 int GetMinimumWidth() const; | |
66 | |
67 // views::View overrides: | |
68 virtual void Layout() OVERRIDE; | |
69 virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE; | |
70 | |
71 // Sets whether the header should be painted as active. | |
72 void set_paint_as_active(bool paint_as_active) { | |
73 paint_as_active_ = paint_as_active; | |
74 } | |
75 | |
76 HeaderPainter* header_painter() { | |
77 return header_painter_.get(); | |
78 } | |
79 | |
80 private: | |
81 // ImmersiveFullscreenController::Delegate overrides: | |
82 virtual void OnImmersiveRevealStarted() OVERRIDE; | |
83 virtual void OnImmersiveRevealEnded() OVERRIDE; | |
84 virtual void OnImmersiveFullscreenExited() OVERRIDE; | |
85 virtual void SetVisibleFraction(double visible_fraction) OVERRIDE; | |
86 virtual std::vector<gfx::Rect> GetVisibleBoundsInScreen() const OVERRIDE; | |
87 | |
88 // FrameMaximizeButtonObserver overrides: | |
89 virtual void OnMaximizeBubbleShown(views::Widget* bubble) OVERRIDE; | |
90 | |
91 // The widget that the caption buttons act on. | |
92 views::Widget* frame_; | |
93 | |
94 // Helper for painting the header. | |
95 scoped_ptr<HeaderPainter> header_painter_; | |
96 | |
97 // View which contains the window caption buttons. | |
98 FrameCaptionButtonContainerView* caption_button_container_; | |
99 | |
100 // The maximize bubble widget. |maximize_bubble_| may be non-NULL but have | |
101 // been already destroyed. | |
102 views::Widget* maximize_bubble_; | |
103 | |
104 // Keeps track of whether |maximize_bubble_| is still alive. | |
105 scoped_ptr<views::WidgetDeletionObserver> maximize_bubble_lifetime_observer_; | |
106 | |
107 // Whether the header should be painted as active. | |
108 bool paint_as_active_; | |
109 | |
110 // The fraction of the header's height which is visible while in fullscreen. | |
111 // This value is meaningless when not in fullscreen. | |
112 double fullscreen_visible_fraction_; | |
113 | |
114 DISALLOW_COPY_AND_ASSIGN(HeaderView); | |
115 }; | |
116 | |
117 CustomFrameViewAsh::HeaderView::HeaderView(views::Widget* frame) | |
38 : frame_(frame), | 118 : frame_(frame), |
119 header_painter_(new ash::HeaderPainter), | |
39 caption_button_container_(NULL), | 120 caption_button_container_(NULL), |
40 header_painter_(new ash::HeaderPainter), | 121 maximize_bubble_(NULL), |
41 frame_border_hit_test_controller_( | 122 paint_as_active_(false), |
42 new FrameBorderHitTestController(frame_)) { | 123 fullscreen_visible_fraction_(0) { |
43 // Unfortunately, there is no views::WidgetDelegate::CanMinimize(). Assume | 124 // Unfortunately, there is no views::WidgetDelegate::CanMinimize(). Assume |
44 // that the window frame can be minimized if it can be maximized. | 125 // that the window frame can be minimized if it can be maximized. |
45 FrameCaptionButtonContainerView::MinimizeAllowed minimize_allowed = | 126 FrameCaptionButtonContainerView::MinimizeAllowed minimize_allowed = |
46 frame_->widget_delegate()->CanMaximize() ? | 127 frame_->widget_delegate()->CanMaximize() ? |
47 FrameCaptionButtonContainerView::MINIMIZE_ALLOWED : | 128 FrameCaptionButtonContainerView::MINIMIZE_ALLOWED : |
48 FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED; | 129 FrameCaptionButtonContainerView::MINIMIZE_DISALLOWED; |
49 caption_button_container_ = new FrameCaptionButtonContainerView(frame, | 130 caption_button_container_ = new FrameCaptionButtonContainerView(frame_, |
50 minimize_allowed); | 131 minimize_allowed); |
51 AddChildView(caption_button_container_); | 132 AddChildView(caption_button_container_); |
133 FrameMaximizeButton* frame_maximize_button = | |
134 caption_button_container_->GetOldStyleSizeButton(); | |
135 if (frame_maximize_button) | |
136 frame_maximize_button->AddObserver(this); | |
52 | 137 |
53 header_painter_->Init(frame_, this, NULL, caption_button_container_); | 138 header_painter_->Init(frame_, this, NULL, caption_button_container_); |
54 } | 139 } |
55 | 140 |
141 CustomFrameViewAsh::HeaderView::~HeaderView() { | |
142 } | |
143 | |
144 void CustomFrameViewAsh::HeaderView::SchedulePaintForTitle() { | |
145 header_painter_->SchedulePaintForTitle(GetTitleFont()); | |
146 } | |
147 | |
148 void CustomFrameViewAsh::HeaderView::ResetWindowControls() { | |
149 caption_button_container_->ResetWindowControls(); | |
150 } | |
151 | |
152 int CustomFrameViewAsh::HeaderView::GetPreferredOnScreenHeight() const { | |
153 if (frame_->IsFullscreen()) { | |
154 return static_cast<int>( | |
155 GetPreferredHeight() * fullscreen_visible_fraction_); | |
156 } | |
157 return GetPreferredHeight(); | |
158 } | |
159 | |
160 int CustomFrameViewAsh::HeaderView::GetPreferredHeight() const { | |
161 // Reserve enough space to see the buttons and the separator line. | |
162 return caption_button_container_->bounds().bottom() + | |
163 header_painter_->HeaderContentSeparatorSize(); | |
164 } | |
165 | |
166 int CustomFrameViewAsh::HeaderView::GetMinimumWidth() const { | |
167 return header_painter_->GetMinimumHeaderWidth(); | |
168 } | |
169 | |
170 void CustomFrameViewAsh::HeaderView::Layout() { | |
171 header_painter_->LayoutHeader(true); | |
172 header_painter_->set_header_height(GetPreferredHeight()); | |
173 } | |
174 | |
175 void CustomFrameViewAsh::HeaderView::OnPaint(gfx::Canvas* canvas) { | |
176 int theme_image_id = 0; | |
177 if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)) | |
178 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; | |
179 else if (paint_as_active_) | |
180 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; | |
181 else | |
182 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; | |
183 | |
184 header_painter_->PaintHeader( | |
185 canvas, | |
186 paint_as_active_ ? HeaderPainter::ACTIVE : HeaderPainter::INACTIVE, | |
187 theme_image_id, | |
188 0); | |
189 header_painter_->PaintTitleBar(canvas, GetTitleFont()); | |
190 header_painter_->PaintHeaderContentSeparator(canvas); | |
191 } | |
192 | |
193 void CustomFrameViewAsh::HeaderView::OnImmersiveRevealStarted() { | |
194 fullscreen_visible_fraction_ = 0; | |
195 SetPaintToLayer(true); | |
196 parent()->Layout(); | |
197 } | |
198 | |
199 void CustomFrameViewAsh::HeaderView::OnImmersiveRevealEnded() { | |
200 fullscreen_visible_fraction_ = 0; | |
201 SetPaintToLayer(false); | |
202 parent()->Layout(); | |
203 } | |
204 | |
205 void CustomFrameViewAsh::HeaderView::OnImmersiveFullscreenExited() { | |
206 fullscreen_visible_fraction_ = 0; | |
207 SetPaintToLayer(false); | |
208 parent()->Layout(); | |
209 } | |
210 | |
211 void CustomFrameViewAsh::HeaderView::SetVisibleFraction( | |
212 double visible_fraction) { | |
213 if (fullscreen_visible_fraction_ != visible_fraction) { | |
214 fullscreen_visible_fraction_ = visible_fraction; | |
215 parent()->Layout(); | |
216 } | |
217 } | |
218 | |
219 std::vector<gfx::Rect> | |
220 CustomFrameViewAsh::HeaderView::GetVisibleBoundsInScreen() const { | |
221 // TODO(pkotwicz): Implement views::View::ConvertRectToScreen(). | |
222 gfx::Rect visible_bounds(GetVisibleBounds()); | |
223 gfx::Point visible_origin_in_screen( | |
224 visible_bounds.origin()); | |
225 views::View::ConvertPointToScreen(this, &visible_origin_in_screen); | |
226 std::vector<gfx::Rect> bounds_in_screen; | |
227 bounds_in_screen.push_back( | |
228 gfx::Rect(visible_origin_in_screen, visible_bounds.size())); | |
229 if (maximize_bubble_lifetime_observer_.get() && | |
230 maximize_bubble_lifetime_observer_->IsWidgetAlive()) { | |
231 bounds_in_screen.push_back(maximize_bubble_->GetWindowBoundsInScreen()); | |
232 } | |
233 return bounds_in_screen; | |
234 } | |
235 | |
236 void CustomFrameViewAsh::HeaderView::OnMaximizeBubbleShown( | |
237 views::Widget* bubble) { | |
238 maximize_bubble_ = bubble; | |
239 maximize_bubble_lifetime_observer_.reset( | |
240 new views::WidgetDeletionObserver(bubble)); | |
241 } | |
242 | |
243 /////////////////////////////////////////////////////////////////////////////// | |
244 // CustomFrameViewAsh::OverlayView | |
245 | |
246 // View which takes up the entire widget and contains the HeaderView. HeaderView | |
247 // is a child of OverlayView to avoid creating a larger texture than necessary | |
248 // when painting the HeaderView to its own layer. | |
249 class CustomFrameViewAsh::OverlayView : public views::View { | |
250 public: | |
251 explicit OverlayView(HeaderView* header_view); | |
252 virtual ~OverlayView(); | |
253 | |
254 // views::View override: | |
255 virtual void Layout() OVERRIDE; | |
256 | |
257 private: | |
258 HeaderView* header_view_; | |
259 | |
260 DISALLOW_COPY_AND_ASSIGN(OverlayView); | |
261 }; | |
262 | |
263 CustomFrameViewAsh::OverlayView::OverlayView(HeaderView* header_view) | |
264 : header_view_(header_view) { | |
265 AddChildView(header_view); | |
266 } | |
267 | |
268 CustomFrameViewAsh::OverlayView::~OverlayView() { | |
269 } | |
270 | |
271 void CustomFrameViewAsh::OverlayView::Layout() { | |
272 int onscreen_height = header_view_->GetPreferredOnScreenHeight(); | |
273 if (onscreen_height == 0) { | |
274 header_view_->SetVisible(false); | |
275 } else { | |
276 int height = header_view_->GetPreferredHeight(); | |
277 header_view_->SetBounds(0, onscreen_height - height, width(), height); | |
278 header_view_->SetVisible(true); | |
279 } | |
280 } | |
281 | |
282 //////////////////////////////////////////////////////////////////////////////// | |
283 // CustomFrameViewAsh, public: | |
284 | |
285 // static | |
286 const char CustomFrameViewAsh::kViewClassName[] = "CustomFrameViewAsh"; | |
287 | |
288 CustomFrameViewAsh::CustomFrameViewAsh(views::Widget* frame) | |
289 : frame_(frame), | |
290 header_view_(new HeaderView(frame)), | |
291 frame_border_hit_test_controller_( | |
292 new FrameBorderHitTestController(frame_)) { | |
293 // |header_view_| is set as the non client view's overlay view so that it can | |
294 // overlay the web contents in immersive fullscreen. | |
295 frame->non_client_view()->SetOverlayView(new OverlayView(header_view_)); | |
296 } | |
297 | |
56 CustomFrameViewAsh::~CustomFrameViewAsh() { | 298 CustomFrameViewAsh::~CustomFrameViewAsh() { |
57 } | 299 } |
58 | 300 |
301 void CustomFrameViewAsh::InitImmersiveFullscreenControllerForView( | |
302 ImmersiveFullscreenController* immersive_fullscreen_controller) { | |
303 immersive_fullscreen_controller->Init(header_view_, frame_, header_view_); | |
304 } | |
305 | |
59 //////////////////////////////////////////////////////////////////////////////// | 306 //////////////////////////////////////////////////////////////////////////////// |
60 // CustomFrameViewAsh, views::NonClientFrameView overrides: | 307 // CustomFrameViewAsh, views::NonClientFrameView overrides: |
308 | |
61 gfx::Rect CustomFrameViewAsh::GetBoundsForClientView() const { | 309 gfx::Rect CustomFrameViewAsh::GetBoundsForClientView() const { |
62 int top_height = NonClientTopBorderHeight(); | 310 int top_height = NonClientTopBorderHeight(); |
63 return HeaderPainter::GetBoundsForClientView(top_height, bounds()); | 311 return HeaderPainter::GetBoundsForClientView(top_height, bounds()); |
64 } | 312 } |
65 | 313 |
66 gfx::Rect CustomFrameViewAsh::GetWindowBoundsForClientBounds( | 314 gfx::Rect CustomFrameViewAsh::GetWindowBoundsForClientBounds( |
67 const gfx::Rect& client_bounds) const { | 315 const gfx::Rect& client_bounds) const { |
68 int top_height = NonClientTopBorderHeight(); | 316 int top_height = NonClientTopBorderHeight(); |
69 return HeaderPainter::GetWindowBoundsForClientBounds(top_height, | 317 return HeaderPainter::GetWindowBoundsForClientBounds(top_height, |
70 client_bounds); | 318 client_bounds); |
71 } | 319 } |
72 | 320 |
73 int CustomFrameViewAsh::NonClientHitTest(const gfx::Point& point) { | 321 int CustomFrameViewAsh::NonClientHitTest(const gfx::Point& point) { |
74 return FrameBorderHitTestController::NonClientHitTest(this, | 322 return FrameBorderHitTestController::NonClientHitTest(this, |
75 header_painter_.get(), point); | 323 header_view_->header_painter(), point); |
76 } | 324 } |
77 | 325 |
78 void CustomFrameViewAsh::GetWindowMask(const gfx::Size& size, | 326 void CustomFrameViewAsh::GetWindowMask(const gfx::Size& size, |
79 gfx::Path* window_mask) { | 327 gfx::Path* window_mask) { |
80 // No window masks in Aura. | 328 // No window masks in Aura. |
81 } | 329 } |
82 | 330 |
83 void CustomFrameViewAsh::ResetWindowControls() { | 331 void CustomFrameViewAsh::ResetWindowControls() { |
84 caption_button_container_->ResetWindowControls(); | 332 header_view_->ResetWindowControls(); |
85 } | 333 } |
86 | 334 |
87 void CustomFrameViewAsh::UpdateWindowIcon() { | 335 void CustomFrameViewAsh::UpdateWindowIcon() { |
88 } | 336 } |
89 | 337 |
90 void CustomFrameViewAsh::UpdateWindowTitle() { | 338 void CustomFrameViewAsh::UpdateWindowTitle() { |
91 header_painter_->SchedulePaintForTitle(GetTitleFont()); | 339 header_view_->SchedulePaintForTitle(); |
340 } | |
341 | |
342 void CustomFrameViewAsh::ShouldPaintAsActiveChanged() { | |
343 header_view_->set_paint_as_active(ShouldPaintAsActive()); | |
344 header_view_->SchedulePaint(); | |
92 } | 345 } |
93 | 346 |
94 //////////////////////////////////////////////////////////////////////////////// | 347 //////////////////////////////////////////////////////////////////////////////// |
95 // CustomFrameViewAsh, views::View overrides: | 348 // CustomFrameViewAsh, views::View overrides: |
96 | 349 |
97 gfx::Size CustomFrameViewAsh::GetPreferredSize() { | 350 gfx::Size CustomFrameViewAsh::GetPreferredSize() { |
98 gfx::Size pref = frame_->client_view()->GetPreferredSize(); | 351 gfx::Size pref = frame_->client_view()->GetPreferredSize(); |
99 gfx::Rect bounds(0, 0, pref.width(), pref.height()); | 352 gfx::Rect bounds(0, 0, pref.width(), pref.height()); |
100 return frame_->non_client_view()->GetWindowBoundsForClientBounds( | 353 return frame_->non_client_view()->GetWindowBoundsForClientBounds( |
101 bounds).size(); | 354 bounds).size(); |
102 } | 355 } |
103 | 356 |
104 void CustomFrameViewAsh::Layout() { | |
105 // Use the shorter maximized layout headers. | |
106 header_painter_->LayoutHeader(true); | |
107 header_painter_->set_header_height(NonClientTopBorderHeight()); | |
108 } | |
109 | |
110 void CustomFrameViewAsh::OnPaint(gfx::Canvas* canvas) { | |
111 if (frame_->IsFullscreen()) | |
112 return; | |
113 | |
114 // Prevent bleeding paint onto the client area below the window frame, which | |
115 // may become visible when the WebContent is transparent. | |
116 canvas->Save(); | |
117 canvas->ClipRect(gfx::Rect(0, 0, width(), NonClientTopBorderHeight())); | |
118 | |
119 bool paint_as_active = ShouldPaintAsActive(); | |
120 int theme_image_id = 0; | |
121 if (header_painter_->ShouldUseMinimalHeaderStyle(HeaderPainter::THEMED_NO)) | |
122 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_MINIMAL; | |
123 else if (paint_as_active) | |
124 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_ACTIVE; | |
125 else | |
126 theme_image_id = IDR_AURA_WINDOW_HEADER_BASE_INACTIVE; | |
127 | |
128 header_painter_->PaintHeader( | |
129 canvas, | |
130 paint_as_active ? HeaderPainter::ACTIVE : HeaderPainter::INACTIVE, | |
131 theme_image_id, | |
132 0); | |
133 header_painter_->PaintTitleBar(canvas, GetTitleFont()); | |
134 header_painter_->PaintHeaderContentSeparator(canvas); | |
135 canvas->Restore(); | |
136 } | |
137 | |
138 const char* CustomFrameViewAsh::GetClassName() const { | 357 const char* CustomFrameViewAsh::GetClassName() const { |
139 return kViewClassName; | 358 return kViewClassName; |
140 } | 359 } |
141 | 360 |
142 gfx::Size CustomFrameViewAsh::GetMinimumSize() { | 361 gfx::Size CustomFrameViewAsh::GetMinimumSize() { |
143 gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize()); | 362 gfx::Size min_client_view_size(frame_->client_view()->GetMinimumSize()); |
144 return gfx::Size( | 363 return gfx::Size( |
145 std::max(header_painter_->GetMinimumHeaderWidth(), | 364 std::max(header_view_->GetMinimumWidth(), min_client_view_size.width()), |
146 min_client_view_size.width()), | |
147 NonClientTopBorderHeight() + min_client_view_size.height()); | 365 NonClientTopBorderHeight() + min_client_view_size.height()); |
148 } | 366 } |
149 | 367 |
150 gfx::Size CustomFrameViewAsh::GetMaximumSize() { | 368 gfx::Size CustomFrameViewAsh::GetMaximumSize() { |
151 return frame_->client_view()->GetMaximumSize(); | 369 return frame_->client_view()->GetMaximumSize(); |
152 } | 370 } |
153 | 371 |
372 bool CustomFrameViewAsh::HitTestRect(const gfx::Rect& rect) const { | |
373 // NonClientView hit tests the NonClientFrameView first instead of going in | |
374 // z-order. Return false so that events get to the OverlayView. | |
375 return false; | |
376 } | |
377 | |
154 //////////////////////////////////////////////////////////////////////////////// | 378 //////////////////////////////////////////////////////////////////////////////// |
155 // CustomFrameViewAsh, private: | 379 // CustomFrameViewAsh, private: |
156 | 380 |
157 int CustomFrameViewAsh::NonClientTopBorderHeight() const { | 381 int CustomFrameViewAsh::NonClientTopBorderHeight() const { |
158 if (frame_->IsFullscreen()) | 382 return header_view_->GetPreferredOnScreenHeight(); |
159 return 0; | |
160 | |
161 // Reserve enough space to see the buttons, including any offset from top and | |
162 // reserving space for the separator line. | |
163 return caption_button_container_->bounds().bottom() + | |
164 header_painter_->HeaderContentSeparatorSize(); | |
165 } | 383 } |
166 | 384 |
167 } // namespace ash | 385 } // namespace ash |
OLD | NEW |