• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/chromeos/native_dialog_window.h"
6 
7 #include <gtk/gtk.h>
8 
9 #include "base/logging.h"
10 #include "base/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/frame/bubble_window.h"
12 #include "chrome/browser/ui/views/window.h"
13 #include "ui/base/gtk/gtk_signal.h"
14 #include "views/controls/native/native_view_host.h"
15 #include "views/window/dialog_delegate.h"
16 #include "views/window/non_client_view.h"
17 #include "views/window/window.h"
18 
19 namespace {
20 
21 const int kDialogPadding = 3;
22 
23 const char kNativeDialogHost[] = "_chromeos_native_dialog_host_";
24 
25 // TODO(xiyuan): Use gtk_window_get_default_widget with GTK 2.14+.
26 // Gets the default widget of given dialog.
GetDialogDefaultWidget(GtkDialog * dialog)27 GtkWidget* GetDialogDefaultWidget(GtkDialog* dialog) {
28   GtkWidget* default_widget = NULL;
29 
30   GList* children = gtk_container_get_children(
31       GTK_CONTAINER(dialog->action_area));
32 
33   GList* current = children;
34   while (current) {
35     GtkWidget* widget = reinterpret_cast<GtkWidget*>(current->data);
36     if (GTK_WIDGET_HAS_DEFAULT(widget)) {
37       default_widget = widget;
38       break;
39     }
40 
41     current = g_list_next(current);
42   }
43 
44   g_list_free(children);
45 
46   return default_widget;
47 }
48 
49 }  // namespace
50 
51 namespace chromeos {
52 
53 class NativeDialogHost : public views::View,
54                          public views::DialogDelegate {
55  public:
56   NativeDialogHost(gfx::NativeView native_dialog,
57                    int flags,
58                    const gfx::Size& size,
59                    const gfx::Size& min_size);
60   ~NativeDialogHost();
61 
62   // views::DialogDelegate implementation:
CanResize() const63   virtual bool CanResize() const { return flags_ & DIALOG_FLAG_RESIZEABLE; }
GetDialogButtons() const64   virtual int GetDialogButtons() const { return 0; }
GetWindowTitle() const65   virtual std::wstring GetWindowTitle() const { return title_; }
GetContentsView()66   virtual views::View* GetContentsView() { return this; }
IsModal() const67   virtual bool IsModal() const { return flags_ & DIALOG_FLAG_MODAL; }
68   virtual void WindowClosing();
69 
70  protected:
71   CHROMEGTK_CALLBACK_0(NativeDialogHost, void, OnCheckResize);
72   CHROMEGTK_CALLBACK_0(NativeDialogHost, void, OnDialogDestroy);
73 
74   // views::View implementation:
75   virtual gfx::Size GetPreferredSize();
76   virtual void Layout();
77   virtual void ViewHierarchyChanged(bool is_add,
78                                     views::View* parent,
79                                     views::View* child);
80  private:
81   // Init and attach to native dialog.
82   void Init();
83 
84   // Check and apply minimum size restriction.
85   void CheckSize();
86 
87   // The GtkDialog whose vbox will be displayed in this view.
88   gfx::NativeView dialog_;
89 
90   // NativeViewHost for the dialog's contents.
91   views::NativeViewHost* contents_view_;
92 
93   std::wstring title_;
94   int flags_;
95   gfx::Size size_;
96   gfx::Size preferred_size_;
97   gfx::Size min_size_;
98 
99   int destroy_signal_id_;
100 
101   DISALLOW_IMPLICIT_CONSTRUCTORS(NativeDialogHost);
102 };
103 
104 ///////////////////////////////////////////////////////////////////////////////
105 // NativeDialogHost, public:
106 
NativeDialogHost(gfx::NativeView native_dialog,int flags,const gfx::Size & size,const gfx::Size & min_size)107 NativeDialogHost::NativeDialogHost(gfx::NativeView native_dialog,
108                                    int flags,
109                                    const gfx::Size& size,
110                                    const gfx::Size& min_size)
111     : dialog_(native_dialog),
112       contents_view_(NULL),
113       flags_(flags),
114       size_(size),
115       preferred_size_(size),
116       min_size_(min_size),
117       destroy_signal_id_(0) {
118   const char* title = gtk_window_get_title(GTK_WINDOW(dialog_));
119   if (title)
120     UTF8ToWide(title, strlen(title), &title_);
121 
122   destroy_signal_id_ = g_signal_connect(dialog_, "destroy",
123       G_CALLBACK(&OnDialogDestroyThunk), this);
124 }
125 
~NativeDialogHost()126 NativeDialogHost::~NativeDialogHost() {
127 }
128 
OnCheckResize(GtkWidget * widget)129 void NativeDialogHost::OnCheckResize(GtkWidget* widget) {
130   // Do auto height resize only when we are asked to do so.
131   if (size_.height() == 0) {
132     gfx::NativeView contents = contents_view_->native_view();
133 
134     // Check whether preferred height has changed. We keep the current width
135     // unchanged and pass "-1" as height to let gtk calculate a proper height.
136     gtk_widget_set_size_request(contents, width(), -1);
137     GtkRequisition requsition = { 0 };
138     gtk_widget_size_request(contents, &requsition);
139 
140     if (preferred_size_.height() != requsition.height) {
141       preferred_size_.set_width(requsition.width);
142       preferred_size_.set_height(requsition.height);
143       CheckSize();
144       SizeToPreferredSize();
145 
146       gfx::Size window_size = window()->non_client_view()->GetPreferredSize();
147       gfx::Rect window_bounds = window()->GetBounds();
148       window_bounds.set_width(window_size.width());
149       window_bounds.set_height(window_size.height());
150       window()->SetWindowBounds(window_bounds, NULL);
151     }
152   }
153 }
154 
OnDialogDestroy(GtkWidget * widget)155 void NativeDialogHost::OnDialogDestroy(GtkWidget* widget) {
156   dialog_ = NULL;
157   destroy_signal_id_ = 0;
158   window()->CloseWindow();
159 }
160 
161 ///////////////////////////////////////////////////////////////////////////////
162 // NativeDialogHost, views::DialogDelegate implementation:
WindowClosing()163 void NativeDialogHost::WindowClosing() {
164   if (dialog_) {
165     // Disconnect the "destroy" signal because we are about to destroy
166     // the dialog ourselves and no longer interested in it.
167     g_signal_handler_disconnect(G_OBJECT(dialog_), destroy_signal_id_);
168     gtk_dialog_response(GTK_DIALOG(dialog_), GTK_RESPONSE_DELETE_EVENT);
169   }
170 }
171 
172 ///////////////////////////////////////////////////////////////////////////////
173 // NativeDialogHost, views::View implementation:
174 
GetPreferredSize()175 gfx::Size NativeDialogHost::GetPreferredSize() {
176   return preferred_size_;
177 }
178 
Layout()179 void NativeDialogHost::Layout() {
180   contents_view_->SetBounds(0, 0, width(), height());
181 }
182 
ViewHierarchyChanged(bool is_add,views::View * parent,views::View * child)183 void NativeDialogHost::ViewHierarchyChanged(bool is_add,
184                                             views::View* parent,
185                                             views::View* child) {
186   if (is_add && child == this)
187     Init();
188 }
189 
190 ///////////////////////////////////////////////////////////////////////////////
191 // NativeDialogHost, private:
Init()192 void NativeDialogHost::Init() {
193   if (contents_view_)
194     return;
195 
196   // Get default widget of the dialog.
197   GtkWidget* default_widget = GetDialogDefaultWidget(GTK_DIALOG(dialog_));
198 
199   // Get focus widget of the dialog.
200   GtkWidget* focus_widget = gtk_window_get_focus(GTK_WINDOW(dialog_));
201 
202   // Create a GtkAlignment as dialog contents container.
203   GtkWidget* contents = gtk_alignment_new(0.5, 0.5, 1.0, 1.0);
204   gtk_alignment_set_padding(GTK_ALIGNMENT(contents),
205       kDialogPadding, kDialogPadding,
206       kDialogPadding, kDialogPadding);
207 
208   // Move dialog contents into our container.
209   GtkWidget* dialog_contents = GTK_DIALOG(dialog_)->vbox;
210   g_object_ref(dialog_contents);
211   gtk_container_remove(GTK_CONTAINER(dialog_), dialog_contents);
212   gtk_container_add(GTK_CONTAINER(contents), dialog_contents);
213   g_object_unref(dialog_contents);
214   gtk_widget_hide(dialog_);
215 
216   g_object_set_data(G_OBJECT(dialog_), kNativeDialogHost,
217       reinterpret_cast<gpointer>(this));
218 
219   gtk_widget_show_all(contents);
220 
221   contents_view_ = new views::NativeViewHost();
222   // TODO(xiyuan): Find a better way to get proper background.
223   contents_view_->set_background(views::Background::CreateSolidBackground(
224       BubbleWindow::kBackgroundColor));
225   AddChildView(contents_view_);
226   contents_view_->Attach(contents);
227 
228   g_signal_connect(window()->GetNativeWindow(), "check-resize",
229       G_CALLBACK(&OnCheckResizeThunk), this);
230 
231   const int padding = 2 * kDialogPadding;
232   // Use gtk's default size if size is not specified.
233   if (size_.IsEmpty()) {
234     // Use given width or height if given.
235     if (size_.width() || size_.height()) {
236       int width = size_.width() == 0 ? -1 : size_.width() + padding;
237       int height = size_.height() == 0 ? -1 : size_.height() + padding;
238       gtk_widget_set_size_request(contents, width, height);
239     }
240 
241     GtkRequisition requsition = { 0 };
242     gtk_widget_size_request(contents, &requsition);
243     preferred_size_.set_width(requsition.width);
244     preferred_size_.set_height(requsition.height);
245   } else {
246     preferred_size_.set_width(size_.width() + padding);
247     preferred_size_.set_height(size_.height() + padding);
248   }
249 
250   CheckSize();
251 
252   if (default_widget)
253     gtk_widget_grab_default(default_widget);
254 
255   if (focus_widget)
256     gtk_widget_grab_focus(focus_widget);
257 }
258 
CheckSize()259 void NativeDialogHost::CheckSize() {
260   // Apply the minimum size.
261   if (preferred_size_.width() < min_size_.width())
262     preferred_size_.set_width(min_size_.width());
263   if (preferred_size_.height() < min_size_.height())
264     preferred_size_.set_height(min_size_.height());
265 }
266 
ShowNativeDialog(gfx::NativeWindow parent,gfx::NativeView native_dialog,int flags,const gfx::Size & size,const gfx::Size & min_size)267 void ShowNativeDialog(gfx::NativeWindow parent,
268                       gfx::NativeView native_dialog,
269                       int flags,
270                       const gfx::Size& size,
271                       const gfx::Size& min_size) {
272   NativeDialogHost* native_dialog_host =
273       new NativeDialogHost(native_dialog, flags, size, min_size);
274   browser::CreateViewsWindow(parent, gfx::Rect(), native_dialog_host);
275   native_dialog_host->window()->Show();
276 }
277 
GetNativeDialogWindow(gfx::NativeView native_dialog)278 gfx::NativeWindow GetNativeDialogWindow(gfx::NativeView native_dialog) {
279   NativeDialogHost* host = reinterpret_cast<NativeDialogHost*>(
280       g_object_get_data(G_OBJECT(native_dialog), kNativeDialogHost));
281   return host ? host->window()->GetNativeWindow() : NULL;
282 }
283 
GetNativeDialogContentsBounds(gfx::NativeView native_dialog)284 gfx::Rect GetNativeDialogContentsBounds(gfx::NativeView native_dialog) {
285   NativeDialogHost* host = reinterpret_cast<NativeDialogHost*>(
286       g_object_get_data(G_OBJECT(native_dialog), kNativeDialogHost));
287   return host ? host->bounds() : gfx::Rect();
288 }
289 
290 }  // namespace chromeos
291