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 "chrome/common/extensions/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// TODO(estade): remove this when UseNewStyle() is the default.
39int HorizontalMargin() {
40  return views::DialogDelegate::UseNewStyle() ? views::kButtonHEdgeMarginNew :
41                                                views::kPanelHorizMargin;
42}
43
44// Returns parent window for extension uninstall dialog.
45// For platforms with an app list, use the app list window if it is visible.
46// For other platforms or when app list is not visible, use the given browser
47// window.
48// Note this function could return NULL if the app list is not visible and
49// there is no browser window.
50gfx::NativeWindow GetParent(Browser* browser) {
51  gfx::NativeWindow window = AppListService::Get()->GetAppListWindow();
52  if (window)
53    return window;
54
55  if (browser && browser->window())
56    return browser->window()->GetNativeWindow();
57
58  return NULL;
59}
60
61// Views implementation of the uninstall dialog.
62class ExtensionUninstallDialogViews : public ExtensionUninstallDialog {
63 public:
64  ExtensionUninstallDialogViews(Profile* profile,
65                                Browser* browser,
66                                ExtensionUninstallDialog::Delegate* delegate);
67  virtual ~ExtensionUninstallDialogViews();
68
69  // Forwards the accept and cancels to the delegate.
70  void ExtensionUninstallAccepted();
71  void ExtensionUninstallCanceled();
72
73  ExtensionUninstallDialogDelegateView* view() { return view_; }
74
75 private:
76  virtual void Show() OVERRIDE;
77
78  ExtensionUninstallDialogDelegateView* view_;
79
80  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogViews);
81};
82
83// The dialog's view, owned by the views framework.
84class ExtensionUninstallDialogDelegateView : public views::DialogDelegateView {
85 public:
86  ExtensionUninstallDialogDelegateView(
87      ExtensionUninstallDialogViews* dialog_view,
88      const extensions::Extension* extension,
89      gfx::ImageSkia* icon);
90  virtual ~ExtensionUninstallDialogDelegateView();
91
92  // Called when the ExtensionUninstallDialog has been destroyed to make sure
93  // we invalidate pointers.
94  void DialogDestroyed() { dialog_ = NULL; }
95
96 private:
97  // views::DialogDelegate:
98  virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE;
99  virtual int GetDefaultDialogButton() const OVERRIDE {
100    return ui::DIALOG_BUTTON_CANCEL;
101  }
102  virtual bool Accept() OVERRIDE;
103  virtual bool Cancel() OVERRIDE;
104
105  // views::WidgetDelegate:
106  virtual ui::ModalType GetModalType() const OVERRIDE {
107    return ui::MODAL_TYPE_WINDOW;
108  }
109  virtual string16 GetWindowTitle() const OVERRIDE;
110
111  // views::View:
112  virtual gfx::Size GetPreferredSize() OVERRIDE;
113
114  virtual void Layout() OVERRIDE;
115
116  ExtensionUninstallDialogViews* dialog_;
117
118  views::ImageView* icon_;
119  views::Label* heading_;
120
121  DISALLOW_COPY_AND_ASSIGN(ExtensionUninstallDialogDelegateView);
122};
123
124ExtensionUninstallDialogViews::ExtensionUninstallDialogViews(
125    Profile* profile,
126    Browser* browser,
127    ExtensionUninstallDialog::Delegate* delegate)
128    : ExtensionUninstallDialog(profile, browser, delegate),
129      view_(NULL) {
130}
131
132ExtensionUninstallDialogViews::~ExtensionUninstallDialogViews() {
133  // Close the widget (the views framework will delete view_).
134  if (view_) {
135    view_->DialogDestroyed();
136    view_->GetWidget()->CloseNow();
137  }
138}
139
140void ExtensionUninstallDialogViews::Show() {
141  gfx::NativeWindow parent = GetParent(browser_);
142  if (browser_ && !parent) {
143    delegate_->ExtensionUninstallCanceled();
144    return;
145  }
146
147  view_ = new ExtensionUninstallDialogDelegateView(this, extension_, &icon_);
148  CreateBrowserModalDialogViews(view_, parent)->Show();
149}
150
151void ExtensionUninstallDialogViews::ExtensionUninstallAccepted() {
152  // The widget gets destroyed when the dialog is accepted.
153  view_ = NULL;
154  delegate_->ExtensionUninstallAccepted();
155}
156
157void ExtensionUninstallDialogViews::ExtensionUninstallCanceled() {
158  // The widget gets destroyed when the dialog is canceled.
159  view_ = NULL;
160  delegate_->ExtensionUninstallCanceled();
161}
162
163ExtensionUninstallDialogDelegateView::ExtensionUninstallDialogDelegateView(
164    ExtensionUninstallDialogViews* dialog_view,
165    const extensions::Extension* extension,
166    gfx::ImageSkia* icon)
167    : dialog_(dialog_view) {
168  // Scale down to icon size, but allow smaller icons (don't scale up).
169  gfx::Size size(icon->width(), icon->height());
170  if (size.width() > kIconSize || size.height() > kIconSize)
171    size = gfx::Size(kIconSize, kIconSize);
172  icon_ = new views::ImageView();
173  icon_->SetImageSize(size);
174  icon_->SetImage(*icon);
175  AddChildView(icon_);
176
177  heading_ = new views::Label(
178      l10n_util::GetStringFUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
179                                 UTF8ToUTF16(extension->name())));
180  heading_->SetMultiLine(true);
181  AddChildView(heading_);
182}
183
184ExtensionUninstallDialogDelegateView::~ExtensionUninstallDialogDelegateView() {
185}
186
187string16 ExtensionUninstallDialogDelegateView::GetDialogButtonLabel(
188    ui::DialogButton button) const {
189  return l10n_util::GetStringUTF16((button == ui::DIALOG_BUTTON_OK) ?
190      IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON : IDS_CANCEL);
191}
192
193bool ExtensionUninstallDialogDelegateView::Accept() {
194  if (dialog_)
195    dialog_->ExtensionUninstallAccepted();
196  return true;
197}
198
199bool ExtensionUninstallDialogDelegateView::Cancel() {
200  if (dialog_)
201    dialog_->ExtensionUninstallCanceled();
202  return true;
203}
204
205string16 ExtensionUninstallDialogDelegateView::GetWindowTitle() const {
206  return l10n_util::GetStringUTF16(IDS_EXTENSION_UNINSTALL_PROMPT_TITLE);
207}
208
209gfx::Size ExtensionUninstallDialogDelegateView::GetPreferredSize() {
210  int width = kRightColumnWidth;
211  width += kIconSize;
212  width += HorizontalMargin() * 2;
213  width += views::kRelatedControlHorizontalSpacing;
214
215  int height = views::kPanelVertMargin * 2;
216  height += heading_->GetHeightForWidth(kRightColumnWidth);
217
218  return gfx::Size(width,
219                   std::max(height, kIconSize + views::kPanelVertMargin * 2));
220}
221
222void ExtensionUninstallDialogDelegateView::Layout() {
223  int x = HorizontalMargin();
224  int y = views::kPanelVertMargin;
225
226  heading_->SizeToFit(kRightColumnWidth);
227
228  if (heading_->height() <= kIconSize) {
229    icon_->SetBounds(x, y, kIconSize, kIconSize);
230    x += kIconSize;
231    x += views::kRelatedControlHorizontalSpacing;
232
233    heading_->SetX(x);
234    heading_->SetY(y + (kIconSize - heading_->height()) / 2);
235  } else {
236    icon_->SetBounds(x,
237                     y + (heading_->height() - kIconSize) / 2,
238                     kIconSize,
239                     kIconSize);
240    x += kIconSize;
241    x += views::kRelatedControlHorizontalSpacing;
242
243    heading_->SetX(x);
244    heading_->SetY(y);
245  }
246}
247
248}  // namespace
249
250// static
251ExtensionUninstallDialog* ExtensionUninstallDialog::Create(
252    Profile* profile,
253    Browser* browser,
254    Delegate* delegate) {
255  return new ExtensionUninstallDialogViews(profile, browser, delegate);
256}
257