[LyX/master] Allow LyX to find pdfs and urls of citation references and follow them from context menu.

Pavel Sanda sanda at lyx.org
Thu Aug 20 06:47:05 UTC 2020


commit e648202e7e35b41851407de449ec454c9b38e68b
Author: Pavel Sanda <sanda at lyx.org>
Date:   Thu Aug 20 08:33:40 2020 +0200

    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(+), 0 deletions(-)

diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc
index c2beb31..691596f 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 e37d563..ab073ae 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 4509101..00cdfcf 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 585d1e1..391faf7 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 3de816a..3e1e023 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: <TARGET>: URL (https:,file:) of the document. \n
+               <EXTERNAL>: 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 1b9f0ef..cdd99f1 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 9d44c3f..0ef0775 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 970a027..6c7c366 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 14a05a0..66db11a 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<docstring> 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 bc15e11..29e4b1f 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


More information about the lyx-cvs mailing list