OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include <gdk/gdk.h> |
| 6 #include <gdk/gdkx.h> |
5 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
6 #include <map> | 8 #include <map> |
7 #include <set> | 9 #include <set> |
8 #include <vector> | 10 #include <vector> |
9 | 11 |
| 12 // Xlib defines RootWindow |
| 13 #undef RootWindow |
| 14 |
10 #include "base/file_util.h" | 15 #include "base/file_util.h" |
11 #include "base/logging.h" | 16 #include "base/logging.h" |
12 #include "base/message_loop.h" | 17 #include "base/message_loop.h" |
13 #include "base/string_util.h" | 18 #include "base/string_util.h" |
14 #include "base/sys_string_conversions.h" | 19 #include "base/sys_string_conversions.h" |
15 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
16 #include "base/threading/thread_restrictions.h" | 21 #include "base/threading/thread_restrictions.h" |
17 #include "base/utf_string_conversions.h" | 22 #include "base/utf_string_conversions.h" |
18 //#include "chrome/browser/ui/gtk/select_file_dialog_impl.h" | 23 #include "chrome/browser/ui/libgtk2ui/gtk2_signal.h" |
19 #include "chrome/browser/ui/select_file_dialog.h" | 24 #include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h" |
20 #include "grit/generated_resources.h" | 25 #include "grit/ui_strings.h" |
21 //#include "ui/base/gtk/gtk_signal.h" | 26 #include "ui/aura/root_window.h" |
| 27 #include "ui/aura/window_observer.h" |
| 28 #include "ui/base/dialogs/select_file_dialog.h" |
22 #include "ui/base/l10n/l10n_util.h" | 29 #include "ui/base/l10n/l10n_util.h" |
23 | 30 |
| 31 namespace { |
| 32 |
| 33 const char kAuraTransientParent[] = "aura-transient-parent"; |
| 34 |
| 35 // Set |dialog| as transient for |parent|, which will keep it on top and center |
| 36 // it above |parent|. |
| 37 void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent) { |
| 38 gtk_widget_realize(dialog); |
| 39 GdkWindow* gdk_window = gtk_widget_get_window(dialog); |
| 40 |
| 41 // TODO(erg): Check to make sure we're using X11 if wayland or some other |
| 42 // display server ever happens. Otherwise, this will crash. |
| 43 XSetTransientForHint(GDK_WINDOW_XDISPLAY(gdk_window), |
| 44 GDK_WINDOW_XID(gdk_window), |
| 45 parent->GetRootWindow()->GetAcceleratedWidget()); |
| 46 |
| 47 // We also set the |parent| as a property of |dialog|, so that we can unlink |
| 48 // the two later. |
| 49 g_object_set_data(G_OBJECT(dialog), kAuraTransientParent, parent); |
| 50 } |
| 51 |
| 52 } // namespace |
| 53 |
| 54 namespace libgtk2ui { |
| 55 |
24 // Implementation of SelectFileDialog that shows a Gtk common dialog for | 56 // Implementation of SelectFileDialog that shows a Gtk common dialog for |
25 // choosing a file or folder. This acts as a modal dialog. | 57 // choosing a file or folder. This acts as a modal dialog. |
26 class SelectFileDialogImplGTK : public SelectFileDialogImpl { | 58 class SelectFileDialogImplGTK : public SelectFileDialogImpl, |
| 59 public aura::WindowObserver { |
27 public: | 60 public: |
28 explicit SelectFileDialogImplGTK(Listener* listener, | 61 explicit SelectFileDialogImplGTK(Listener* listener, |
29 ui::SelectFilePolicy* policy); | 62 ui::SelectFilePolicy* policy); |
30 | 63 |
31 protected: | 64 protected: |
32 virtual ~SelectFileDialogImplGTK(); | 65 virtual ~SelectFileDialogImplGTK(); |
33 | 66 |
34 // SelectFileDialog implementation. | 67 // SelectFileDialog implementation. |
35 // |params| is user data we pass back via the Listener interface. | 68 // |params| is user data we pass back via the Listener interface. |
36 virtual void SelectFileImpl(Type type, | 69 virtual void SelectFileImpl(Type type, |
37 const string16& title, | 70 const string16& title, |
38 const FilePath& default_path, | 71 const FilePath& default_path, |
39 const FileTypeInfo* file_types, | 72 const FileTypeInfo* file_types, |
40 int file_type_index, | 73 int file_type_index, |
41 const FilePath::StringType& default_extension, | 74 const FilePath::StringType& default_extension, |
42 gfx::NativeWindow owning_window, | 75 gfx::NativeWindow owning_window, |
43 void* params) OVERRIDE; | 76 void* params) OVERRIDE; |
44 | 77 |
45 private: | 78 private: |
46 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE; | 79 virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE; |
47 | 80 |
| 81 // Overridden from aura::WindowObserver: |
| 82 virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; |
| 83 |
48 // Add the filters from |file_types_| to |chooser|. | 84 // Add the filters from |file_types_| to |chooser|. |
49 void AddFilters(GtkFileChooser* chooser); | 85 void AddFilters(GtkFileChooser* chooser); |
50 | 86 |
51 // Notifies the listener that a single file was chosen. | 87 // Notifies the listener that a single file was chosen. |
52 void FileSelected(GtkWidget* dialog, const FilePath& path); | 88 void FileSelected(GtkWidget* dialog, const FilePath& path); |
53 | 89 |
54 // Notifies the listener that multiple files were chosen. | 90 // Notifies the listener that multiple files were chosen. |
55 void MultiFilesSelected(GtkWidget* dialog, | 91 void MultiFilesSelected(GtkWidget* dialog, |
56 const std::vector<FilePath>& files); | 92 const std::vector<FilePath>& files); |
57 | 93 |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
146 SelectFileDialogImplGTK::~SelectFileDialogImplGTK() { | 182 SelectFileDialogImplGTK::~SelectFileDialogImplGTK() { |
147 while (dialogs_.begin() != dialogs_.end()) { | 183 while (dialogs_.begin() != dialogs_.end()) { |
148 gtk_widget_destroy(*(dialogs_.begin())); | 184 gtk_widget_destroy(*(dialogs_.begin())); |
149 } | 185 } |
150 } | 186 } |
151 | 187 |
152 bool SelectFileDialogImplGTK::HasMultipleFileTypeChoicesImpl() { | 188 bool SelectFileDialogImplGTK::HasMultipleFileTypeChoicesImpl() { |
153 return file_types_.extensions.size() > 1; | 189 return file_types_.extensions.size() > 1; |
154 } | 190 } |
155 | 191 |
| 192 void SelectFileDialogImplGTK::OnWindowDestroying(aura::Window* window) { |
| 193 std::set<aura::Window*>::iterator iter = parents_.find(window); |
| 194 if (iter != parents_.end()) { |
| 195 (*iter)->RemoveObserver(this); |
| 196 parents_.erase(iter); |
| 197 } |
| 198 } |
| 199 |
156 // We ignore |default_extension|. | 200 // We ignore |default_extension|. |
157 void SelectFileDialogImplGTK::SelectFileImpl( | 201 void SelectFileDialogImplGTK::SelectFileImpl( |
158 Type type, | 202 Type type, |
159 const string16& title, | 203 const string16& title, |
160 const FilePath& default_path, | 204 const FilePath& default_path, |
161 const FileTypeInfo* file_types, | 205 const FileTypeInfo* file_types, |
162 int file_type_index, | 206 int file_type_index, |
163 const FilePath::StringType& default_extension, | 207 const FilePath::StringType& default_extension, |
164 gfx::NativeWindow owning_window, | 208 gfx::NativeWindow owning_window, |
165 void* params) { | 209 void* params) { |
166 type_ = type; | 210 type_ = type; |
167 // |owning_window| can be null when user right-clicks on a downloadable item | 211 // |owning_window| can be null when user right-clicks on a downloadable item |
168 // and chooses 'Open Link in New Tab' when 'Ask where to save each file | 212 // and chooses 'Open Link in New Tab' when 'Ask where to save each file |
169 // before downloading.' preference is turned on. (http://crbug.com/29213) | 213 // before downloading.' preference is turned on. (http://crbug.com/29213) |
170 if (owning_window) | 214 if (owning_window) { |
| 215 owning_window->AddObserver(this); |
171 parents_.insert(owning_window); | 216 parents_.insert(owning_window); |
| 217 } |
172 | 218 |
173 std::string title_string = UTF16ToUTF8(title); | 219 std::string title_string = UTF16ToUTF8(title); |
174 | 220 |
175 file_type_index_ = file_type_index; | 221 file_type_index_ = file_type_index; |
176 if (file_types) | 222 if (file_types) |
177 file_types_ = *file_types; | 223 file_types_ = *file_types; |
178 else | 224 else |
179 file_types_.include_all_files = true; | 225 file_types_.include_all_files = true; |
180 | 226 |
181 GtkWidget* dialog = NULL; | 227 GtkWidget* dialog = NULL; |
(...skipping 22 matching lines...) Expand all Loading... |
204 | 250 |
205 preview_ = gtk_image_new(); | 251 preview_ = gtk_image_new(); |
206 g_signal_connect(dialog, "destroy", | 252 g_signal_connect(dialog, "destroy", |
207 G_CALLBACK(OnFileChooserDestroyThunk), this); | 253 G_CALLBACK(OnFileChooserDestroyThunk), this); |
208 g_signal_connect(dialog, "update-preview", | 254 g_signal_connect(dialog, "update-preview", |
209 G_CALLBACK(OnUpdatePreviewThunk), this); | 255 G_CALLBACK(OnUpdatePreviewThunk), this); |
210 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_); | 256 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_); |
211 | 257 |
212 params_map_[dialog] = params; | 258 params_map_[dialog] = params; |
213 | 259 |
214 // Set window-to-parent modality by adding the dialog to the same window | 260 // TODO(erg): Figure out how to fake GTK window-to-parent modality without |
215 // group as the parent. | 261 // having the parent be a real GtkWindow. |
216 gtk_window_group_add_window(gtk_window_get_group(owning_window), | |
217 GTK_WINDOW(dialog)); | |
218 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); | 262 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); |
219 | 263 |
220 gtk_widget_show_all(dialog); | 264 gtk_widget_show_all(dialog); |
221 } | 265 } |
222 | 266 |
223 void SelectFileDialogImplGTK::AddFilters(GtkFileChooser* chooser) { | 267 void SelectFileDialogImplGTK::AddFilters(GtkFileChooser* chooser) { |
224 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | 268 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { |
225 GtkFileFilter* filter = NULL; | 269 GtkFileFilter* filter = NULL; |
226 std::set<std::string> fallback_labels; | 270 std::set<std::string> fallback_labels; |
227 | 271 |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
303 if (listener_) | 347 if (listener_) |
304 listener_->FileSelectionCanceled(params); | 348 listener_->FileSelectionCanceled(params); |
305 gtk_widget_destroy(dialog); | 349 gtk_widget_destroy(dialog); |
306 } | 350 } |
307 | 351 |
308 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenHelper( | 352 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenHelper( |
309 const std::string& title, | 353 const std::string& title, |
310 const FilePath& default_path, | 354 const FilePath& default_path, |
311 gfx::NativeWindow parent) { | 355 gfx::NativeWindow parent) { |
312 GtkWidget* dialog = | 356 GtkWidget* dialog = |
313 gtk_file_chooser_dialog_new(title.c_str(), parent, | 357 gtk_file_chooser_dialog_new(title.c_str(), NULL, |
314 GTK_FILE_CHOOSER_ACTION_OPEN, | 358 GTK_FILE_CHOOSER_ACTION_OPEN, |
315 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 359 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
316 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 360 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, |
317 NULL); | 361 NULL); |
| 362 SetGtkTransientForAura(dialog, parent); |
318 AddFilters(GTK_FILE_CHOOSER(dialog)); | 363 AddFilters(GTK_FILE_CHOOSER(dialog)); |
319 | 364 |
320 if (!default_path.empty()) { | 365 if (!default_path.empty()) { |
321 if (CallDirectoryExistsOnUIThread(default_path)) { | 366 if (CallDirectoryExistsOnUIThread(default_path)) { |
322 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | 367 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
323 default_path.value().c_str()); | 368 default_path.value().c_str()); |
324 } else { | 369 } else { |
325 // If the file doesn't exist, this will just switch to the correct | 370 // If the file doesn't exist, this will just switch to the correct |
326 // directory. That's good enough. | 371 // directory. That's good enough. |
327 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | 372 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), |
328 default_path.value().c_str()); | 373 default_path.value().c_str()); |
329 } | 374 } |
330 } else if (!last_opened_path_->empty()) { | 375 } else if (!last_opened_path_->empty()) { |
331 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | 376 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
332 last_opened_path_->value().c_str()); | 377 last_opened_path_->value().c_str()); |
333 } | 378 } |
334 return dialog; | 379 return dialog; |
335 } | 380 } |
336 | 381 |
337 GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog( | 382 GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog( |
338 const std::string& title, | 383 const std::string& title, |
339 const FilePath& default_path, | 384 const FilePath& default_path, |
340 gfx::NativeWindow parent) { | 385 gfx::NativeWindow parent) { |
341 std::string title_string = !title.empty() ? title : | 386 std::string title_string = !title.empty() ? title : |
342 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | 387 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); |
343 | 388 |
344 GtkWidget* dialog = | 389 GtkWidget* dialog = |
345 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | 390 gtk_file_chooser_dialog_new(title_string.c_str(), NULL, |
346 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, | 391 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, |
347 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 392 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
348 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | 393 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, |
349 NULL); | 394 NULL); |
| 395 SetGtkTransientForAura(dialog, parent); |
350 | 396 |
351 if (!default_path.empty()) { | 397 if (!default_path.empty()) { |
352 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | 398 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), |
353 default_path.value().c_str()); | 399 default_path.value().c_str()); |
354 } else if (!last_opened_path_->empty()) { | 400 } else if (!last_opened_path_->empty()) { |
355 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | 401 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
356 last_opened_path_->value().c_str()); | 402 last_opened_path_->value().c_str()); |
357 } | 403 } |
358 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | 404 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); |
359 g_signal_connect(dialog, "response", | 405 g_signal_connect(dialog, "response", |
(...skipping 28 matching lines...) Expand all Loading... |
388 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); | 434 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); |
389 return dialog; | 435 return dialog; |
390 } | 436 } |
391 | 437 |
392 GtkWidget* SelectFileDialogImplGTK::CreateSaveAsDialog(const std::string& title, | 438 GtkWidget* SelectFileDialogImplGTK::CreateSaveAsDialog(const std::string& title, |
393 const FilePath& default_path, gfx::NativeWindow parent) { | 439 const FilePath& default_path, gfx::NativeWindow parent) { |
394 std::string title_string = !title.empty() ? title : | 440 std::string title_string = !title.empty() ? title : |
395 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | 441 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); |
396 | 442 |
397 GtkWidget* dialog = | 443 GtkWidget* dialog = |
398 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | 444 gtk_file_chooser_dialog_new(title_string.c_str(), NULL, |
399 GTK_FILE_CHOOSER_ACTION_SAVE, | 445 GTK_FILE_CHOOSER_ACTION_SAVE, |
400 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | 446 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, |
401 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | 447 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, |
402 NULL); | 448 NULL); |
| 449 SetGtkTransientForAura(dialog, parent); |
403 | 450 |
404 AddFilters(GTK_FILE_CHOOSER(dialog)); | 451 AddFilters(GTK_FILE_CHOOSER(dialog)); |
405 if (!default_path.empty()) { | 452 if (!default_path.empty()) { |
406 // Since the file may not already exist, we use | 453 // Since the file may not already exist, we use |
407 // set_current_folder() followed by set_current_name(), as per the | 454 // set_current_folder() followed by set_current_name(), as per the |
408 // recommendation of the GTK docs. | 455 // recommendation of the GTK docs. |
409 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | 456 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), |
410 default_path.DirName().value().c_str()); | 457 default_path.DirName().value().c_str()); |
411 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), | 458 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), |
412 default_path.BaseName().value().c_str()); | 459 default_path.BaseName().value().c_str()); |
(...skipping 18 matching lines...) Expand all Loading... |
431 } | 478 } |
432 | 479 |
433 void SelectFileDialogImplGTK::FileDialogDestroyed(GtkWidget* dialog) { | 480 void SelectFileDialogImplGTK::FileDialogDestroyed(GtkWidget* dialog) { |
434 dialogs_.erase(dialog); | 481 dialogs_.erase(dialog); |
435 | 482 |
436 // Parent may be NULL in a few cases: 1) on shutdown when | 483 // Parent may be NULL in a few cases: 1) on shutdown when |
437 // AllBrowsersClosed() trigger this handler after all the browser | 484 // AllBrowsersClosed() trigger this handler after all the browser |
438 // windows got destroyed, or 2) when the parent tab has been opened by | 485 // windows got destroyed, or 2) when the parent tab has been opened by |
439 // 'Open Link in New Tab' context menu on a downloadable item and | 486 // 'Open Link in New Tab' context menu on a downloadable item and |
440 // the tab has no content (see the comment in SelectFile as well). | 487 // the tab has no content (see the comment in SelectFile as well). |
441 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(dialog)); | 488 aura::Window* parent = reinterpret_cast<aura::Window*>( |
| 489 g_object_get_data(G_OBJECT(dialog), kAuraTransientParent)); |
442 if (!parent) | 490 if (!parent) |
443 return; | 491 return; |
444 std::set<GtkWindow*>::iterator iter = parents_.find(parent); | 492 std::set<aura::Window*>::iterator iter = parents_.find(parent); |
445 if (iter != parents_.end()) | 493 if (iter != parents_.end()) { |
| 494 (*iter)->RemoveObserver(this); |
446 parents_.erase(iter); | 495 parents_.erase(iter); |
447 else | 496 } else { |
448 NOTREACHED(); | 497 NOTREACHED(); |
| 498 } |
449 } | 499 } |
450 | 500 |
451 bool SelectFileDialogImplGTK::IsCancelResponse(gint response_id) { | 501 bool SelectFileDialogImplGTK::IsCancelResponse(gint response_id) { |
452 bool is_cancel = response_id == GTK_RESPONSE_CANCEL || | 502 bool is_cancel = response_id == GTK_RESPONSE_CANCEL || |
453 response_id == GTK_RESPONSE_DELETE_EVENT; | 503 response_id == GTK_RESPONSE_DELETE_EVENT; |
454 if (is_cancel) | 504 if (is_cancel) |
455 return true; | 505 return true; |
456 | 506 |
457 DCHECK(response_id == GTK_RESPONSE_ACCEPT); | 507 DCHECK(response_id == GTK_RESPONSE_ACCEPT); |
458 return false; | 508 return false; |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
539 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, | 589 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, |
540 kPreviewHeight, NULL); | 590 kPreviewHeight, NULL); |
541 g_free(filename); | 591 g_free(filename); |
542 if (pixbuf) { | 592 if (pixbuf) { |
543 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); | 593 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); |
544 g_object_unref(pixbuf); | 594 g_object_unref(pixbuf); |
545 } | 595 } |
546 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | 596 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), |
547 pixbuf ? TRUE : FALSE); | 597 pixbuf ? TRUE : FALSE); |
548 } | 598 } |
| 599 |
| 600 } // namespace libgtk2ui |
OLD | NEW |