| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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 <gtk/gtk.h> | |
| 6 #include <map> | |
| 7 #include <set> | |
| 8 | |
| 9 #include "app/gtk_signal.h" | |
| 10 #include "app/l10n_util.h" | |
| 11 #include "base/file_util.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/message_loop.h" | |
| 14 #include "base/mime_util.h" | |
| 15 #include "base/sys_string_conversions.h" | |
| 16 #include "base/threading/thread.h" | |
| 17 #include "base/threading/thread_restrictions.h" | |
| 18 #include "base/utf_string_conversions.h" | |
| 19 #include "chrome/browser/browser_thread.h" | |
| 20 #include "chrome/browser/shell_dialogs.h" | |
| 21 #include "grit/generated_resources.h" | |
| 22 | |
| 23 // The size of the preview we display for selected image files. We set height | |
| 24 // larger than width because generally there is more free space vertically | |
| 25 // than horiztonally (setting the preview image will alway expand the width of | |
| 26 // the dialog, but usually not the height). The image's aspect ratio will always | |
| 27 // be preserved. | |
| 28 static const int kPreviewWidth = 256; | |
| 29 static const int kPreviewHeight = 512; | |
| 30 | |
| 31 // Implementation of SelectFileDialog that shows a Gtk common dialog for | |
| 32 // choosing a file or folder. This acts as a modal dialog. | |
| 33 class SelectFileDialogImpl : public SelectFileDialog { | |
| 34 public: | |
| 35 explicit SelectFileDialogImpl(Listener* listener); | |
| 36 | |
| 37 // BaseShellDialog implementation. | |
| 38 virtual bool IsRunning(gfx::NativeWindow parent_window) const; | |
| 39 virtual void ListenerDestroyed(); | |
| 40 | |
| 41 // SelectFileDialog implementation. | |
| 42 // |params| is user data we pass back via the Listener interface. | |
| 43 virtual void SelectFile(Type type, | |
| 44 const string16& title, | |
| 45 const FilePath& default_path, | |
| 46 const FileTypeInfo* file_types, | |
| 47 int file_type_index, | |
| 48 const FilePath::StringType& default_extension, | |
| 49 gfx::NativeWindow owning_window, | |
| 50 void* params); | |
| 51 | |
| 52 private: | |
| 53 virtual ~SelectFileDialogImpl(); | |
| 54 | |
| 55 // Add the filters from |file_types_| to |chooser|. | |
| 56 void AddFilters(GtkFileChooser* chooser); | |
| 57 | |
| 58 // Notifies the listener that a single file was chosen. | |
| 59 void FileSelected(GtkWidget* dialog, const FilePath& path); | |
| 60 | |
| 61 // Notifies the listener that multiple files were chosen. | |
| 62 void MultiFilesSelected(GtkWidget* dialog, | |
| 63 const std::vector<FilePath>& files); | |
| 64 | |
| 65 // Notifies the listener that no file was chosen (the action was canceled). | |
| 66 // Dialog is passed so we can find that |params| pointer that was passed to | |
| 67 // us when we were told to show the dialog. | |
| 68 void FileNotSelected(GtkWidget* dialog); | |
| 69 | |
| 70 GtkWidget* CreateSelectFolderDialog(const std::string& title, | |
| 71 const FilePath& default_path, gfx::NativeWindow parent); | |
| 72 | |
| 73 GtkWidget* CreateFileOpenDialog(const std::string& title, | |
| 74 const FilePath& default_path, gfx::NativeWindow parent); | |
| 75 | |
| 76 GtkWidget* CreateMultiFileOpenDialog(const std::string& title, | |
| 77 const FilePath& default_path, gfx::NativeWindow parent); | |
| 78 | |
| 79 GtkWidget* CreateSaveAsDialog(const std::string& title, | |
| 80 const FilePath& default_path, gfx::NativeWindow parent); | |
| 81 | |
| 82 // Removes and returns the |params| associated with |dialog| from | |
| 83 // |params_map_|. | |
| 84 void* PopParamsForDialog(GtkWidget* dialog); | |
| 85 | |
| 86 // Take care of internal data structures when a file dialog is destroyed. | |
| 87 void FileDialogDestroyed(GtkWidget* dialog); | |
| 88 | |
| 89 // Check whether response_id corresponds to the user cancelling/closing the | |
| 90 // dialog. Used as a helper for the below callbacks. | |
| 91 bool IsCancelResponse(gint response_id); | |
| 92 | |
| 93 // Common function for OnSelectSingleFileDialogResponse and | |
| 94 // OnSelectSingleFolderDialogResponse. | |
| 95 void SelectSingleFileHelper(GtkWidget* dialog, | |
| 96 gint response_id, | |
| 97 bool allow_folder); | |
| 98 | |
| 99 // Common function for CreateFileOpenDialog and CreateMultiFileOpenDialog. | |
| 100 GtkWidget* CreateFileOpenHelper(const std::string& title, | |
| 101 const FilePath& default_path, | |
| 102 gfx::NativeWindow parent); | |
| 103 | |
| 104 // Wrapper for file_util::DirectoryExists() that allow access on the UI | |
| 105 // thread. Use this only in the file dialog functions, where it's ok | |
| 106 // because the file dialog has to do many stats anyway. One more won't | |
| 107 // hurt too badly and it's likely already cached. | |
| 108 bool CallDirectoryExistsOnUIThread(const FilePath& path); | |
| 109 | |
| 110 // Callback for when the user responds to a Save As or Open File dialog. | |
| 111 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | |
| 112 OnSelectSingleFileDialogResponse, gint); | |
| 113 | |
| 114 // Callback for when the user responds to a Select Folder dialog. | |
| 115 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | |
| 116 OnSelectSingleFolderDialogResponse, gint); | |
| 117 | |
| 118 // Callback for when the user responds to a Open Multiple Files dialog. | |
| 119 CHROMEGTK_CALLBACK_1(SelectFileDialogImpl, void, | |
| 120 OnSelectMultiFileDialogResponse, gint); | |
| 121 | |
| 122 // Callback for when the file chooser gets destroyed. | |
| 123 CHROMEGTK_CALLBACK_0(SelectFileDialogImpl, void, OnFileChooserDestroy); | |
| 124 | |
| 125 // Callback for when we update the preview for the selection. | |
| 126 CHROMEGTK_CALLBACK_0(SelectFileDialogImpl, void, OnUpdatePreview); | |
| 127 | |
| 128 // The listener to be notified of selection completion. | |
| 129 Listener* listener_; | |
| 130 | |
| 131 // A map from dialog windows to the |params| user data associated with them. | |
| 132 std::map<GtkWidget*, void*> params_map_; | |
| 133 | |
| 134 // The file filters. | |
| 135 FileTypeInfo file_types_; | |
| 136 | |
| 137 // The index of the default selected file filter. | |
| 138 // Note: This starts from 1, not 0. | |
| 139 size_t file_type_index_; | |
| 140 | |
| 141 // The set of all parent windows for which we are currently running dialogs. | |
| 142 std::set<GtkWindow*> parents_; | |
| 143 | |
| 144 // The type of dialog we are showing the user. | |
| 145 Type type_; | |
| 146 | |
| 147 // These two variables track where the user last saved a file or opened a | |
| 148 // file so that we can display future dialogs with the same starting path. | |
| 149 static FilePath* last_saved_path_; | |
| 150 static FilePath* last_opened_path_; | |
| 151 | |
| 152 // The GtkImage widget for showing previews of selected images. | |
| 153 GtkWidget* preview_; | |
| 154 | |
| 155 // All our dialogs. | |
| 156 std::set<GtkWidget*> dialogs_; | |
| 157 | |
| 158 DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); | |
| 159 }; | |
| 160 | |
| 161 FilePath* SelectFileDialogImpl::last_saved_path_ = NULL; | |
| 162 FilePath* SelectFileDialogImpl::last_opened_path_ = NULL; | |
| 163 | |
| 164 // static | |
| 165 SelectFileDialog* SelectFileDialog::Create(Listener* listener) { | |
| 166 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 167 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 168 return new SelectFileDialogImpl(listener); | |
| 169 } | |
| 170 | |
| 171 SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener) | |
| 172 : listener_(listener), | |
| 173 file_type_index_(0), | |
| 174 type_(SELECT_NONE), | |
| 175 preview_(NULL) { | |
| 176 if (!last_saved_path_) { | |
| 177 last_saved_path_ = new FilePath(); | |
| 178 last_opened_path_ = new FilePath(); | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 SelectFileDialogImpl::~SelectFileDialogImpl() { | |
| 183 while (dialogs_.begin() != dialogs_.end()) { | |
| 184 gtk_widget_destroy(*(dialogs_.begin())); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const { | |
| 189 return parents_.find(parent_window) != parents_.end(); | |
| 190 } | |
| 191 | |
| 192 void SelectFileDialogImpl::ListenerDestroyed() { | |
| 193 listener_ = NULL; | |
| 194 } | |
| 195 | |
| 196 // We ignore |default_extension|. | |
| 197 void SelectFileDialogImpl::SelectFile( | |
| 198 Type type, | |
| 199 const string16& title, | |
| 200 const FilePath& default_path, | |
| 201 const FileTypeInfo* file_types, | |
| 202 int file_type_index, | |
| 203 const FilePath::StringType& default_extension, | |
| 204 gfx::NativeWindow owning_window, | |
| 205 void* params) { | |
| 206 type_ = type; | |
| 207 // |owning_window| can be null when user right-clicks on a downloadable item | |
| 208 // and chooses 'Open Link in New Tab' when 'Ask where to save each file | |
| 209 // before downloading.' preference is turned on. (http://crbug.com/29213) | |
| 210 if (owning_window) | |
| 211 parents_.insert(owning_window); | |
| 212 | |
| 213 std::string title_string = UTF16ToUTF8(title); | |
| 214 | |
| 215 file_type_index_ = file_type_index; | |
| 216 if (file_types) | |
| 217 file_types_ = *file_types; | |
| 218 else | |
| 219 file_types_.include_all_files = true; | |
| 220 | |
| 221 GtkWidget* dialog = NULL; | |
| 222 switch (type) { | |
| 223 case SELECT_FOLDER: | |
| 224 dialog = CreateSelectFolderDialog(title_string, default_path, | |
| 225 owning_window); | |
| 226 break; | |
| 227 case SELECT_OPEN_FILE: | |
| 228 dialog = CreateFileOpenDialog(title_string, default_path, owning_window); | |
| 229 break; | |
| 230 case SELECT_OPEN_MULTI_FILE: | |
| 231 dialog = CreateMultiFileOpenDialog(title_string, default_path, | |
| 232 owning_window); | |
| 233 break; | |
| 234 case SELECT_SAVEAS_FILE: | |
| 235 dialog = CreateSaveAsDialog(title_string, default_path, owning_window); | |
| 236 break; | |
| 237 default: | |
| 238 NOTREACHED(); | |
| 239 return; | |
| 240 } | |
| 241 dialogs_.insert(dialog); | |
| 242 | |
| 243 preview_ = gtk_image_new(); | |
| 244 g_signal_connect(dialog, "destroy", | |
| 245 G_CALLBACK(OnFileChooserDestroyThunk), this); | |
| 246 g_signal_connect(dialog, "update-preview", | |
| 247 G_CALLBACK(OnUpdatePreviewThunk), this); | |
| 248 gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_); | |
| 249 | |
| 250 params_map_[dialog] = params; | |
| 251 | |
| 252 // Set window-to-parent modality by adding the dialog to the same window | |
| 253 // group as the parent. | |
| 254 gtk_window_group_add_window(gtk_window_get_group(owning_window), | |
| 255 GTK_WINDOW(dialog)); | |
| 256 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); | |
| 257 | |
| 258 gtk_widget_show_all(dialog); | |
| 259 } | |
| 260 | |
| 261 void SelectFileDialogImpl::AddFilters(GtkFileChooser* chooser) { | |
| 262 for (size_t i = 0; i < file_types_.extensions.size(); ++i) { | |
| 263 GtkFileFilter* filter = NULL; | |
| 264 for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) { | |
| 265 if (!file_types_.extensions[i][j].empty()) { | |
| 266 if (!filter) | |
| 267 filter = gtk_file_filter_new(); | |
| 268 std::string mime_type = mime_util::GetFileMimeType( | |
| 269 FilePath("name.").Append(file_types_.extensions[i][j])); | |
| 270 gtk_file_filter_add_mime_type(filter, mime_type.c_str()); | |
| 271 } | |
| 272 } | |
| 273 // We didn't find any non-empty extensions to filter on. | |
| 274 if (!filter) | |
| 275 continue; | |
| 276 | |
| 277 // The description vector may be blank, in which case we are supposed to | |
| 278 // use some sort of default description based on the filter. | |
| 279 if (i < file_types_.extension_description_overrides.size()) { | |
| 280 gtk_file_filter_set_name(filter, UTF16ToUTF8( | |
| 281 file_types_.extension_description_overrides[i]).c_str()); | |
| 282 } else { | |
| 283 // There is no system default filter description so we use | |
| 284 // the MIME type itself if the description is blank. | |
| 285 std::string mime_type = mime_util::GetFileMimeType( | |
| 286 FilePath("name.").Append(file_types_.extensions[i][0])); | |
| 287 gtk_file_filter_set_name(filter, mime_type.c_str()); | |
| 288 } | |
| 289 | |
| 290 gtk_file_chooser_add_filter(chooser, filter); | |
| 291 if (i == file_type_index_ - 1) | |
| 292 gtk_file_chooser_set_filter(chooser, filter); | |
| 293 } | |
| 294 | |
| 295 // Add the *.* filter, but only if we have added other filters (otherwise it | |
| 296 // is implied). | |
| 297 if (file_types_.include_all_files && file_types_.extensions.size() > 0) { | |
| 298 GtkFileFilter* filter = gtk_file_filter_new(); | |
| 299 gtk_file_filter_add_pattern(filter, "*"); | |
| 300 gtk_file_filter_set_name(filter, | |
| 301 l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES).c_str()); | |
| 302 gtk_file_chooser_add_filter(chooser, filter); | |
| 303 } | |
| 304 } | |
| 305 | |
| 306 void SelectFileDialogImpl::FileSelected(GtkWidget* dialog, | |
| 307 const FilePath& path) { | |
| 308 if (type_ == SELECT_SAVEAS_FILE) | |
| 309 *last_saved_path_ = path.DirName(); | |
| 310 else if (type_ == SELECT_OPEN_FILE) | |
| 311 *last_opened_path_ = path.DirName(); | |
| 312 else if (type_ == SELECT_FOLDER) | |
| 313 *last_opened_path_ = path.DirName().DirName(); | |
| 314 else | |
| 315 NOTREACHED(); | |
| 316 | |
| 317 if (listener_) { | |
| 318 GtkFileFilter* selected_filter = | |
| 319 gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog)); | |
| 320 GSList* filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog)); | |
| 321 int idx = g_slist_index(filters, selected_filter); | |
| 322 g_slist_free(filters); | |
| 323 listener_->FileSelected(path, idx + 1, PopParamsForDialog(dialog)); | |
| 324 } | |
| 325 gtk_widget_destroy(dialog); | |
| 326 } | |
| 327 | |
| 328 void SelectFileDialogImpl::MultiFilesSelected(GtkWidget* dialog, | |
| 329 const std::vector<FilePath>& files) { | |
| 330 *last_opened_path_ = files[0].DirName(); | |
| 331 | |
| 332 if (listener_) | |
| 333 listener_->MultiFilesSelected(files, PopParamsForDialog(dialog)); | |
| 334 gtk_widget_destroy(dialog); | |
| 335 } | |
| 336 | |
| 337 void SelectFileDialogImpl::FileNotSelected(GtkWidget* dialog) { | |
| 338 void* params = PopParamsForDialog(dialog); | |
| 339 if (listener_) | |
| 340 listener_->FileSelectionCanceled(params); | |
| 341 gtk_widget_destroy(dialog); | |
| 342 } | |
| 343 | |
| 344 bool SelectFileDialogImpl::CallDirectoryExistsOnUIThread(const FilePath& path) { | |
| 345 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 346 return file_util::DirectoryExists(path); | |
| 347 } | |
| 348 | |
| 349 GtkWidget* SelectFileDialogImpl::CreateFileOpenHelper( | |
| 350 const std::string& title, | |
| 351 const FilePath& default_path, | |
| 352 gfx::NativeWindow parent) { | |
| 353 GtkWidget* dialog = | |
| 354 gtk_file_chooser_dialog_new(title.c_str(), parent, | |
| 355 GTK_FILE_CHOOSER_ACTION_OPEN, | |
| 356 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
| 357 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
| 358 NULL); | |
| 359 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
| 360 | |
| 361 if (!default_path.empty()) { | |
| 362 if (CallDirectoryExistsOnUIThread(default_path)) { | |
| 363 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 364 default_path.value().c_str()); | |
| 365 } else { | |
| 366 // If the file doesn't exist, this will just switch to the correct | |
| 367 // directory. That's good enough. | |
| 368 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
| 369 default_path.value().c_str()); | |
| 370 } | |
| 371 } else if (!last_opened_path_->empty()) { | |
| 372 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 373 last_opened_path_->value().c_str()); | |
| 374 } | |
| 375 return dialog; | |
| 376 } | |
| 377 | |
| 378 GtkWidget* SelectFileDialogImpl::CreateSelectFolderDialog( | |
| 379 const std::string& title, | |
| 380 const FilePath& default_path, | |
| 381 gfx::NativeWindow parent) { | |
| 382 std::string title_string = !title.empty() ? title : | |
| 383 l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE); | |
| 384 | |
| 385 GtkWidget* dialog = | |
| 386 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
| 387 GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, | |
| 388 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
| 389 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, | |
| 390 NULL); | |
| 391 | |
| 392 if (!default_path.empty()) { | |
| 393 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | |
| 394 default_path.value().c_str()); | |
| 395 } else if (!last_opened_path_->empty()) { | |
| 396 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 397 last_opened_path_->value().c_str()); | |
| 398 } | |
| 399 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 400 g_signal_connect(dialog, "response", | |
| 401 G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this); | |
| 402 return dialog; | |
| 403 } | |
| 404 | |
| 405 GtkWidget* SelectFileDialogImpl::CreateFileOpenDialog( | |
| 406 const std::string& title, | |
| 407 const FilePath& default_path, | |
| 408 gfx::NativeWindow parent) { | |
| 409 std::string title_string = !title.empty() ? title : | |
| 410 l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE); | |
| 411 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
| 412 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 413 g_signal_connect(dialog, "response", | |
| 414 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
| 415 return dialog; | |
| 416 } | |
| 417 | |
| 418 GtkWidget* SelectFileDialogImpl::CreateMultiFileOpenDialog( | |
| 419 const std::string& title, | |
| 420 const FilePath& default_path, | |
| 421 gfx::NativeWindow parent) { | |
| 422 std::string title_string = !title.empty() ? title : | |
| 423 l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE); | |
| 424 GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent); | |
| 425 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE); | |
| 426 g_signal_connect(dialog, "response", | |
| 427 G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this); | |
| 428 return dialog; | |
| 429 } | |
| 430 | |
| 431 GtkWidget* SelectFileDialogImpl::CreateSaveAsDialog(const std::string& title, | |
| 432 const FilePath& default_path, gfx::NativeWindow parent) { | |
| 433 std::string title_string = !title.empty() ? title : | |
| 434 l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE); | |
| 435 | |
| 436 GtkWidget* dialog = | |
| 437 gtk_file_chooser_dialog_new(title_string.c_str(), parent, | |
| 438 GTK_FILE_CHOOSER_ACTION_SAVE, | |
| 439 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | |
| 440 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, | |
| 441 NULL); | |
| 442 | |
| 443 AddFilters(GTK_FILE_CHOOSER(dialog)); | |
| 444 if (!default_path.empty()) { | |
| 445 // Since the file may not already exist, we use | |
| 446 // set_current_folder() followed by set_current_name(), as per the | |
| 447 // recommendation of the GTK docs. | |
| 448 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 449 default_path.DirName().value().c_str()); | |
| 450 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), | |
| 451 default_path.BaseName().value().c_str()); | |
| 452 } else if (!last_saved_path_->empty()) { | |
| 453 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | |
| 454 last_saved_path_->value().c_str()); | |
| 455 } | |
| 456 gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE); | |
| 457 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), | |
| 458 TRUE); | |
| 459 g_signal_connect(dialog, "response", | |
| 460 G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this); | |
| 461 return dialog; | |
| 462 } | |
| 463 | |
| 464 void* SelectFileDialogImpl::PopParamsForDialog(GtkWidget* dialog) { | |
| 465 std::map<GtkWidget*, void*>::iterator iter = params_map_.find(dialog); | |
| 466 DCHECK(iter != params_map_.end()); | |
| 467 void* params = iter->second; | |
| 468 params_map_.erase(iter); | |
| 469 return params; | |
| 470 } | |
| 471 | |
| 472 void SelectFileDialogImpl::FileDialogDestroyed(GtkWidget* dialog) { | |
| 473 dialogs_.erase(dialog); | |
| 474 | |
| 475 // Parent may be NULL in a few cases: 1) on shutdown when | |
| 476 // AllBrowsersClosed() trigger this handler after all the browser | |
| 477 // windows got destroyed, or 2) when the parent tab has been opened by | |
| 478 // 'Open Link in New Tab' context menu on a downloadable item and | |
| 479 // the tab has no content (see the comment in SelectFile as well). | |
| 480 GtkWindow* parent = gtk_window_get_transient_for(GTK_WINDOW(dialog)); | |
| 481 if (!parent) | |
| 482 return; | |
| 483 std::set<GtkWindow*>::iterator iter = parents_.find(parent); | |
| 484 if (iter != parents_.end()) | |
| 485 parents_.erase(iter); | |
| 486 else | |
| 487 NOTREACHED(); | |
| 488 } | |
| 489 | |
| 490 bool SelectFileDialogImpl::IsCancelResponse(gint response_id) { | |
| 491 bool is_cancel = response_id == GTK_RESPONSE_CANCEL || | |
| 492 response_id == GTK_RESPONSE_DELETE_EVENT; | |
| 493 if (is_cancel) | |
| 494 return true; | |
| 495 | |
| 496 DCHECK(response_id == GTK_RESPONSE_ACCEPT); | |
| 497 return false; | |
| 498 } | |
| 499 | |
| 500 void SelectFileDialogImpl::SelectSingleFileHelper(GtkWidget* dialog, | |
| 501 gint response_id, | |
| 502 bool allow_folder) { | |
| 503 if (IsCancelResponse(response_id)) { | |
| 504 FileNotSelected(dialog); | |
| 505 return; | |
| 506 } | |
| 507 | |
| 508 gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
| 509 if (!filename) { | |
| 510 FileNotSelected(dialog); | |
| 511 return; | |
| 512 } | |
| 513 | |
| 514 FilePath path(filename); | |
| 515 g_free(filename); | |
| 516 | |
| 517 if (allow_folder) { | |
| 518 FileSelected(dialog, path); | |
| 519 return; | |
| 520 } | |
| 521 | |
| 522 if (CallDirectoryExistsOnUIThread(path)) | |
| 523 FileNotSelected(dialog); | |
| 524 else | |
| 525 FileSelected(dialog, path); | |
| 526 } | |
| 527 | |
| 528 void SelectFileDialogImpl::OnSelectSingleFileDialogResponse( | |
| 529 GtkWidget* dialog, gint response_id) { | |
| 530 return SelectSingleFileHelper(dialog, response_id, false); | |
| 531 } | |
| 532 | |
| 533 void SelectFileDialogImpl::OnSelectSingleFolderDialogResponse( | |
| 534 GtkWidget* dialog, gint response_id) { | |
| 535 return SelectSingleFileHelper(dialog, response_id, true); | |
| 536 } | |
| 537 | |
| 538 void SelectFileDialogImpl::OnSelectMultiFileDialogResponse( | |
| 539 GtkWidget* dialog, gint response_id) { | |
| 540 if (IsCancelResponse(response_id)) { | |
| 541 FileNotSelected(dialog); | |
| 542 return; | |
| 543 } | |
| 544 | |
| 545 GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog)); | |
| 546 if (!filenames) { | |
| 547 FileNotSelected(dialog); | |
| 548 return; | |
| 549 } | |
| 550 | |
| 551 std::vector<FilePath> filenames_fp; | |
| 552 for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { | |
| 553 FilePath path(static_cast<char*>(iter->data)); | |
| 554 g_free(iter->data); | |
| 555 if (CallDirectoryExistsOnUIThread(path)) | |
| 556 continue; | |
| 557 filenames_fp.push_back(path); | |
| 558 } | |
| 559 g_slist_free(filenames); | |
| 560 | |
| 561 if (filenames_fp.empty()) { | |
| 562 FileNotSelected(dialog); | |
| 563 return; | |
| 564 } | |
| 565 MultiFilesSelected(dialog, filenames_fp); | |
| 566 } | |
| 567 | |
| 568 void SelectFileDialogImpl::OnFileChooserDestroy(GtkWidget* dialog) { | |
| 569 FileDialogDestroyed(dialog); | |
| 570 } | |
| 571 | |
| 572 void SelectFileDialogImpl::OnUpdatePreview(GtkWidget* chooser) { | |
| 573 gchar* filename = gtk_file_chooser_get_preview_filename( | |
| 574 GTK_FILE_CHOOSER(chooser)); | |
| 575 if (!filename) | |
| 576 return; | |
| 577 // This will preserve the image's aspect ratio. | |
| 578 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth, | |
| 579 kPreviewHeight, NULL); | |
| 580 g_free(filename); | |
| 581 if (pixbuf) { | |
| 582 gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf); | |
| 583 g_object_unref(pixbuf); | |
| 584 } | |
| 585 gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser), | |
| 586 pixbuf ? TRUE : FALSE); | |
| 587 } | |
| OLD | NEW |