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