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