Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: ui/shell_dialogs/gtk/select_file_dialog_impl_gtk.cc

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

Powered by Google App Engine
This is Rietveld 408576698