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