]> git.lyx.org Git - lyx.git/blob - src/insets/InsetListings.cpp
Cosmetics.
[lyx.git] / src / insets / InsetListings.cpp
1 /**
2  * \file InsetListings.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Bo Peng
7  * \author Jürgen Spitzmüller
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "InsetListings.h"
15 #include "InsetCaption.h"
16
17 #include "Buffer.h"
18 #include "BufferParams.h"
19 #include "Counters.h"
20 #include "Cursor.h"
21 #include "DispatchResult.h"
22 #include "FuncRequest.h"
23 #include "FuncStatus.h"
24 #include "support/gettext.h"
25 #include "InsetList.h"
26 #include "Language.h"
27 #include "MetricsInfo.h"
28 #include "TextClass.h"
29
30 #include "support/docstream.h"
31 #include "support/lstrings.h"
32
33 #include <sstream>
34
35 using namespace std;
36 using namespace lyx::support;
37
38 namespace lyx {
39
40
41 char const lstinline_delimiters[] =
42         "!*()-=+|;:'\"`,<.>/?QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm";
43
44 InsetListings::InsetListings(Buffer const & buf, InsetListingsParams const & par)
45         : InsetCollapsable(buf, par.status())
46 {}
47
48
49 InsetListings::~InsetListings()
50 {
51         InsetListingsMailer(*this).hideDialog();
52 }
53
54
55 Inset::DisplayType InsetListings::display() const
56 {
57         return params().isInline() || params().isFloat() ? Inline : AlignLeft;
58 }
59
60
61 void InsetListings::updateLabels(ParIterator const & it)
62 {
63         Counters & cnts = buffer().params().documentClass().counters();
64         string const saveflt = cnts.current_float();
65
66         // Tell to captions what the current float is
67         cnts.current_float("listing");
68
69         InsetCollapsable::updateLabels(it);
70
71         //reset afterwards
72         cnts.current_float(saveflt);
73 }
74
75
76 void InsetListings::write(ostream & os) const
77 {
78         os << "listings" << "\n";
79         InsetListingsParams const & par = params();
80         // parameter string is encoded to be a valid lyx token.
81         string opt = par.encodedString();
82         if (!opt.empty())
83                 os << "lstparams \"" << opt << "\"\n";
84         if (par.isInline())
85                 os << "inline true\n";
86         else
87                 os << "inline false\n";
88         InsetCollapsable::write(os);
89 }
90
91
92 void InsetListings::read(Lexer & lex)
93 {
94         while (lex.isOK()) {
95                 lex.next();
96                 string const token = lex.getString();
97                 if (token == "lstparams") {
98                         lex.next();
99                         string const value = lex.getString();
100                         params().fromEncodedString(value);
101                 } else if (token == "inline") {
102                         lex.next();
103                         params().setInline(lex.getBool());
104                 } else {
105                         // no special option, push back 'status' etc
106                         lex.pushToken(token);
107                         break;
108                 }
109         }
110         InsetCollapsable::read(lex);
111 }
112
113
114 docstring InsetListings::editMessage() const
115 {
116         return _("Opened Listing Inset");
117 }
118
119
120 int InsetListings::latex(odocstream & os, OutputParams const & runparams) const
121 {
122         string param_string = params().params();
123         // NOTE: I use {} to quote text, which is an experimental feature
124         // of the listings package (see page 25 of the manual)
125         int lines = 0;
126         bool isInline = params().isInline();
127         // get the paragraphs. We can not output them directly to given odocstream
128         // because we can not yet determine the delimiter character of \lstinline
129         docstring code;
130         ParagraphList::const_iterator par = paragraphs().begin();
131         ParagraphList::const_iterator end = paragraphs().end();
132
133         while (par != end) {
134                 pos_type siz = par->size();
135                 bool captionline = false;
136                 for (pos_type i = 0; i < siz; ++i) {
137                         if (i == 0 && par->isInset(i) && i + 1 == siz)
138                                 captionline = true;
139                         // ignore all struck out text and (caption) insets
140                         if (par->isDeleted(i) || par->isInset(i))
141                                 continue;
142                         code += par->getChar(i);
143                 }
144                 ++par;
145                 // for the inline case, if there are multiple paragraphs
146                 // they are simply joined. Otherwise, expect latex errors.
147                 if (par != end && !isInline && !captionline) {
148                         code += "\n";
149                         ++lines;
150                 }
151         }
152         if (isInline) {
153                 char const * delimiter = lstinline_delimiters;
154                 for (; delimiter != '\0'; ++delimiter)
155                         if (!contains(code, *delimiter))
156                                 break;
157                 // This code piece contains all possible special character? !!!
158                 // Replace ! with a warning message and use ! as delimiter.
159                 if (*delimiter == '\0') {
160                         code = subst(code, from_ascii("!"), from_ascii(" WARNING: no lstline delimiter can be used "));
161                         delimiter = lstinline_delimiters;
162                 }
163                 if (param_string.empty())
164                         os << "\\lstinline" << *delimiter;
165                 else
166                         os << "\\lstinline[" << from_ascii(param_string) << "]" << *delimiter;
167                 os << code
168                    << *delimiter;
169         } else {
170                 OutputParams rp = runparams;
171                 // FIXME: the line below would fix bug 4182,
172                 // but real_current_font moved to cursor.
173                 //rp.local_font = &text_.real_current_font;
174                 rp.moving_arg = true;
175                 docstring const caption = getCaption(rp);
176                 runparams.encoding = rp.encoding;
177                 if (param_string.empty() && caption.empty())
178                         os << "\n\\begingroup\n\\inputencoding{latin1}\n\\begin{lstlisting}\n";
179                 else {
180                         os << "\n\\begingroup\n\\inputencoding{latin1}\n\\begin{lstlisting}[";
181                         if (!caption.empty()) {
182                                 os << "caption={" << caption << '}';
183                                 if (!param_string.empty())
184                                         os << ',';
185                         }
186                         os << from_utf8(param_string) << "]\n";
187                 }
188                 lines += 4;
189                 os << code << "\n\\end{lstlisting}\n\\endgroup\n";
190                 lines += 3;
191         }
192
193         return lines;
194 }
195
196
197 void InsetListings::doDispatch(Cursor & cur, FuncRequest & cmd)
198 {
199         switch (cmd.action) {
200
201         case LFUN_INSET_MODIFY: {
202                 InsetListingsMailer::string2params(to_utf8(cmd.argument()), params());
203                 break;
204         }
205         case LFUN_INSET_DIALOG_UPDATE:
206                 InsetListingsMailer(*this).updateDialog(&cur.bv());
207                 break;
208         case LFUN_MOUSE_RELEASE: {
209                 if (cmd.button() == mouse_button::button3 && hitButton(cmd)) {
210                         InsetListingsMailer(*this).showDialog(&cur.bv());
211                         break;
212                 }
213                 InsetCollapsable::doDispatch(cur, cmd);
214                 break;
215         }
216         default:
217                 InsetCollapsable::doDispatch(cur, cmd);
218                 break;
219         }
220 }
221
222
223 bool InsetListings::getStatus(Cursor & cur, FuncRequest const & cmd,
224         FuncStatus & status) const
225 {
226         switch (cmd.action) {
227                 case LFUN_INSET_DIALOG_UPDATE:
228                         status.enabled(true);
229                         return true;
230                 case LFUN_CAPTION_INSERT:
231                         status.enabled(!params().isInline());
232                         return true;
233                 default:
234                         return InsetCollapsable::getStatus(cur, cmd, status);
235         }
236 }
237
238
239 void InsetListings::setButtonLabel()
240 {
241         // FIXME UNICODE
242         if (decoration() == InsetLayout::Classic)
243                 setLabel(isOpen() ?  _("Listing") : getNewLabel(_("Listing")));
244         else
245                 setLabel(getNewLabel(_("Listing")));
246 }
247
248
249 void InsetListings::validate(LaTeXFeatures & features) const
250 {
251         features.require("listings");
252         InsetCollapsable::validate(features);
253 }
254
255
256 bool InsetListings::showInsetDialog(BufferView * bv) const
257 {
258         InsetListingsMailer(const_cast<InsetListings &>(*this)).showDialog(bv);
259         return true;
260 }
261
262
263 docstring InsetListings::getCaption(OutputParams const & runparams) const
264 {
265         if (paragraphs().empty())
266                 return docstring();
267
268         ParagraphList::const_iterator pit = paragraphs().begin();
269         for (; pit != paragraphs().end(); ++pit) {
270                 InsetList::const_iterator it = pit->insetList().begin();
271                 for (; it != pit->insetList().end(); ++it) {
272                         Inset & inset = *it->inset;
273                         if (inset.lyxCode() == CAPTION_CODE) {
274                                 odocstringstream ods;
275                                 InsetCaption * ins =
276                                         static_cast<InsetCaption *>(it->inset);
277                                 ins->getOptArg(ods, runparams);
278                                 ins->getArgument(ods, runparams);
279                                 return ods.str();
280                         }
281                 }
282         }
283         return docstring();
284 }
285
286
287 string const InsetListingsMailer::name_("listings");
288
289 InsetListingsMailer::InsetListingsMailer(InsetListings & inset)
290         : inset_(inset)
291 {}
292
293
294 string const InsetListingsMailer::inset2string(Buffer const &) const
295 {
296         return params2string(inset_.params());
297 }
298
299
300 void InsetListingsMailer::string2params(string const & in,
301                                    InsetListingsParams & params)
302 {
303         params = InsetListingsParams();
304         if (in.empty())
305                 return;
306         istringstream data(in);
307         Lexer lex(0, 0);
308         lex.setStream(data);
309         // discard "listings", which is only used to determine inset
310         lex.next();
311         params.read(lex);
312 }
313
314
315 string const
316 InsetListingsMailer::params2string(InsetListingsParams const & params)
317 {
318         ostringstream data;
319         data << name_ << " ";
320         params.write(data);
321         return data.str();
322 }
323
324
325 } // namespace lyx