#12576 improved file name chooser implementation for GUI dialogs

- code reorganization to move the file name chooser methods in GuiDialog class
- on Mac add explicit raise of the current dialog window on close of the file browser to work around the bug 12576
This commit is contained in:
Stephan Witt 2023-08-05 08:56:35 +02:00
parent 550c79215b
commit 65ec100f6a
15 changed files with 220 additions and 181 deletions

View File

@ -491,7 +491,7 @@ void GuiBibtex::applyView()
} }
QString GuiBibtex::browseBib(QString const & in_name) const QString GuiBibtex::browseBib(QString const & in_name)
{ {
QString const label1 = qt_("D&ocuments"); QString const label1 = qt_("D&ocuments");
QString const dir1 = toqstr(lyxrc.document_path); QString const dir1 = toqstr(lyxrc.document_path);
@ -501,7 +501,7 @@ QString GuiBibtex::browseBib(QString const & in_name) const
} }
QString GuiBibtex::browseBst(QString const & in_name) const QString GuiBibtex::browseBst(QString const & in_name)
{ {
QString const label1 = qt_("D&ocuments"); QString const label1 = qt_("D&ocuments");
QString const dir1 = toqstr(lyxrc.document_path); QString const dir1 = toqstr(lyxrc.document_path);

View File

@ -56,9 +56,9 @@ private:
void updateContents() override; void updateContents() override;
/// Browse for a .bib file /// Browse for a .bib file
QString browseBib(QString const & in_name) const; QString browseBib(QString const & in_name);
/// Browse for a .bst file /// Browse for a .bst file
QString browseBst(QString const & in_name) const; QString browseBst(QString const & in_name);
/// get the list of bst files /// get the list of bst files
QStringList bibStyles() const; QStringList bibStyles() const;
/// get the list of bib files /// get the list of bib files

View File

@ -153,7 +153,7 @@ void GuiCompare::selectOldFile()
} }
QString GuiCompare::browse(QString const & in_name) const QString GuiCompare::browse(QString const & in_name)
{ {
QString const title = qt_("Select document"); QString const title = qt_("Select document");

View File

@ -80,7 +80,7 @@ private:
void enableControls(bool enable); void enableControls(bool enable);
/// browse for a file /// browse for a file
QString browse(QString const & in_name) const; QString browse(QString const & in_name);
/// retrieve the buffer from the specified filename /// retrieve the buffer from the specified filename
Buffer const * bufferFromFileName(std::string const & file) const; Buffer const * bufferFromFileName(std::string const & file) const;

View File

@ -10,12 +10,14 @@
#include <config.h> #include <config.h>
#include "FileDialog.h"
#include "GuiApplication.h"
#include "GuiDialog.h" #include "GuiDialog.h"
#include "GuiView.h" #include "GuiView.h"
#include "qt_helpers.h" #include "qt_helpers.h"
#include "support/debug.h" #include "support/debug.h"
#include "support/filetools.h"
#include <QCloseEvent> #include <QCloseEvent>
#include <QDialogButtonBox> #include <QDialogButtonBox>
@ -156,6 +158,130 @@ void GuiDialog::updateView()
setUpdatesEnabled(true); setUpdatesEnabled(true);
} }
QString GuiDialog::browseFile(QString const & filename,
QString const & title,
QStringList const & filters,
bool save,
QString const & label1,
QString const & dir1,
QString const & label2,
QString const & dir2,
QString const & fallback_dir)
{
QString lastPath = ".";
if (!filename.isEmpty())
lastPath = onlyPath(filename);
else if(!fallback_dir.isEmpty())
lastPath = fallback_dir;
FileDialog dlg(title);
dlg.setButton1(label1, dir1);
dlg.setButton2(label2, dir2);
FileDialog::Result result;
if (save)
result = dlg.save(lastPath, filters, onlyFileName(filename));
else
result = dlg.open(lastPath, filters, onlyFileName(filename));
if (guiApp->platformName() == "cocoa") {
QWidget * dialog = asQWidget();
dialog->raise();
dialog->activateWindow();
}
return result.second;
}
/** Launch a file dialog and return the chosen directory.
pathname: a suggested pathname.
title: the title of the dialog.
dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
*/
QString GuiDialog::browseDir(QString const & pathname,
QString const & title,
QString const & label1,
QString const & dir1,
QString const & label2,
QString const & dir2)
{
QString lastPath = ".";
if (!pathname.isEmpty())
lastPath = onlyPath(pathname);
FileDialog dlg(title);
dlg.setButton1(label1, dir1);
dlg.setButton2(label2, dir2);
FileDialog::Result const result =
dlg.opendir(lastPath, onlyFileName(pathname));
if (guiApp->platformName() == "cocoa") {
QWidget * dialog = asQWidget();
dialog->raise();
dialog->activateWindow();
}
return result.second;
}
QString GuiDialog::browseRelToParent(
QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save,
QString const & label1,
QString const & dir1,
QString const & label2,
QString const & dir2)
{
QString const fname = makeAbsPath(filename, relpath);
QString const outname =
browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
QString const reloutname =
toqstr(support::makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
if (reloutname.startsWith("../"))
return outname;
else
return reloutname;
}
QString GuiDialog::browseRelToSub(
QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save,
QString const & label1,
QString const & dir1,
QString const & label2,
QString const & dir2)
{
QString const fname = makeAbsPath(filename, relpath);
QString const outname =
browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
QString const reloutname =
toqstr(support::makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
QString testname = reloutname;
testname.remove(QRegularExpression("^(\\.\\./)+"));
if (testname.contains("/"))
return outname;
else
return reloutname;
}
} // namespace frontend } // namespace frontend
} // namespace lyx } // namespace lyx

View File

@ -112,6 +112,72 @@ public:
/// Update the display of the dialog whilst it is still visible. /// Update the display of the dialog whilst it is still visible.
void updateView() override; void updateView() override;
/** Launch a file dialog and return the chosen file.
filename: a suggested filename.
title: the title of the dialog.
filters: *.ps etc.
dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
*/
QString browseFile(QString const & filename,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString(),
QString const & fallback_dir = QString());
/** Launch a file dialog and return the chosen directory.
pathname: a suggested pathname.
title: the title of the dialog.
dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
*/
QString browseDir(QString const & pathname,
QString const & title,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString());
/** Wrappers around browseFile which try to provide a filename relative to relpath.
\param title: title for dialog
\param filters: *.ps, etc
\param save: whether to save dialog info (current path, etc) for next use.
The \param labelN and \param dirN arguments provide for extra buttons
in the dialog (e.g., "Templates" and a path to that directory).
The difference between the functions concerns when we think we have a
relative path.
In \c browseRelToParent, we return a relative path only if it IS NOT of
the form "../../foo.txt".
In \c browseRelToSub, we return a relative path only if it IS of the
form "../../foo.txt".
*/
QString browseRelToParent(QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString());
QString browseRelToSub(QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString());
private: private:
ButtonController bc_; ButtonController bc_;
/// are we updating ? /// are we updating ?

View File

@ -682,7 +682,7 @@ static QStringList templateFilters(QString const & template_name)
QString GuiExternal::browse(QString const & input, QString GuiExternal::browse(QString const & input,
QString const & template_name) const QString const & template_name)
{ {
QString const title = qt_("Select external file"); QString const title = qt_("Select external file");
QString const bufpath = bufferFilePath(); QString const bufpath = bufferFilePath();

View File

@ -68,7 +68,7 @@ private:
/// ///
QString browse(QString const & input_file, QString browse(QString const & input_file,
QString const & template_name) const; QString const & template_name);
/// ///
MapType extra_; MapType extra_;

View File

@ -823,7 +823,7 @@ void GuiGraphics::dispatchParams()
} }
QString GuiGraphics::browse(QString const & in_name) const QString GuiGraphics::browse(QString const & in_name)
{ {
QString const title = qt_("Select graphics file"); QString const title = qt_("Select graphics file");

View File

@ -71,7 +71,7 @@ private:
/// does the bounding box differ from the file? /// does the bounding box differ from the file?
bool isChangedBB(); bool isChangedBB();
/// Browse for a file /// Browse for a file
QString browse(QString const &) const; QString browse(QString const &);
/// Read the Bounding Box from a eps or ps-file /// Read the Bounding Box from a eps or ps-file
std::string readBoundingBox(std::string const & file); std::string readBoundingBox(std::string const & file);
/// test if file exist /// test if file exist

View File

@ -359,7 +359,7 @@ void GuiInclude::browse()
} }
QString GuiInclude::browse(QString const & in_name, Type in_type) const QString GuiInclude::browse(QString const & in_name, Type in_type)
{ {
QString const title = qt_("Select document to include"); QString const title = qt_("Select document to include");

View File

@ -79,7 +79,7 @@ private:
/// update /// update
void updateContents() override {} void updateContents() override {}
/// Browse for a file /// Browse for a file
QString browse(QString const &, Type) const; QString browse(QString const &, Type);
private: private:
/// ///

View File

@ -80,124 +80,6 @@ using namespace lyx::support;
using namespace lyx::support::os; using namespace lyx::support::os;
namespace lyx { namespace lyx {
namespace frontend {
/////////////////////////////////////////////////////////////////////
//
// Browser Helpers
//
/////////////////////////////////////////////////////////////////////
/** Launch a file dialog and return the chosen file.
filename: a suggested filename.
title: the title of the dialog.
filters: *.ps etc.
dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
*/
QString browseFile(QString const & filename,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString(),
QString const & fallback_dir = QString())
{
QString lastPath = ".";
if (!filename.isEmpty())
lastPath = onlyPath(filename);
else if(!fallback_dir.isEmpty())
lastPath = fallback_dir;
FileDialog dlg(title);
dlg.setButton1(label1, dir1);
dlg.setButton2(label2, dir2);
FileDialog::Result result;
if (save)
result = dlg.save(lastPath, filters, onlyFileName(filename));
else
result = dlg.open(lastPath, filters, onlyFileName(filename));
return result.second;
}
/** Launch a file dialog and return the chosen directory.
pathname: a suggested pathname.
title: the title of the dialog.
dir1 = (name, dir), dir2 = (name, dir): extra buttons on the dialog.
*/
QString browseDir(QString const & pathname,
QString const & title,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString())
{
QString lastPath = ".";
if (!pathname.isEmpty())
lastPath = onlyPath(pathname);
FileDialog dlg(title);
dlg.setButton1(label1, dir1);
dlg.setButton2(label2, dir2);
FileDialog::Result const result =
dlg.opendir(lastPath, onlyFileName(pathname));
return result.second;
}
} // namespace frontend
QString browseRelToParent(QString const & filename, QString const & relpath,
QString const & title, QStringList const & filters, bool save,
QString const & label1, QString const & dir1,
QString const & label2, QString const & dir2)
{
QString const fname = makeAbsPath(filename, relpath);
QString const outname =
frontend::browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
QString const reloutname =
toqstr(makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
if (reloutname.startsWith("../"))
return outname;
else
return reloutname;
}
QString browseRelToSub(QString const & filename, QString const & relpath,
QString const & title, QStringList const & filters, bool save,
QString const & label1, QString const & dir1,
QString const & label2, QString const & dir2)
{
QString const fname = makeAbsPath(filename, relpath);
QString const outname =
frontend::browseFile(fname, title, filters, save, label1, dir1, label2, dir2);
QString const reloutname =
toqstr(makeRelPath(qstring_to_ucs4(outname), qstring_to_ucs4(relpath)));
QString testname = reloutname;
testname.remove(QRegularExpression("^(\\.\\./)+"));
if (testname.contains("/"))
return outname;
else
return reloutname;
}
///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////
// //
@ -1478,7 +1360,7 @@ void PrefPaths::updateRC(LyXRC const & rc)
void PrefPaths::selectExampledir() void PrefPaths::selectExampledir()
{ {
QString file = browseDir(internalPath(exampleDirED->text()), QString file = form_->browseDir(internalPath(exampleDirED->text()),
qt_("Select directory for example files")); qt_("Select directory for example files"));
if (!file.isEmpty()) if (!file.isEmpty())
exampleDirED->setText(file); exampleDirED->setText(file);
@ -1487,7 +1369,7 @@ void PrefPaths::selectExampledir()
void PrefPaths::selectTemplatedir() void PrefPaths::selectTemplatedir()
{ {
QString file = browseDir(internalPath(templateDirED->text()), QString file = form_->browseDir(internalPath(templateDirED->text()),
qt_("Select a document templates directory")); qt_("Select a document templates directory"));
if (!file.isEmpty()) if (!file.isEmpty())
templateDirED->setText(file); templateDirED->setText(file);
@ -1496,7 +1378,7 @@ void PrefPaths::selectTemplatedir()
void PrefPaths::selectTempdir() void PrefPaths::selectTempdir()
{ {
QString file = browseDir(internalPath(tempDirED->text()), QString file = form_->browseDir(internalPath(tempDirED->text()),
qt_("Select a temporary directory")); qt_("Select a temporary directory"));
if (!file.isEmpty()) if (!file.isEmpty())
tempDirED->setText(file); tempDirED->setText(file);
@ -1505,7 +1387,7 @@ void PrefPaths::selectTempdir()
void PrefPaths::selectBackupdir() void PrefPaths::selectBackupdir()
{ {
QString file = browseDir(internalPath(backupDirED->text()), QString file = form_->browseDir(internalPath(backupDirED->text()),
qt_("Select a backups directory")); qt_("Select a backups directory"));
if (!file.isEmpty()) if (!file.isEmpty())
backupDirED->setText(file); backupDirED->setText(file);
@ -1514,7 +1396,7 @@ void PrefPaths::selectBackupdir()
void PrefPaths::selectWorkingdir() void PrefPaths::selectWorkingdir()
{ {
QString file = browseDir(internalPath(workingDirED->text()), QString file = form_->browseDir(internalPath(workingDirED->text()),
qt_("Select a document directory")); qt_("Select a document directory"));
if (!file.isEmpty()) if (!file.isEmpty())
workingDirED->setText(file); workingDirED->setText(file);
@ -1523,7 +1405,7 @@ void PrefPaths::selectWorkingdir()
void PrefPaths::selectThesaurusdir() void PrefPaths::selectThesaurusdir()
{ {
QString file = browseDir(internalPath(thesaurusDirED->text()), QString file = form_->browseDir(internalPath(thesaurusDirED->text()),
qt_("Set the path to the thesaurus dictionaries")); qt_("Set the path to the thesaurus dictionaries"));
if (!file.isEmpty()) if (!file.isEmpty())
thesaurusDirED->setText(file); thesaurusDirED->setText(file);
@ -1532,7 +1414,7 @@ void PrefPaths::selectThesaurusdir()
void PrefPaths::selectHunspelldir() void PrefPaths::selectHunspelldir()
{ {
QString file = browseDir(internalPath(hunspellDirED->text()), QString file = form_->browseDir(internalPath(hunspellDirED->text()),
qt_("Set the path to the Hunspell dictionaries")); qt_("Set the path to the Hunspell dictionaries"));
if (!file.isEmpty()) if (!file.isEmpty())
hunspellDirED->setText(file); hunspellDirED->setText(file);
@ -3718,6 +3600,12 @@ QString GuiPreferences::browseLibFile(QString const & dir,
guilyxfiles_->passParams(fromqstr(dir)); guilyxfiles_->passParams(fromqstr(dir));
guilyxfiles_->selectItem(name); guilyxfiles_->selectItem(name);
guilyxfiles_->exec(); guilyxfiles_->exec();
if (frontend::guiApp->platformName() == "cocoa") {
QWidget * dialog_ = asQWidget();
dialog_->raise();
dialog_->activateWindow();
}
QString const result = uifile_; QString const result = uifile_;
@ -3756,7 +3644,7 @@ QString GuiPreferences::browsekbmap(QString const & file)
QString GuiPreferences::browse(QString const & file, QString GuiPreferences::browse(QString const & file,
QString const & title) const QString const & title)
{ {
return browseFile(file, title, QStringList(), true); return browseFile(file, title, QStringList(), true);
} }

View File

@ -92,7 +92,7 @@ public:
QString browsekbmap(QString const & file); QString browsekbmap(QString const & file);
/// general browse /// general browse
QString browse(QString const & file, QString const & title) const; QString browse(QString const & file, QString const & title);
/// set a color /// set a color
void setColor(ColorCode col, QString const & hex); void setColor(ColorCode col, QString const & hex);

View File

@ -120,47 +120,6 @@ support::FileName imageLibFileSearch(QString & dir, QString const & name,
QString const & ext = QString(), QString const & ext = QString(),
support::search_mode mode = support::must_exist); support::search_mode mode = support::must_exist);
/** Wrappers around browseFile which try to provide a filename
relative to relpath.
\param title: title for dialog
\param filters: *.ps, etc
\param save: whether to save dialog info (current path, etc) for next use.
The \param labelN and \param dirN arguments provide for extra buttons
in the dialog (e.g., "Templates" and a path to that directory).
The difference between the functions concerns when we think we have a
relative path.
In \c browseRelToParent, we return a relative path only if it IS NOT of
the form "../../foo.txt".
In \c browseRelToSub, we return a relative path only if it IS of the
form "../../foo.txt".
*/
QString browseRelToParent(QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString());
QString browseRelToSub(QString const & filename,
QString const & relpath,
QString const & title,
QStringList const & filters,
bool save = false,
QString const & label1 = QString(),
QString const & dir1 = QString(),
QString const & label2 = QString(),
QString const & dir2 = QString());
/** Build filelists of all available bst/cls/sty-files. Done through /** Build filelists of all available bst/cls/sty-files. Done through
* kpsewhich and an external script, saved in *Files.lst. * kpsewhich and an external script, saved in *Files.lst.
* \param arg: cls, sty, bst, or bib, as required by TeXFiles.py. * \param arg: cls, sty, bst, or bib, as required by TeXFiles.py.