2 * \file InsetHyperlink.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
9 * Full author contact details are available in file CREDITS.
13 #include "InsetHyperlink.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "LaTeXFeatures.h"
20 #include "output_docbook.h"
21 #include "output_xhtml.h"
23 #include "texstream.h"
25 #include "support/debug.h"
26 #include "support/docstream.h"
27 #include "support/FileName.h"
28 #include "support/filetools.h"
29 #include "support/gettext.h"
30 #include "support/lstrings.h"
31 #include "support/qstring_helpers.h"
33 #include "frontends/alert.h"
35 #include <QtGui/QDesktopServices>
39 using namespace lyx::support;
44 InsetHyperlink::InsetHyperlink(Buffer * buf, InsetCommandParams const & p)
45 : InsetCommand(buf, p)
49 ParamInfo const & InsetHyperlink::findInfo(string const & /* cmdName */)
51 static ParamInfo param_info_;
52 if (param_info_.empty()) {
53 param_info_.add("name", ParamInfo::LATEX_OPTIONAL,
54 ParamInfo::HANDLING_LATEXIFY);
55 param_info_.add("target", ParamInfo::LATEX_REQUIRED);
56 param_info_.add("type", ParamInfo::LATEX_REQUIRED);
57 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
63 docstring InsetHyperlink::screenLabel() const
65 // TODO: replace with unicode hyperlink character = U+1F517
66 docstring const temp = _("Hyperlink: ");
70 url += getParam("name");
72 url += getParam("target");
74 // elide if long and no name was provided
75 if (url.length() > 30) {
76 docstring end = url.substr(url.length() - 17, url.length());
77 support::truncateWithEllipsis(url, 13);
81 // elide if long (approx number of chars in line of article class)
82 if (url.length() > 80) {
83 docstring end = url.substr(url.length() - 67, url.length());
84 support::truncateWithEllipsis(url, 13);
92 void InsetHyperlink::doDispatch(Cursor & cur, FuncRequest & cmd)
94 switch (cmd.action()) {
101 InsetCommand::doDispatch(cur, cmd);
107 bool InsetHyperlink::getStatus(Cursor & cur, FuncRequest const & cmd,
108 FuncStatus & flag) const
110 switch (cmd.action()) {
111 case LFUN_INSET_EDIT: {
112 QUrl url(toqstr(getParam("target")),QUrl::StrictMode);
113 bool url_valid = getParam("type").empty() && url.isValid();
115 flag.setEnabled(url_valid || getParam("type") == "file:");
120 return InsetCommand::getStatus(cur, cmd, flag);
125 void InsetHyperlink::viewTarget() const
127 if (getParam("type").empty()) { //==Web
128 QUrl url(toqstr(getParam("target")),QUrl::StrictMode);
129 if (!QDesktopServices::openUrl(url))
130 LYXERR0("Unable to open URL!");
132 } else if (getParam("type") == "file:") {
133 FileName url = makeAbsPath(to_utf8(getParam("target")), buffer().filePath());
134 string const format = theFormats().getFormatFromFile(url);
135 theFormats().view(buffer(), url, format);
140 void InsetHyperlink::latex(otexstream & os,
141 OutputParams const & runparams) const
143 docstring url = getParam("target");
144 docstring name = getParam("name");
145 static char_type const chars_url[2] = {'%', '#'};
147 // For the case there is no name given, the target is set as name.
148 // Do this before !url.empty() and !name.empty() to handle characters
149 // such as % correctly.
154 // Use URI/URL-style percent-encoded string (hexadecimal).
155 // We exclude some characters that must not be transformed
156 // in hrefs: % # / : ? = & ! * ' ( ) ; @ + $ , [ ]
157 // or that we need to treat manually: \.
158 url = to_percent_encoding(url, from_ascii("%#\\/:?=&!*'();@+$,[]"));
159 // We handle \ manually since \\ is valid
160 for (size_t i = 0, pos;
161 (pos = url.find('\\', i)) != string::npos;
163 if (url[pos + 1] != '\\')
164 url.replace(pos, 1, from_ascii("%5C"));
167 // The characters in chars_url[] need to be escaped in the url
168 // field because otherwise LaTeX will fail when the hyperlink is
169 // within an argument of another command, e.g. in a \footnote. It
170 // is important that they are escaped as "\#" and not as "\#{}".
171 // FIXME this is not necessary in outside of commands.
172 for (int k = 0; k < 2; k++)
173 for (size_t i = 0, pos;
174 (pos = url.find(chars_url[k], i)) != string::npos;
176 url.replace(pos, 1, from_ascii("\\") + chars_url[k]);
178 // add "http://" when the type is web (type = empty)
179 // and no "://" or "run:" is given
180 docstring type = getParam("type");
181 if (url.find(from_ascii("://")) == string::npos
182 && url.find(from_ascii("run:")) == string::npos
184 url = from_ascii("http://") + url;
186 } // end if (!url.empty())
189 name = params().prepareCommand(runparams, name,
190 ParamInfo::HANDLING_LATEXIFY);
191 // replace the tilde by the \sim character as suggested in the
192 // LaTeX FAQ for URLs
193 if (getParam("literal") != from_ascii("true")) {
194 docstring const sim = from_ascii("$\\sim$");
195 for (size_t i = 0, pos;
196 (pos = name.find('~', i)) != string::npos;
198 name.replace(pos, 1, sim);
202 if (runparams.moving_arg)
205 // output the ready \href command
206 os << "\\href{" << getParam("type") << url << "}{" << name << '}';
210 int InsetHyperlink::plaintext(odocstringstream & os,
211 OutputParams const &, size_t) const
213 odocstringstream oss;
215 oss << '[' << getParam("target");
216 if (getParam("name").empty())
219 oss << "||" << getParam("name") << ']';
221 docstring const str = oss.str();
227 void InsetHyperlink::docbook(XMLStream & xs, OutputParams const &) const
229 xs << xml::StartTag("link", "xlink:href=\"" + subst(getParam("target"), from_ascii("&"), from_ascii("&")) + "\"");
230 xs << xml::escapeString(getParam("name"));
231 xs << xml::EndTag("link");
235 docstring InsetHyperlink::xhtml(XMLStream & xs, OutputParams const &) const
237 docstring const & target =
238 xml::escapeString(getParam("target"), XMLStream::ESCAPE_AND);
239 docstring const & name = getParam("name");
240 xs << xml::StartTag("a", to_utf8("href=\"" + target + "\""));
241 xs << (name.empty() ? target : name);
242 xs << xml::EndTag("a");
247 void InsetHyperlink::toString(odocstream & os) const
249 odocstringstream ods;
250 plaintext(ods, OutputParams(0), INT_MAX);
255 void InsetHyperlink::forOutliner(docstring & os, size_t const, bool const) const
257 docstring const & n = getParam("name");
262 os += getParam("target");
266 docstring InsetHyperlink::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
268 docstring url = getParam("target");
269 docstring type = getParam("type");
270 docstring guitype = _("www");
271 if (type == "mailto:")
272 guitype = _("email");
273 else if (type == "file:")
275 return bformat(_("Hyperlink (%1$s) to %2$s"), guitype, url);
279 void InsetHyperlink::validate(LaTeXFeatures & features) const
281 features.require("hyperref");
282 InsetCommand::validate(features);
286 pair<int, int> InsetHyperlink::isWords() const
288 docstring const label = getParam("name");
289 return pair<int, int>(label.size(), wordCount(label));
293 string InsetHyperlink::contextMenuName() const
295 return "context-hyperlink";