media_galleries_dialog_views.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 "components/web_modal/web_contents_modal_dialog_host.h"
10#include "components/web_modal/web_contents_modal_dialog_manager.h"
11#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
12#include "content/public/browser/web_contents.h"
13#include "grit/generated_resources.h"
14#include "grit/locale_settings.h"
15#include "third_party/skia/include/core/SkColor.h"
16#include "ui/base/l10n/l10n_util.h"
17#include "ui/native_theme/native_theme.h"
18#include "ui/views/border.h"
19#include "ui/views/controls/button/checkbox.h"
20#include "ui/views/controls/button/label_button.h"
21#include "ui/views/controls/label.h"
22#include "ui/views/controls/menu/menu_runner.h"
23#include "ui/views/controls/scroll_view.h"
24#include "ui/views/controls/separator.h"
25#include "ui/views/layout/box_layout.h"
26#include "ui/views/layout/grid_layout.h"
27#include "ui/views/layout/layout_constants.h"
28#include "ui/views/view.h"
29#include "ui/views/widget/widget.h"
30#include "ui/views/window/dialog_client_view.h"
31
32using web_modal::WebContentsModalDialogManager;
33using web_modal::WebContentsModalDialogManagerDelegate;
34
35namespace {
36
37// Equal to the #969696 color used in spec (note WebUI color is #999).
38const SkColor kDeemphasizedTextColor = SkColorSetRGB(159, 159, 159);
39
40const int kScrollAreaHeight = 192;
41
42// This container has the right Layout() impl to use within a ScrollView.
43class ScrollableView : public views::View {
44 public:
45  ScrollableView() {}
46  virtual ~ScrollableView() {}
47
48  virtual void Layout() OVERRIDE;
49
50 private:
51  DISALLOW_COPY_AND_ASSIGN(ScrollableView);
52};
53
54void ScrollableView::Layout() {
55  gfx::Size pref = GetPreferredSize();
56  int width = pref.width();
57  int height = pref.height();
58  if (parent()) {
59    width = std::max(parent()->width(), width);
60    height = std::max(parent()->height(), height);
61  }
62  SetBounds(x(), y(), width, height);
63
64  views::View::Layout();
65}
66
67}  // namespace
68
69typedef MediaGalleriesDialogController::GalleryPermissionsVector
70    GalleryPermissionsVector;
71
72MediaGalleriesDialogViews::MediaGalleriesDialogViews(
73    MediaGalleriesDialogController* controller)
74    : controller_(controller),
75      window_(NULL),
76      contents_(new views::View()),
77      add_gallery_button_(NULL),
78      confirm_available_(false),
79      accepted_(false) {
80  InitChildViews();
81
82  // Ownership of |contents_| is handed off by this call. |window_| will take
83  // care of deleting itself after calling DeleteDelegate().
84  WebContentsModalDialogManager* web_contents_modal_dialog_manager =
85      WebContentsModalDialogManager::FromWebContents(
86          controller->web_contents());
87  DCHECK(web_contents_modal_dialog_manager);
88  WebContentsModalDialogManagerDelegate* modal_delegate =
89      web_contents_modal_dialog_manager->delegate();
90  DCHECK(modal_delegate);
91  window_ = views::Widget::CreateWindowAsFramelessChild(
92      this, modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
93  web_contents_modal_dialog_manager->ShowDialog(window_->GetNativeView());
94}
95
96MediaGalleriesDialogViews::~MediaGalleriesDialogViews() {}
97
98void MediaGalleriesDialogViews::InitChildViews() {
99  // Outer dialog layout.
100  contents_->RemoveAllChildViews(true);
101  int dialog_content_width = views::Widget::GetLocalizedContentsWidth(
102      IDS_MEDIA_GALLERIES_DIALOG_CONTENT_WIDTH_CHARS);
103  views::GridLayout* layout = views::GridLayout::CreatePanel(contents_);
104  contents_->SetLayoutManager(layout);
105
106  int column_set_id = 0;
107  views::ColumnSet* columns = layout->AddColumnSet(column_set_id);
108  columns->AddColumn(views::GridLayout::LEADING,
109                     views::GridLayout::LEADING,
110                     1,
111                     views::GridLayout::FIXED,
112                     dialog_content_width,
113                     0);
114
115  // Message text.
116  views::Label* subtext = new views::Label(controller_->GetSubtext());
117  subtext->SetMultiLine(true);
118  subtext->SetHorizontalAlignment(gfx::ALIGN_LEFT);
119  layout->StartRow(0, column_set_id);
120  layout->AddView(
121      subtext, 1, 1,
122      views::GridLayout::FILL, views::GridLayout::LEADING,
123      dialog_content_width, subtext->GetHeightForWidth(dialog_content_width));
124  layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
125
126  // Scrollable area for checkboxes.
127  ScrollableView* scroll_container = new ScrollableView();
128  scroll_container->SetLayoutManager(new views::BoxLayout(
129      views::BoxLayout::kVertical, 0, 0,
130      views::kRelatedControlSmallVerticalSpacing));
131  scroll_container->set_border(views::Border::CreateEmptyBorder(
132      views::kRelatedControlVerticalSpacing,
133      0,
134      views::kRelatedControlVerticalSpacing,
135      0));
136
137  // Add attached galleries checkboxes.
138  checkbox_map_.clear();
139  new_checkbox_map_.clear();
140  GalleryPermissionsVector permissions = controller_->AttachedPermissions();
141  for (GalleryPermissionsVector::const_iterator iter = permissions.begin();
142       iter != permissions.end(); ++iter) {
143    int spacing = 0;
144    if (iter + 1 == permissions.end())
145      spacing = views::kRelatedControlSmallVerticalSpacing;
146    AddOrUpdateGallery(iter->pref_info, iter->allowed, scroll_container,
147                       spacing);
148  }
149
150  GalleryPermissionsVector unattached_permissions =
151      controller_->UnattachedPermissions();
152
153  if (!unattached_permissions.empty()) {
154    // Separator line.
155    views::Separator* separator = new views::Separator(
156        views::Separator::HORIZONTAL);
157    scroll_container->AddChildView(separator);
158
159    // Unattached locations section.
160    views::Label* unattached_text = new views::Label(
161        controller_->GetUnattachedLocationsHeader());
162    unattached_text->SetMultiLine(true);
163    unattached_text->SetHorizontalAlignment(gfx::ALIGN_LEFT);
164    unattached_text->set_border(views::Border::CreateEmptyBorder(
165        views::kRelatedControlVerticalSpacing,
166        views::kPanelHorizMargin,
167        views::kRelatedControlVerticalSpacing,
168        0));
169    scroll_container->AddChildView(unattached_text);
170
171    // Add unattached galleries checkboxes.
172    for (GalleryPermissionsVector::const_iterator iter =
173             unattached_permissions.begin();
174         iter != unattached_permissions.end(); ++iter) {
175      AddOrUpdateGallery(iter->pref_info, iter->allowed, scroll_container, 0);
176    }
177  }
178
179  confirm_available_ = controller_->HasPermittedGalleries();
180
181  // Add the scrollable area to the outer dialog view. It will squeeze against
182  // the title/subtitle and buttons to occupy all available space in the dialog.
183  views::ScrollView* scroll_view =
184      views::ScrollView::CreateScrollViewWithBorder();
185  scroll_view->SetContents(scroll_container);
186  layout->StartRowWithPadding(1, column_set_id,
187                              0, views::kRelatedControlVerticalSpacing);
188  layout->AddView(scroll_view, 1, 1,
189                  views::GridLayout::FILL, views::GridLayout::FILL,
190                  dialog_content_width, kScrollAreaHeight);
191}
192
193void MediaGalleriesDialogViews::UpdateGalleries() {
194  InitChildViews();
195  contents_->Layout();
196}
197
198bool MediaGalleriesDialogViews::AddOrUpdateGallery(
199    const MediaGalleryPrefInfo& gallery,
200    bool permitted,
201    views::View* container,
202    int trailing_vertical_space) {
203  base::string16 label = gallery.GetGalleryDisplayName();
204  base::string16 tooltip_text = gallery.GetGalleryTooltip();
205  base::string16 details = gallery.GetGalleryAdditionalDetails();
206
207  CheckboxMap::iterator iter = checkbox_map_.find(gallery.pref_id);
208  if (iter != checkbox_map_.end() &&
209      gallery.pref_id != kInvalidMediaGalleryPrefId) {
210    views::Checkbox* checkbox = iter->second;
211    checkbox->SetChecked(permitted);
212    checkbox->SetText(label);
213    checkbox->SetElideBehavior(views::Label::ELIDE_IN_MIDDLE);
214    checkbox->SetTooltipText(tooltip_text);
215    // Replace the details string.
216    views::View* checkbox_view = checkbox->parent();
217    DCHECK_EQ(2, checkbox_view->child_count());
218    views::Label* secondary_text =
219        static_cast<views::Label*>(checkbox_view->child_at(1));
220    secondary_text->SetText(details);
221    return false;
222  }
223
224  views::Checkbox* checkbox = new views::Checkbox(label);
225  checkbox->set_listener(this);
226  if (gallery.pref_id != kInvalidMediaGalleryPrefId)
227    checkbox->set_context_menu_controller(this);
228  checkbox->SetTooltipText(tooltip_text);
229  views::Label* secondary_text = new views::Label(details);
230  if (gallery.pref_id != kInvalidMediaGalleryPrefId)
231    secondary_text->set_context_menu_controller(this);
232  secondary_text->SetTooltipText(tooltip_text);
233  secondary_text->SetEnabledColor(kDeemphasizedTextColor);
234  secondary_text->SetTooltipText(tooltip_text);
235  secondary_text->set_border(views::Border::CreateEmptyBorder(
236      0,
237      views::kRelatedControlSmallHorizontalSpacing,
238      0,
239      views::kRelatedControlSmallHorizontalSpacing));
240
241  views::View* checkbox_view = new views::View();
242  if (gallery.pref_id != kInvalidMediaGalleryPrefId)
243    checkbox_view->set_context_menu_controller(this);
244  checkbox_view->set_border(views::Border::CreateEmptyBorder(
245      0,
246      views::kPanelHorizMargin,
247      trailing_vertical_space,
248      0));
249  checkbox_view->SetLayoutManager(
250      new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
251  checkbox_view->AddChildView(checkbox);
252  checkbox_view->AddChildView(secondary_text);
253
254  container->AddChildView(checkbox_view);
255
256  checkbox->SetChecked(permitted);
257  if (gallery.pref_id != kInvalidMediaGalleryPrefId)
258    checkbox_map_[gallery.pref_id] = checkbox;
259  else
260    new_checkbox_map_[checkbox] = gallery;
261
262  return true;
263}
264
265string16 MediaGalleriesDialogViews::GetWindowTitle() const {
266  return controller_->GetHeader();
267}
268
269void MediaGalleriesDialogViews::DeleteDelegate() {
270  controller_->DialogFinished(accepted_);
271}
272
273views::Widget* MediaGalleriesDialogViews::GetWidget() {
274  return contents_->GetWidget();
275}
276
277const views::Widget* MediaGalleriesDialogViews::GetWidget() const {
278  return contents_->GetWidget();
279}
280
281views::View* MediaGalleriesDialogViews::GetContentsView() {
282  return contents_;
283}
284
285string16 MediaGalleriesDialogViews::GetDialogButtonLabel(
286    ui::DialogButton button) const {
287  return l10n_util::GetStringUTF16(button == ui::DIALOG_BUTTON_OK ?
288      IDS_MEDIA_GALLERIES_DIALOG_CONFIRM :
289      IDS_MEDIA_GALLERIES_DIALOG_CANCEL);
290}
291
292bool MediaGalleriesDialogViews::IsDialogButtonEnabled(
293    ui::DialogButton button) const {
294  return button != ui::DIALOG_BUTTON_OK || confirm_available_;
295}
296
297ui::ModalType MediaGalleriesDialogViews::GetModalType() const {
298#if defined(USE_ASH)
299  return ui::MODAL_TYPE_CHILD;
300#else
301  return views::WidgetDelegate::GetModalType();
302#endif
303}
304
305views::View* MediaGalleriesDialogViews::CreateExtraView() {
306  DCHECK(!add_gallery_button_);
307  add_gallery_button_ = new views::LabelButton(this,
308      l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY));
309  add_gallery_button_->SetStyle(views::Button::STYLE_NATIVE_TEXTBUTTON);
310  return add_gallery_button_;
311}
312
313bool MediaGalleriesDialogViews::Cancel() {
314  return true;
315}
316
317bool MediaGalleriesDialogViews::Accept() {
318  accepted_ = true;
319
320  return true;
321}
322
323// TODO(wittman): Remove this override once we move to the new style frame view
324// on all dialogs.
325views::NonClientFrameView* MediaGalleriesDialogViews::CreateNonClientFrameView(
326    views::Widget* widget) {
327  return CreateConstrainedStyleNonClientFrameView(
328      widget,
329      controller_->web_contents()->GetBrowserContext());
330}
331
332void MediaGalleriesDialogViews::ButtonPressed(views::Button* sender,
333                                              const ui::Event& event) {
334  confirm_available_ = true;
335  GetWidget()->client_view()->AsDialogClientView()->UpdateDialogButtons();
336
337  if (sender == add_gallery_button_) {
338    controller_->OnAddFolderClicked();
339    return;
340  }
341
342  for (CheckboxMap::const_iterator iter = checkbox_map_.begin();
343       iter != checkbox_map_.end(); ++iter) {
344    if (sender == iter->second) {
345      controller_->DidToggleGalleryId(iter->first,
346                                      iter->second->checked());
347      return;
348    }
349  }
350  for (NewCheckboxMap::const_iterator iter = new_checkbox_map_.begin();
351       iter != new_checkbox_map_.end(); ++iter) {
352    if (sender == iter->first) {
353      controller_->DidToggleNewGallery(iter->second, iter->first->checked());
354    }
355  }
356}
357
358void MediaGalleriesDialogViews::ShowContextMenuForView(
359    views::View* source,
360    const gfx::Point& point,
361    ui::MenuSourceType source_type) {
362  for (CheckboxMap::const_iterator iter = checkbox_map_.begin();
363       iter != checkbox_map_.end(); ++iter) {
364    if (iter->second->parent()->Contains(source)) {
365      ShowContextMenu(point, source_type, iter->first);
366      return;
367    }
368  }
369}
370
371void MediaGalleriesDialogViews::ShowContextMenu(const gfx::Point& point,
372                                                ui::MenuSourceType source_type,
373                                                MediaGalleryPrefId id) {
374  context_menu_runner_.reset(new views::MenuRunner(
375      controller_->GetContextMenuModel(id)));
376
377  if (context_menu_runner_->RunMenuAt(
378          GetWidget(), NULL, gfx::Rect(point.x(), point.y(), 0, 0),
379          views::MenuItemView::TOPLEFT, source_type,
380          views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU) ==
381      views::MenuRunner::MENU_DELETED) {
382    return;
383  }
384}
385
386// MediaGalleriesDialogViewsController -----------------------------------------
387
388// static
389MediaGalleriesDialog* MediaGalleriesDialog::Create(
390    MediaGalleriesDialogController* controller) {
391  return new MediaGalleriesDialogViews(controller);
392}
393