]> git.lyx.org Git - features.git/blob - src/insets/InsetSpace.cpp
one more mistake...
[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                 InsetSpaceParams params;
124                 string2params(to_utf8(cmd.argument()), params);
125                 params_.kind = params.kind;
126                 params_.length = params.length;
127                 break;
128         }
129
130         case LFUN_MOUSE_RELEASE:
131                 if (!cur.selection() && cmd.button() == mouse_button::button1)
132                         cur.bv().showDialog("space", params2string(params()), this);
133                 break;
134
135         default:
136                 Inset::doDispatch(cur, cmd);
137                 break;
138         }
139 }
140
141
142 bool InsetSpace::getStatus(Cursor & cur, FuncRequest const & cmd,
143         FuncStatus & status) const
144 {
145         switch (cmd.action) {
146         // we handle these
147         case LFUN_INSET_MODIFY:
148                 if (cmd.getArg(0) == "space") {
149                         InsetSpaceParams params;
150                         string2params(to_utf8(cmd.argument()), params);
151                         status.setOnOff(params_.kind == params.kind);
152                 } else
153                         status.enabled(true);
154                 return true;
155         default:
156                 return Inset::getStatus(cur, cmd, status);
157         }
158 }
159
160
161 void InsetSpace::edit(Cursor & cur, bool, EntryDirection)
162 {
163         cur.bv().showDialog("space", params2string(params()), this);
164 }
165
166
167 void InsetSpace::metrics(MetricsInfo & mi, Dimension & dim) const
168 {
169         if (isStretchableSpace()) {
170                 // The metrics for this kinds are calculated externally in
171                 // \c TextMetrics::computeRowMetrics. Those are dummy value:
172                 dim = Dimension(10, 10, 10);
173                 return;
174         }
175
176         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
177         dim.asc = fm.maxAscent();
178         dim.des = fm.maxDescent();
179
180         switch (params_.kind) {
181                 case InsetSpaceParams::THIN:
182                 case InsetSpaceParams::NEGTHIN:
183                         dim.wid = fm.width(char_type('M')) / 6;
184                         break;
185                 case InsetSpaceParams::PROTECTED:
186                 case InsetSpaceParams::NORMAL:
187                         dim.wid = fm.width(char_type(' '));
188                         break;
189                 case InsetSpaceParams::QUAD:
190                         dim.wid = fm.width(char_type('M'));
191                         break;
192                 case InsetSpaceParams::QQUAD:
193                         dim.wid = 2 * fm.width(char_type('M'));
194                         break;
195                 case InsetSpaceParams::ENSPACE:
196                 case InsetSpaceParams::ENSKIP:
197                         dim.wid = int(0.5 * fm.width(char_type('M')));
198                         break;
199                 case InsetSpaceParams::CUSTOM:
200                 case InsetSpaceParams::CUSTOM_PROTECTED:
201                         dim.wid = params_.length.inBP();
202                         break;
203                 case InsetSpaceParams::HFILL:
204                 case InsetSpaceParams::HFILL_PROTECTED:
205                 case InsetSpaceParams::DOTFILL:
206                 case InsetSpaceParams::HRULEFILL:
207                         // shut up compiler
208                         break;
209         }
210         // Cache the inset dimension.
211         setDimCache(mi, dim);
212 }
213
214
215 void InsetSpace::draw(PainterInfo & pi, int x, int y) const
216 {
217         Dimension const dim = dimension(*pi.base.bv);
218
219         if (isStretchableSpace()) {
220                 int const asc = theFontMetrics(pi.base.font).ascent('M');
221                 int const desc = theFontMetrics(pi.base.font).descent('M');
222                 int const x0 = x + 1;
223                 int const x1 = x + dim.wid - 2;
224                 int const y0 = y + desc;
225                 int const y1 = y - asc;
226                 int const y2 = y - asc / 2;
227
228                 if (params_.kind == InsetSpaceParams::HFILL) {
229                         pi.pain.line(x0, y1, x0, y0, Color_added_space);
230                         pi.pain.line(x0, y2 , x1, y2, Color_added_space,
231                                 frontend::Painter::line_onoffdash);
232                         pi.pain.line(x1, y1, x1, y0, Color_added_space);
233                 } else if (params_.kind == InsetSpaceParams::HFILL_PROTECTED) {
234                         pi.pain.line(x0, y1, x0, y0, Color_latex);
235                         pi.pain.line(x0, y2 , x1, y2, Color_latex,
236                                 frontend::Painter::line_onoffdash);
237                         pi.pain.line(x1, y1, x1, y0, Color_latex);
238                 } else if (params_.kind == InsetSpaceParams::DOTFILL) {
239                         pi.pain.line(x0, y1, x0, y0, Color_special);
240                         pi.pain.line(x0, y, x1, y, Color_special,
241                                 frontend::Painter::line_onoffdash);
242                         pi.pain.line(x1, y1, x1, y0, Color_special);
243                 } if (params_.kind == InsetSpaceParams::HRULEFILL) {
244                         pi.pain.line(x0, y1, x0, y0, Color_special);
245                         pi.pain.line(x0, y, x1, y, Color_special);
246                         pi.pain.line(x1, y1, x1, y0, Color_special);
247                 }
248                 return;
249         }
250
251         int const w = dim.wid;
252         int const h = theFontMetrics(pi.base.font).ascent('x');
253         int xp[4], yp[4];
254
255         xp[0] = x;
256         yp[0] = y - max(h / 4, 1);
257         if (params_.kind == InsetSpaceParams::NORMAL ||
258             params_.kind == InsetSpaceParams::PROTECTED) {
259                 xp[1] = x;     yp[1] = y;
260                 xp[2] = x + w; yp[2] = y;
261         } else {
262                 xp[1] = x;     yp[1] = y + max(h / 4, 1);
263                 xp[2] = x + w; yp[2] = y + max(h / 4, 1);
264         }
265         xp[3] = x + w;
266         yp[3] = y - max(h / 4, 1);
267
268         if (params_.kind == InsetSpaceParams::PROTECTED ||
269             params_.kind == InsetSpaceParams::ENSPACE ||
270             params_.kind == InsetSpaceParams::NEGTHIN ||
271             params_.kind == InsetSpaceParams::CUSTOM_PROTECTED)
272                 pi.pain.lines(xp, yp, 4, Color_latex);
273         else
274                 pi.pain.lines(xp, yp, 4, Color_special);
275 }
276
277
278 void InsetSpaceParams::write(ostream & os) const
279 {
280         string command;
281         switch (kind) {
282         case InsetSpaceParams::NORMAL:
283                 os << "\\space{}";
284                 break;
285         case InsetSpaceParams::PROTECTED:
286                 os <<  "~";
287                 break;
288         case InsetSpaceParams::THIN:
289                 os <<  "\\thinspace{}";
290                 break;
291         case InsetSpaceParams::QUAD:
292                 os <<  "\\quad{}";
293                 break;
294         case InsetSpaceParams::QQUAD:
295                 os <<  "\\qquad{}";
296                 break;
297         case InsetSpaceParams::ENSPACE:
298                 os <<  "\\enspace{}";
299                 break;
300         case InsetSpaceParams::ENSKIP:
301                 os <<  "\\enskip{}";
302                 break;
303         case InsetSpaceParams::NEGTHIN:
304                 os <<  "\\negthinspace{}";
305                 break;
306         case InsetSpaceParams::HFILL:
307                 os <<  "\\hfill{}";
308                 break;
309         case InsetSpaceParams::HFILL_PROTECTED:
310                 os <<  "\\hspace*{\\fill}";
311                 break;
312         case InsetSpaceParams::DOTFILL:
313                 os <<  "\\dotfill{}";
314                 break;
315         case InsetSpaceParams::HRULEFILL:
316                 os <<  "\\hrulefill{}";
317                 break;
318         case InsetSpaceParams::CUSTOM:
319                 os <<  "\\hspace{}";
320                 break;
321         case InsetSpaceParams::CUSTOM_PROTECTED:
322                 os <<  "\\hspace*{}";
323                 break;
324         }
325         
326         if (!length.empty())
327                 os << "\n\\length " << length.asString();
328 }
329
330
331 void InsetSpaceParams::read(Lexer & lex)
332 {
333         lex.next();
334         string const command = lex.getString();
335
336         if (command == "\\space{}")
337                 kind = InsetSpaceParams::NORMAL;
338         else if (command == "~")
339                 kind = InsetSpaceParams::PROTECTED;
340         else if (command == "\\thinspace{}")
341                 kind = InsetSpaceParams::THIN;
342         else if (command == "\\quad{}")
343                 kind = InsetSpaceParams::QUAD;
344         else if (command == "\\qquad{}")
345                 kind = InsetSpaceParams::QQUAD;
346         else if (command == "\\enspace{}")
347                 kind = InsetSpaceParams::ENSPACE;
348         else if (command == "\\enskip{}")
349                 kind = InsetSpaceParams::ENSKIP;
350         else if (command == "\\negthinspace{}")
351                 kind = InsetSpaceParams::NEGTHIN;
352         else if (command == "\\hfill{}")
353                 kind = InsetSpaceParams::HFILL;
354         else if (command == "\\hspace*{\\fill}")
355                 kind = InsetSpaceParams::HFILL_PROTECTED;
356         else if (command == "\\dotfill{}")
357                 kind = InsetSpaceParams::DOTFILL;
358         else if (command == "\\hrulefill{}")
359                 kind = InsetSpaceParams::HRULEFILL;
360         else if (command == "\\hspace{}")
361                 kind = InsetSpaceParams::CUSTOM;
362         else if (command == "\\hspace*{}")
363                 kind = InsetSpaceParams::CUSTOM_PROTECTED;
364         else
365                 lex.printError("InsetSpace: Unknown kind: `$$Token'");
366
367
368         string token;
369         lex >> token;
370         if (token == "\\length") {
371                 lex.next();
372                 string const len = lex.getString();
373                 length = Length(len);
374                 lex.next();
375                 token = lex.getString();
376         }
377         if (!lex)
378                 return;
379         if (token != "\\end_inset")
380                 lex.printError("Missing \\end_inset at this point. "
381                                "Read: `$$Token'");
382 }
383
384
385 void InsetSpace::write(ostream & os) const
386 {
387         os << "Space ";
388         params_.write(os);
389 }
390
391
392 void InsetSpace::read(Lexer & lex)
393 {
394         params_.read(lex);
395 }
396
397
398 int InsetSpace::latex(odocstream & os, OutputParams const & runparams) const
399 {
400         switch (params_.kind) {
401         case InsetSpaceParams::NORMAL:
402                 os << (runparams.free_spacing ? " " : "\\ ");
403                 break;
404         case InsetSpaceParams::PROTECTED:
405                 os << (runparams.free_spacing ? ' ' : '~');
406                 break;
407         case InsetSpaceParams::THIN:
408                 os << (runparams.free_spacing ? " " : "\\,");
409                 break;
410         case InsetSpaceParams::QUAD:
411                 os << (runparams.free_spacing ? " " : "\\quad{}");
412                 break;
413         case InsetSpaceParams::QQUAD:
414                 os << (runparams.free_spacing ? " " : "\\qquad{}");
415                 break;
416         case InsetSpaceParams::ENSPACE:
417                 os << (runparams.free_spacing ? " " : "\\enspace{}");
418                 break;
419         case InsetSpaceParams::ENSKIP:
420                 os << (runparams.free_spacing ? " " : "\\enskip{}");
421                 break;
422         case InsetSpaceParams::NEGTHIN:
423                 os << (runparams.free_spacing ? " " : "\\negthinspace{}");
424                 break;
425         case InsetSpaceParams::HFILL:
426                 os << (runparams.free_spacing ? " " : "\\hfill{}");
427                 break;
428         case InsetSpaceParams::HFILL_PROTECTED:
429                 os << (runparams.free_spacing ? " " : "\\hspace*{\\fill}");
430                 break;
431         case InsetSpaceParams::DOTFILL:
432                 os << (runparams.free_spacing ? " " : "\\dotfill{}");
433                 break;
434         case InsetSpaceParams::HRULEFILL:
435                 os << (runparams.free_spacing ? " " : "\\hrulefill{}");
436                 break;
437         case InsetSpaceParams::CUSTOM:
438                 if (runparams.free_spacing)
439                         os << " ";
440                 else
441                         os << "\\hspace{" << from_ascii(params_.length.asLatexString()) << "}";
442                 break;
443         case InsetSpaceParams::CUSTOM_PROTECTED:
444                 if (runparams.free_spacing)
445                         os << " ";
446                 else
447                         os << "\\hspace*{" << from_ascii(params_.length.asLatexString()) << "}";
448                 break;
449         }
450         return 0;
451 }
452
453
454 int InsetSpace::plaintext(odocstream & os, OutputParams const &) const
455 {
456         switch (params_.kind) {
457         case InsetSpaceParams::HFILL:
458         case InsetSpaceParams::HFILL_PROTECTED:
459                 os << "     ";
460                 return 5;
461         case InsetSpaceParams::DOTFILL:
462                 os << ".....";
463                 return 5;
464         case InsetSpaceParams::HRULEFILL:
465                 os << "_____";
466                 return 5;
467         default:
468                 os << ' ';
469                 return 1;
470         }
471 }
472
473
474 int InsetSpace::docbook(odocstream & os, OutputParams const &) const
475 {
476         switch (params_.kind) {
477         case InsetSpaceParams::NORMAL:
478         case InsetSpaceParams::QUAD:
479         case InsetSpaceParams::QQUAD:
480         case InsetSpaceParams::ENSKIP:
481                 os << " ";
482                 break;
483         case InsetSpaceParams::PROTECTED:
484         case InsetSpaceParams::ENSPACE:
485         case InsetSpaceParams::THIN:
486         case InsetSpaceParams::NEGTHIN:
487                 os << "&nbsp;";
488                 break;
489         case InsetSpaceParams::HFILL:
490         case InsetSpaceParams::HFILL_PROTECTED:
491                 os << '\n';
492         case InsetSpaceParams::DOTFILL:
493                 // FIXME
494                 os << '\n';
495         case InsetSpaceParams::HRULEFILL:
496                 // FIXME
497                 os << '\n';
498         case InsetSpaceParams::CUSTOM:
499         case InsetSpaceParams::CUSTOM_PROTECTED:
500                 // FIXME
501                 os << '\n';
502         }
503         return 0;
504 }
505
506
507 void InsetSpace::textString(odocstream & os) const
508 {
509         plaintext(os, OutputParams(0));
510 }
511
512
513 bool InsetSpace::isStretchableSpace() const
514 {
515         return (params_.kind == InsetSpaceParams::HFILL ||
516                 params_.kind == InsetSpaceParams::HFILL_PROTECTED ||
517                 params_.kind == InsetSpaceParams::DOTFILL ||
518                 params_.kind == InsetSpaceParams::HRULEFILL);
519 }
520
521
522 docstring InsetSpace::contextMenu(BufferView const &, int, int) const
523 {
524         return from_ascii("context-space");
525 }
526
527
528 void InsetSpace::string2params(string const & in, InsetSpaceParams & params)
529 {
530         params = InsetSpaceParams();
531         if (in.empty())
532                 return;
533
534         istringstream data(in);
535         Lexer lex(0,0);
536         lex.setStream(data);
537
538         string name;
539         lex >> name;
540         if (!lex || name != "space") {
541                 LYXERR0("Expected arg 1 to be \"space\" in " << in);
542                 return;
543         }
544
545         params.read(lex);
546 }
547
548
549 string InsetSpace::params2string(InsetSpaceParams const & params)
550 {
551         ostringstream data;
552         data << "space" << ' ';
553         params.write(data);
554         return data.str();
555 }
556
557
558 } // namespace lyx