From e648202e7e35b41851407de449ec454c9b38e68b Mon Sep 17 00:00:00 2001 From: Pavel Sanda Date: Thu, 20 Aug 2020 08:33:40 +0200 Subject: [PATCH] Allow LyX to find pdfs and urls of citation references and follow them from context menu. Currently tested: - url & doi fields for bibtex. - all documented eprinttypes of biblatex - absolute paths of first entry of 'file' field for jabref and kbibtex - external script searching for author + year pdf Additional polishing will follow. https://www.mail-archive.com/lyx-devel@lists.lyx.org/msg212505.html --- lib/ui/stdcontext.inc | 1 + src/BiblioInfo.cpp | 78 +++++++++++++++++++++++++++++++++ src/BiblioInfo.h | 5 +++ src/FuncCode.h | 1 + src/LyXAction.cpp | 12 +++++ src/frontends/qt/GuiView.cpp | 8 ++++ src/frontends/qt/qt_helpers.cpp | 15 +++++++ src/frontends/qt/qt_helpers.h | 3 ++ src/insets/InsetCitation.cpp | 44 +++++++++++++++++++ src/insets/InsetCitation.h | 2 + 10 files changed, 169 insertions(+) diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc index c2beb316f4..691596f865 100644 --- a/lib/ui/stdcontext.inc +++ b/lib/ui/stdcontext.inc @@ -128,6 +128,7 @@ Menuset CiteStyles Separator Item "Settings...|S" "inset-settings" + Item "Open Citation Content|O" "inset-edit" End diff --git a/src/BiblioInfo.cpp b/src/BiblioInfo.cpp index e37d56389a..ab073ae600 100644 --- a/src/BiblioInfo.cpp +++ b/src/BiblioInfo.cpp @@ -651,6 +651,75 @@ docstring const BibTeXInfo::getYear() const } +void BibTeXInfo::getLocators(docstring & doi, docstring & url, docstring & file) const +{ + if (is_bibtex_) { + // get "doi" entry from citation record + doi = operator[]("doi"); + if (!doi.empty() && !prefixIs(doi,from_ascii("http"))) + doi = "https://doi.org/" + doi; + // get "url" entry from citation record + url = operator[]("url"); + // get "file" entry from citation record + file = operator[]("file"); + + // Jabref case, field has a format: + // Description:Location:Filetype;Description:Location:Filetype... + // We will grab only first pdf + if (!file.empty()) { + docstring ret, filedest, tmp; + ret = split(file, tmp, ':'); + tmp = split(ret, filedest, ':'); + //TODO howto deal with relative directories? + FileName f(to_utf8(filedest)); + if (f.exists()) + file = "file:///" + filedest; + } + + // kbibtex case, format: + // file1.pdf;file2.pdf + // We will grab only first pdf + docstring kfile; + if (file.empty()) + kfile = operator[]("localfile"); + if (!kfile.empty()) { + docstring filedest, tmp; + tmp = split(kfile, filedest, ';'); + //TODO howto deal with relative directories? + FileName f(to_utf8(filedest)); + if (f.exists()) + file = "file:///" + filedest; + } + + if (!url.empty()) + return; + + // try biblatex specific fields, see its manual + // 3.13.7 "Electronic Publishing Informationl" + docstring eprinttype = operator[]("eprinttype"); + docstring eprint = operator[]("eprint"); + if (eprint.empty()) + return; + + if (eprinttype == "arxiv") + url = "https://arxiv.org/abs/" + eprint; + if (eprinttype == "jstor") + url = "https://www.jstor.org/stable/" + eprint; + if (eprinttype == "pubmed") + url = "http://www.ncbi.nlm.nih.gov/pubmed/" + eprint; + if (eprinttype == "hdl") + url = "https://hdl.handle.net/" + eprint; + if (eprinttype == "googlebooks") + url = "http://books.google.com/books?id=" + eprint; + + return; + } + + // Here can be handled the bibliography environment. All one could do + // here is let LyX scan the entry for URL or HRef insets. +} + + namespace { docstring parseOptions(docstring const & format, string & optkey, @@ -1269,6 +1338,15 @@ docstring const BiblioInfo::getCiteNumber(docstring const & key) const return data.citeNumber(); } +void BiblioInfo::getLocators(docstring const & key, docstring & doi, docstring & url, docstring & file) const +{ + BiblioInfo::const_iterator it = find(key); + if (it == end()) + return; + BibTeXInfo const & data = it->second; + data.getLocators(doi,url,file); +} + docstring const BiblioInfo::getYear(docstring const & key, bool use_modifier) const { diff --git a/src/BiblioInfo.h b/src/BiblioInfo.h index 4509101fd3..00cdfcfd1d 100644 --- a/src/BiblioInfo.h +++ b/src/BiblioInfo.h @@ -70,6 +70,8 @@ public: bool const allnames = false, bool const beginning = true) const; /// docstring const getYear() const; + /// + void getLocators(docstring & doi, docstring & url, docstring & file) const; /// \return formatted BibTeX data suitable for framing. /// \param vector of pointers to crossref/xdata information docstring const & getInfo(BibTeXInfoList const & xrefs, @@ -213,6 +215,9 @@ public: /// language. docstring const getYear(docstring const & key, Buffer const & buf, bool use_modifier = false) const; + /// get either local pdf or web location of the citation referenced by key. + /// DOI/file are prefixed so they form proper URL for generic qt handler + void getLocators(docstring const & key, docstring & doi, docstring & url, docstring & file) const; /// docstring const getCiteNumber(docstring const & key) const; /// \return formatted BibTeX data associated with a given key. diff --git a/src/FuncCode.h b/src/FuncCode.h index 585d1e1580..391faf71ce 100644 --- a/src/FuncCode.h +++ b/src/FuncCode.h @@ -490,6 +490,7 @@ enum FuncCode LFUN_MASTER_BUFFER_FORALL, // spitz 20191231 LFUN_IF_RELATIVES, // spitz 20200102 LFUN_WINDOW_RAISE, // forenr, 20202104 + LFUN_CITATION_OPEN, // sanda, 20200815 LFUN_LASTACTION // end of the table }; diff --git a/src/LyXAction.cpp b/src/LyXAction.cpp index 3de816a68f..3e1e023550 100644 --- a/src/LyXAction.cpp +++ b/src/LyXAction.cpp @@ -1239,6 +1239,18 @@ void LyXAction::init() * \endvar */ { LFUN_CITATION_INSERT, "citation-insert", Noop, Edit }, +/*! + * \var lyx::FuncCode lyx::LFUN_CITATION_OPEN + * \li Action: Opens the corresponding pdf/url for a given citation inset. + * \li Syntax: citation-open [EXTERNAL] TARGET + * \li Params: : URL (https:,file:) of the document. \n + : Use external executable script for finding target \n + and launching viewer. In this case TARGET consists of author and year \n + and will be passed as an input argument to the script. + * \li Origin: Sanda, 16 Aug 2020 + * \endvar + */ + { LFUN_CITATION_OPEN, "citation-open", ReadOnly | NoUpdate | Argument, Edit }, /*! * \var lyx::FuncCode lyx::LFUN_CLIPBOARD_PASTE diff --git a/src/frontends/qt/GuiView.cpp b/src/frontends/qt/GuiView.cpp index 1b9f0ef27c..cdd99f137a 100644 --- a/src/frontends/qt/GuiView.cpp +++ b/src/frontends/qt/GuiView.cpp @@ -2371,6 +2371,10 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag) flag.setOnOff(lyxrc.spellcheck_continuously); break; + case LFUN_CITATION_OPEN: + enable = true; + break; + default: return false; } @@ -4620,6 +4624,10 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr) dr.screenUpdate(Update::Force); break; + case LFUN_CITATION_OPEN: { + frontend::showTarget(argument); + } + default: // The LFUN must be for one of BufferView, Buffer or Cursor; // let's try that: diff --git a/src/frontends/qt/qt_helpers.cpp b/src/frontends/qt/qt_helpers.cpp index 9d44c3f9c1..0ef077590b 100644 --- a/src/frontends/qt/qt_helpers.cpp +++ b/src/frontends/qt/qt_helpers.cpp @@ -21,7 +21,10 @@ #include "BufferParams.h" #include "FloatList.h" +#include "FuncRequest.h" #include "Language.h" +#include "LyX.h" +#include "LyXAction.h" #include "TextClass.h" #include "support/convert.h" @@ -293,6 +296,18 @@ void showDirectory(FileName const & directory) if (!QDesktopServices::openUrl(qurl)) LYXERR0("Unable to open QUrl even though dir exists!"); } + +void showTarget(string const & target){ + LYXERR(Debug::INSETS, "Showtarget:" << target << "\n"); + if (prefixIs(target,"EXTERNAL ")) { + string tmp,tar; + tar = split(target, tmp, ' '); + FuncRequest cmd = FuncRequest(LFUN_VC_COMMAND,"U . \"lyxpaperview " + tar + "\""); + lyx::dispatch(cmd); + return; + } + QDesktopServices::openUrl(QUrl(toqstr(target), QUrl::TolerantMode)); +} } // namespace frontend QString const qt_(char const * str, const char *) diff --git a/src/frontends/qt/qt_helpers.h b/src/frontends/qt/qt_helpers.h index 970a02707f..6c7c366f3d 100644 --- a/src/frontends/qt/qt_helpers.h +++ b/src/frontends/qt/qt_helpers.h @@ -101,6 +101,9 @@ void setSectionResizeMode(QHeaderView * view, QHeaderView::ResizeMode mode); /// Shows a directory in OSs file browser void showDirectory(support::FileName const & directory); +/// handle request for showing citation content - shows pdf or +/// web page in target; external script can be used for pdf view +void showTarget(std::string const & target); } // namespace frontend diff --git a/src/insets/InsetCitation.cpp b/src/insets/InsetCitation.cpp index 14a05a097a..66db11a593 100644 --- a/src/insets/InsetCitation.cpp +++ b/src/insets/InsetCitation.cpp @@ -23,6 +23,7 @@ #include "FuncRequest.h" #include "FuncStatus.h" #include "LaTeXFeatures.h" +#include "LyX.h" #include "output_xhtml.h" #include "output_docbook.h" #include "ParIterator.h" @@ -133,6 +134,9 @@ CitationStyle InsetCitation::getCitationStyle(BufferParams const & bp, string co void InsetCitation::doDispatch(Cursor & cur, FuncRequest & cmd) { switch (cmd.action()) { + case LFUN_INSET_EDIT: + openCitation(); + break; case LFUN_INSET_MODIFY: { buffer().removeBiblioTempFiles(); cache.recalculate = true; @@ -165,6 +169,44 @@ void InsetCitation::doDispatch(Cursor & cur, FuncRequest & cmd) } +void InsetCitation::openCitation(){ + Buffer const & buf = *buffer_; + // Only after the buffer is loaded from file... + if (!buf.isFullyLoaded()) + return; + + BiblioInfo const & bi = buf.masterBibInfo(); + if (bi.empty()) + return; + + docstring const & key = getParam("key"); + if (key.empty()) + return; + + vector keys = getVectorFromString(key); + docstring year, author, doi, url, file; + for (docstring const & kvar : keys) { + year = bi.getYear(kvar, buffer(), false); + author = bi.getAuthorOrEditorList(kvar, buffer()); + bi.getLocators(kvar, doi, url, file); + LYXERR(Debug::INSETS, "Locators: doi:" << doi << " url:" + << url << " file:" << file << " author:" << author << " year:" << year); + docstring locator; + if (!file.empty()) { + locator = file; + } else if (!doi.empty()) { + locator = doi; + } else if (!url.empty()) { + locator = url; + } else { + locator = "EXTERNAL " + year + " " + author; + } + FuncRequest cmd = FuncRequest(LFUN_CITATION_OPEN, locator); + lyx::dispatch(cmd); + } +} + + bool InsetCitation::getStatus(Cursor & cur, FuncRequest const & cmd, FuncStatus & status) const { @@ -203,6 +245,8 @@ bool InsetCitation::getStatus(Cursor & cur, FuncRequest const & cmd, } } return true; + case LFUN_INSET_EDIT: + return true; default: return InsetCommand::getStatus(cur, cmd, status); } diff --git a/src/insets/InsetCitation.h b/src/insets/InsetCitation.h index bc15e11a6f..29e4b1f67b 100644 --- a/src/insets/InsetCitation.h +++ b/src/insets/InsetCitation.h @@ -94,6 +94,8 @@ public: QualifiedList getQualifiedLists(docstring const & p) const; /// static bool last_literal; + /// + void openCitation(); private: /// tries to make a pretty label and makes a basic one if not -- 2.39.5