]> git.lyx.org Git - features.git/blob - src/insets/InsetHyperlink.cpp
DocBook: merge code duplicates for HTML and CALS tables.
[features.git] / src / insets / InsetHyperlink.cpp
1 /**
2  * \file InsetHyperlink.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author José Matos
7  * \author Uwe Stöhr
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13 #include "InsetHyperlink.h"
14
15 #include "Buffer.h"
16 #include "Format.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "LaTeXFeatures.h"
20 #include "output_docbook.h"
21 #include "output_xhtml.h"
22 #include "xml.h"
23 #include "texstream.h"
24
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"
32
33 #include "frontends/alert.h"
34
35 #include <QtGui/QDesktopServices>
36 #include <QUrl>
37
38 using namespace std;
39 using namespace lyx::support;
40
41 namespace lyx {
42
43
44 InsetHyperlink::InsetHyperlink(Buffer * buf, InsetCommandParams const & p)
45         : InsetCommand(buf, p)
46 {}
47
48
49 ParamInfo const & InsetHyperlink::findInfo(string const & /* cmdName */)
50 {
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);
58         }
59         return param_info_;
60 }
61
62
63 docstring InsetHyperlink::screenLabel() const
64 {
65         // TODO: replace with unicode hyperlink character = U+1F517
66         docstring const temp = _("Hyperlink: ");
67
68         docstring url;
69
70         url += getParam("name");
71         if (url.empty()) {
72                 url += getParam("target");
73
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);
78                         url += end;
79                 }
80         } else {
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);
85                         url += end;
86                 }
87         }
88         return temp + url;
89 }
90
91
92 void InsetHyperlink::doDispatch(Cursor & cur, FuncRequest & cmd)
93 {
94         switch (cmd.action()) {
95
96         case LFUN_INSET_EDIT:
97                 viewTarget();
98                 break;
99
100         default:
101                 InsetCommand::doDispatch(cur, cmd);
102                 break;
103         }
104 }
105
106
107 bool InsetHyperlink::getStatus(Cursor & cur, FuncRequest const & cmd,
108                 FuncStatus & flag) const
109 {
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();
114
115                 flag.setEnabled(url_valid || getParam("type") == "file:");
116                 return true;
117                 }
118
119         default:
120                 return InsetCommand::getStatus(cur, cmd, flag);
121         }
122 }
123
124
125 void InsetHyperlink::viewTarget() const
126 {
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!");
131
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);
136         }
137 }
138
139
140 void InsetHyperlink::latex(otexstream & os,
141                            OutputParams const & runparams) const
142 {
143         docstring url = getParam("target");
144         docstring name = getParam("name");
145         static char_type const chars_url[2] = {'%', '#'};
146
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.
150         if (name.empty())
151                 name = url;
152
153         if (!url.empty()) {
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;
162                         i = pos + 2) {
163                         if (url[pos + 1] != '\\')
164                                 url.replace(pos, 1, from_ascii("%5C"));
165                 }
166
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;
175                                 i = pos + 2)
176                                 url.replace(pos, 1, from_ascii("\\") + chars_url[k]);
177
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
183                         && type.empty())
184                         url = from_ascii("http://") + url;
185
186         } // end if (!url.empty())
187
188         if (!name.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;
197                                 i = pos + 1)
198                                 name.replace(pos, 1, sim);
199                 }
200         }
201
202         if (runparams.moving_arg)
203                 os << "\\protect";
204
205         // output the ready \href command
206         os << "\\href{" << getParam("type") << url << "}{" << name << '}';
207 }
208
209
210 int InsetHyperlink::plaintext(odocstringstream & os,
211         OutputParams const &, size_t) const
212 {
213         odocstringstream oss;
214
215         oss << '[' << getParam("target");
216         if (getParam("name").empty())
217                 oss << ']';
218         else
219                 oss << "||" << getParam("name") << ']';
220
221         docstring const str = oss.str();
222         os << str;
223         return str.size();
224 }
225
226
227 void InsetHyperlink::docbook(XMLStream & xs, OutputParams const &) const
228 {
229         xs << xml::StartTag("link", "xlink:href=\"" + subst(getParam("target"), from_ascii("&"), from_ascii("&amp;")) + "\"");
230         xs << xml::escapeString(getParam("name"));
231         xs << xml::EndTag("link");
232 }
233
234
235 docstring InsetHyperlink::xhtml(XMLStream & xs, OutputParams const &) const
236 {
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");
243         return docstring();
244 }
245
246
247 void InsetHyperlink::toString(odocstream & os) const
248 {
249         odocstringstream ods;
250         plaintext(ods, OutputParams(0), INT_MAX);
251         os << ods.str();
252 }
253
254
255 void InsetHyperlink::forOutliner(docstring & os, size_t const, bool const) const
256 {
257         docstring const & n = getParam("name");
258         if (!n.empty()) {
259                 os += n;
260                 return;
261         }
262         os += getParam("target");
263 }
264
265
266 docstring InsetHyperlink::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
267 {
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:")
274                 guitype = _("file");
275         return bformat(_("Hyperlink (%1$s) to %2$s"), guitype, url);
276 }
277
278
279 void InsetHyperlink::validate(LaTeXFeatures & features) const
280 {
281         features.require("hyperref");
282         InsetCommand::validate(features);
283 }
284
285
286 pair<int, int> InsetHyperlink::isWords() const
287 {
288         docstring const label = getParam("name");
289         return pair<int, int>(label.size(), wordCount(label));
290 }
291
292
293 string InsetHyperlink::contextMenuName() const
294 {
295         return "context-hyperlink";
296 }
297
298
299 } // namespace lyx