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