Introduce a native file dialog for GTK+ 2.x

Change-Id: I3cb29218a54b9120c2ab6e2e32b810a111a7bf3d
Reviewed-by: Friedemann Kleint <Friedemann.Kleint@digia.com>
Reviewed-by: Jens Bache-Wiig <jens.bache-wiig@digia.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
J-P Nurmi 2013-02-01 10:36:42 +01:00 committed by The Qt Project
parent e88011357e
commit 468d010fdf
3 changed files with 282 additions and 1 deletions

View File

@ -233,6 +233,245 @@ void QGtk2ColorDialogHelper::applyOptions()
gtk_widget_hide(helpButton);
}
QGtk2FileDialogHelper::QGtk2FileDialogHelper()
{
d.reset(new QGtk2Dialog(gtk_file_chooser_dialog_new("", 0,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL)));
connect(d.data(), SIGNAL(accept()), this, SLOT(onAccepted()));
connect(d.data(), SIGNAL(reject()), this, SIGNAL(reject()));
g_signal_connect(GTK_FILE_CHOOSER(d->gtkDialog()), "selection-changed", G_CALLBACK(onSelectionChanged), this);
g_signal_connect_swapped(GTK_FILE_CHOOSER(d->gtkDialog()), "current-folder-changed", G_CALLBACK(onCurrentFolderChanged), this);
}
QGtk2FileDialogHelper::~QGtk2FileDialogHelper()
{
}
bool QGtk2FileDialogHelper::show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent)
{
_dir.clear();
_selection.clear();
applyOptions();
return d->show(flags, modality, parent);
}
void QGtk2FileDialogHelper::exec()
{
d->exec();
}
void QGtk2FileDialogHelper::hide()
{
// After GtkFileChooserDialog has been hidden, gtk_file_chooser_get_current_folder()
// & gtk_file_chooser_get_filenames() will return bogus values -> cache the actual
// values before hiding the dialog
_dir = directory();
_selection = selectedFiles();
d->hide();
}
bool QGtk2FileDialogHelper::defaultNameFilterDisables() const
{
return false;
}
void QGtk2FileDialogHelper::setDirectory(const QString &directory)
{
GtkDialog *gtkDialog = d->gtkDialog();
gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), directory.toUtf8());
}
QString QGtk2FileDialogHelper::directory() const
{
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_current_folder()
// returns a bogus value -> return the cached value before hiding
if (!_dir.isEmpty())
return _dir;
QString ret;
GtkDialog *gtkDialog = d->gtkDialog();
gchar *folder = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(gtkDialog));
if (folder) {
ret = QString::fromUtf8(folder);
g_free(folder);
}
return ret;
}
void QGtk2FileDialogHelper::selectFile(const QString &filename)
{
GtkDialog *gtkDialog = d->gtkDialog();
gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toUtf8());
}
QStringList QGtk2FileDialogHelper::selectedFiles() const
{
// While GtkFileChooserDialog is hidden, gtk_file_chooser_get_filenames()
// returns a bogus value -> return the cached value before hiding
if (!_selection.isEmpty())
return _selection;
QStringList selection;
GtkDialog *gtkDialog = d->gtkDialog();
GSList *filenames = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(gtkDialog));
for (GSList *it = filenames; it; it = it->next)
selection += QString::fromUtf8((const char*)it->data);
g_slist_free(filenames);
return selection;
}
void QGtk2FileDialogHelper::setFilter()
{
applyOptions();
}
void QGtk2FileDialogHelper::selectNameFilter(const QString &filter)
{
GtkFileFilter *gtkFilter = _filters.value(filter);
if (gtkFilter) {
GtkDialog *gtkDialog = d->gtkDialog();
gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
}
}
QString QGtk2FileDialogHelper::selectedNameFilter() const
{
GtkDialog *gtkDialog = d->gtkDialog();
GtkFileFilter *gtkFilter = gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(gtkDialog));
return _filterNames.value(gtkFilter);
}
void QGtk2FileDialogHelper::onAccepted()
{
emit accept();
QString filter = selectedNameFilter();
if (filter.isEmpty())
emit filterSelected(filter);
QStringList files = selectedFiles();
emit filesSelected(files);
if (files.count() == 1)
emit fileSelected(files.first());
}
void QGtk2FileDialogHelper::onSelectionChanged(GtkDialog *gtkDialog, QGtk2FileDialogHelper *helper)
{
QString selection;
gchar *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(gtkDialog));
if (filename) {
selection = QString::fromUtf8(filename);
g_free(filename);
}
emit helper->currentChanged(selection);
}
void QGtk2FileDialogHelper::onCurrentFolderChanged(QGtk2FileDialogHelper *dialog)
{
emit dialog->directoryEntered(dialog->directory());
}
static GtkFileChooserAction gtkFileChooserAction(const QSharedPointer<QFileDialogOptions> &options)
{
switch (options->fileMode()) {
case QFileDialogOptions::AnyFile:
case QFileDialogOptions::ExistingFile:
case QFileDialogOptions::ExistingFiles:
if (options->acceptMode() == QFileDialogOptions::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_OPEN;
else
return GTK_FILE_CHOOSER_ACTION_SAVE;
case QFileDialogOptions::Directory:
case QFileDialogOptions::DirectoryOnly:
default:
if (options->acceptMode() == QFileDialogOptions::AcceptOpen)
return GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
else
return GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
}
}
void QGtk2FileDialogHelper::applyOptions()
{
GtkDialog *gtkDialog = d->gtkDialog();
const QSharedPointer<QFileDialogOptions> &opts = options();
gtk_window_set_title(GTK_WINDOW(gtkDialog), opts->windowTitle().toUtf8());
gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(gtkDialog), true);
const GtkFileChooserAction action = gtkFileChooserAction(opts);
gtk_file_chooser_set_action(GTK_FILE_CHOOSER(gtkDialog), action);
const bool selectMultiple = opts->fileMode() == QFileDialogOptions::ExistingFiles;
gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(gtkDialog), selectMultiple);
const bool confirmOverwrite = !opts->testOption(QFileDialogOptions::DontConfirmOverwrite);
gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(gtkDialog), confirmOverwrite);
const QStringList nameFilters = opts->nameFilters();
if (!nameFilters.isEmpty())
setNameFilters(nameFilters);
const QString initialDirectory = opts->initialDirectory();
if (!initialDirectory.isEmpty())
setDirectory(initialDirectory);
foreach (const QString &filename, opts->initiallySelectedFiles())
selectFile(filename);
const QString initialNameFilter = opts->initiallySelectedNameFilter();
if (!initialNameFilter.isEmpty())
selectNameFilter(initialNameFilter);
GtkWidget *acceptButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_OK);
if (acceptButton) {
if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
gtk_button_set_label(GTK_BUTTON(acceptButton), opts->labelText(QFileDialogOptions::Accept).toUtf8());
else if (opts->acceptMode() == QFileDialogOptions::AcceptOpen)
gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_OPEN);
else
gtk_button_set_label(GTK_BUTTON(acceptButton), GTK_STOCK_SAVE);
}
GtkWidget *rejectButton = gtk_dialog_get_widget_for_response(gtkDialog, GTK_RESPONSE_CANCEL);
if (rejectButton) {
if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
gtk_button_set_label(GTK_BUTTON(rejectButton), opts->labelText(QFileDialogOptions::Reject).toUtf8());
else
gtk_button_set_label(GTK_BUTTON(rejectButton), GTK_STOCK_CANCEL);
}
}
void QGtk2FileDialogHelper::setNameFilters(const QStringList& filters)
{
GtkDialog *gtkDialog = d->gtkDialog();
foreach (GtkFileFilter *filter, _filters)
gtk_file_chooser_remove_filter(GTK_FILE_CHOOSER(gtkDialog), filter);
_filters.clear();
_filterNames.clear();
foreach (const QString &filter, filters) {
GtkFileFilter *gtkFilter = gtk_file_filter_new();
const QString name = filter.left(filter.indexOf(QLatin1Char('(')));
const QStringList extensions = cleanFilterList(filter);
gtk_file_filter_set_name(gtkFilter, name.isEmpty() ? extensions.join(QStringLiteral(", ")).toUtf8() : name.toUtf8());
foreach (const QString &ext, extensions)
gtk_file_filter_add_pattern(gtkFilter, ext.toUtf8());
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(gtkDialog), gtkFilter);
_filters.insert(filter, gtkFilter);
_filterNames.insert(gtkFilter, filter);
}
}
QT_END_NAMESPACE
#include "qgtk2dialoghelpers.moc"

View File

@ -45,6 +45,9 @@
#include <QtCore/qscopedpointer.h>
#include <qpa/qplatformdialoghelper.h>
typedef struct _GtkDialog GtkDialog;
typedef struct _GtkFileFilter GtkFileFilter;
QT_BEGIN_NAMESPACE
class QGtk2Dialog;
@ -74,6 +77,43 @@ private:
mutable QScopedPointer<QGtk2Dialog> d;
};
class QGtk2FileDialogHelper : public QPlatformFileDialogHelper
{
Q_OBJECT
public:
QGtk2FileDialogHelper();
~QGtk2FileDialogHelper();
virtual bool show(Qt::WindowFlags flags, Qt::WindowModality modality, QWindow *parent);
virtual void exec();
virtual void hide();
virtual bool defaultNameFilterDisables() const;
virtual void setDirectory(const QString &directory);
virtual QString directory() const;
virtual void selectFile(const QString &filename);
virtual QStringList selectedFiles() const;
virtual void setFilter();
virtual void selectNameFilter(const QString &filter);
virtual QString selectedNameFilter() const;
private Q_SLOTS:
void onAccepted();
private:
static void onSelectionChanged(GtkDialog *dialog, QGtk2FileDialogHelper *helper);
static void onCurrentFolderChanged(QGtk2FileDialogHelper *helper);
void applyOptions();
void setNameFilters(const QStringList &filters);
QString _dir;
QStringList _selection;
QHash<QString, GtkFileFilter*> _filters;
QHash<GtkFileFilter*, QString> _filterNames;
mutable QScopedPointer<QGtk2Dialog> d;
};
QT_END_NAMESPACE
#endif // QGTK2DIALOGHELPERS_P_H

View File

@ -56,7 +56,7 @@ QGtk2Theme::QGtk2Theme()
bool QGtk2Theme::usePlatformNativeDialog(DialogType type) const
{
return type == ColorDialog;
return type == ColorDialog || type == FileDialog;
}
QPlatformDialogHelper *QGtk2Theme::createPlatformDialogHelper(DialogType type) const
@ -64,6 +64,8 @@ QPlatformDialogHelper *QGtk2Theme::createPlatformDialogHelper(DialogType type) c
switch (type) {
case ColorDialog:
return new QGtk2ColorDialogHelper;
case FileDialog:
return new QGtk2FileDialogHelper;
default:
return 0;
}