]> git.lyx.org Git - features.git/blob - src/insets/InsetERT.cpp
XHTML/DocBook: merge code duplicates for vertical alignment.
[features.git] / src / insets / InsetERT.cpp
1 /**
2  * \file InsetERT.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Lars Gullik Bjønnes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetERT.h"
15
16 #include "Cursor.h"
17 #include "FuncRequest.h"
18 #include "FuncStatus.h"
19 #include "InsetLayout.h"
20 #include "Language.h"
21 #include "Lexer.h"
22 #include "xml.h"
23 #include "ParagraphParameters.h"
24 #include "Paragraph.h"
25 #include "output_docbook.h"
26
27 #include "support/docstream.h"
28 #include "support/gettext.h"
29 #include "support/lstrings.h"
30 #include "support/TempFile.h"
31 #include "Encoding.h"
32
33 #include <sstream>
34 #include <regex>
35 #include <iostream>
36
37 using namespace std;
38 using namespace lyx::support;
39
40 namespace lyx {
41
42 InsetERT::InsetERT(Buffer * buf, CollapseStatus status)
43         : InsetCollapsible(buf)
44 {
45         status_ = status;
46 }
47
48
49 InsetERT::InsetERT(InsetERT const & old)
50         : InsetCollapsible(old)
51 {}
52
53
54 void InsetERT::write(ostream & os) const
55 {
56         os << "ERT" << "\n";
57         InsetCollapsible::write(os);
58 }
59
60
61 int InsetERT::plaintext(odocstringstream & os,
62         OutputParams const & rp, size_t max_length) const
63 {
64         if (!rp.inIndexEntry)
65                 // do not output TeX code
66                 return 0;
67
68         ParagraphList::const_iterator par = paragraphs().begin();
69         ParagraphList::const_iterator end = paragraphs().end();
70
71         while (par != end && os.str().size() <= max_length) {
72                 pos_type siz = par->size();
73                 for (pos_type i = 0; i < siz; ++i) {
74                         char_type const c = par->getChar(i);
75                         // output the active characters
76                         switch (c) {
77                         case '|':
78                         case '!':
79                         case '@':
80                                 os.put(c);
81                                 break;
82                         default:
83                                 break;
84                         }
85                 }
86                 ++par;
87         }
88         return 0;
89 }
90
91
92 void InsetERT::docbook(XMLStream & xs, OutputParams const & runparams) const
93 {
94         auto const begin = paragraphs().begin();
95         auto par = begin;
96         auto const end = paragraphs().end();
97
98         odocstringstream os; // No need for XML handling here.
99
100         // Recreate the logic of makeParagraph in output_docbook.cpp, but much simplified: never open <para>
101         // in an ERT, use simple line breaks.
102         // New line after each paragraph of the ERT, save the last one.
103         while (true) { // For each paragraph in the ERT...
104         std::vector<docstring> pars_prepend;
105         std::vector<docstring> pars;
106         std::vector<docstring> pars_append;
107         tie(pars_prepend, pars, pars_append) = par->simpleDocBookOnePar(buffer(), runparams, text().outerFont(distance(begin, par)), 0, false, true);
108
109         for (docstring const & parXML : pars_prepend)
110             xs << XMLStream::ESCAPE_NONE << parXML;
111                 auto p = pars.begin();
112                 while (true) { // For each line of this ERT paragraph...
113                         os << *p;
114                         ++p;
115                         if (p != pars.end())
116                                 os << "\n";
117                         else
118                                 break;
119                 }
120         for (docstring const & parXML : pars_append)
121             xs << XMLStream::ESCAPE_NONE << parXML;
122
123                 ++par;
124                 if (par != end)
125                         os << "\n";
126                 else
127                         break;
128         }
129
130 //      // Implement the special case of \and: split the current item.
131 //      if (os.str() == "\\and" || os.str() == "\\and ") {
132 //              auto lay = getLayout();
133 //      }
134
135         // Try to recognise some commands to have a nicer DocBook output.
136         bool output_as_comment = true;
137
138         // First step: some commands have a direct mapping to DocBook, mostly because the mapping is simply text or
139         // an XML entity.
140         // Logic is similar to that of convertLaTeXCommands in BiblioInfo.cpp.
141         // TODO: make the code even more similar by looping over the string and applying all conversions. (What is not
142         //  recognised should simply be put in comments: have a list of elements that are either already recognised or are
143         //  not yet recognised? Global transformations like \string should then come first.)
144         {
145                 docstring os_trimmed = trim(os.str());
146
147                 // Rewrite \"u to \"{u}.
148                 static regex const regNoBraces(R"(^\\\W\w)");
149                 if (regex_search(to_utf8(os_trimmed), regNoBraces)) {
150                         os_trimmed.insert(3, from_ascii("}"));
151                         os_trimmed.insert(2, from_ascii("{"));
152                 }
153
154                 // Rewrite \" u to \"{u}.
155                 static regex const regSpace(R"(^\\\W \w)");
156                 if (regex_search(to_utf8(os_trimmed), regSpace)) {
157                         os_trimmed[2] = '{';
158                         os_trimmed.insert(4, from_ascii("}"));
159                 }
160
161                 std::cout << to_utf8(os_trimmed) << std::endl;
162
163                 // Look into the global table of Unicode characters if there is a match.
164                 bool termination;
165                 docstring rem;
166                 docstring const converted = Encodings::fromLaTeXCommand(os_trimmed,
167                                                                         Encodings::TEXT_CMD, termination, rem);
168                 if (!converted.empty()) {
169                         // Don't output the characters directly, even if the document should be encoded in UTF-8, for editors that
170                         // do not support all these funky characters.
171                         for (const char_type& character : converted) {
172                                 xs << XMLStream::ESCAPE_NONE << from_ascii("&#" + std::to_string(character) + ';');
173                         }
174                         output_as_comment = false;
175                 }
176         }
177
178         // Second step: the command \string can be ignored. If that's the only command in the ERT, then done.
179         // There may be several occurrences. (\string is 7 characters long.)
180         if (os.str().length() >= 7) {
181                 docstring os_str = os.str();
182
183                 while (os_str.length() >= 7) {
184                         auto os_text = os_str.find(from_ascii("\\string"));
185
186                         if (os_text != lyx::docstring::npos && !std::isalpha(static_cast<int>(os_str[os_text + 7]))) {
187                                 os_str = os_str.substr(0, os_text) + os_str.substr(os_text + 7, os_str.length());
188
189                                 if (os_str.find('\\') == std::string::npos) {
190                                         xs << os_str;
191                                         output_as_comment = false;
192                                         break;
193                                 }
194                         } else {
195                                 break;
196                         }
197                 }
198         }
199
200         // Otherwise, output the ERT as a comment with the appropriate escaping if the command is not recognised.
201         if (output_as_comment) {
202                 xs << XMLStream::ESCAPE_NONE << "<!-- ";
203                 xs << XMLStream::ESCAPE_COMMENTS << os.str();
204                 xs << XMLStream::ESCAPE_NONE << " -->";
205         }
206 }
207
208
209 void InsetERT::doDispatch(Cursor & cur, FuncRequest & cmd)
210 {
211         switch (cmd.action()) {
212         case LFUN_INSET_MODIFY:
213                 if (cmd.getArg(0) == "ert") {
214                         cur.recordUndoInset(this);
215                         setStatus(cur, string2params(to_utf8(cmd.argument())));
216                         break;
217                 }
218                 //fall-through
219         default:
220                 InsetCollapsible::doDispatch(cur, cmd);
221                 break;
222         }
223
224 }
225
226
227 bool InsetERT::getStatus(Cursor & cur, FuncRequest const & cmd,
228         FuncStatus & status) const
229 {
230         switch (cmd.action()) {
231         case LFUN_INSET_INSERT:
232                 status.setEnabled(false);
233                 return true;
234         case LFUN_INSET_MODIFY:
235                 if (cmd.getArg(0) == "ert") {
236                         status.setEnabled(true);
237                         return true;
238                 }
239                 //fall through
240
241         default:
242                 return InsetCollapsible::getStatus(cur, cmd, status);
243         }
244 }
245
246
247
248 docstring const InsetERT::buttonLabel(BufferView const & bv) const
249 {
250         // U+1F512 LOCK
251         docstring const locked = tempfile_ ? docstring(1, 0x1F512) : docstring();
252         if (decoration() == InsetDecoration::CLASSIC)
253                 return locked + (isOpen(bv) ? _("ERT") : getNewLabel(_("ERT")));
254         return locked + getNewLabel(_("ERT"));
255 }
256
257
258 InsetCollapsible::CollapseStatus InsetERT::string2params(string const & in)
259 {
260         if (in.empty())
261                 return Collapsed;
262         istringstream data(in);
263         Lexer lex;
264         lex.setStream(data);
265         lex.setContext("InsetERT::string2params");
266         lex >> "ert";
267         int s;
268         lex >> s;
269         return static_cast<CollapseStatus>(s);
270 }
271
272
273 string InsetERT::params2string(CollapseStatus status)
274 {
275         ostringstream data;
276         data << "ert" << ' ' << status;
277         return data.str();
278 }
279
280
281 docstring InsetERT::xhtml(XMLStream &, OutputParams const &) const
282 {
283         return docstring();
284 }
285
286 } // namespace lyx