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/browser.h"
13#include "chrome/browser/ui/browser_window.h"
14#include "chrome/browser/ui/views/constrained_window_views.h"
15#include "extensions/common/extension.h"
16#include "grit/generated_resources.h"
17#include "ui/base/l10n/l10n_util.h"
18#include "ui/compositor/compositor.h"
19#include "ui/compositor/layer.h"
20#include "ui/views/controls/image_view.h"
21#include "ui/views/controls/label.h"
22#include "ui/views/layout/layout_constants.h"
23#include "ui/views/view.h"
24#include "ui/views/widget/widget.h"
25#include "ui/views/window/dialog_delegate.h"
26
27#if defined(USE_ASH)
28#include "ash/shell.h"
29#endif
30
31namespace {
32
33const int kRightColumnWidth = 210;
34const int kIconSize = 69;
35
36class ExtensionUninstallDialogDelegateView;
37
38// Returns parent window for extension uninstall dialog.
39gfx::NativeWindow GetParent(Browser* browser) {
40  if (browser && browser->window())
41    return browser->window()->GetNativeWindow();
42  return NULL;
43}
44
45// Views implementation of the uninstall dialog.
46class ExtensionUninstallDialogViews : public ExtensionUninstallDialog {
47 public:
48  ExtensionUninstallDialogViews(Profile* profile,
49                                Browser* browser,
50                                ExtensionUninstallDialog::Delegate* delegate);
51  virtual ~ExtensionUninstallDialogViews();
52
53  // Forwards the accept and cancels to the delegate.
54  void ExtensionUninstallAccepted();
55  void ExtensionUninstallCanceled();
56
57  ExtensionUninstallDialogDelegateView* view() { return view_; }
58
59 private:
60  virtual void Show() OVERRIDE;
61
62  ExtensionUninstallDialogDelegateView* view_;
63  bool show_in_app_list_;
64
65  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews);
66};
67
68// The dialog's view, owned by the views framework.
69class ExtensionUninstallDialogDelegateView : public views::DialogDelegateView {
70 public:
71  ExtensionUninstallDialogDelegateView(
72      ExtensionUninstallDialogViews* dialog_view,
73      const extensions::Extension* extension,
74      gfx::ImageSkia* icon);
75  virtual ~ExtensionUninstallDialogDelegateView();
76
77  // Called when the ExtensionUninstallDialog has been destroyed to make sure
78  // we invalidate pointers.
79  void DialogDestroyed() { dialog_ = NULL; }
80
81 private:
82  // views::DialogDelegate:
83  virtual base::string16 GetDialogButtonLabel(
84      ui::DialogButton button) const OVERRIDE;
85  virtual int GetDefaultDialogButton() const OVERRIDE {
86    return ui::DIALOG_BUTTON_CANCEL;
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() OVERRIDE;
99
100  virtual void Layout() OVERRIDE;
101
102  ExtensionUninstallDialogViews* dialog_;
103
104  views::ImageView* icon_;
105  views::Label* heading_;
106
107  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView);
108};
109
110ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
111    Profile* profile,
112    Browser* browser,
113    ExtensionUninstallDialog::Delegate* delegate)
114    : ExtensionUninstallDialog(profile, browser, delegate),
115      view_(NULL),
116      show_in_app_list_(!browser) {
117}
118
119ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
120  // Close the widget (the views framework will delete view_).
121  if (view_) {
122    view_->DialogDestroyed();
123    view_->GetWidget()->CloseNow();
124  }
125}
126
127void ExtensionUninstallDialogViews::Show() {
128  // TODO(tapted): A true |desktop_type| needs to be passed in at creation time
129  // to remove reliance on GetActiveDesktop(). http://crbug.com/308360
130  gfx::NativeWindow parent = show_in_app_list_ ?
131      AppListService::Get(chrome::GetActiveDesktop())->GetAppListWindow() :
132      GetParent(browser_);
133  if (browser_ && !parent) {
134    delegate_->ExtensionUninstallCanceled();
135    return;
136  }
137
138  view_ = new ExtensionUninstallDialogDelegateView(this, extension_, &icon_);
139  CreateBrowserModalDialogViews(view_, parent)->Show();
140}
141
142void ExtensionUninstallDialogViews::ExtensionUninstallAccepted() {
143  // The widget gets destroyed when the dialog is accepted.
144  view_ = NULL;
145  delegate_->ExtensionUninstallAccepted();
146}
147
148void ExtensionUninstallDialogViews::ExtensionUninstallCanceled() {
149  // The widget gets destroyed when the dialog is canceled.
150  view_ = NULL;
151  delegate_->ExtensionUninstallCanceled();
152}
153
154ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
155    ExtensionUninstallDialogViews* dialog_view,
156    const extensions::Extension* extension,
157    gfx::ImageSkia* icon)
158    : dialog_(dialog_view) {
159  // Scale down to icon size, but allow smaller icons (don't scale up).
160  gfx::Size size(icon->width(), icon->height());
161  if (size.width() > kIconSize || size.height() > kIconSize)
162    size = gfx::Size(kIconSize, kIconSize);
163  icon_ = new views::ImageView();
164  icon_->SetImageSize(size);
165  icon_->SetImage(*icon);
166  AddChildView(icon_);
167
168  heading_ = new views::Label(
169      l10n_util::GetStringFUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
170                                 UTF8ToUTF16(extension->name())));
171  heading_->SetMultiLine(true);
172  AddChildView(heading_);
173}
174
175ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
176}
177
178base::string16 ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
179    ui::DialogButton button) const {
180  return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK) ?
181      IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON : IDS_CANCEL);
182}
183
184bool ExtensionUninstallDialogDelegateView::Accept() {
185  if (dialog_)
186    dialog_->ExtensionUninstallAccepted();
187  return true;
188}
189
190bool ExtensionUninstallDialogDelegateView::Cancel() {
191  if (dialog_)
192    dialog_->ExtensionUninstallCanceled();
193  return true;
194}
195
196base::string16 ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
197  return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE);
198}
199
200gfx::Size ExtensionUninstallDialogDelegateView::GetPreferredSize() {
201  int width = kRightColumnWidth;
202  width += kIconSize;
203  width += views::kButtonHEdgeMarginNew * 2;
204  width += views::kRelatedControlHorizontalSpacing;
205
206  int height = views::kPanelVertMargin * 2;
207  height += heading_->GetHeightForWidth(kRightColumnWidth);
208
209  return gfx::Size(width,
210                   std::max(height, kIconSize + views::kPanelVertMargin * 2));
211}
212
213void ExtensionUninstallDialogDelegateView::Layout() {
214  int x = views::kButtonHEdgeMarginNew;
215  int y = views::kPanelVertMargin;
216
217  heading_->SizeToFit(kRightColumnWidth);
218
219  if (heading_->height() <= kIconSize) {
220    icon_->SetBounds(x, y, kIconSize, kIconSize);
221    x += kIconSize;
222    x += views::kRelatedControlHorizontalSpacing;
223
224    heading_->SetX(x);
225    heading_->SetY(y + (kIconSize - heading_->height()) / 2);
226  } else {
227    icon_->SetBounds(x,
228                     y + (heading_->height() - kIconSize) / 2,
229                     kIconSize,
230                     kIconSize);
231    x += kIconSize;
232    x += views::kRelatedControlHorizontalSpacing;
233
234    heading_->SetX(x);
235    heading_->SetY(y);
236  }
237}
238
239}  // namespace
240
241// static
242ExtensionUninstallDialog* ExtensionUninstallDialog::Create(
243    Profile* profile,
244    Browser* browser,
245    Delegate* delegate) {
246  return new ExtensionUninstallDialogViews(profile, browser, delegate);
247}
248