• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
3 // found in the LICENSE file.
4 
5 #include "ui/views/window/dialog_client_view.h"
6 
7 #include <algorithm>
8 
9 #include "ui/events/keycodes/keyboard_codes.h"
10 #include "ui/views/background.h"
11 #include "ui/views/controls/button/blue_button.h"
12 #include "ui/views/controls/button/label_button.h"
13 #include "ui/views/layout/layout_constants.h"
14 #include "ui/views/widget/widget.h"
15 #include "ui/views/window/dialog_delegate.h"
16 
17 namespace views {
18 
19 namespace {
20 
21 // The group used by the buttons.  This name is chosen voluntarily big not to
22 // conflict with other groups that could be in the dialog content.
23 const int kButtonGroup = 6666;
24 
25 // Returns true if the given view should be shown (i.e. exists and is
26 // visible).
ShouldShow(View * view)27 bool ShouldShow(View* view) {
28   return view && view->visible();
29 }
30 
31 }  // namespace
32 
33 ///////////////////////////////////////////////////////////////////////////////
34 // DialogClientView, public:
35 
DialogClientView(Widget * owner,View * contents_view)36 DialogClientView::DialogClientView(Widget* owner, View* contents_view)
37     : ClientView(owner, contents_view),
38       ok_button_(NULL),
39       cancel_button_(NULL),
40       default_button_(NULL),
41       focus_manager_(NULL),
42       extra_view_(NULL),
43       footnote_view_(NULL),
44       notified_delegate_(false) {
45 }
46 
~DialogClientView()47 DialogClientView::~DialogClientView() {
48 }
49 
AcceptWindow()50 void DialogClientView::AcceptWindow() {
51   // Only notify the delegate once. See |notified_delegate_|'s comment.
52   if (!notified_delegate_ && GetDialogDelegate()->Accept(false)) {
53     notified_delegate_ = true;
54     Close();
55   }
56 }
57 
CancelWindow()58 void DialogClientView::CancelWindow() {
59   // Only notify the delegate once. See |notified_delegate_|'s comment.
60   if (!notified_delegate_ && GetDialogDelegate()->Cancel()) {
61     notified_delegate_ = true;
62     Close();
63   }
64 }
65 
UpdateDialogButtons()66 void DialogClientView::UpdateDialogButtons() {
67   const int buttons = GetDialogDelegate()->GetDialogButtons();
68   ui::Accelerator escape(ui::VKEY_ESCAPE, ui::EF_NONE);
69   if (default_button_)
70     default_button_->SetIsDefault(false);
71   default_button_ = NULL;
72 
73   if (buttons & ui::DIALOG_BUTTON_OK) {
74     if (!ok_button_) {
75       ok_button_ = CreateDialogButton(ui::DIALOG_BUTTON_OK);
76       if (!(buttons & ui::DIALOG_BUTTON_CANCEL))
77         ok_button_->AddAccelerator(escape);
78       AddChildView(ok_button_);
79     }
80 
81     UpdateButton(ok_button_, ui::DIALOG_BUTTON_OK);
82   } else if (ok_button_) {
83     delete ok_button_;
84     ok_button_ = NULL;
85   }
86 
87   if (buttons & ui::DIALOG_BUTTON_CANCEL) {
88     if (!cancel_button_) {
89       cancel_button_ = CreateDialogButton(ui::DIALOG_BUTTON_CANCEL);
90       cancel_button_->AddAccelerator(escape);
91       AddChildView(cancel_button_);
92     }
93 
94     UpdateButton(cancel_button_, ui::DIALOG_BUTTON_CANCEL);
95   } else if (cancel_button_) {
96     delete cancel_button_;
97     cancel_button_ = NULL;
98   }
99 
100   // Use the escape key to close the window if there are no dialog buttons.
101   if (!has_dialog_buttons())
102     AddAccelerator(escape);
103   else
104     ResetAccelerators();
105 }
106 
107 ///////////////////////////////////////////////////////////////////////////////
108 // DialogClientView, ClientView overrides:
109 
CanClose()110 bool DialogClientView::CanClose() {
111   if (notified_delegate_)
112     return true;
113 
114   // The dialog is closing but no Accept or Cancel action has been performed
115   // before: it's a Close action.
116   if (GetDialogDelegate()->Close()) {
117     notified_delegate_ = true;
118     GetDialogDelegate()->OnClosed();
119     return true;
120   }
121   return false;
122 }
123 
AsDialogClientView()124 DialogClientView* DialogClientView::AsDialogClientView() {
125   return this;
126 }
127 
AsDialogClientView() const128 const DialogClientView* DialogClientView::AsDialogClientView() const {
129   return this;
130 }
131 
OnWillChangeFocus(View * focused_before,View * focused_now)132 void DialogClientView::OnWillChangeFocus(View* focused_before,
133                                          View* focused_now) {
134   // Make the newly focused button default or restore the dialog's default.
135   const int default_button = GetDialogDelegate()->GetDefaultDialogButton();
136   LabelButton* new_default_button = NULL;
137   if (focused_now &&
138       !strcmp(focused_now->GetClassName(), LabelButton::kViewClassName)) {
139     new_default_button = static_cast<LabelButton*>(focused_now);
140   } else if (default_button == ui::DIALOG_BUTTON_OK && ok_button_) {
141     new_default_button = ok_button_;
142   } else if (default_button == ui::DIALOG_BUTTON_CANCEL && cancel_button_) {
143     new_default_button = cancel_button_;
144   }
145 
146   if (default_button_ && default_button_ != new_default_button)
147     default_button_->SetIsDefault(false);
148   default_button_ = new_default_button;
149   if (default_button_ && !default_button_->is_default())
150     default_button_->SetIsDefault(true);
151 }
152 
OnDidChangeFocus(View * focused_before,View * focused_now)153 void DialogClientView::OnDidChangeFocus(View* focused_before,
154                                         View* focused_now) {
155 }
156 
157 ////////////////////////////////////////////////////////////////////////////////
158 // DialogClientView, View overrides:
159 
GetPreferredSize()160 gfx::Size DialogClientView::GetPreferredSize() {
161   // Initialize the size to fit the buttons and extra view row.
162   gfx::Size size(
163       (ok_button_ ? ok_button_->GetPreferredSize().width() : 0) +
164       (cancel_button_ ? cancel_button_->GetPreferredSize().width() : 0) +
165       (cancel_button_ && ok_button_ ? kRelatedButtonHSpacing : 0) +
166       (ShouldShow(extra_view_) ? extra_view_->GetPreferredSize().width() : 0) +
167       (ShouldShow(extra_view_) && has_dialog_buttons() ?
168            kRelatedButtonHSpacing : 0),
169       0);
170 
171   int buttons_height = GetButtonsAndExtraViewRowHeight();
172   if (buttons_height != 0) {
173     size.Enlarge(0, buttons_height + kRelatedControlVerticalSpacing);
174     // Inset the buttons and extra view.
175     const gfx::Insets insets = GetButtonRowInsets();
176     size.Enlarge(insets.width(), insets.height());
177   }
178 
179   // Increase the size as needed to fit the contents view.
180   // NOTE: The contents view is not inset on the top or side client view edges.
181   gfx::Size contents_size = contents_view()->GetPreferredSize();
182   size.Enlarge(0, contents_size.height());
183   size.set_width(std::max(size.width(), contents_size.width()));
184 
185   // Increase the size as needed to fit the footnote view.
186   if (ShouldShow(footnote_view_)) {
187     gfx::Size footnote_size = footnote_view_->GetPreferredSize();
188     if (!footnote_size.IsEmpty())
189       size.set_width(std::max(size.width(), footnote_size.width()));
190 
191     int footnote_height = footnote_view_->GetHeightForWidth(size.width());
192     size.Enlarge(0, footnote_height);
193   }
194 
195   return size;
196 }
197 
Layout()198 void DialogClientView::Layout() {
199   gfx::Rect bounds = GetContentsBounds();
200 
201   // Layout the footnote view.
202   if (ShouldShow(footnote_view_)) {
203     const int height = footnote_view_->GetHeightForWidth(bounds.width());
204     footnote_view_->SetBounds(bounds.x(), bounds.bottom() - height,
205                               bounds.width(), height);
206     if (height != 0)
207       bounds.Inset(0, 0, 0, height);
208   }
209 
210   // Layout the row containing the buttons and the extra view.
211   if (has_dialog_buttons() || ShouldShow(extra_view_)) {
212     bounds.Inset(GetButtonRowInsets());
213     const int height = GetButtonsAndExtraViewRowHeight();
214     gfx::Rect row_bounds(bounds.x(), bounds.bottom() - height,
215                          bounds.width(), height);
216     if (cancel_button_) {
217       const gfx::Size size = cancel_button_->GetPreferredSize();
218       row_bounds.set_width(row_bounds.width() - size.width());
219       cancel_button_->SetBounds(row_bounds.right(), row_bounds.y(),
220                                 size.width(), height);
221       row_bounds.set_width(row_bounds.width() - kRelatedButtonHSpacing);
222     }
223     if (ok_button_) {
224       const gfx::Size size = ok_button_->GetPreferredSize();
225       row_bounds.set_width(row_bounds.width() - size.width());
226       ok_button_->SetBounds(row_bounds.right(), row_bounds.y(),
227                             size.width(), height);
228       row_bounds.set_width(row_bounds.width() - kRelatedButtonHSpacing);
229     }
230     if (extra_view_) {
231       row_bounds.set_width(std::min(row_bounds.width(),
232           extra_view_->GetPreferredSize().width()));
233       extra_view_->SetBoundsRect(row_bounds);
234     }
235 
236     if (height > 0)
237       bounds.Inset(0, 0, 0, height + kRelatedControlVerticalSpacing);
238   }
239 
240   // Layout the contents view to the top and side edges of the contents bounds.
241   // NOTE: The local insets do not apply to the contents view sides or top.
242   const gfx::Rect contents_bounds = GetContentsBounds();
243   contents_view()->SetBounds(contents_bounds.x(), contents_bounds.y(),
244       contents_bounds.width(), bounds.bottom() - contents_bounds.y());
245 }
246 
AcceleratorPressed(const ui::Accelerator & accelerator)247 bool DialogClientView::AcceleratorPressed(const ui::Accelerator& accelerator) {
248   DCHECK_EQ(accelerator.key_code(), ui::VKEY_ESCAPE);
249   Close();
250   return true;
251 }
252 
ViewHierarchyChanged(const ViewHierarchyChangedDetails & details)253 void DialogClientView::ViewHierarchyChanged(
254     const ViewHierarchyChangedDetails& details) {
255   ClientView::ViewHierarchyChanged(details);
256   if (details.is_add && details.child == this) {
257     // The old dialog style needs an explicit background color, while the new
258     // dialog style simply inherits the bubble's frame view color.
259     const DialogDelegate* dialog = GetDialogDelegate();
260     if (dialog && !dialog->UseNewStyleForThisDialog())
261       set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
262           GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
263 
264     focus_manager_ = GetFocusManager();
265     if (focus_manager_)
266       GetFocusManager()->AddFocusChangeListener(this);
267 
268     UpdateDialogButtons();
269     CreateExtraView();
270     CreateFootnoteView();
271   } else if (!details.is_add && details.child == this) {
272     if (focus_manager_)
273       focus_manager_->RemoveFocusChangeListener(this);
274     focus_manager_ = NULL;
275   } else if (!details.is_add) {
276     if (details.child == default_button_)
277       default_button_ = NULL;
278     if (details.child == ok_button_)
279       ok_button_ = NULL;
280     if (details.child == cancel_button_)
281       cancel_button_ = NULL;
282   }
283 }
284 
NativeViewHierarchyChanged()285 void DialogClientView::NativeViewHierarchyChanged() {
286   FocusManager* focus_manager = GetFocusManager();
287   if (focus_manager_ != focus_manager) {
288     if (focus_manager_)
289       focus_manager_->RemoveFocusChangeListener(this);
290     focus_manager_ = focus_manager;
291     if (focus_manager_)
292       focus_manager_->AddFocusChangeListener(this);
293   }
294 }
295 
296 ////////////////////////////////////////////////////////////////////////////////
297 // DialogClientView, ButtonListener implementation:
298 
ButtonPressed(Button * sender,const ui::Event & event)299 void DialogClientView::ButtonPressed(Button* sender, const ui::Event& event) {
300   // Check for a valid delegate to avoid handling events after destruction.
301   if (!GetDialogDelegate())
302     return;
303 
304   if (sender == ok_button_)
305     AcceptWindow();
306   else if (sender == cancel_button_)
307     CancelWindow();
308   else
309     NOTREACHED();
310 }
311 
312 ////////////////////////////////////////////////////////////////////////////////
313 // DialogClientView, protected:
314 
DialogClientView(View * contents_view)315 DialogClientView::DialogClientView(View* contents_view)
316     : ClientView(NULL, contents_view),
317       ok_button_(NULL),
318       cancel_button_(NULL),
319       default_button_(NULL),
320       focus_manager_(NULL),
321       extra_view_(NULL),
322       footnote_view_(NULL),
323       notified_delegate_(false) {}
324 
GetDialogDelegate() const325 DialogDelegate* DialogClientView::GetDialogDelegate() const {
326   return GetWidget()->widget_delegate()->AsDialogDelegate();
327 }
328 
CreateExtraView()329 void DialogClientView::CreateExtraView() {
330   if (extra_view_)
331     return;
332 
333   extra_view_ = GetDialogDelegate()->CreateExtraView();
334   if (extra_view_) {
335     extra_view_->SetGroup(kButtonGroup);
336     AddChildView(extra_view_);
337   }
338 }
339 
CreateFootnoteView()340 void DialogClientView::CreateFootnoteView() {
341   if (footnote_view_)
342     return;
343 
344   footnote_view_ = GetDialogDelegate()->CreateFootnoteView();
345   if (footnote_view_)
346     AddChildView(footnote_view_);
347 }
348 
ChildPreferredSizeChanged(View * child)349 void DialogClientView::ChildPreferredSizeChanged(View* child) {
350   if (child == footnote_view_ || child == extra_view_)
351     Layout();
352 }
353 
ChildVisibilityChanged(View * child)354 void DialogClientView::ChildVisibilityChanged(View* child) {
355   ChildPreferredSizeChanged(child);
356 }
357 
358 ////////////////////////////////////////////////////////////////////////////////
359 // DialogClientView, private:
360 
CreateDialogButton(ui::DialogButton type)361 LabelButton* DialogClientView::CreateDialogButton(ui::DialogButton type) {
362   const string16 title = GetDialogDelegate()->GetDialogButtonLabel(type);
363   LabelButton* button = NULL;
364   if (GetDialogDelegate()->UseNewStyleForThisDialog() &&
365       GetDialogDelegate()->GetDefaultDialogButton() == type &&
366       GetDialogDelegate()->ShouldDefaultButtonBeBlue()) {
367     button = new BlueButton(this, title);
368   } else {
369     button = new LabelButton(this, title);
370     button->SetStyle(Button::STYLE_NATIVE_TEXTBUTTON);
371   }
372   button->SetFocusable(true);
373 
374   const int kDialogMinButtonWidth = 75;
375   button->set_min_size(gfx::Size(kDialogMinButtonWidth, 0));
376   button->SetGroup(kButtonGroup);
377   return button;
378 }
379 
UpdateButton(LabelButton * button,ui::DialogButton type)380 void DialogClientView::UpdateButton(LabelButton* button,
381                                     ui::DialogButton type) {
382   DialogDelegate* dialog = GetDialogDelegate();
383   button->SetText(dialog->GetDialogButtonLabel(type));
384   button->SetEnabled(dialog->IsDialogButtonEnabled(type));
385 
386   if (type == dialog->GetDefaultDialogButton()) {
387     default_button_ = button;
388     button->SetIsDefault(true);
389   }
390 }
391 
GetButtonsAndExtraViewRowHeight() const392 int DialogClientView::GetButtonsAndExtraViewRowHeight() const {
393   int extra_view_height = ShouldShow(extra_view_) ?
394       extra_view_->GetPreferredSize().height() : 0;
395   int buttons_height = std::max(
396       ok_button_ ? ok_button_->GetPreferredSize().height() : 0,
397       cancel_button_ ? cancel_button_->GetPreferredSize().height() : 0);
398   return std::max(extra_view_height, buttons_height);
399 }
400 
GetButtonRowInsets() const401 gfx::Insets DialogClientView::GetButtonRowInsets() const {
402   // NOTE: The insets only apply to the buttons, extra view, and footnote view.
403   return GetButtonsAndExtraViewRowHeight() == 0 ? gfx::Insets() :
404       gfx::Insets(0, kButtonHEdgeMarginNew,
405                   kButtonVEdgeMarginNew, kButtonHEdgeMarginNew);
406 }
407 
Close()408 void DialogClientView::Close() {
409   GetWidget()->Close();
410   GetDialogDelegate()->OnClosed();
411 }
412 
413 }  // namespace views
414