This source file includes following definitions.
- FileFilterCaseInsensitive
- OnFileFilterDataDestroyed
- NewSelectFileDialogImplGTK
- preview_
- HasMultipleFileTypeChoicesImpl
- OnWindowDestroying
- SelectFileImpl
- AddFilters
- FileSelected
- MultiFilesSelected
- FileNotSelected
- CreateFileOpenHelper
- CreateSelectFolderDialog
- CreateFileOpenDialog
- CreateMultiFileOpenDialog
- CreateSaveAsDialog
- PopParamsForDialog
- FileDialogDestroyed
- IsCancelResponse
- SelectSingleFileHelper
- OnSelectSingleFileDialogResponse
- OnSelectSingleFolderDialogResponse
- OnSelectMultiFileDialogResponse
- OnFileChooserDestroy
- OnUpdatePreview
#include <gtk/gtk.h>
#include <map>
#include <set>
#include <vector>
#undef RootWindow
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "chrome/browser/ui/libgtk2ui/gtk2_signal.h"
#include "chrome/browser/ui/libgtk2ui/gtk2_util.h"
#include "chrome/browser/ui/libgtk2ui/select_file_dialog_impl.h"
#include "grit/ui_strings.h"
#include "ui/aura/window_observer.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/shell_dialogs/select_file_dialog.h"
namespace {
gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info,
std::string* file_extension) {
return EndsWith(file_info->filename, *file_extension, false);
}
void OnFileFilterDataDestroyed(std::string* file_extension) {
delete file_extension;
}
}
namespace libgtk2ui {
class SelectFileDialogImplGTK : public SelectFileDialogImpl,
public aura::WindowObserver {
public:
explicit SelectFileDialogImplGTK(Listener* listener,
ui::SelectFilePolicy* policy);
protected:
virtual ~SelectFileDialogImplGTK();
virtual void SelectFileImpl(
Type type,
const base::string16& title,
const base::FilePath& default_path,
const FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) OVERRIDE;
private:
virtual bool HasMultipleFileTypeChoicesImpl() OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
void AddFilters(GtkFileChooser* chooser);
void FileSelected(GtkWidget* dialog, const base::FilePath& path);
void MultiFilesSelected(GtkWidget* dialog,
const std::vector<base::FilePath>& files);
void FileNotSelected(GtkWidget* dialog);
GtkWidget* CreateSelectFolderDialog(
Type type,
const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent);
GtkWidget* CreateFileOpenDialog(const std::string& title,
const base::FilePath& default_path, gfx::NativeWindow parent);
GtkWidget* CreateMultiFileOpenDialog(const std::string& title,
const base::FilePath& default_path, gfx::NativeWindow parent);
GtkWidget* CreateSaveAsDialog(const std::string& title,
const base::FilePath& default_path, gfx::NativeWindow parent);
void* PopParamsForDialog(GtkWidget* dialog);
void FileDialogDestroyed(GtkWidget* dialog);
bool IsCancelResponse(gint response_id);
void SelectSingleFileHelper(GtkWidget* dialog,
gint response_id,
bool allow_folder);
GtkWidget* CreateFileOpenHelper(const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent);
CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void,
OnSelectSingleFileDialogResponse, int);
CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void,
OnSelectSingleFolderDialogResponse, int);
CHROMEGTK_CALLBACK_1(SelectFileDialogImplGTK, void,
OnSelectMultiFileDialogResponse, int);
CHROMEGTK_CALLBACK_0(SelectFileDialogImplGTK, void, OnFileChooserDestroy);
CHROMEGTK_CALLBACK_0(SelectFileDialogImplGTK, void, OnUpdatePreview);
std::map<GtkWidget*, void*> params_map_;
GtkWidget* preview_;
std::set<GtkWidget*> dialogs_;
DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImplGTK);
};
static const int kPreviewWidth = 256;
static const int kPreviewHeight = 512;
SelectFileDialogImpl* SelectFileDialogImpl::NewSelectFileDialogImplGTK(
Listener* listener, ui::SelectFilePolicy* policy) {
return new SelectFileDialogImplGTK(listener, policy);
}
SelectFileDialogImplGTK::SelectFileDialogImplGTK(Listener* listener,
ui::SelectFilePolicy* policy)
: SelectFileDialogImpl(listener, policy),
preview_(NULL) {
}
SelectFileDialogImplGTK::~SelectFileDialogImplGTK() {
while (dialogs_.begin() != dialogs_.end()) {
gtk_widget_destroy(*(dialogs_.begin()));
}
}
bool SelectFileDialogImplGTK::HasMultipleFileTypeChoicesImpl() {
return file_types_.extensions.size() > 1;
}
void SelectFileDialogImplGTK::OnWindowDestroying(aura::Window* window) {
for (std::set<GtkWidget*>::iterator it = dialogs_.begin();
it != dialogs_.end(); ++it) {
aura::Window* parent = GetAuraTransientParent(*it);
if (parent == window)
ClearAuraTransientParent(*it);
}
std::set<aura::Window*>::iterator iter = parents_.find(window);
if (iter != parents_.end()) {
(*iter)->RemoveObserver(this);
parents_.erase(iter);
}
}
void SelectFileDialogImplGTK::SelectFileImpl(
Type type,
const base::string16& title,
const base::FilePath& default_path,
const FileTypeInfo* file_types,
int file_type_index,
const base::FilePath::StringType& default_extension,
gfx::NativeWindow owning_window,
void* params) {
type_ = type;
if (owning_window) {
owning_window->AddObserver(this);
parents_.insert(owning_window);
}
std::string title_string = base::UTF16ToUTF8(title);
file_type_index_ = file_type_index;
if (file_types)
file_types_ = *file_types;
GtkWidget* dialog = NULL;
switch (type) {
case SELECT_FOLDER:
case SELECT_UPLOAD_FOLDER:
dialog = CreateSelectFolderDialog(type, title_string, default_path,
owning_window);
break;
case SELECT_OPEN_FILE:
dialog = CreateFileOpenDialog(title_string, default_path, owning_window);
break;
case SELECT_OPEN_MULTI_FILE:
dialog = CreateMultiFileOpenDialog(title_string, default_path,
owning_window);
break;
case SELECT_SAVEAS_FILE:
dialog = CreateSaveAsDialog(title_string, default_path, owning_window);
break;
default:
NOTREACHED();
return;
}
g_signal_connect(dialog, "delete-event",
G_CALLBACK(gtk_widget_hide_on_delete), NULL);
dialogs_.insert(dialog);
preview_ = gtk_image_new();
g_signal_connect(dialog, "destroy",
G_CALLBACK(OnFileChooserDestroyThunk), this);
g_signal_connect(dialog, "update-preview",
G_CALLBACK(OnUpdatePreviewThunk), this);
gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(dialog), preview_);
params_map_[dialog] = params;
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
gtk_widget_show_all(dialog);
}
void SelectFileDialogImplGTK::AddFilters(GtkFileChooser* chooser) {
for (size_t i = 0; i < file_types_.extensions.size(); ++i) {
GtkFileFilter* filter = NULL;
std::set<std::string> fallback_labels;
for (size_t j = 0; j < file_types_.extensions[i].size(); ++j) {
const std::string& current_extension = file_types_.extensions[i][j];
if (!current_extension.empty()) {
if (!filter)
filter = gtk_file_filter_new();
scoped_ptr<std::string> file_extension(
new std::string("." + current_extension));
fallback_labels.insert(std::string("*").append(*file_extension));
gtk_file_filter_add_custom(
filter,
GTK_FILE_FILTER_FILENAME,
reinterpret_cast<GtkFileFilterFunc>(FileFilterCaseInsensitive),
file_extension.release(),
reinterpret_cast<GDestroyNotify>(OnFileFilterDataDestroyed));
}
}
if (!filter)
continue;
if (i < file_types_.extension_description_overrides.size()) {
gtk_file_filter_set_name(filter, base::UTF16ToUTF8(
file_types_.extension_description_overrides[i]).c_str());
} else {
std::vector<std::string> fallback_labels_vector(fallback_labels.begin(),
fallback_labels.end());
std::string fallback_label = JoinString(fallback_labels_vector, ',');
gtk_file_filter_set_name(filter, fallback_label.c_str());
}
gtk_file_chooser_add_filter(chooser, filter);
if (i == file_type_index_ - 1)
gtk_file_chooser_set_filter(chooser, filter);
}
if (file_types_.include_all_files && !file_types_.extensions.empty()) {
GtkFileFilter* filter = gtk_file_filter_new();
gtk_file_filter_add_pattern(filter, "*");
gtk_file_filter_set_name(filter,
l10n_util::GetStringUTF8(IDS_SAVEAS_ALL_FILES).c_str());
gtk_file_chooser_add_filter(chooser, filter);
}
}
void SelectFileDialogImplGTK::FileSelected(GtkWidget* dialog,
const base::FilePath& path) {
if (type_ == SELECT_SAVEAS_FILE) {
*last_saved_path_ = path.DirName();
} else if (type_ == SELECT_OPEN_FILE || type_ == SELECT_FOLDER ||
type_ == SELECT_UPLOAD_FOLDER) {
*last_opened_path_ = path.DirName();
} else {
NOTREACHED();
}
if (listener_) {
GtkFileFilter* selected_filter =
gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog));
GSList* filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog));
int idx = g_slist_index(filters, selected_filter);
g_slist_free(filters);
listener_->FileSelected(path, idx + 1, PopParamsForDialog(dialog));
}
gtk_widget_destroy(dialog);
}
void SelectFileDialogImplGTK::MultiFilesSelected(GtkWidget* dialog,
const std::vector<base::FilePath>& files) {
*last_opened_path_ = files[0].DirName();
if (listener_)
listener_->MultiFilesSelected(files, PopParamsForDialog(dialog));
gtk_widget_destroy(dialog);
}
void SelectFileDialogImplGTK::FileNotSelected(GtkWidget* dialog) {
void* params = PopParamsForDialog(dialog);
if (listener_)
listener_->FileSelectionCanceled(params);
gtk_widget_destroy(dialog);
}
GtkWidget* SelectFileDialogImplGTK::CreateFileOpenHelper(
const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent) {
GtkWidget* dialog =
gtk_file_chooser_dialog_new(title.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
SetGtkTransientForAura(dialog, parent);
AddFilters(GTK_FILE_CHOOSER(dialog));
if (!default_path.empty()) {
if (CallDirectoryExistsOnUIThread(default_path)) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
default_path.value().c_str());
} else {
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
default_path.value().c_str());
}
} else if (!last_opened_path_->empty()) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
last_opened_path_->value().c_str());
}
return dialog;
}
GtkWidget* SelectFileDialogImplGTK::CreateSelectFolderDialog(
Type type,
const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent) {
std::string title_string = title;
if (title_string.empty()) {
title_string = (type == SELECT_UPLOAD_FOLDER) ?
l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_TITLE) :
l10n_util::GetStringUTF8(IDS_SELECT_FOLDER_DIALOG_TITLE);
}
std::string accept_button_label = (type == SELECT_UPLOAD_FOLDER) ?
l10n_util::GetStringUTF8(IDS_SELECT_UPLOAD_FOLDER_DIALOG_UPLOAD_BUTTON) :
GTK_STOCK_OPEN;
GtkWidget* dialog =
gtk_file_chooser_dialog_new(title_string.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
accept_button_label.c_str(),
GTK_RESPONSE_ACCEPT,
NULL);
SetGtkTransientForAura(dialog, parent);
if (!default_path.empty()) {
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
default_path.value().c_str());
} else if (!last_opened_path_->empty()) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
last_opened_path_->value().c_str());
}
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
g_signal_connect(dialog, "response",
G_CALLBACK(OnSelectSingleFolderDialogResponseThunk), this);
return dialog;
}
GtkWidget* SelectFileDialogImplGTK::CreateFileOpenDialog(
const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent) {
std::string title_string = !title.empty() ? title :
l10n_util::GetStringUTF8(IDS_OPEN_FILE_DIALOG_TITLE);
GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
g_signal_connect(dialog, "response",
G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this);
return dialog;
}
GtkWidget* SelectFileDialogImplGTK::CreateMultiFileOpenDialog(
const std::string& title,
const base::FilePath& default_path,
gfx::NativeWindow parent) {
std::string title_string = !title.empty() ? title :
l10n_util::GetStringUTF8(IDS_OPEN_FILES_DIALOG_TITLE);
GtkWidget* dialog = CreateFileOpenHelper(title_string, default_path, parent);
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
g_signal_connect(dialog, "response",
G_CALLBACK(OnSelectMultiFileDialogResponseThunk), this);
return dialog;
}
GtkWidget* SelectFileDialogImplGTK::CreateSaveAsDialog(const std::string& title,
const base::FilePath& default_path, gfx::NativeWindow parent) {
std::string title_string = !title.empty() ? title :
l10n_util::GetStringUTF8(IDS_SAVE_AS_DIALOG_TITLE);
GtkWidget* dialog =
gtk_file_chooser_dialog_new(title_string.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_SAVE,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
NULL);
SetGtkTransientForAura(dialog, parent);
AddFilters(GTK_FILE_CHOOSER(dialog));
if (!default_path.empty()) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
default_path.DirName().value().c_str());
gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
default_path.BaseName().value().c_str());
} else if (!last_saved_path_->empty()) {
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
last_saved_path_->value().c_str());
}
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), FALSE);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
TRUE);
g_signal_connect(dialog, "response",
G_CALLBACK(OnSelectSingleFileDialogResponseThunk), this);
return dialog;
}
void* SelectFileDialogImplGTK::PopParamsForDialog(GtkWidget* dialog) {
std::map<GtkWidget*, void*>::iterator iter = params_map_.find(dialog);
DCHECK(iter != params_map_.end());
void* params = iter->second;
params_map_.erase(iter);
return params;
}
void SelectFileDialogImplGTK::FileDialogDestroyed(GtkWidget* dialog) {
dialogs_.erase(dialog);
aura::Window* parent = GetAuraTransientParent(dialog);
if (!parent)
return;
std::set<aura::Window*>::iterator iter = parents_.find(parent);
if (iter != parents_.end()) {
(*iter)->RemoveObserver(this);
parents_.erase(iter);
} else {
NOTREACHED();
}
}
bool SelectFileDialogImplGTK::IsCancelResponse(gint response_id) {
bool is_cancel = response_id == GTK_RESPONSE_CANCEL ||
response_id == GTK_RESPONSE_DELETE_EVENT;
if (is_cancel)
return true;
DCHECK(response_id == GTK_RESPONSE_ACCEPT);
return false;
}
void SelectFileDialogImplGTK::SelectSingleFileHelper(GtkWidget* dialog,
gint response_id,
bool allow_folder) {
if (IsCancelResponse(response_id)) {
FileNotSelected(dialog);
return;
}
gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
if (!filename) {
FileNotSelected(dialog);
return;
}
base::FilePath path(filename);
g_free(filename);
if (allow_folder) {
FileSelected(dialog, path);
return;
}
if (CallDirectoryExistsOnUIThread(path))
FileNotSelected(dialog);
else
FileSelected(dialog, path);
}
void SelectFileDialogImplGTK::OnSelectSingleFileDialogResponse(
GtkWidget* dialog, int response_id) {
SelectSingleFileHelper(dialog, response_id, false);
}
void SelectFileDialogImplGTK::OnSelectSingleFolderDialogResponse(
GtkWidget* dialog, int response_id) {
SelectSingleFileHelper(dialog, response_id, true);
}
void SelectFileDialogImplGTK::OnSelectMultiFileDialogResponse(GtkWidget* dialog,
int response_id) {
if (IsCancelResponse(response_id)) {
FileNotSelected(dialog);
return;
}
GSList* filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
if (!filenames) {
FileNotSelected(dialog);
return;
}
std::vector<base::FilePath> filenames_fp;
for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) {
base::FilePath path(static_cast<char*>(iter->data));
g_free(iter->data);
if (CallDirectoryExistsOnUIThread(path))
continue;
filenames_fp.push_back(path);
}
g_slist_free(filenames);
if (filenames_fp.empty()) {
FileNotSelected(dialog);
return;
}
MultiFilesSelected(dialog, filenames_fp);
}
void SelectFileDialogImplGTK::OnFileChooserDestroy(GtkWidget* dialog) {
FileDialogDestroyed(dialog);
}
void SelectFileDialogImplGTK::OnUpdatePreview(GtkWidget* chooser) {
gchar* filename = gtk_file_chooser_get_preview_filename(
GTK_FILE_CHOOSER(chooser));
if (!filename)
return;
GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file_at_size(filename, kPreviewWidth,
kPreviewHeight, NULL);
g_free(filename);
if (pixbuf) {
gtk_image_set_from_pixbuf(GTK_IMAGE(preview_), pixbuf);
g_object_unref(pixbuf);
}
gtk_file_chooser_set_preview_widget_active(GTK_FILE_CHOOSER(chooser),
pixbuf ? TRUE : FALSE);
}
}