Skip to content

Commit aa4aff6

Browse files
committed
Add and use FileDialog utility
FileDialog functions are to be used like QFileDialog static functions. Added value: - Add upper case extensions to filters iff needed. - Hide filter details when any entry gets to long. Resolves #938.
1 parent 7c79f8e commit aa4aff6

File tree

12 files changed

+247
-44
lines changed

12 files changed

+247
-44
lines changed

src/CMakeLists.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ set(Mapper_Common_SRCS
9292
gui/autosave_dialog.cpp
9393
gui/color_dialog.cpp
9494
gui/configure_grid_dialog.cpp
95+
gui/file_dialog.cpp
9596
gui/georeferencing_dialog.cpp
9697
gui/home_screen_controller.cpp
9798
gui/main_window.cpp
@@ -324,10 +325,9 @@ install(TARGETS Mapper
324325
# Workaround Qt private include dir issue
325326
# Cf. https://bugreports.qt.io/browse/QTBUG-37417
326327

328+
set(PRIVATE_MODULES Core Gui)
327329
if(WIN32)
328-
set(PRIVATE_MODULES Core Gui PrintSupport)
329-
else()
330-
set(PRIVATE_MODULES)
330+
list(APPEND PRIVATE_MODULES PrintSupport)
331331
endif()
332332
foreach(module ${PRIVATE_MODULES})
333333
set(qt_module Qt${module})

src/fileformats/file_format.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ FileFormat::~FileFormat() = default;
5555

5656
void FileFormat::addExtension(const QString& file_extension)
5757
{
58-
file_extensions << file_extension << file_extension.toUpper();
58+
file_extensions << file_extension;
5959
format_filter = QString::fromLatin1("%1 (*.%2)").arg(format_description, file_extensions.join(QString::fromLatin1(" *.")));
6060
}
6161

src/gui/file_dialog.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright 2017 Kai Pastor
3+
*
4+
* This file is part of OpenOrienteering.
5+
*
6+
* OpenOrienteering is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* OpenOrienteering is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#include "file_dialog.h"
22+
23+
#include <algorithm>
24+
#include <iterator>
25+
26+
#include <QtGlobal>
27+
#include <QStringRef> // IWYU pragma: keep
28+
#include <QVector>
29+
30+
#ifndef QTBUG_51712_QUIRK_ENABLED
31+
# if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(QT_TESTLIB_LIB)
32+
# define QTBUG_51712_QUIRK_ENABLED 1
33+
# else
34+
# define QTBUG_51712_QUIRK_ENABLED 0
35+
# endif
36+
#endif
37+
38+
#if QTBUG_51712_QUIRK_ENABLED
39+
# include <memory>
40+
# include <private/qguiapplication_p.h>
41+
# include <qpa/qplatformdialoghelper.h>
42+
# include <qpa/qplatformtheme.h>
43+
# include <QByteArray>
44+
# include <QLatin1Char>
45+
# include <QMetaObject>
46+
# include <QObject>
47+
# include <QStringList>
48+
#endif
49+
50+
51+
bool FileDialog::needUpperCaseExtensions()
52+
{
53+
#if QTBUG_51712_QUIRK_ENABLED
54+
auto platform_theme = QGuiApplicationPrivate::platformTheme();
55+
if (platform_theme
56+
&& platform_theme->usePlatformNativeDialog(QPlatformTheme::DialogType::FileDialog))
57+
{
58+
std::unique_ptr<QPlatformDialogHelper> helper {
59+
platform_theme->createPlatformDialogHelper(QPlatformTheme::DialogType::FileDialog)
60+
};
61+
if (helper)
62+
return qstrncmp(helper->metaObject()->className(), "QGtk", 4) == 0;
63+
}
64+
#endif
65+
return false;
66+
}
67+
68+
69+
void FileDialog::adjustParameters(QString& filter, QFileDialog::Options& options)
70+
{
71+
using std::begin;
72+
using std::end;
73+
74+
constexpr int max_filter_length = 100;
75+
static const auto separator = QString::fromLatin1(";;");
76+
#if QT_VERSION >= 0x50400
77+
const auto filters = filter.splitRef(separator);
78+
#else
79+
const auto filters = filter.split(separator);
80+
#endif
81+
82+
bool has_long_filters = std::any_of(begin(filters), end(filters), [](auto&& item) {
83+
return item.length() > max_filter_length;
84+
});
85+
86+
#if QTBUG_51712_QUIRK_ENABLED
87+
static auto need_upper_case = needUpperCaseExtensions();
88+
if (need_upper_case)
89+
{
90+
QStringList new_filters;
91+
new_filters.reserve(filter.size());
92+
for (auto&& item : filters)
93+
{
94+
QString new_item;
95+
new_item.reserve(2 * item.length());
96+
auto split_0 = item.indexOf(QLatin1Char('('));
97+
auto split_1 = item.lastIndexOf(QLatin1Char(')'));
98+
new_item.append(item.left(split_1));
99+
new_item.append(QLatin1Char(' '));
100+
#if QT_VERSION >= 0x50400
101+
new_item.append(item.mid(split_0+1).toString().toUpper());
102+
#else
103+
new_item.append(item.mid(split_0+1).toUpper());
104+
#endif
105+
new_filters.append(new_item);
106+
if (new_item.length() > max_filter_length)
107+
has_long_filters = true;
108+
}
109+
filter = new_filters.join(separator);
110+
}
111+
#endif
112+
113+
if (has_long_filters)
114+
options |= QFileDialog::HideNameFilterDetails;
115+
}

src/gui/file_dialog.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
* Copyright 2017 Kai Pastor
3+
*
4+
* This file is part of OpenOrienteering.
5+
*
6+
* OpenOrienteering is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* OpenOrienteering is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with OpenOrienteering. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
20+
21+
#ifndef OPENORIENTEERING_UTIL_FILE_DIALOG_H
22+
#define OPENORIENTEERING_UTIL_FILE_DIALOG_H
23+
24+
#include <QFileDialog>
25+
#include <QString>
26+
27+
class QWidget;
28+
29+
30+
/**
31+
* A collection of file dialog utility functions.
32+
*/
33+
namespace FileDialog
34+
{
35+
/**
36+
* Returns true if upper case extensions have to be added explicitly
37+
* to filters in file dialogs.
38+
*/
39+
bool needUpperCaseExtensions();
40+
41+
42+
/**
43+
* Adjusts filter and options for file dialogs.
44+
*
45+
* Adds upper case version of the extension when needed.
46+
* Sets QFileDialog::HideNameFilterDetails when the length of any particular
47+
* filter exceeds a certain treshold.
48+
*/
49+
void adjustParameters(QString& filter, QFileDialog::Options& options);
50+
51+
52+
/**
53+
* Calls QFileDialog::getOpenFileName with adjusted parameters.
54+
*
55+
* \see adjustParameters, QFileDialog::getOpenFileName
56+
*/
57+
inline
58+
QString getOpenFileName(QWidget* parent = nullptr,
59+
const QString& caption = {},
60+
const QString& dir = {},
61+
QString filter = {},
62+
QString* selected_filter = nullptr,
63+
QFileDialog::Options options = {})
64+
{
65+
adjustParameters(filter, options);
66+
return QFileDialog::getOpenFileName(parent, caption, dir, filter, selected_filter, options);
67+
}
68+
69+
70+
/**
71+
* Calls QFileDialog::getSaveFileName with adjusted parameters.
72+
*
73+
* \see adjustParameters, QFileDialog::getSaveFileName
74+
*/
75+
inline
76+
QString getSaveFileName(QWidget* parent = nullptr,
77+
const QString& caption = {},
78+
const QString& dir = {},
79+
QString filter = {},
80+
QString* selected_filter = nullptr,
81+
QFileDialog::Options options = {})
82+
{
83+
adjustParameters(filter, options);
84+
return QFileDialog::getSaveFileName(parent, caption, dir, filter, selected_filter, options);
85+
}
86+
87+
}
88+
89+
#endif

src/gui/main_window.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "fileformats/file_format_registry.h"
5353
#include "gui/about_dialog.h"
5454
#include "gui/autosave_dialog.h"
55+
#include "gui/file_dialog.h"
5556
#include "gui/home_screen_controller.h"
5657
#include "gui/settings_dialog.h"
5758
#include "gui/map/map_editor.h"
@@ -1019,7 +1020,7 @@ QString MainWindow::getOpenFileName(QWidget* parent, const QString& title, FileF
10191020

10201021
filters += tr("All files") + QLatin1String(" (*.*)");
10211022

1022-
QString path = QFileDialog::getOpenFileName(parent, title, open_directory, filters);
1023+
QString path = FileDialog::getOpenFileName(parent, title, open_directory, filters);
10231024
QFileInfo info(path);
10241025
return info.canonicalFilePath();
10251026
}
@@ -1053,7 +1054,7 @@ bool MainWindow::showSaveAsDialog()
10531054
}
10541055

10551056
QString filter; // will be set to the selected filter by QFileDialog
1056-
QString path = QFileDialog::getSaveFileName(this, tr("Save file"), save_directory, filters, &filter);
1057+
QString path = FileDialog::getSaveFileName(this, tr("Save file"), save_directory, filters, &filter);
10571058

10581059
// On Windows, when the user enters "sample", we get "sample.omap *.xmap".
10591060
// (Fixed in upstream qtbase/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp

src/gui/map/map_editor.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
#include <QDockWidget>
4545
#include <QEvent>
4646
#include <QFile>
47-
#include <QFileDialog>
4847
#include <QFileInfo>
4948
#include <QFlags>
5049
#include <QFont>
@@ -108,6 +107,7 @@
108107
#include "fileformats/file_format.h"
109108
#include "fileformats/file_format_registry.h"
110109
#include "gui/configure_grid_dialog.h"
110+
#include "gui/file_dialog.h"
111111
#include "gui/georeferencing_dialog.h"
112112
#include "gui/main_window.h"
113113
#include "gui/print_widget.h"
@@ -3891,13 +3891,13 @@ void MapEditorController::importClicked()
38913891
map_names.removeDuplicates();
38923892
map_extensions.removeDuplicates();
38933893

3894-
QString filename = QFileDialog::getOpenFileName(
3895-
window,
3896-
tr("Import %1, GPX, OSM or DXF file").arg(
3897-
map_names.join(QString::fromLatin1(", "))),
3898-
import_directory,
3899-
QString::fromLatin1("%1 (%2 *.gpx *.osm *.dxf);;%3 (*.*)").arg(
3900-
tr("Importable files"), QLatin1String("*.") + map_extensions.join(QString::fromLatin1(" *.")), tr("All files")) );
3894+
QString filename = FileDialog::getOpenFileName(
3895+
window,
3896+
tr("Import %1, GPX, OSM or DXF file").arg(
3897+
map_names.join(QString::fromLatin1(", "))),
3898+
import_directory,
3899+
QString::fromLatin1("%1 (%2 *.gpx *.osm *.dxf);;%3 (*.*)").arg(
3900+
tr("Importable files"), QLatin1String("*.") + map_extensions.join(QString::fromLatin1(" *.")), tr("All files")) );
39013901
if (filename.isEmpty() || filename.isNull())
39023902
return;
39033903

src/gui/map/new_map_dialog.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
#include <QComboBox>
3030
#include <QDialogButtonBox>
3131
#include <QDir>
32-
#include <QFileDialog>
3332
#include <QFileInfo>
3433
#include <QFlags>
3534
#include <QFormLayout>
@@ -51,8 +50,9 @@
5150

5251
#include "fileformats/file_format.h"
5352
#include "fileformats/file_format_registry.h"
54-
#include "util/util.h"
53+
#include "gui/file_dialog.h"
5554
#include "gui/util_gui.h"
55+
#include "util/util.h"
5656

5757
// IWYU pragma: no_forward_declare QLabel
5858
// IWYU pragma: no_forward_declare QVBoxLayout
@@ -259,7 +259,7 @@ void NewMapDialog::showFileDialog()
259259
filters + QLatin1String(";;") +
260260
tr("All files") + QLatin1String(" (*.*)");
261261

262-
QString path = QFileDialog::getOpenFileName(this, tr("Load symbol set from a file..."), open_directory, filters);
262+
QString path = FileDialog::getOpenFileName(this, tr("Load symbol set from a file..."), open_directory, filters);
263263
path = QFileInfo(path).canonicalFilePath();
264264
if (path.isEmpty())
265265
return;

src/gui/print_widget.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#include <QComboBox>
3232
#include <QDialogButtonBox>
3333
#include <QDoubleSpinBox>
34-
#include <QFileDialog>
3534
#include <QFileInfo>
3635
#include <QFormLayout>
3736
#include <QHBoxLayout>
@@ -54,6 +53,7 @@
5453

5554
#include "core/map.h"
5655
#include "core/map_printer.h"
56+
#include "gui/file_dialog.h"
5757
#include "gui/main_window.h"
5858
#include "gui/print_progress_dialog.h"
5959
#include "gui/print_tool.h"
@@ -1122,7 +1122,7 @@ void PrintWidget::exportToImage()
11221122
filter_template.arg(tr("TIFF"), QString::fromLatin1("*.tif *.tiff")),
11231123
filter_template.arg(tr("JPEG"), QString::fromLatin1("*.jpg *.jpeg")),
11241124
tr("All files (*.*)") };
1125-
QString path = QFileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;")));
1125+
QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;")));
11261126
if (path.isEmpty())
11271127
return;
11281128

@@ -1186,7 +1186,7 @@ void PrintWidget::exportToPdf()
11861186
static const QString filter_template(QString::fromLatin1("%1 (%2)"));
11871187
QStringList filters = { filter_template.arg(tr("PDF"), QString::fromLatin1("*.pdf")),
11881188
tr("All files (*.*)") };
1189-
QString path = QFileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;")));
1189+
QString path = FileDialog::getSaveFileName(this, tr("Export map ..."), {}, filters.join(QString::fromLatin1(";;")));
11901190
if (path.isEmpty())
11911191
{
11921192
return;

src/gui/symbols/replace_symbol_set_dialog.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
#include <QColor>
3636
#include <QDialogButtonBox>
3737
#include <QFile>
38-
#include <QFileDialog>
3938
#include <QFlags>
4039
#include <QFormLayout>
4140
#include <QGuiApplication>
@@ -71,6 +70,7 @@
7170
#include "core/symbols/symbol.h"
7271
#include "core/symbols/text_symbol.h"
7372
#include "fileformats/file_format.h"
73+
#include "gui/file_dialog.h"
7474
#include "gui/main_window.h"
7575
#include "gui/util_gui.h"
7676
#include "gui/widgets/symbol_dropdown.h"
@@ -230,8 +230,8 @@ void ReplaceSymbolSetDialog::resetReplacements()
230230
void ReplaceSymbolSetDialog::openCrtFile()
231231
{
232232
auto dir = QLatin1String{"data:/symbol sets"};
233-
auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt *.CRT)"}};
234-
QString path = QFileDialog::getOpenFileName(this, tr("Open CRT file..."), dir, filter);
233+
auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}};
234+
QString path = FileDialog::getOpenFileName(this, tr("Open CRT file..."), dir, filter);
235235
if (!path.isEmpty())
236236
openCrtFile(path);
237237
}
@@ -326,8 +326,8 @@ bool ReplaceSymbolSetDialog::saveCrtFile()
326326
{
327327
/// \todo Choose user-writable directory.
328328
auto dir = QLatin1String{"data:/symbol sets"};
329-
auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt *.CRT)"}};
330-
QString path = QFileDialog::getSaveFileName(this, tr("Save CRT file..."), dir, filter);
329+
auto filter = QString{tr("CRT file") + QLatin1String{" (*.crt)"}};
330+
QString path = FileDialog::getSaveFileName(this, tr("Save CRT file..."), dir, filter);
331331
if (!path.isEmpty())
332332
{
333333
updateMappingFromTable();

0 commit comments

Comments
 (0)