| OLD | NEW |
| (Empty) |
| 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/ui/libgtkui/select_file_dialog_impl_gtk2.h" | |
| 6 | |
| 7 #include <gdk/gdkx.h> | |
| 8 #include <gtk/gtk.h> | |
| 9 #include <stddef.h> | |
| 10 #include <sys/stat.h> | |
| 11 #include <sys/types.h> | |
| 12 #include <unistd.h> | |
| 13 | |
| 14 #include <map> | |
| 15 #include <memory> | |
| 16 #include <set> | |
| 17 #include <vector> | |
| 18 | |
| 19 // Xlib defines RootWindow | |
| 20 #undef RootWindow | |
| 21 | |
| 22 #include "base/logging.h" | |
| 23 #include "base/message_loop/message_loop.h" | |
| 24 #include "base/strings/string_util.h" | |
| 25 #include "base/strings/sys_string_conversions.h" | |
| 26 #include "base/strings/utf_string_conversions.h" | |
| 27 #include "base/threading/thread.h" | |
| 28 #include "base/threading/thread_restrictions.h" | |
| 29 #include "chrome/browser/ui/libgtkui/gtk2_signal.h" | |
| 30 #include "chrome/browser/ui/libgtkui/gtk2_util.h" | |
| 31 #include "chrome/browser/ui/libgtkui/select_file_dialog_impl.h" | |
| 32 #include "ui/aura/window_observer.h" | |
| 33 #include "ui/base/l10n/l10n_util.h" | |
| 34 #include "ui/events/platform/x11/x11_event_source.h" | |
| 35 #include "ui/shell_dialogs/select_file_dialog.h" | |
| 36 #include "ui/strings/grit/ui_strings.h" | |
| 37 #include "ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h" | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Makes sure that .jpg also shows .JPG. | |
| 42 gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info, | |
| 43 std::string* file_extension) { | |
| 44 return base::EndsWith(file_info->filename, *file_extension, | |
| 45 base::CompareCase::INSENSITIVE_ASCII); | |
| 46 } | |
| 47 | |
| 48 // Deletes |data| when gtk_file_filter_add_custom() is done with it. | |
| 49 void OnFileFilterDataDestroyed(std::string* file_extension) { | |
| 50 delete file_extension; | |
| 51 } | |
| 52 | |
| 53 // Runs DesktopWindowTreeHostX11::EnableEventListening() when the file-picker | |
| 54 // is closed. | |
| 55 void OnFilePickerDestroy(base::Closure* callback) { | |
| 56 callback->Run(); | |
| 57 } | |
| 58 | |
| 59 } // namespace | |
| 60 | |
| 61 namespace libgtkui { | |
| 62 | |
| 63 // The size of the preview we display for selected image files. We set height | |
| 64 // larger than width because generally there is more free space vertically | |
| 65 // than horiztonally (setting the preview image will alway expand the width of | |
| 66 // the dialog, but usually not the height). The image's aspect ratio will always | |
| 67 // be preserved. | |
| 68 static const int kPreviewWidth = 256; | |
| 69 static const int kPreviewHeight = 512; | |
| 70 | |
| 71 SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplGTK( | |
| 72 Listener* listener, ui::SelectFilePolicy* policy) { | |
| 73 return new SelectFileDialogImplGTK(listener, policy); | |
| 74 } | |
| 75 | |
| 76 SelectFileDialogImplGTK::SelectFileDialogImplGTK(Listener* listener, | |
| 77 ui::SelectFilePolicy* policy) | |
| 78 : SelectFileDialogImpl(listener, policy), | |
| 79 preview_(NULL) { | |
| 80 } | |
| 81 | |
| 82 SelectFileDialogImplGTK::~SelectFileDialogImplGTK() { | |
| 83 for (std::set<aura::Window*>::iterator iter = parents_.begin(); | |
| 84 iter != parents_.end(); ++iter) { | |
| 85 (*iter)->RemoveObserver(this); | |
| 86 } | |
| 87 while (dialogs_.begin() != dialogs_.end()) { | |
| 88 gtk_widget_destroy(*(dialogs_.begin())); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 bool SelectFileDialogImplGTK::IsRunning(gfx::NativeWindow parent_window) const { | |
| 93 return parents_.find(parent_window) != parents_.end(); | |
| 94 } | |
| 95 | |
| 96 bool SelectFileDialogImplGTK::HasMultipleFileTypeChoicesImpl() { | |
| 97 return file_types_.extensions.size() > 1; | |
| 98 } | |
| 99 | |
| 100 void SelectFileDialogImplGTK::OnWindowDestroying(aura::Window* window) { | |
| 101 // Remove the |parent| property associated with the |dialog|. | |
| 102 for (std::set<GtkWidget*>::iterator it = dialogs_.begin(); | |
| 103 it != dialogs_.end(); ++it) { | |
| 104 aura::Window* parent = GetAuraTransientParent(*it); | |
| 105 if (parent == window) | |
| 106 ClearAuraTransientParent(*it); | |
| 107 } | |
| 108 | |
| 109 std::set<aura::Window*>::iterator iter = parents_.find(window); | |
| 110 if (iter != parents_.end()) { | |
| 111 (*iter)->RemoveObserver(this); | |
| 112 parents_.erase(iter); | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 // We ignore |default_extension|. | |
| 117 void SelectFileDialogImplGTK::SelectFileImpl( | |
| 118 Type type, | |
| 119 const base::string16& title, | |
| 120 const base::FilePath& default_path, | |
| 121 const FileTypeInfo* file_types, | |
| 122 int file_type_index, | |
| 123 const base::FilePath::StringType& default_extension, | |
| 124 gfx::NativeWindow owning_window, | |
| 125 void* params) { | |
| 126 type_ = type; | |
| 127 if (owning_window) { | |
| 128 owning_window->AddObserver(this); | |
| 129 parents_.insert(owning_window); | |
| 130 } | |
| 131 | |
| 132 std::string title_string = base::UTF16ToUTF8(title); | |
| 133 | |
| 134 file_type_index_ = file_type_index; | |
| 135 if (file_types) | |
| 136 file_types_ = *file_types; | |
| 137 | |
| 138 GtkWidget* dialog = NULL; | |
| 139 switch (type) { | |
| 140 case SELECT_FOLDER: | |
| 141 case SELECT_UPLOAD_FOLDER: | |
| 142 dialog = CreateSelectFolderDialog(type, title_string, default_path, | |
| 143 owning_window); | |
| 144 break; | |
| 145 case SELECT_OPEN_FILE: | |
| 146 dialog = CreateFileOpenDialog(title_string, default_path, owning_window); | |
| 147 break; | |
| 148 case SELECT_OPEN_MULTI_FILE: | |
| 149 dialog = CreateMultiFileOpenDialog(title_string, default_path, | |
| 150 owning_window); | |
| 151 break; | |
| 152 case SELECT_SAVEAS_FILE: | |
| 153 dialog = CreateSaveAsDialog(title_string, default_path, owning_window); | |
| 154 break; | |
| 155 default: | |
| 156 NOTREACHED(); | |
| 157 return; | |
| 158 } | |
| 159 g_signal_connect(dialog, "delete-event", | |
| 160 G_CALLBACK(gtk_widget_hide_on_delete), NULL); | |
| 161 dialogs_.insert(dialog); | |
| 162 | |
| 163 preview_ = gtk_image_new(); | |
| 164 g_signal_connect(dialog, "destroy", | |
| 165 G_CALLBACK(OnFileChooserDestroyThunk), this); | |
| 166 g_signal_connect(dialog, "update-preview", | |
| 167 G_CALLBACK(OnUpdatePreviewThunk), this); | |
| 168 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_); | |
| 169 | |
| 170 params_map_[dialog] = params; | |
| 171 | |
| 172 // Disable input events handling in the host window to make this dialog modal. | |
| 173 if (owning_window) { | |
| 174 aura::WindowTreeHost* host = owning_window->GetHost(); | |
| 175 if (host) { | |
| 176 std::unique_ptr<base::Closure> callback = | |
| 177 views::DesktopWindowTreeHostX11::GetHostForXID( | |
| 178 host->GetAcceleratedWidget())->DisableEventListening( | |
| 179 GDK_WINDOW_XID(gtk_widget_get_window(dialog))); | |
| 180 // OnFilePickerDestroy() is called when |dialog| destroyed, which allows | |
| 181 // to invoke the callback function to re-enable event handling on the | |
| 182 // owning window. | |
| 183 g_object_set_data_full(G_OBJECT(dialog), "callback", callback.release(), | |
| 184 reinterpret_cast<GDestroyNotify>(OnFilePickerDestroy)); | |
| 185 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 gtk_widget_show_all(dialog); | |
| 190 | |
| 191 // We need to call gtk_window_present after making the widgets visible to make | |
| 192 // sure window gets correctly raised and gets focus. | |
| 193 gtk_window_present_with_time( | |
| 194 GTK_WINDOW(dialog), ui::X11EventSource::GetInstance()->GetTimestamp()); | |
| 195 } | |
| 196 | |
| 197 void SelectFileDialogImplGTK::AddFilters(GtkFileChooser* chooser) { | |
| 198 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | |
| 199 GtkFileFilter* filter = NULL; | |
| 200 std::set<std::string> fallback_labels; | |
| 201 | |
| 202 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) { | |
| 203 const std::string& current_extension = file_types_.extensions[i][j]; | |
| 204 if (!current_extension.empty()) { | |
| 205 if (!filter) | |
| 206 filter = gtk_file_filter_new(); | |
| 207 std::unique_ptr<std::string> file_extension( | |
| 208 new std::string("." + current_extension)); | |
| 209 fallback_labels.insert(std::string("*").append(*file_extension)); | |
| 210 gtk_file_filter_add_custom( | |
| 211 filter, | |
| 212 GTK_FILE_FILTER_FILENAME, | |
| 213 reinterpret_cast<GtkFileFilterFunc>(FileFilterCaseInsensitive), | |
| 214 file_extension.release(), | |
| 215 reinterpret_cast<GDestroyNotify>(OnFileFilterDataDestroyed)); | |
| 216 } | |
| 217 } | |
| 218 // We didn't find any non-empty extensions to filter on. | |
| 219 if (!filter) | |
| 220 continue; | |
| 221 | |
| 222 // The description vector may be blank, in which case we are supposed to | |
| 223 // use some sort of default description based on the filter. | |
| 224 if (i < file_types_.extension_description_overrides.size()) { | |
| 225 gtk_file_filter_set_name(filter, base::UTF16ToUTF8( | |
| 226 file_types_.extension_description_overrides[i]).c_str()); | |
| 227 } else { | |
| 228 // There is no system default filter description so we use | |
| 229 // the extensions themselves if the description is blank. | |
| 230 std::vector<std::string> fallback_labels_vector(fallback_labels.begin(), | |
| 231 fallback_labels.end()); | |
| 232 std::string fallback_label = | |
| 233 base::JoinString(fallback_labels_vector, ","); | |
| 234 gtk_file_filter_set_name(filter, fallback_label.c_str()); | |
| 235 } | |
| 236 | |
| 237 gtk_file_chooser_add_filter(chooser, filter); | |
| 238 if (i == file_type_index_ - 1) | |
| 239 gtk_file_chooser_set_filter(chooser, filter); | |
| 240 } | |
| 241 | |
| 242 // Add the *.* filter, but only if we have added other filters (otherwise it | |
| 243 // is implied). | |
| 244 if (file_types_.include_all_files && !file_types_.extensions.empty()) { | |
| 245 GtkFileFilter* filter = gtk_file_filter_new(); | |
| 246 gtk_file_filter_add_pattern(filter, "*"); | |
| 247 gtk_file_filter_set_name(filter, | |
| 248 l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES).c_str()); | |
| 249 gtk_file_chooser_add_filter(chooser, filter); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 void SelectFileDialogImplGTK::FileSelected(GtkWidget* dialog, | |
| 254 const base::FilePath& path) { | |
| 255 if (type_ == SELECT_SAVEAS_FILE) { | |
| 256 *last_saved_path_ = path.DirName(); | |
| 257 } else if (type_ == SELECT_OPEN_FILE || type_ == SELECT_FOLDER || | |
| 258 type_ == SELECT_UPLOAD_FOLDER) { | |
| 259 *last_opened_path_ = path.DirName(); | |
| 260 } else { | |
| 261 NOTREACHED(); | |
| 262 } | |
| 263 | |
| 264 if (listener_) { | |
| 265 GtkFileFilter* selected_filter = | |
| 266 gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); | |
| 267 GSList* filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog)); | |
| 268 int idx = g_slist_index(filters, selected_filter); | |
| 269 g_slist_free(filters); | |
| 270 listener_->FileSelected(path, idx + 1, PopParamsForDialog(dialog)); | |
| 271 } | |
| 272 gtk_widget_destroy(dialog); | |
| 273 } | |
| 274 | |
| 275 void SelectFileDialogImplGTK::MultiFilesSelected(GtkWidget* dialog, | |
| 276 const std::vector<base::FilePath>& files) { | |
| 277 *last_opened_path_ = files[0].DirName(); | |
| 278 | |
| 279 if (listener_) | |
| 280 listener_->MultiFilesSelected(files, PopParamsForDialog(dialog)); | |
| 281 gtk_widget_destroy(dialog); | |
| 282 } | |
| 283 | |
| 284 void SelectFileDialogImplGTK::FileNotSelected(GtkWidget* dialog) { | |
| 285 void* params = PopParamsForDialog(dialog); | |
| 286 if (listener_) | |
| 287 listener_->FileSelectionCanceled(params); | |
| 288 gtk_widget_destroy(dialog); | |
| 289 } | |
| 290 | |
| 291 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenHelper( | |
| 292 const std::string& title, | |
| 293 const base::FilePath& default_path, | |
| 294 gfx::NativeWindow parent) { | |
| 295 GtkWidget* dialog = | |
| 296 gtk_file_chooser_dialog_new(title.c_str(), NULL, | |
| 297 GTK_FILE_CHOOSER_ACTION_OPEN, | |
| 298 "_Cancel", GTK_RESPONSE_CANCEL, | |
| 299 "_Open", GTK_RESPONSE_ACCEPT, | |
| 300 nullptr); | |
| 301 SetGtkTransientForAura(dialog, parent); | |
| 302 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
| 303 | |
| 304 if (!default_path.empty()) { | |
| 305 if (CallDirectoryExistsOnUIThread(default_path)) { | |
| 306 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 307 default_path.value().c_str()); | |
| 308 } else { | |
| 309 // If the file doesn't exist, this will just switch to the correct | |
| 310 // directory. That's good enough. | |
| 311 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
| 312 default_path.value().c_str()); | |
| 313 } | |
| 314 } else if (!last_opened_path_->empty()) { | |
| 315 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 316 last_opened_path_->value().c_str()); | |
| 317 } | |
| 318 return dialog; | |
| 319 } | |
| 320 | |
| 321 GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog( | |
| 322 Type type, | |
| 323 const std::string& title, | |
| 324 const base::FilePath& default_path, | |
| 325 gfx::NativeWindow parent) { | |
| 326 std::string title_string = title; | |
| 327 if (title_string.empty()) { | |
| 328 title_string = (type == SELECT_UPLOAD_FOLDER) ? | |
| 329 l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) : | |
| 330 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | |
| 331 } | |
| 332 std::string accept_button_label = (type == SELECT_UPLOAD_FOLDER) ? | |
| 333 l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON) : | |
| 334 "_Open"; | |
| 335 | |
| 336 GtkWidget* dialog = | |
| 337 gtk_file_chooser_dialog_new(title_string.c_str(), NULL, | |
| 338 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, | |
| 339 "_Cancel", GTK_RESPONSE_CANCEL, | |
| 340 accept_button_label.c_str(), | |
| 341 GTK_RESPONSE_ACCEPT, | |
| 342 nullptr); | |
| 343 SetGtkTransientForAura(dialog, parent); | |
| 344 | |
| 345 if (!default_path.empty()) { | |
| 346 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
| 347 default_path.value().c_str()); | |
| 348 } else if (!last_opened_path_->empty()) { | |
| 349 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 350 last_opened_path_->value().c_str()); | |
| 351 } | |
| 352 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 353 g_signal_connect(dialog, "response", | |
| 354 G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); | |
| 355 return dialog; | |
| 356 } | |
| 357 | |
| 358 GtkWidget* SelectFileDialogImplGTK::CreateFileOpenDialog( | |
| 359 const std::string& title, | |
| 360 const base::FilePath& default_path, | |
| 361 gfx::NativeWindow parent) { | |
| 362 std::string title_string = !title.empty() ? title : | |
| 363 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); | |
| 364 | |
| 365 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
| 366 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 367 g_signal_connect(dialog, "response", | |
| 368 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
| 369 return dialog; | |
| 370 } | |
| 371 | |
| 372 GtkWidget* SelectFileDialogImplGTK::CreateMultiFileOpenDialog( | |
| 373 const std::string& title, | |
| 374 const base::FilePath& default_path, | |
| 375 gfx::NativeWindow parent) { | |
| 376 std::string title_string = !title.empty() ? title : | |
| 377 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); | |
| 378 | |
| 379 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
| 380 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | |
| 381 g_signal_connect(dialog, "response", | |
| 382 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); | |
| 383 return dialog; | |
| 384 } | |
| 385 | |
| 386 GtkWidget* SelectFileDialogImplGTK::CreateSaveAsDialog(const std::string& title, | |
| 387 const base::FilePath& default_path, gfx::NativeWindow parent) { | |
| 388 std::string title_string = !title.empty() ? title : | |
| 389 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | |
| 390 | |
| 391 GtkWidget* dialog = | |
| 392 gtk_file_chooser_dialog_new(title_string.c_str(), NULL, | |
| 393 GTK_FILE_CHOOSER_ACTION_SAVE, | |
| 394 "_Cancel", GTK_RESPONSE_CANCEL, | |
| 395 "_Save", GTK_RESPONSE_ACCEPT, | |
| 396 nullptr); | |
| 397 SetGtkTransientForAura(dialog, parent); | |
| 398 | |
| 399 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
| 400 if (!default_path.empty()) { | |
| 401 if (CallDirectoryExistsOnUIThread(default_path)) { | |
| 402 // If this is an existing directory, navigate to that directory, with no | |
| 403 // filename. | |
| 404 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 405 default_path.value().c_str()); | |
| 406 } else { | |
| 407 // The default path does not exist, or is an existing file. We use | |
| 408 // set_current_folder() followed by set_current_name(), as per the | |
| 409 // recommendation of the GTK docs. | |
| 410 gtk_file_chooser_set_current_folder( | |
| 411 GTK_FILE_CHOOSER(dialog), default_path.DirName().value().c_str()); | |
| 412 gtk_file_chooser_set_current_name( | |
| 413 GTK_FILE_CHOOSER(dialog), default_path.BaseName().value().c_str()); | |
| 414 } | |
| 415 } else if (!last_saved_path_->empty()) { | |
| 416 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 417 last_saved_path_->value().c_str()); | |
| 418 } | |
| 419 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 420 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), | |
| 421 TRUE); | |
| 422 g_signal_connect(dialog, "response", | |
| 423 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
| 424 return dialog; | |
| 425 } | |
| 426 | |
| 427 void* SelectFileDialogImplGTK::PopParamsForDialog(GtkWidget* dialog) { | |
| 428 std::map<GtkWidget*, void*>::iterator iter = params_map_.find(dialog); | |
| 429 DCHECK(iter != params_map_.end()); | |
| 430 void* params = iter->second; | |
| 431 params_map_.erase(iter); | |
| 432 return params; | |
| 433 } | |
| 434 | |
| 435 bool SelectFileDialogImplGTK::IsCancelResponse(gint response_id) { | |
| 436 bool is_cancel = response_id == GTK_RESPONSE_CANCEL || | |
| 437 response_id == GTK_RESPONSE_DELETE_EVENT; | |
| 438 if (is_cancel) | |
| 439 return true; | |
| 440 | |
| 441 DCHECK(response_id == GTK_RESPONSE_ACCEPT); | |
| 442 return false; | |
| 443 } | |
| 444 | |
| 445 void SelectFileDialogImplGTK::SelectSingleFileHelper(GtkWidget* dialog, | |
| 446 gint response_id, | |
| 447 bool allow_folder) { | |
| 448 if (IsCancelResponse(response_id)) { | |
| 449 FileNotSelected(dialog); | |
| 450 return; | |
| 451 } | |
| 452 | |
| 453 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
| 454 if (!filename) { | |
| 455 FileNotSelected(dialog); | |
| 456 return; | |
| 457 } | |
| 458 | |
| 459 base::FilePath path(filename); | |
| 460 g_free(filename); | |
| 461 | |
| 462 if (allow_folder) { | |
| 463 FileSelected(dialog, path); | |
| 464 return; | |
| 465 } | |
| 466 | |
| 467 if (CallDirectoryExistsOnUIThread(path)) | |
| 468 FileNotSelected(dialog); | |
| 469 else | |
| 470 FileSelected(dialog, path); | |
| 471 } | |
| 472 | |
| 473 void SelectFileDialogImplGTK::OnSelectSingleFileDialogResponse( | |
| 474 GtkWidget* dialog, int response_id) { | |
| 475 SelectSingleFileHelper(dialog, response_id, false); | |
| 476 } | |
| 477 | |
| 478 void SelectFileDialogImplGTK::OnSelectSingleFolderDialogResponse( | |
| 479 GtkWidget* dialog, int response_id) { | |
| 480 SelectSingleFileHelper(dialog, response_id, true); | |
| 481 } | |
| 482 | |
| 483 void SelectFileDialogImplGTK::OnSelectMultiFileDialogResponse(GtkWidget* dialog, | |
| 484 int response_id) { | |
| 485 if (IsCancelResponse(response_id)) { | |
| 486 FileNotSelected(dialog); | |
| 487 return; | |
| 488 } | |
| 489 | |
| 490 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | |
| 491 if (!filenames) { | |
| 492 FileNotSelected(dialog); | |
| 493 return; | |
| 494 } | |
| 495 | |
| 496 std::vector<base::FilePath> filenames_fp; | |
| 497 for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { | |
| 498 base::FilePath path(static_cast<char*>(iter->data)); | |
| 499 g_free(iter->data); | |
| 500 if (CallDirectoryExistsOnUIThread(path)) | |
| 501 continue; | |
| 502 filenames_fp.push_back(path); | |
| 503 } | |
| 504 g_slist_free(filenames); | |
| 505 | |
| 506 if (filenames_fp.empty()) { | |
| 507 FileNotSelected(dialog); | |
| 508 return; | |
| 509 } | |
| 510 MultiFilesSelected(dialog, filenames_fp); | |
| 511 } | |
| 512 | |
| 513 void SelectFileDialogImplGTK::OnFileChooserDestroy(GtkWidget* dialog) { | |
| 514 dialogs_.erase(dialog); | |
| 515 | |
| 516 // |parent| can be NULL when closing the host window | |
| 517 // while opening the file-picker. | |
| 518 aura::Window* parent = GetAuraTransientParent(dialog); | |
| 519 if (!parent) | |
| 520 return; | |
| 521 std::set<aura::Window*>::iterator iter = parents_.find(parent); | |
| 522 if (iter != parents_.end()) { | |
| 523 (*iter)->RemoveObserver(this); | |
| 524 parents_.erase(iter); | |
| 525 } else { | |
| 526 NOTREACHED(); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 void SelectFileDialogImplGTK::OnUpdatePreview(GtkWidget* chooser) { | |
| 531 gchar* filename = gtk_file_chooser_get_preview_filename( | |
| 532 GTK_FILE_CHOOSER(chooser)); | |
| 533 if (!filename) { | |
| 534 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | |
| 535 FALSE); | |
| 536 return; | |
| 537 } | |
| 538 | |
| 539 // Don't attempt to open anything which isn't a regular file. If a named pipe, | |
| 540 // this may hang. See https://crbug.com/534754. | |
| 541 struct stat stat_buf; | |
| 542 if (stat(filename, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode)) { | |
| 543 g_free(filename); | |
| 544 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | |
| 545 FALSE); | |
| 546 return; | |
| 547 } | |
| 548 | |
| 549 // This will preserve the image's aspect ratio. | |
| 550 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, | |
| 551 kPreviewHeight, NULL); | |
| 552 g_free(filename); | |
| 553 if (pixbuf) { | |
| 554 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); | |
| 555 g_object_unref(pixbuf); | |
| 556 } | |
| 557 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | |
| 558 pixbuf ? TRUE : FALSE); | |
| 559 } | |
| 560 | |
| 561 } // namespace libgtkui | |
| OLD | NEW |