]> git.lyx.org Git - lyx.git/blob - src/insets/InsetNomencl.cpp
Improve handling of top and bottom margin
[lyx.git] / src / insets / InsetNomencl.cpp
1 /**
2  * \file InsetNomencl.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author O. U. Baran
8  * \author Uwe Stöhr
9  * \author Jürgen Spitzmüller
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13 #include <config.h>
14
15 #include "InsetNomencl.h"
16 #include "InsetNote.h"
17
18 #include "Buffer.h"
19 #include "Cursor.h"
20 #include "DispatchResult.h"
21 #include "Font.h"
22 #include "Encoding.h"
23 #include "FuncRequest.h"
24 #include "FuncStatus.h"
25 #include "InsetIterator.h"
26 #include "InsetList.h"
27 #include "Language.h"
28 #include "LaTeXFeatures.h"
29 #include "Length.h"
30 #include "LyX.h"
31 #include "OutputParams.h"
32 #include "xml.h"
33 #include "texstream.h"
34 #include "TocBackend.h"
35
36 #include "frontends/FontMetrics.h"
37
38 #include "support/debug.h"
39 #include "support/docstream.h"
40 #include "support/gettext.h"
41 #include "support/lstrings.h"
42
43 using namespace std;
44 using namespace lyx::support;
45
46 namespace lyx {
47
48
49 /////////////////////////////////////////////////////////////////////
50 //
51 // InsetNomencl
52 //
53 /////////////////////////////////////////////////////////////////////
54
55 InsetNomencl::InsetNomencl(Buffer * buf, InsetCommandParams const & p)
56         : InsetCommand(buf, p),
57           nomenclature_entry_id(xml::uniqueID(from_ascii("nomen")))
58 {}
59
60
61 ParamInfo const & InsetNomencl::findInfo(string const & /* cmdName */)
62 {
63         static ParamInfo param_info_;
64         if (param_info_.empty()) {
65                 param_info_.add("prefix", ParamInfo::LATEX_OPTIONAL);
66                 param_info_.add("symbol", ParamInfo::LATEX_REQUIRED,
67                                 ParamInfo::ParamHandling(ParamInfo::HANDLING_LATEXIFY
68                                                          | ParamInfo::HANDLING_INDEX_ESCAPE));
69                 param_info_.add("description", ParamInfo::LATEX_REQUIRED,
70                                 ParamInfo::ParamHandling(ParamInfo::HANDLING_LATEXIFY
71                                                          | ParamInfo::HANDLING_INDEX_ESCAPE));
72                 param_info_.add("literal", ParamInfo::LYX_INTERNAL);
73         }
74         return param_info_;
75 }
76
77
78 docstring InsetNomencl::screenLabel() const
79 {
80         size_t const maxLabelChars = 25;
81         docstring label = _("Nom: ") + getParam("symbol");
82         support::truncateWithEllipsis(label, maxLabelChars);
83         return label;
84 }
85
86
87 docstring InsetNomencl::toolTip(BufferView const & /*bv*/, int /*x*/, int /*y*/) const
88 {
89         docstring tip = _("Nomenclature Symbol: ") + getParam("symbol") + "\n";
90         tip += _("Description: ") + "\t"
91                 + subst(getParam("description"), from_ascii("\\\\"), from_ascii("\n\t"));
92         if (!getParam("prefix").empty())
93                 tip += "\n" + _("Sorting: ") + getParam("prefix");
94         return tip;
95 }
96
97
98 int InsetNomencl::plaintext(odocstringstream & os,
99         OutputParams const &, size_t) const
100 {
101         docstring s = "[" + getParam("symbol") + ": " + getParam("description") + "]";
102         os << s;
103         return s.size();
104 }
105
106
107 void InsetNomencl::docbook(XMLStream & xs, OutputParams const &) const
108 {
109         docstring attr = "linkend=\"" + nomenclature_entry_id + "\"";
110         xs << xml::StartTag("glossterm", attr);
111         xs << xml::escapeString(getParam("symbol"));
112         xs << xml::EndTag("glossterm");
113 }
114
115
116 docstring InsetNomencl::xhtml(XMLStream &, OutputParams const &) const
117 {
118         return docstring();
119 }
120
121
122 void InsetNomencl::validate(LaTeXFeatures & features) const
123 {
124         features.require("nomencl");
125         InsetCommand::validate(features);
126 }
127
128
129 void InsetNomencl::addToToc(DocIterator const & cpit, bool output_active,
130                                                         UpdateType, TocBackend & backend) const
131 {
132         docstring const str = getParam("symbol");
133         TocBuilder & b = backend.builder("nomencl");
134         b.pushItem(cpit, str, output_active);
135         b.pop();
136 }
137
138
139 /////////////////////////////////////////////////////////////////////
140 //
141 // InsetPrintNomencl
142 //
143 /////////////////////////////////////////////////////////////////////
144
145 InsetPrintNomencl::InsetPrintNomencl(Buffer * buf, InsetCommandParams const & p)
146         : InsetCommand(buf, p)
147 {}
148
149
150 ParamInfo const & InsetPrintNomencl::findInfo(string const & /* cmdName */)
151 {
152         // The symbol width is set via nomencl's \nomlabelwidth in
153         // InsetPrintNomencl::latex and not as optional parameter of
154         // \printnomenclature
155         static ParamInfo param_info_;
156         if (param_info_.empty()) {
157                 // how is the width set?
158                 // values: none|auto|custom
159                 param_info_.add("set_width", ParamInfo::LYX_INTERNAL);
160                 // custom width
161                 param_info_.add("width", ParamInfo::LYX_INTERNAL);
162         }
163         return param_info_;
164 }
165
166
167 docstring InsetPrintNomencl::screenLabel() const
168 {
169         return _("Nomenclature");
170 }
171
172
173 struct NomenclEntry {
174         NomenclEntry() : par(0) {}
175         NomenclEntry(docstring s, docstring d, Paragraph const * p)
176           : symbol(s), desc(d), par(p)
177         {}
178
179         docstring symbol;
180         docstring desc;
181         Paragraph const * par;
182 };
183
184
185 typedef map<docstring, NomenclEntry > EntryMap;
186
187
188 docstring InsetPrintNomencl::xhtml(XMLStream &, OutputParams const & op) const
189 {
190         shared_ptr<Toc const> toc = buffer().tocBackend().toc("nomencl");
191
192         EntryMap entries;
193         Toc::const_iterator it = toc->begin();
194         Toc::const_iterator const en = toc->end();
195         for (; it != en; ++it) {
196                 DocIterator dit = it->dit();
197                 Paragraph const & par = dit.innerParagraph();
198                 Inset const * inset = par.getInset(dit.top().pos());
199                 if (!inset)
200                         return docstring();
201                 InsetCommand const * ic = inset->asInsetCommand();
202                 if (!ic)
203                         return docstring();
204
205                 // FIXME We need a link to the paragraph here, so we
206                 // need some kind of struct.
207                 docstring const symbol = ic->getParam("symbol");
208                 docstring const desc = ic->getParam("description");
209                 docstring const prefix = ic->getParam("prefix");
210                 docstring const sortas = prefix.empty() ? symbol : prefix;
211
212                 entries[sortas] = NomenclEntry(symbol, desc, &par);
213         }
214
215         if (entries.empty())
216                 return docstring();
217
218         // we'll use our own stream, because we are going to defer everything.
219         // that's how we deal with the fact that we're probably inside a standard
220         // paragraph, and we don't want to be.
221         odocstringstream ods;
222         XMLStream xs(ods);
223
224         InsetLayout const & il = getLayout();
225         string const & tag = il.htmltag();
226         docstring toclabel = translateIfPossible(from_ascii("Nomenclature"),
227                 op.local_font->language()->lang());
228
229         xs << xml::StartTag("div", "class='nomencl'")
230            << xml::StartTag(tag, "class='nomencl'")
231                  << toclabel
232                  << xml::EndTag(tag)
233            << xml::CR()
234            << xml::StartTag("dl")
235            << xml::CR();
236
237         EntryMap::const_iterator eit = entries.begin();
238         EntryMap::const_iterator const een = entries.end();
239         for (; eit != een; ++eit) {
240                 NomenclEntry const & ne = eit->second;
241                 string const parid = ne.par->magicLabel();
242                 xs << xml::StartTag("dt")
243                    << xml::StartTag("a", "href='#" + parid + "' class='nomencl'")
244                    << ne.symbol
245                    << xml::EndTag("a")
246                    << xml::EndTag("dt")
247                    << xml::CR()
248                    << xml::StartTag("dd")
249                    << ne.desc
250                    << xml::EndTag("dd")
251                    << xml::CR();
252         }
253
254         xs << xml::EndTag("dl")
255            << xml::CR()
256            << xml::EndTag("div")
257            << xml::CR();
258
259         return ods.str();
260 }
261
262
263 void InsetPrintNomencl::doDispatch(Cursor & cur, FuncRequest & cmd)
264 {
265         switch (cmd.action()) {
266
267         case LFUN_INSET_MODIFY: {
268                 InsetCommandParams p(NOMENCL_PRINT_CODE);
269                 // FIXME UNICODE
270                 InsetCommand::string2params(to_utf8(cmd.argument()), p);
271                 if (p.getCmdName().empty()) {
272                         cur.noScreenUpdate();
273                         break;
274                 }
275
276                 cur.recordUndo();
277                 setParams(p);
278                 break;
279         }
280
281         default:
282                 InsetCommand::doDispatch(cur, cmd);
283                 break;
284         }
285 }
286
287
288 bool InsetPrintNomencl::getStatus(Cursor & cur, FuncRequest const & cmd,
289         FuncStatus & status) const
290 {
291         switch (cmd.action()) {
292
293         case LFUN_INSET_DIALOG_UPDATE:
294         case LFUN_INSET_MODIFY:
295                 status.setEnabled(true);
296                 return true;
297
298         default:
299                 return InsetCommand::getStatus(cur, cmd, status);
300         }
301 }
302
303
304 void InsetPrintNomencl::docbook(XMLStream & xs, OutputParams const & runparams) const
305 {
306         shared_ptr<Toc const> toc = buffer().tocBackend().toc("nomencl");
307
308         EntryMap entries;
309         Toc::const_iterator it = toc->begin();
310         Toc::const_iterator const en = toc->end();
311         for (; it != en; ++it) {
312                 DocIterator dit = it->dit();
313                 Paragraph const & par = dit.innerParagraph();
314                 Inset const * inset = par.getInset(dit.top().pos());
315                 if (!inset)
316                         return;
317                 InsetCommand const * ic = inset->asInsetCommand();
318                 if (!ic)
319                         return;
320
321                 // FIXME We need a link to the paragraph here, so we
322                 // need some kind of struct.
323                 docstring const symbol = ic->getParam("symbol");
324                 docstring const desc = ic->getParam("description");
325                 docstring const prefix = ic->getParam("prefix");
326                 docstring const sortas = prefix.empty() ? symbol : prefix;
327
328                 entries[sortas] = NomenclEntry(symbol, desc, &par);
329         }
330
331         if (entries.empty())
332                 return;
333
334         // As opposed to XHTML, no need to defer everything until the end of time, so write directly to xs.
335         // TODO: At least, that's what was done before...
336
337         docstring toclabel = translateIfPossible(from_ascii("Nomenclature"),
338                                                                                          runparams.local_font->language()->lang());
339
340         xs << xml::StartTag("glossary");
341         xs << xml::CR();
342         xs << xml::StartTag("title");
343         xs << toclabel;
344         xs << xml::EndTag("title");
345         xs << xml::CR();
346
347         EntryMap::const_iterator eit = entries.begin();
348         EntryMap::const_iterator const een = entries.end();
349         for (; eit != een; ++eit) {
350                 NomenclEntry const & ne = eit->second;
351                 string const parid = ne.par->magicLabel();
352
353                 xs << xml::StartTag("glossentry", "xml:id=\"" + parid + "\"");
354                 xs << xml::CR();
355                 xs << xml::StartTag("glossterm");
356                 xs << ne.symbol;
357                 xs << xml::EndTag("glossterm");
358                 xs << xml::CR();
359                 xs << xml::StartTag("glossdef");
360                 xs << xml::CR();
361                 xs << xml::StartTag("para");
362                 xs << ne.desc;
363                 xs << xml::EndTag("para");
364                 xs << xml::CR();
365                 xs << xml::EndTag("glossdef");
366                 xs << xml::CR();
367                 xs << xml::EndTag("glossentry");
368                 xs << xml::CR();
369         }
370
371         xs << xml::EndTag("glossary");
372         xs << xml::CR();
373 }
374
375
376 namespace {
377 docstring nomenclWidest(Buffer const & buffer, OutputParams const & runparams)
378 {
379         // nomenclWidest() determines and returns the widest used
380         // nomenclature symbol in the document
381
382         int w = 0;
383         docstring symb;
384         InsetNomencl const * nomencl = 0;
385         ParagraphList::const_iterator it = buffer.paragraphs().begin();
386         ParagraphList::const_iterator end = buffer.paragraphs().end();
387
388         for (; it != end; ++it) {
389                 if (it->insetList().empty())
390                         continue;
391                 InsetList::const_iterator iit = it->insetList().begin();
392                 InsetList::const_iterator eend = it->insetList().end();
393                 for (; iit != eend; ++iit) {
394                         Inset * inset = iit->inset;
395                         if (inset->lyxCode() != NOMENCL_CODE)
396                                 continue;
397                         nomencl = static_cast<InsetNomencl const *>(inset);
398                         // Use proper formatting. We do not escape makeindex chars here
399                         docstring const symbol = nomencl ?
400                                 nomencl->params().prepareCommand(runparams, nomencl->getParam("symbol"),
401                                                         ParamInfo::HANDLING_LATEXIFY)
402                                 : docstring();
403                         // This is only an approximation,
404                         // but the best we can get.
405                         int const wx = use_gui ?
406                                 theFontMetrics(Font()).width(symbol) :
407                                 symbol.size();
408                         if (wx > w) {
409                                 w = wx;
410                                 symb = symbol;
411                         }
412                 }
413         }
414         // return the widest (or an empty) string
415         return symb;
416 }
417 } // namespace
418
419
420 void InsetPrintNomencl::latex(otexstream & os, OutputParams const & runparams_in) const
421 {
422         OutputParams runparams = runparams_in;
423         if (getParam("set_width") == "auto") {
424                 docstring widest = nomenclWidest(buffer(), runparams);
425                 // Set the label width via nomencl's command \nomlabelwidth.
426                 // This must be output before the command \printnomenclature
427                 if (!widest.empty()) {
428                         os << "\\settowidth{\\nomlabelwidth}{"
429                            << widest
430                            << "}\n";
431                 }
432         } else if (getParam("set_width") == "custom") {
433                 // custom length as optional arg of \printnomenclature
434                 string const width =
435                         Length(to_ascii(getParam("width"))).asLatexString();
436                 os << '\\'
437                    << from_ascii(getCmdName())
438                    << '['
439                    << from_ascii(width)
440                    << "]"
441                    << termcmd;
442                 return;
443         }
444         // output the command \printnomenclature
445         os << getCommand(runparams);
446 }
447
448
449 void InsetPrintNomencl::validate(LaTeXFeatures & features) const
450 {
451         features.useInsetLayout(getLayout());
452         features.require("nomencl");
453 }
454
455
456 InsetCode InsetPrintNomencl::lyxCode() const
457 {
458         return NOMENCL_PRINT_CODE;
459 }
460
461
462 string InsetPrintNomencl::contextMenuName() const
463 {
464         return "context-nomenclprint";
465 }
466
467
468 } // namespace lyx