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 "chrome/browser/ui/views/extensions/media_galleries_dialog_views.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/ui/views/constrained_window_views.h"
9#include "chrome/browser/ui/views/extensions/media_gallery_checkbox_view.h"
10#include "chrome/grit/generated_resources.h"
11#include "chrome/grit/locale_settings.h"
12#include "components/web_modal/popup_manager.h"
13#include "content/public/browser/web_contents.h"
14#include "ui/base/l10n/l10n_util.h"
15#include "ui/native_theme/native_theme.h"
16#include "ui/views/border.h"
17#include "ui/views/controls/button/checkbox.h"
18#include "ui/views/controls/button/image_button.h"
19#include "ui/views/controls/button/label_button.h"
20#include "ui/views/controls/label.h"
21#include "ui/views/controls/menu/menu_runner.h"
22#include "ui/views/controls/scroll_view.h"
23#include "ui/views/controls/separator.h"
24#include "ui/views/layout/box_layout.h"
25#include "ui/views/layout/grid_layout.h"
26#include "ui/views/layout/layout_constants.h"
27#include "ui/views/view.h"
28#include "ui/views/widget/widget.h"
29#include "ui/views/window/dialog_client_view.h"
30
31namespace {
32
33const int kScrollAreaHeight = 192;
34
35// This container has the right Layout() impl to use within a ScrollView.
36class ScrollableView : public views::View {
37 public:
38  ScrollableView() {}
39  virtual ~ScrollableView() {}
40
41  virtual void Layout() OVERRIDE;
42
43 private:
44  DISALLOW_COPY_AND_ASSIGN(ScrollableView);
45};
46
47void ScrollableView::Layout() {
48  gfx::Size pref = GetPreferredSize();
49  int width = pref.width();
50  int height = pref.height();
51  if (parent()) {
52    width = parent()->width();
53    height = std::max(parent()->height(), height);
54  }
55  SetBounds(x(), y(), width, height);
56
57  views::View::Layout();
58}
59
60}  // namespace
61
62MediaGalleriesDialogViews::MediaGalleriesDialogViews(
63    MediaGalleriesDialogController* controller)
64    : controller_(controller),
65      contents_(new views::View()),
66      auxiliary_button_(NULL),
67      confirm_available_(false),
68      accepted_(false) {
69  InitChildViews();
70  if (ControllerHasWebContents())
71    ShowWebModalDialogViews(this, controller->WebContents());
72}
73
74MediaGalleriesDialogViews::~MediaGalleriesDialogViews() {
75  if (!ControllerHasWebContents())
76    delete contents_;
77}
78
79void MediaGalleriesDialogViews::AcceptDialogForTesting() {
80  accepted_ = true;
81
82  web_modal::PopupManager* popup_manager =
83      web_modal::PopupManager::FromWebContents(controller_->WebContents());
84  DCHECK(popup_manager);
85  popup_manager->CloseAllDialogsForTesting(controller_->WebContents());
86}
87
88void MediaGalleriesDialogViews::InitChildViews() {
89  // Outer dialog layout.
90  contents_->RemoveAllChildViews(true);
91  checkbox_map_.clear();
92
93  int dialog_content_width = views::Widget::GetLocalizedContentsWidth(
94      IDS_MEDIA_GALLERIES_DIALOG_CONTENT_WIDTH_CHARS);
95  views::GridLayout* layout = views::GridLayout::CreatePanel(contents_);
96  contents_->SetLayoutManager(layout);
97
98  int column_set_id = 0;
99  views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
100  columns->AddColumn(views::GridLayout::LEADING,
101                     views::GridLayout::LEADING,
102                     1,
103                     views::GridLayout::FIXED,
104                     dialog_content_width,
105                     0);
106
107  // Message text.
108  views::Label* subtext = new views::Label(controller_->GetSubtext());
109  subtext->SetMultiLine(true);
110  subtext->SetHorizontalAlignment(gfx::ALIGN_LEFT);
111  layout->StartRow(0, column_set_id);
112  layout->AddView(
113      subtext, 1, 1,
114      views::GridLayout::FILL, views::GridLayout::LEADING,
115      dialog_content_width, subtext->GetHeightForWidth(dialog_content_width));
116  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
117
118  // Scrollable area for checkboxes.
119  ScrollableView* scroll_container = new ScrollableView();
120  scroll_container->SetLayoutManager(new views::BoxLayout(
121      views::BoxLayout::kVertical, 0, 0,
122      views::kRelatedControlSmallVerticalSpacing));
123  scroll_container->SetBorder(
124      views::Border::CreateEmptyBorder(views::kRelatedControlVerticalSpacing,
125                                       0,
126                                       views::kRelatedControlVerticalSpacing,
127                                       0));
128
129  std::vector<base::string16> section_headers =
130      controller_->GetSectionHeaders();
131  for (size_t i = 0; i < section_headers.size(); i++) {
132    MediaGalleriesDialogController::Entries entries =
133        controller_->GetSectionEntries(i);
134
135    // Header and separator line.
136    if (!section_headers[i].empty() && !entries.empty()) {
137      views::Separator* separator = new views::Separator(
138          views::Separator::HORIZONTAL);
139      scroll_container->AddChildView(separator);
140
141      views::Label* header = new views::Label(section_headers[i]);
142      header->SetMultiLine(true);
143      header->SetHorizontalAlignment(gfx::ALIGN_LEFT);
144      header->SetBorder(views::Border::CreateEmptyBorder(
145          views::kRelatedControlVerticalSpacing,
146          views::kPanelHorizMargin,
147          views::kRelatedControlVerticalSpacing,
148          0));
149      scroll_container->AddChildView(header);
150    }
151
152    // Checkboxes.
153    MediaGalleriesDialogController::Entries::const_iterator iter;
154    for (iter = entries.begin(); iter != entries.end(); ++iter) {
155      int spacing = 0;
156      if (iter + 1 == entries.end())
157        spacing = views::kRelatedControlSmallVerticalSpacing;
158      AddOrUpdateGallery(*iter, scroll_container, spacing);
159    }
160  }
161
162  confirm_available_ = controller_->IsAcceptAllowed();
163
164  // Add the scrollable area to the outer dialog view. It will squeeze against
165  // the title/subtitle and buttons to occupy all available space in the dialog.
166  views::ScrollView* scroll_view =
167      views::ScrollView::CreateScrollViewWithBorder();
168  scroll_view->SetContents(scroll_container);
169  layout->StartRowWithPadding(1, column_set_id,
170                              0, views::kRelatedControlVerticalSpacing);
171  layout->AddView(scroll_view, 1, 1,
172                  views::GridLayout::FILL, views::GridLayout::FILL,
173                  dialog_content_width, kScrollAreaHeight);
174}
175
176void MediaGalleriesDialogViews::UpdateGalleries() {
177  InitChildViews();
178  contents_->Layout();
179
180  if (ControllerHasWebContents())
181    GetWidget()->client_view()->AsDialogClientView()->UpdateDialogButtons();
182}
183
184bool MediaGalleriesDialogViews::AddOrUpdateGallery(
185    const MediaGalleriesDialogController::Entry& gallery,
186    views::View* container,
187    int trailing_vertical_space) {
188  bool show_folder_viewer = controller_->ShouldShowFolderViewer(gallery);
189
190  CheckboxMap::iterator iter = checkbox_map_.find(gallery.pref_info.pref_id);
191  if (iter != checkbox_map_.end()) {
192    views::Checkbox* checkbox = iter->second->checkbox();
193    checkbox->SetChecked(gallery.selected);
194    checkbox->SetText(gallery.pref_info.GetGalleryDisplayName());
195    checkbox->SetTooltipText(gallery.pref_info.GetGalleryTooltip());
196    base::string16 details = gallery.pref_info.GetGalleryAdditionalDetails();
197    iter->second->secondary_text()->SetText(details);
198    iter->second->secondary_text()->SetVisible(details.length() > 0);
199    iter->second->folder_viewer_button()->SetVisible(show_folder_viewer);
200    return false;
201  }
202
203  MediaGalleryCheckboxView* gallery_view =
204      new MediaGalleryCheckboxView(gallery.pref_info, show_folder_viewer,
205                                   trailing_vertical_space, this, this);
206  gallery_view->checkbox()->SetChecked(gallery.selected);
207  container->AddChildView(gallery_view);
208  checkbox_map_[gallery.pref_info.pref_id] = gallery_view;
209
210  return true;
211}
212
213base::string16 MediaGalleriesDialogViews::GetWindowTitle() const {
214  return controller_->GetHeader();
215}
216
217void MediaGalleriesDialogViews::DeleteDelegate() {
218  controller_->DialogFinished(accepted_);
219}
220
221views::Widget* MediaGalleriesDialogViews::GetWidget() {
222  return contents_->GetWidget();
223}
224
225const views::Widget* MediaGalleriesDialogViews::GetWidget() const {
226  return contents_->GetWidget();
227}
228
229views::View* MediaGalleriesDialogViews::GetContentsView() {
230  return contents_;
231}
232
233base::string16 MediaGalleriesDialogViews::GetDialogButtonLabel(
234    ui::DialogButton button) const {
235  if (button == ui::DIALOG_BUTTON_OK)
236    return controller_->GetAcceptButtonText();
237  return l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_CANCEL);
238}
239
240bool MediaGalleriesDialogViews::IsDialogButtonEnabled(
241    ui::DialogButton button) const {
242  return button != ui::DIALOG_BUTTON_OK || confirm_available_;
243}
244
245ui::ModalType MediaGalleriesDialogViews::GetModalType() const {
246  return ui::MODAL_TYPE_CHILD;
247}
248
249views::View* MediaGalleriesDialogViews::CreateExtraView() {
250  DCHECK(!auxiliary_button_);
251  base::string16 button_label = controller_->GetAuxiliaryButtonText();
252  if (!button_label.empty()) {
253    auxiliary_button_ = new views::LabelButton(this, button_label);
254    auxiliary_button_->SetStyle(views::Button::STYLE_BUTTON);
255  }
256  return auxiliary_button_;
257}
258
259bool MediaGalleriesDialogViews::Cancel() {
260  return true;
261}
262
263bool MediaGalleriesDialogViews::Accept() {
264  accepted_ = true;
265  return true;
266}
267
268void MediaGalleriesDialogViews::ButtonPressed(views::Button* sender,
269                                              const ui::Event& /* event */) {
270  confirm_available_ = true;
271
272  if (ControllerHasWebContents())
273    GetWidget()->client_view()->AsDialogClientView()->UpdateDialogButtons();
274
275  if (sender == auxiliary_button_) {
276    controller_->DidClickAuxiliaryButton();
277    return;
278  }
279
280  for (CheckboxMap::const_iterator iter = checkbox_map_.begin();
281       iter != checkbox_map_.end(); ++iter) {
282    if (sender == iter->second->checkbox()) {
283      controller_->DidToggleEntry(iter->first,
284                                  iter->second->checkbox()->checked());
285      return;
286    }
287    if (sender == iter->second->folder_viewer_button()) {
288      controller_->DidClickOpenFolderViewer(iter->first);
289      return;
290    }
291  }
292}
293
294void MediaGalleriesDialogViews::ShowContextMenuForView(
295    views::View* source,
296    const gfx::Point& point,
297    ui::MenuSourceType source_type) {
298  for (CheckboxMap::const_iterator iter = checkbox_map_.begin();
299       iter != checkbox_map_.end(); ++iter) {
300    if (iter->second->Contains(source)) {
301      ShowContextMenu(point, source_type, iter->first);
302      return;
303    }
304  }
305}
306
307void MediaGalleriesDialogViews::ShowContextMenu(const gfx::Point& point,
308                                                ui::MenuSourceType source_type,
309                                                MediaGalleryPrefId id) {
310  context_menu_runner_.reset(new views::MenuRunner(
311      controller_->GetContextMenu(id),
312      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU));
313
314  if (context_menu_runner_->RunMenuAt(GetWidget(),
315                                      NULL,
316                                      gfx::Rect(point.x(), point.y(), 0, 0),
317                                      views::MENU_ANCHOR_TOPLEFT,
318                                      source_type) ==
319      views::MenuRunner::MENU_DELETED) {
320    return;
321  }
322}
323
324bool MediaGalleriesDialogViews::ControllerHasWebContents() const {
325  return controller_->WebContents() != NULL;
326}
327
328// MediaGalleriesDialogViewsController -----------------------------------------
329
330// static
331MediaGalleriesDialog* MediaGalleriesDialog::Create(
332    MediaGalleriesDialogController* controller) {
333  return new MediaGalleriesDialogViews(controller);
334}
335