[LyX/master] New attempt on #9906: allow following hyperlinks via context menu.

Pavel Sanda sanda at lyx.org
Sun Aug 16 22:10:24 UTC 2020


On Sun, Aug 16, 2020 at 02:21:35PM -0400, Scott Kostyshak wrote:
> On Sun, Aug 16, 2020 at 04:34:36PM +0200, Pavel Sanda wrote:
> > On Sun, Aug 16, 2020 at 10:01:22AM -0400, Scott Kostyshak wrote:
> 
> > > I like that idea a lot and would personally find it helpful. Here is a
> > > related request, although not many extra details:
> > > 
> > >   https://www.mail-archive.com/search?l=mid&q=CAO7dr0jWBgOY8_ozNBQo4FuQpyFZACr1oQ8xzCs%2BnMNS-1Qt8Q%40mail.gmail.com
> > 
> > This seems asking pretty much what is proposed in the first bullet point.
> > Does your bibtex database contain full paths or they are relative to the document?
> > Are the filename unique enough that using 'locate filename' would not return
> > many false-positives (this is the way of my shell script).
> 
> My current workflow is that I use a cite key like lastname_year, and the
> PDF file is lastname_year.pdf. The path, relative to the .bib file, is
> currently ./pdfs/lastname_year.pdf. I currently use the following
> directory structure:
> 
>   paper.lyx
>   lit/paper.bib
>   lit/pdfs
> 
> Scott

Attached is updated patch.
For a given citation it does:
1. check for existence of file= field in bib file, if found launch pdf viewer.
2. else check for existence of DOI/URL in bib file. If found launch browser with DOI/URL
3. else trigger external scirpt while giving author's name + year.


There are several issues:
a) file= field is not standard bibtex, as I understood it's specific for jabref,
   which I do not use. Currenly it will work if full path is given but I have
   no clue what to expect from jabref.
b) currently only bibtex is supported, because I do not have experience with other
   engines.
c) external script name is hardcoded. That's not nice, but given that
   only coders would be able to use this feature I'm not sure about making
   it new rc value. We could provide example of such script with proper name
   and distribute it with our sources.
   Opinions?
   
I think a) needs touch from someone who works with jabref, b) needs someone who is
using different biblio engine(s).

So except for c) where I would like to get your feedback I suggest to commit
what I have and let others fill in the bits, it should be fairly easy with the
infrastructure already in the place.

Pavel
-------------- next part --------------
diff --git a/lib/ui/stdcontext.inc b/lib/ui/stdcontext.inc
index c2beb316f4..b47cd4d7f8 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..b307eb390a 100644
--- a/src/BiblioInfo.cpp
+++ b/src/BiblioInfo.cpp
@@ -651,6 +651,23 @@ docstring const BibTeXInfo::getYear() const
 }
 
 
+void BibTeXInfo::getLocators(docstring &doi, docstring &url, docstring &file) const
+{
+	if (is_bibtex_) {
+		doi = operator[]("doi");
+		if (!doi.empty() && !prefixIs(doi,from_ascii("http")))
+			doi = "https://doi.org/" + doi;
+		url = operator[]("url");
+		file = operator[]("file");
+		//TODO some directory subst needed for Jabref?
+		if (!file.empty() && !prefixIs(file,from_ascii("file:///")))
+			file = "file:///" + file;
+		return;
+	}
+	//TODO I have no clue about non-bibtex cases
+}
+
+
 namespace {
 
 docstring parseOptions(docstring const & format, string & optkey,
@@ -1269,6 +1286,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..80b95e42f3 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: <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 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..3b7753422d 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<<"Showtarget:" << target << "\n";
+	if (prefixIs(target,"EXTERNAL ")) {
+		string tmp,tar;
+		tar = split(target, tmp, ' ');
+		FuncRequest cmd = FuncRequest(LFUN_VC_COMMAND,"U . \"paperview " + 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..7b4eacd469 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"
@@ -158,6 +159,9 @@ void InsetCitation::doDispatch(Cursor & cur, FuncRequest & cmd)
 			cmd = FuncRequest(LFUN_INSET_MODIFY, "changetype " + newcmdname);
 		}
 	}
+	case LFUN_INSET_EDIT: {
+		openCitation();
+	}
 	// fall through
 	default:
 		InsetCommand::doDispatch(cur, cmd);
@@ -165,6 +169,48 @@ 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);
+//		LYXERR0("Locators: doi:" << doi << " url:"
+//		        << url << " file:" << file << " author:" << author << " year:" << year);
+		LYXERR(Debug::INSETS, "Locators: doi:" << doi << " url:"
+		        << url << " file:" << file << " author:" << author << " year:" << year);
+		docstring locator;
+		if (!file.empty()) {
+			//TODO if file exists
+			//TODO Quotes?
+			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 +249,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


More information about the lyx-devel mailing list