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