From f6083280596217987accb55538ba8bc57814b302 Mon Sep 17 00:00:00 2001 From: Daniel Ramoeller Date: Tue, 20 Dec 2022 11:55:25 +0100 Subject: [PATCH] Allow multiple selections in the file open dialog Fix for bug #4315. --- src/frontends/qt/FileDialog.cpp | 38 ++++++--- src/frontends/qt/FileDialog.h | 6 ++ src/frontends/qt/GuiApplication.cpp | 4 +- src/frontends/qt/GuiView.cpp | 119 +++++++++++++++------------- src/frontends/qt/GuiView.h | 2 +- 5 files changed, 99 insertions(+), 70 deletions(-) diff --git a/src/frontends/qt/FileDialog.cpp b/src/frontends/qt/FileDialog.cpp index 1491939a99..704c34c9e8 100644 --- a/src/frontends/qt/FileDialog.cpp +++ b/src/frontends/qt/FileDialog.cpp @@ -133,21 +133,39 @@ FileDialog::Result FileDialog::save(QString const & path, FileDialog::Result FileDialog::open(QString const & path, QStringList const & filters, QString const & suggested) +{ + FileDialog::Result result; + FileDialog::Results results = openMulti(path, filters, suggested, false); + result.first = results.first; + result.second = results.second.at(0); + return result; +} + + +FileDialog::Results FileDialog::openMulti(QString const & path, + QStringList const & filters, QString const & suggested, bool multi) { LYXERR(Debug::GUI, "Select with path \"" << path << "\", mask \"" << filters.join(";;") << "\", suggested \"" << suggested << '"'); - FileDialog::Result result; - result.first = FileDialog::Chosen; + FileDialog::Results results; + results.first = FileDialog::Chosen; if (lyxrc.use_native_filedialog) { QString const startsWith = makeAbsPath(suggested, path); - QString const file = QFileDialog::getOpenFileName(qApp->focusWidget(), - title_, startsWith, filters.join(";;")); - if (file.isNull()) - result.first = FileDialog::Later; + QStringList files; + if (multi) + files = QFileDialog::getOpenFileNames(qApp->focusWidget(), + title_, startsWith, filters.join(";;")); else - result.second = internalPath(file); + files << QFileDialog::getOpenFileName(qApp->focusWidget(), + title_, startsWith, filters.join(";;")); + if (files.isEmpty()) + results.first = FileDialog::Later; + else { + for (const auto& file : files) + results.second << internalPath(file); + } } else { LyXFileDialog dlg(title_, path, filters, private_->b1, private_->b2); @@ -158,12 +176,12 @@ FileDialog::Result FileDialog::open(QString const & path, int res = dlg.exec(); LYXERR(Debug::GUI, "result " << res); if (res == QDialog::Accepted) - result.second = internalPath(dlg.selectedFiles()[0]); + results.second << internalPath(dlg.selectedFiles()[0]); else - result.first = FileDialog::Later; + results.first = FileDialog::Later; dlg.hide(); } - return result; + return results; } diff --git a/src/frontends/qt/FileDialog.h b/src/frontends/qt/FileDialog.h index fe75b5bf1e..3d6a549a7e 100644 --- a/src/frontends/qt/FileDialog.h +++ b/src/frontends/qt/FileDialog.h @@ -41,6 +41,9 @@ public: /// result return typedef std::pair Result; + /// result return + typedef std::pair Results; + /** * Constructs a file dialog with title \param title. * @@ -58,6 +61,9 @@ public: /// Choose a file for opening, starting in directory \c path. Result open(QString const & path, QStringList const & filters, QString const & suggested = QString()); + /// Choose several files for opening, starting in directory \c path. + Results openMulti(QString const & path, QStringList const & filters, + QString const & suggested = QString(), bool multi = true); /// Choose a directory, starting in directory \c path. Result opendir(QString const & path = QString(), diff --git a/src/frontends/qt/GuiApplication.cpp b/src/frontends/qt/GuiApplication.cpp index 967f134153..25ca4b2eff 100644 --- a/src/frontends/qt/GuiApplication.cpp +++ b/src/frontends/qt/GuiApplication.cpp @@ -1858,7 +1858,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) // We want the ui session to be saved per document and not per // window number. The filename crc is a good enough identifier. createView(support::checksum(fname)); - current_view_->openDocument(fname, cmd.origin()); + current_view_->openDocuments(fname, cmd.origin()); if (!current_view_->documentBufferView()) current_view_->close(); else if (cmd.origin() == FuncRequest::LYXSERVER) { @@ -1867,7 +1867,7 @@ void GuiApplication::dispatch(FuncRequest const & cmd, DispatchResult & dr) current_view_->showNormal(); } } else { - current_view_->openDocument(fname, cmd.origin()); + current_view_->openDocuments(fname, cmd.origin()); if (cmd.origin() == FuncRequest::LYXSERVER) { current_view_->raise(); current_view_->activateWindow(); diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp index e7a3b296d9..461b09cfd5 100644 --- a/src/frontends/qt/GuiView.cpp +++ b/src/frontends/qt/GuiView.cpp @@ -2792,7 +2792,7 @@ Buffer * GuiView::loadDocument(FileName const & filename, bool tolastfiles) } -void GuiView::openDocument(string const & fname, int origin) +void GuiView::openDocuments(string const & fname, int origin) { string initpath = lyxrc.document_path; @@ -2803,10 +2803,10 @@ void GuiView::openDocument(string const & fname, int origin) initpath = trypath; } - string filename; + QStringList files; if (fname.empty()) { - FileDialog dlg(qt_("Select document to open")); + FileDialog dlg(qt_("Select documents to open")); dlg.setButton1(qt_("D&ocuments"), toqstr(lyxrc.document_path)); dlg.setButton2(qt_("&Examples"), toqstr(lyxrc.example_path)); @@ -2815,74 +2815,79 @@ void GuiView::openDocument(string const & fname, int origin) qt_("LyX Document Backups (*.lyx~)"), qt_("All Files (*.*)") }); - FileDialog::Result result = - dlg.open(toqstr(initpath), filter); + FileDialog::Results results = + dlg.openMulti(toqstr(initpath), filter); - if (result.first == FileDialog::Later) + if (results.first == FileDialog::Later) return; - filename = fromqstr(result.second); + files = results.second; // check selected filename - if (filename.empty()) { + if (files.isEmpty()) { message(_("Canceled.")); return; } } else - filename = fname; - - // get absolute path of file and add ".lyx" to the filename if - // necessary. - FileName const fullname = - fileSearch(string(), filename, "lyx", support::may_not_exist); - if (!fullname.empty()) - filename = fullname.absFileName(); - - if (!fullname.onlyPath().isDirectory()) { - Alert::warning(_("Invalid filename"), - bformat(_("The directory in the given path\n%1$s\ndoes not exist."), - from_utf8(fullname.absFileName()))); - return; - } + files << toqstr(fname); + + // iterate over all selected files + for (auto const & file : files) { + string filename = fromqstr(file); + + // get absolute path of file and add ".lyx" to the filename if + // necessary. + FileName const fullname = + fileSearch(string(), filename, "lyx", support::may_not_exist); + if (!fullname.empty()) + filename = fullname.absFileName(); + + if (!fullname.onlyPath().isDirectory()) { + Alert::warning(_("Invalid filename"), + bformat(_("The directory in the given path\n%1$s\ndoes not exist."), + from_utf8(fullname.absFileName()))); + continue; + } - // if the file doesn't exist and isn't already open (bug 6645), - // let the user create one - if (!fullname.exists() && !theBufferList().exists(fullname) && - !LyXVC::file_not_found_hook(fullname)) { - // see bug #12609 - if (origin == FuncRequest::MENU) { - docstring const & msg = - bformat(_("File\n" - "%1$s\n" - "does not exist. Create empty file?"), - from_utf8(filename)); - int ret = Alert::prompt(_("File does not exist"), - msg, 0, 1, - _("Create &File"), - _("&Cancel")); - if (ret == 1) - return; + // if the file doesn't exist and isn't already open (bug 6645), + // let the user create one + if (!fullname.exists() && !theBufferList().exists(fullname) && + !LyXVC::file_not_found_hook(fullname)) { + // see bug #12609 + if (origin == FuncRequest::MENU) { + docstring const & msg = + bformat(_("File\n" + "%1$s\n" + "does not exist. Create empty file?"), + from_utf8(filename)); + int ret = Alert::prompt(_("File does not exist"), + msg, 0, 1, + _("Create &File"), + _("&Cancel")); + if (ret == 1) + continue; + } + Buffer * const b = newFile(filename, string(), true); + if (b) + setBuffer(b); + continue; } - Buffer * const b = newFile(filename, string(), true); - if (b) - setBuffer(b); - return; - } - docstring const disp_fn = makeDisplayPath(filename); - message(bformat(_("Opening document %1$s..."), disp_fn)); + docstring const disp_fn = makeDisplayPath(filename); + message(bformat(_("Opening document %1$s..."), disp_fn)); - docstring str2; - Buffer * buf = loadDocument(fullname); - if (buf) { - str2 = bformat(_("Document %1$s opened."), disp_fn); - if (buf->lyxvc().inUse()) - str2 += " " + from_utf8(buf->lyxvc().versionString()) + - " " + _("Version control detected."); - } else { - str2 = bformat(_("Could not open document %1$s"), disp_fn); + docstring str2; + Buffer * buf = loadDocument(fullname); + if (buf) { + str2 = bformat(_("Document %1$s opened."), disp_fn); + if (buf->lyxvc().inUse()) + str2 += " " + from_utf8(buf->lyxvc().versionString()) + + " " + _("Version control detected."); + } else { + str2 = bformat(_("Could not open document %1$s"), disp_fn); + } + message(str2); } - message(str2); } // FIXME: clean that diff --git a/src/frontends/qt/GuiView.h b/src/frontends/qt/GuiView.h index 57461d09bf..29687c387b 100644 --- a/src/frontends/qt/GuiView.h +++ b/src/frontends/qt/GuiView.h @@ -164,7 +164,7 @@ public: /// closes the buffer bool closeBuffer(Buffer & buf); /// - void openDocument(std::string const & filename, int origin); + void openDocuments(std::string const & filename, int origin); /// void importDocument(std::string const &); -- 2.39.5