extension_uninstall_dialog_view.cc revision e4256316f8b5e8d1ec0df1f7762771622a53fa63
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/extensions/extension_uninstall_dialog.h"
6
7#include "base/basictypes.h"
8#include "base/compiler_specific.h"
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "chrome/browser/ui/app_list/app_list_service.h"
12#include "chrome/browser/ui/views/constrained_window_views.h"
13#include "chrome/grit/generated_resources.h"
14#include "extensions/common/extension.h"
15#include "ui/aura/window_tracker.h"
16#include "ui/base/l10n/l10n_util.h"
17#include "ui/compositor/compositor.h"
18#include "ui/compositor/layer.h"
19#include "ui/views/controls/image_view.h"
20#include "ui/views/controls/label.h"
21#include "ui/views/layout/layout_constants.h"
22#include "ui/views/view.h"
23#include "ui/views/widget/widget.h"
24#include "ui/views/window/dialog_delegate.h"
25
26namespace {
27
28const int kRightColumnWidth = 210;
29const int kIconSize = 64;
30
31class ExtensionUninstallDialogDelegateView;
32
33// Views implementation of the uninstall dialog.
34class ExtensionUninstallDialogViews
35    : public extensions::ExtensionUninstallDialog {
36 public:
37  ExtensionUninstallDialogViews(
38      Profile* profile,
39      aura::Window* parent,
40      extensions::ExtensionUninstallDialog::Delegate* delegate);
41  virtual ~ExtensionUninstallDialogViews();
42
43  // Called when the ExtensionUninstallDialogDelegate has been destroyed to make
44  // sure we invalidate pointers.
45  void DialogDelegateDestroyed() { view_ = NULL; }
46
47  // Forwards the accept and cancels to the delegate.
48  void ExtensionUninstallAccepted();
49  void ExtensionUninstallCanceled();
50
51 private:
52  virtual void Show() OVERRIDE;
53
54  ExtensionUninstallDialogDelegateView* view_;
55
56  // The dialog's parent window.
57  aura::Window* parent_;
58
59  // Tracks whether |parent_| got destroyed.
60  aura::WindowTracker parent_window_tracker_;
61
62  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews);
63};
64
65// The dialog's view, owned by the views framework.
66class ExtensionUninstallDialogDelegateView : public views::DialogDelegateView {
67 public:
68  ExtensionUninstallDialogDelegateView(
69      ExtensionUninstallDialogViews* dialog_view,
70      const extensions::Extension* extension,
71      const extensions::Extension* triggering_extension,
72      gfx::ImageSkia* image);
73  virtual ~ExtensionUninstallDialogDelegateView();
74
75  // Called when the ExtensionUninstallDialog has been destroyed to make sure
76  // we invalidate pointers.
77  void DialogDestroyed() { dialog_ = NULL; }
78
79 private:
80  // views::DialogDelegate:
81  virtual base::string16 GetDialogButtonLabel(
82      ui::DialogButton button) const OVERRIDE;
83  virtual int GetDefaultDialogButton() const OVERRIDE {
84    // Default to accept when triggered via chrome://extensions page.
85    return triggered_by_extension_ ?
86        ui::DIALOG_BUTTON_CANCEL : ui::DIALOG_BUTTON_OK;
87  }
88  virtual bool Accept() OVERRIDE;
89  virtual bool Cancel() OVERRIDE;
90
91  // views::WidgetDelegate:
92  virtual ui::ModalType GetModalType() const OVERRIDE {
93    return ui::MODAL_TYPE_WINDOW;
94  }
95  virtual base::string16 GetWindowTitle() const OVERRIDE;
96
97  // views::View:
98  virtual gfx::Size GetPreferredSize() const OVERRIDE;
99
100  virtual void Layout() OVERRIDE;
101
102  ExtensionUninstallDialogViews* dialog_;
103
104  views::ImageView* icon_;
105  views::Label* heading_;
106  bool triggered_by_extension_;
107
108  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView);
109};
110
111ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
112    Profile* profile,
113    aura::Window* parent,
114    extensions::ExtensionUninstallDialog::Delegate* delegate)
115    : extensions::ExtensionUninstallDialog(profile, delegate),
116      view_(NULL),
117      parent_(parent) {
118  if (parent_)
119    parent_window_tracker_.Add(parent_);
120}
121
122ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
123  // Close the widget (the views framework will delete view_).
124  if (view_) {
125    view_->DialogDestroyed();
126    view_->GetWidget()->CloseNow();
127  }
128}
129
130void ExtensionUninstallDialogViews::Show() {
131  if (parent_ && !parent_window_tracker_.Contains(parent_)) {
132    delegate_->ExtensionUninstallCanceled();
133    return;
134  }
135
136  view_ = new ExtensionUninstallDialogDelegateView(
137      this, extension_, triggering_extension_, &icon_);
138  CreateBrowserModalDialogViews(view_, parent_)->Show();
139}
140
141void ExtensionUninstallDialogViews::ExtensionUninstallAccepted() {
142  // The widget gets destroyed when the dialog is accepted.
143  view_->DialogDestroyed();
144  view_ = NULL;
145  delegate_->ExtensionUninstallAccepted();
146}
147
148void ExtensionUninstallDialogViews::ExtensionUninstallCanceled() {
149  // The widget gets destroyed when the dialog is canceled.
150  view_->DialogDestroyed();
151  view_ = NULL;
152  delegate_->ExtensionUninstallCanceled();
153}
154
155ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
156    ExtensionUninstallDialogViews* dialog_view,
157    const extensions::Extension* extension,
158    const extensions::Extension* triggering_extension,
159    gfx::ImageSkia* image)
160    : dialog_(dialog_view),
161      triggered_by_extension_(triggering_extension != NULL) {
162  // Scale down to icon size, but allow smaller icons (don't scale up).
163  gfx::Size size(image->width(), image->height());
164  if (size.width() > kIconSize || size.height() > kIconSize)
165    size = gfx::Size(kIconSize, kIconSize);
166  icon_ = new views::ImageView();
167  icon_->SetImageSize(size);
168  icon_->SetImage(*image);
169  AddChildView(icon_);
170
171  heading_ = new views::Label(base::UTF8ToUTF16(dialog_->GetHeadingText()));
172  heading_->SetMultiLine(true);
173  heading_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
174  AddChildView(heading_);
175}
176
177ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
178  // If we're here, 2 things could have happened. Either the user closed the
179  // dialog nicely and one of ExtensionUninstallAccepted or
180  // ExtensionUninstallCanceled has been called (in which case dialog_ will be
181  // NULL), *or* neither of them have been called and we are being forced closed
182  // by our parent widget. In this case, we need to make sure to notify dialog_
183  // not to call us again, since we're about to be freed by the Widget
184  // framework.
185  if (dialog_)
186    dialog_->DialogDelegateDestroyed();
187}
188
189base::string16 ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
190    ui::DialogButton button) const {
191  return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK) ?
192      IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON : IDS_CANCEL);
193}
194
195bool ExtensionUninstallDialogDelegateView::Accept() {
196  if (dialog_)
197    dialog_->ExtensionUninstallAccepted();
198  return true;
199}
200
201bool ExtensionUninstallDialogDelegateView::Cancel() {
202  if (dialog_)
203    dialog_->ExtensionUninstallCanceled();
204  return true;
205}
206
207base::string16 ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
208  return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE);
209}
210
211gfx::Size ExtensionUninstallDialogDelegateView::GetPreferredSize() const {
212  int width = kRightColumnWidth;
213  width += kIconSize;
214  width += views::kButtonHEdgeMarginNew * 2;
215  width += views::kRelatedControlHorizontalSpacing;
216
217  int height = views::kPanelVertMargin * 2;
218  height += heading_->GetHeightForWidth(kRightColumnWidth);
219
220  return gfx::Size(width,
221                   std::max(height, kIconSize + views::kPanelVertMargin * 2));
222}
223
224void ExtensionUninstallDialogDelegateView::Layout() {
225  int x = views::kButtonHEdgeMarginNew;
226  int y = views::kPanelVertMargin;
227
228  heading_->SizeToFit(kRightColumnWidth);
229
230  if (heading_->height() <= kIconSize) {
231    icon_->SetBounds(x, y, kIconSize, kIconSize);
232    x += kIconSize;
233    x += views::kRelatedControlHorizontalSpacing;
234
235    heading_->SetX(x);
236    heading_->SetY(y + (kIconSize - heading_->height()) / 2);
237  } else {
238    icon_->SetBounds(x,
239                     y + (heading_->height() - kIconSize) / 2,
240                     kIconSize,
241                     kIconSize);
242    x += kIconSize;
243    x += views::kRelatedControlHorizontalSpacing;
244
245    heading_->SetX(x);
246    heading_->SetY(y);
247  }
248}
249
250}  // namespace
251
252// static
253extensions::ExtensionUninstallDialog*
254extensions::ExtensionUninstallDialog::Create(Profile* profile,
255                                             gfx::NativeWindow parent,
256                                             Delegate* delegate) {
257  return new ExtensionUninstallDialogViews(profile, parent, delegate);
258}
259