From 468d010fdf851804d8f02dd93c382b512ce5c67e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Fri, 1 Feb 2013 10:36:42 +0100 Subject: Introduce a native file dialog for GTK+ 2.x Change-Id: I3cb29218a54b9120c2ab6e2e32b810a111a7bf3d Reviewed-by: Friedemann Kleint Reviewed-by: Jens Bache-Wiig Reviewed-by: Shawn Rutledge --- .../platformthemes/gtk2/qgtk2dialoghelpers.cpp | 239 +++++++++++++++++++++ .../platformthemes/gtk2/qgtk2dialoghelpers.h | 40 ++++ src/plugins/platformthemes/gtk2/qgtk2theme.cpp | 4 +- 3 files changed, 282 insertions(+), 1 deletion(-) (limited to 'src/plugins/platformthemes') diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp index 19d4e9e469..4f9210a3af 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp +++ b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp @@ -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 &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 &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" diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h index 9dc6991601..301a2aa6ae 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h +++ b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.h @@ -45,6 +45,9 @@ #include #include +typedef struct _GtkDialog GtkDialog; +typedef struct _GtkFileFilter GtkFileFilter; + QT_BEGIN_NAMESPACE class QGtk2Dialog; @@ -74,6 +77,43 @@ private: mutable QScopedPointer 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 _filters; + QHash _filterNames; + mutable QScopedPointer d; +}; + QT_END_NAMESPACE #endif // QGTK2DIALOGHELPERS_P_H diff --git a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp index c7e612d1d6..c685d7b13c 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2theme.cpp +++ b/src/plugins/platformthemes/gtk2/qgtk2theme.cpp @@ -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; } -- cgit v1.2.3