]> git.lyx.org Git - features.git/blob - src/insets/InsetSpace.cpp
simplify Lexer use a bit
[features.git] / src / insets / InsetSpace.cpp
1 /**
2  * \file InsetSpace.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup Nielsen
7  * \author Jean-Marc Lasgouttes
8  * \author Lars Gullik Bjønnes
9  * \author Jürgen Spitzmüller
10  *
11  * Full author contact details are available in file CREDITS.
12  */
13
14 #include <config.h>
15
16 #include "InsetSpace.h"
17
18 #include "BufferView.h"
19 #include "Cursor.h"
20 #include "Dimension.h"
21 #include "FuncRequest.h"
22 #include "FuncStatus.h"
23 #include "Length.h"
24 #include "Lexer.h"
25 #include "MetricsInfo.h"
26 #include "OutputParams.h"
27
28 #include "support/debug.h"
29 #include "support/docstream.h"
30 #include "support/gettext.h"
31 #include "support/lstrings.h"
32
33 #include "frontends/Application.h"
34 #include "frontends/FontMetrics.h"
35 #include "frontends/Painter.h"
36
37 using namespace std;
38
39 namespace lyx {
40
41
42 InsetSpace::InsetSpace(InsetSpaceParams const & params)
43         : params_(params)
44 {}
45
46
47 InsetSpaceParams::Kind InsetSpace::kind() const
48 {
49         return params_.kind;
50 }
51
52
53 Length InsetSpace::length() const
54 {
55         return params_.length;
56 }
57
58
59 InsetSpace::~InsetSpace()
60 {
61         hideDialogs("space", this);
62 }
63
64
65 docstring InsetSpace::toolTip(BufferView const &, int, int) const
66 {
67         docstring message;
68         switch (params_.kind) {
69         case InsetSpaceParams::NORMAL:
70                 message = _("Interword Space");
71                 break;
72         case InsetSpaceParams::PROTECTED:
73                 message = _("Protected Space");
74                 break;
75         case InsetSpaceParams::THIN:
76                 message = _("Thin Space");
77                 break;
78         case InsetSpaceParams::QUAD:
79                 message = _("Quad Space");
80                 break;
81         case InsetSpaceParams::QQUAD:
82                 message = _("QQuad Space");
83                 break;
84         case InsetSpaceParams::ENSPACE:
85                 message = _("Enspace");
86                 break;
87         case InsetSpaceParams::ENSKIP:
88                 message = _("Enskip");
89                 break;
90         case InsetSpaceParams::NEGTHIN:
91                 message = _("Negative Thin Space");
92                 break;
93         case InsetSpaceParams::HFILL:
94                 message = _("Horizontal Fill");
95                 break;
96         case InsetSpaceParams::HFILL_PROTECTED:
97                 message = _("Protected Horizontal Fill");
98                 break;
99         case InsetSpaceParams::DOTFILL:
100                 message = _("Horizontal Fill (Dots)");
101                 break;
102         case InsetSpaceParams::HRULEFILL:
103                 message = _("Horizontal Fill (Rule)");
104                 break;
105         case InsetSpaceParams::CUSTOM:
106                 message = support::bformat(_("Horizontal Space (%1$s)"),
107                                 params_.length.asDocstring());
108                 break;
109         case InsetSpaceParams::CUSTOM_PROTECTED:
110                 message = support::bformat(_("Protected Horizontal Space (%1$s)"),
111                                 params_.length.asDocstring());
112                 break;
113         }
114         return message;
115 }
116
117
118 void InsetSpace::doDispatch(Cursor & cur, FuncRequest & cmd)
119 {
120         switch (cmd.action) {
121
122         case LFUN_INSET_MODIFY: {
123                 string2params(to_utf8(cmd.argument()), params_);
124                 break;
125         }
126
127         case LFUN_MOUSE_RELEASE:
128                 if (!cur.selection() && cmd.button() == mouse_button::button1)
129                         cur.bv().showDialog("space", params2string(params()), this);
130                 break;
131
132         default:
133                 Inset::doDispatch(cur, cmd);
134                 break;
135         }
136 }
137
138
139 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
140         FuncStatus & status) const
141 {
142         switch (cmd.action) {
143         // we handle these
144         case LFUN_INSET_MODIFY:
145                 if (cmd.getArg(0) == "space") {
146                         InsetSpaceParams params;
147                         string2params(to_utf8(cmd.argument()), params);
148                         status.setOnOff(params_.kind == params.kind);
149                 } else {
150                         status.enabled(true);
151                 }
152                 return true;
153         default:
154                 return Inset::getStatus(cur, cmd, status);
155         }
156 }
157
158
159 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
160 {
161         cur.bv().showDialog("space", params2string(params()), this);
162 }
163
164
165 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
166 {
167         if (isStretchableSpace()) {
168                 // The metrics for this kinds are calculated externally in
169                 // \c TextMetrics::computeRowMetrics. Those are dummy value:
170                 dim = Dimension(10, 10, 10);
171                 return;
172         }
173
174         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
175         dim.asc = fm.maxAscent();
176         dim.des = fm.maxDescent();
177
178         switch (params_.kind) {
179                 case InsetSpaceParams::THIN:
180                 case InsetSpaceParams::NEGTHIN:
181                         dim.wid = fm.width(char_type('M')) / 6;
182                         break;
183                 case InsetSpaceParams::PROTECTED:
184                 case InsetSpaceParams::NORMAL:
185                         dim.wid = fm.width(char_type(' '));
186                         break;
187                 case InsetSpaceParams::QUAD:
188                         dim.wid = fm.width(char_type('M'));
189                         break;
190                 case InsetSpaceParams::QQUAD:
191                         dim.wid = 2 * fm.width(char_type('M'));
192                         break;
193                 case InsetSpaceParams::ENSPACE:
194                 case InsetSpaceParams::ENSKIP:
195                         dim.wid = int(0.5 * fm.width(char_type('M')));
196                         break;
197                 case InsetSpaceParams::CUSTOM:
198                 case InsetSpaceParams::CUSTOM_PROTECTED:
199                         dim.wid = params_.length.inBP();
200                         break;
201                 case InsetSpaceParams::HFILL:
202                 case InsetSpaceParams::HFILL_PROTECTED:
203                 case InsetSpaceParams::DOTFILL:
204                 case InsetSpaceParams::HRULEFILL:
205                         // shut up compiler
206                         break;
207         }
208         // Cache the inset dimension.
209         setDimCache(mi, dim);
210 }
211
212
213 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
214 {
215         Dimension const dim = dimension(*pi.base.bv);
216
217         if (isStretchableSpace()) {
218                 int const asc = theFontMetrics(pi.base.font).ascent('M');
219                 int const desc = theFontMetrics(pi.base.font).descent('M');
220                 int const x0 = x + 1;
221                 int const x1 = x + dim.wid - 2;
222                 int const y0 = y + desc;
223                 int const y1 = y - asc;
224                 int const y2 = y - asc / 2;
225
226                 if (params_.kind == InsetSpaceParams::HFILL) {
227                         pi.pain.line(x0, y1, x0, y0, Color_added_space);
228                         pi.pain.line(x0, y2 , x1, y2, Color_added_space,
229                                 frontend::Painter::line_onoffdash);
230                         pi.pain.line(x1, y1, x1, y0, Color_added_space);
231                 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
232                         pi.pain.line(x0, y1, x0, y0, Color_latex);
233                         pi.pain.line(x0, y2 , x1, y2, Color_latex,
234                                 frontend::Painter::line_onoffdash);
235                         pi.pain.line(x1, y1, x1, y0, Color_latex);
236                 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
237                         pi.pain.line(x0, y1, x0, y0, Color_special);
238                         pi.pain.line(x0, y, x1, y, Color_special,
239                                 frontend::Painter::line_onoffdash);
240                         pi.pain.line(x1, y1, x1, y0, Color_special);
241                 } if (params_.kind == InsetSpaceParams::HRULEFILL) {
242                         pi.pain.line(x0, y1, x0, y0, Color_special);
243                         pi.pain.line(x0, y, x1, y, Color_special);
244                         pi.pain.line(x1, y1, x1, y0, Color_special);
245                 }
246                 return;
247         }
248
249         int const w = dim.wid;
250         int const h = theFontMetrics(pi.base.font).ascent('x');
251         int xp[4], yp[4];
252
253         xp[0] = x;
254         yp[0] = y - max(h / 4, 1);
255         if (params_.kind == InsetSpaceParams::NORMAL ||
256             params_.kind == InsetSpaceParams::PROTECTED) {
257                 xp[1] = x;     yp[1] = y;
258                 xp[2] = x + w; yp[2] = y;
259         } else {
260                 xp[1] = x;     yp[1] = y + max(h / 4, 1);
261                 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
262         }
263         xp[3] = x + w;
264         yp[3] = y - max(h / 4, 1);
265
266         if (params_.kind == InsetSpaceParams::PROTECTED ||
267             params_.kind == InsetSpaceParams::ENSPACE ||
268             params_.kind == InsetSpaceParams::NEGTHIN ||
269             params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
270                 pi.pain.lines(xp, yp, 4, Color_latex);
271         else
272                 pi.pain.lines(xp, yp, 4, Color_special);
273 }
274
275
276 void InsetSpaceParams::write(ostream & os) const
277 {
278         string command;
279         switch (kind) {
280         case InsetSpaceParams::NORMAL:
281                 os << "\\space{}";
282                 break;
283         case InsetSpaceParams::PROTECTED:
284                 os <<  "~";
285                 break;
286         case InsetSpaceParams::THIN:
287                 os <<  "\\thinspace{}";
288                 break;
289         case InsetSpaceParams::QUAD:
290                 os <<  "\\quad{}";
291                 break;
292         case InsetSpaceParams::QQUAD:
293                 os <<  "\\qquad{}";
294                 break;
295         case InsetSpaceParams::ENSPACE:
296                 os <<  "\\enspace{}";
297                 break;
298         case InsetSpaceParams::ENSKIP:
299                 os <<  "\\enskip{}";
300                 break;
301         case InsetSpaceParams::NEGTHIN:
302                 os <<  "\\negthinspace{}";
303                 break;
304         case InsetSpaceParams::HFILL:
305                 os <<  "\\hfill{}";
306                 break;
307         case InsetSpaceParams::HFILL_PROTECTED:
308                 os <<  "\\hspace*{\\fill}";
309                 break;
310         case InsetSpaceParams::DOTFILL:
311                 os <<  "\\dotfill{}";
312                 break;
313         case InsetSpaceParams::HRULEFILL:
314                 os <<  "\\hrulefill{}";
315                 break;
316         case InsetSpaceParams::CUSTOM:
317                 os <<  "\\hspace{}";
318                 break;
319         case InsetSpaceParams::CUSTOM_PROTECTED:
320                 os <<  "\\hspace*{}";
321                 break;
322         }
323         
324         if (!length.empty())
325                 os << "\n\\length " << length.asString();
326 }
327
328
329 void InsetSpaceParams::read(Lexer & lex)
330 {
331         lex.next();
332         string const command = lex.getString();
333
334         if (command == "\\space{}")
335                 kind = InsetSpaceParams::NORMAL;
336         else if (command == "~")
337                 kind = InsetSpaceParams::PROTECTED;
338         else if (command == "\\thinspace{}")
339                 kind = InsetSpaceParams::THIN;
340         else if (command == "\\quad{}")
341                 kind = InsetSpaceParams::QUAD;
342         else if (command == "\\qquad{}")
343                 kind = InsetSpaceParams::QQUAD;
344         else if (command == "\\enspace{}")
345                 kind = InsetSpaceParams::ENSPACE;
346         else if (command == "\\enskip{}")
347                 kind = InsetSpaceParams::ENSKIP;
348         else if (command == "\\negthinspace{}")
349                 kind = InsetSpaceParams::NEGTHIN;
350         else if (command == "\\hfill{}")
351                 kind = InsetSpaceParams::HFILL;
352         else if (command == "\\hspace*{\\fill}")
353                 kind = InsetSpaceParams::HFILL_PROTECTED;
354         else if (command == "\\dotfill{}")
355                 kind = InsetSpaceParams::DOTFILL;
356         else if (command == "\\hrulefill{}")
357                 kind = InsetSpaceParams::HRULEFILL;
358         else if (command == "\\hspace{}")
359                 kind = InsetSpaceParams::CUSTOM;
360         else if (command == "\\hspace*{}")
361                 kind = InsetSpaceParams::CUSTOM_PROTECTED;
362         else
363                 lex.printError("InsetSpace: Unknown kind: `$$Token'");
364
365
366         string token;
367         lex >> token;
368         if (token == "\\length") {
369                 lex.next();
370                 string const len = lex.getString();
371                 length = Length(len);
372                 lex.next();
373                 token = lex.getString();
374         }
375         if (!lex)
376                 return;
377         if (token != "\\end_inset")
378                 lex.printError("Missing \\end_inset at this point. "
379                                "Read: `$$Token'");
380 }
381
382
383 void InsetSpace::write(ostream & os) const
384 {
385         os << "Space ";
386         params_.write(os);
387 }
388
389
390 void InsetSpace::read(Lexer & lex)
391 {
392         params_.read(lex);
393 }
394
395
396 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
397 {
398         switch (params_.kind) {
399         case InsetSpaceParams::NORMAL:
400                 os << (runparams.free_spacing ? " " : "\\ ");
401                 break;
402         case InsetSpaceParams::PROTECTED:
403                 os << (runparams.free_spacing ? ' ' : '~');
404                 break;
405         case InsetSpaceParams::THIN:
406                 os << (runparams.free_spacing ? " " : "\\,");
407                 break;
408         case InsetSpaceParams::QUAD:
409                 os << (runparams.free_spacing ? " " : "\\quad{}");
410                 break;
411         case InsetSpaceParams::QQUAD:
412                 os << (runparams.free_spacing ? " " : "\\qquad{}");
413                 break;
414         case InsetSpaceParams::ENSPACE:
415                 os << (runparams.free_spacing ? " " : "\\enspace{}");
416                 break;
417         case InsetSpaceParams::ENSKIP:
418                 os << (runparams.free_spacing ? " " : "\\enskip{}");
419                 break;
420         case InsetSpaceParams::NEGTHIN:
421                 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
422                 break;
423         case InsetSpaceParams::HFILL:
424                 os << (runparams.free_spacing ? " " : "\\hfill{}");
425                 break;
426         case InsetSpaceParams::HFILL_PROTECTED:
427                 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
428                 break;
429         case InsetSpaceParams::DOTFILL:
430                 os << (runparams.free_spacing ? " " : "\\dotfill{}");
431                 break;
432         case InsetSpaceParams::HRULEFILL:
433                 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
434                 break;
435         case InsetSpaceParams::CUSTOM:
436                 if (runparams.free_spacing)
437                         os << " ";
438                 else
439                         os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
440                 break;
441         case InsetSpaceParams::CUSTOM_PROTECTED:
442                 if (runparams.free_spacing)
443                         os << " ";
444                 else
445                         os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
446                 break;
447         }
448         return 0;
449 }
450
451
452 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
453 {
454         switch (params_.kind) {
455         case InsetSpaceParams::HFILL:
456         case InsetSpaceParams::HFILL_PROTECTED:
457                 os << "     ";
458                 return 5;
459         case InsetSpaceParams::DOTFILL:
460                 os << ".....";
461                 return 5;
462         case InsetSpaceParams::HRULEFILL:
463                 os << "_____";
464                 return 5;
465         default:
466                 os << ' ';
467                 return 1;
468         }
469 }
470
471
472 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
473 {
474         switch (params_.kind) {
475         case InsetSpaceParams::NORMAL:
476         case InsetSpaceParams::QUAD:
477         case InsetSpaceParams::QQUAD:
478         case InsetSpaceParams::ENSKIP:
479                 os << " ";
480                 break;
481         case InsetSpaceParams::PROTECTED:
482         case InsetSpaceParams::ENSPACE:
483         case InsetSpaceParams::THIN:
484         case InsetSpaceParams::NEGTHIN:
485                 os << "&nbsp;";
486                 break;
487         case InsetSpaceParams::HFILL:
488         case InsetSpaceParams::HFILL_PROTECTED:
489                 os << '\n';
490         case InsetSpaceParams::DOTFILL:
491                 // FIXME
492                 os << '\n';
493         case InsetSpaceParams::HRULEFILL:
494                 // FIXME
495                 os << '\n';
496         case InsetSpaceParams::CUSTOM:
497         case InsetSpaceParams::CUSTOM_PROTECTED:
498                 // FIXME
499                 os << '\n';
500         }
501         return 0;
502 }
503
504
505 void InsetSpace::textString(odocstream & os) const
506 {
507         plaintext(os, OutputParams(0));
508 }
509
510
511 bool InsetSpace::isStretchableSpace() const
512 {
513         return (params_.kind == InsetSpaceParams::HFILL ||
514                 params_.kind == InsetSpaceParams::HFILL_PROTECTED ||
515                 params_.kind == InsetSpaceParams::DOTFILL ||
516                 params_.kind == InsetSpaceParams::HRULEFILL);
517 }
518
519
520 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
521 {
522         return from_ascii("context-space");
523 }
524
525
526 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
527 {
528         params = InsetSpaceParams();
529         if (in.empty())
530                 return;
531
532         istringstream data(in);
533         Lexer lex;
534         lex.setStream(data);
535
536         string name;
537         lex >> name;
538         if (!lex || name != "space") {
539                 LYXERR0("Expected arg 1 to be \"space\" in " << in);
540                 return;
541         }
542
543         params.read(lex);
544 }
545
546
547 string InsetSpace::params2string(InsetSpaceParams const & params)
548 {
549         ostringstream data;
550         data << "space" << ' ';
551         params.write(data);
552         return data.str();
553 }
554
555
556 } // namespace lyx