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