]> git.lyx.org Git - features.git/blob - src/vspace.C
Added %x support in vspace.C (LyXLenght) and use it for the minipages.
[features.git] / src / vspace.C
1 // -*- C++ -*-
2 /* This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *       
7  *           Copyright 1995 Matthias Ettrich
8  *           Copyright 1995-2000 The LyX Team.
9  *
10  * ====================================================== */
11
12 #include <config.h>
13
14 #ifdef __GNUG__
15 #pragma implementation "vspace.h"
16 #endif
17
18 #include "lyx_main.h"
19 #include "buffer.h"
20 #include "vspace.h"
21 #include "lyxrc.h"
22 #include "lyxtext.h"
23 #include "BufferView.h"
24 #include "support/lstrings.h"
25
26
27 namespace {
28
29 /*  length units
30  */
31
32 int const num_units = LyXLength::UNIT_NONE;
33
34 // I am not sure if "mu" should be possible to select (Lgb)
35 char const * unit_name[num_units] = { "sp", "pt", "bp", "dd",
36                                       "mm", "pc", "cc", "cm",
37                                       "in", "ex", "em", "mu",
38                                       "%",  "c%", "p%", "l%" }; 
39
40
41 /*  The following static items form a simple scanner for
42  *  length strings, used by isValid[Glue]Length.  See comments there.
43  */
44 float           number[4] = { 0, 0, 0, 0 };
45 LyXLength::UNIT unit[4]   = { LyXLength::UNIT_NONE,
46                               LyXLength::UNIT_NONE,
47                               LyXLength::UNIT_NONE,
48                               LyXLength::UNIT_NONE };
49 int number_index;
50 int unit_index;
51
52
53 inline
54 void lyx_advance(string & data, unsigned int n)
55 {
56         data.erase(0, n);
57 }
58
59
60 inline
61 bool isEndOfData(string const & data)
62 {
63         return frontStrip(data).empty();
64 }
65
66
67 char nextToken(string & data)
68 {
69         data = frontStrip(data);
70         if (data.empty())
71                 return '\0';
72         else if (data[0] == '+') {
73                 lyx_advance(data, 1);
74                 return '+';
75         } else if (prefixIs(data, "plus")) {
76                 lyx_advance(data, 4);
77                 return '+';
78         } else if (data[0] == '-') {
79                 lyx_advance(data, 1);
80                 return '-';
81         } else if (prefixIs(data, "minus")) {
82                 lyx_advance(data, 5);
83                 return '-';
84         } else {
85                 string::size_type i = data.find_first_not_of("0123456789.");
86
87                 if (i != 0) {
88                         if (number_index > 3) return 'E';
89
90                         string buffer;
91                 
92                         // we have found some number
93                         if (i == string::npos) {
94                                 buffer = data;
95                                 i = data.size() + 1;
96                         } else
97                                 buffer = data.substr(0, i);
98
99                         lyx_advance(data, i);
100
101                         if (isStrDbl(buffer)) {
102                                 number[number_index] = strToDbl(buffer);
103                                 ++number_index;
104                                 return 'n';
105                         } else return 'E';
106                 }
107                 
108                 i = data.find_first_not_of("abcdefghijklmnopqrstuvwxyz%");
109                 if (i != 0) {
110                         if (unit_index > 3) return 'E';
111
112                         string buffer;
113                 
114                         // we have found some alphabetical string
115                         if (i == string::npos) {
116                                 buffer = data;
117                                 i = data.size() + 1;
118                         } else
119                                 buffer = data.substr(0, i);
120
121                         // possibly we have "mmplus" string or similar
122                         if (buffer.size() > 5 && (buffer.substr(2,4) == string("plus") || buffer.substr(2,5) == string("minus"))) {
123                                 lyx_advance(data, 2);
124                                 unit[unit_index] = unitFromString(buffer.substr(0, 2));
125                         } else {
126                                 lyx_advance(data, i);
127                                 unit[unit_index] = unitFromString(buffer);
128                         }
129
130                         if (unit[unit_index] != LyXLength::UNIT_NONE) {
131                                 ++unit_index;
132                                 return 'u';
133                         } else return 'E';  // Error
134                 }
135                 return 'E';  // Error
136         }
137 }
138
139
140 struct LaTeXLength {
141         char const * pattern;
142         int   plus_val_index, minus_val_index,
143                 plus_uni_index, minus_uni_index;
144 };
145
146
147 LaTeXLength table[] = {
148         { "nu",       0, 0, 0, 0 },
149         { "nu+nu",    2, 0, 2, 0 },
150         { "nu+nu-nu", 2, 3, 2, 3 },
151         { "nu+-nu",   2, 2, 2, 2 },
152         { "nu-nu",    0, 2, 0, 2 },
153         { "nu-nu+nu", 3, 2, 3, 2 },
154         { "nu-+nu",   2, 2, 2, 2 },
155         { "n+nu",     2, 0, 1, 0 },
156         { "n+n-nu",   2, 3, 1, 1 },
157         { "n+-nu",    2, 2, 1, 1 },
158         { "n-nu",     0, 2, 0, 1 },
159         { "n-n+nu",   3, 2, 1, 1 },
160         { "n-+nu",    2, 2, 1, 1 },
161         { "",         0, 0, 0, 0 }   // sentinel, must be empty
162 };
163
164 } // namespace anon
165
166 const char * stringFromUnit(int unit)
167 {
168     if (unit < 0 || unit >= num_units)
169         return 0;
170     return unit_name[unit];
171 }
172
173
174 LyXLength::UNIT unitFromString (string const & data)
175 {
176         int i = 0;
177         while ((i < num_units) && (data != unit_name[i])) ++i;
178         return static_cast<LyXLength::UNIT>(i);
179 }
180
181
182 bool isValidGlueLength (string const & data, LyXGlueLength * result)
183 {
184         // This parser is table-driven.  First, it constructs a "pattern"
185         // that describes the sequence of tokens in "data".  For example,
186         // "n-nu" means: number, minus sign, number, unit.  As we go along,
187         // numbers and units are stored into static arrays.  Then, "pattern"
188         // is searched in the "table".  If it is found, the associated
189         // table entries tell us which number and unit should go where
190         // in the LyXLength structure.  Example: if "data" has the "pattern"
191         // "nu+nu-nu", the associated table entries are "2, 3, 2, 3".  
192         // That means, "plus_val" is the second number that was seen
193         // in the input, "minus_val" is the third number, and "plus_uni"
194         // and "minus_uni" are the second and third units, respectively.
195         // ("val" and "uni" are always the first items seen in "data".)
196         //     This is the most elegant solution I could find -- a straight-
197         // forward approach leads to very long, tedious code that would be
198         // much harder to understand and maintain. (AS)
199
200         if (data.empty())
201                 return true;
202         string buffer = frontStrip(data);
203
204         // To make isValidGlueLength recognize negative values as
205         // the first number this little hack is needed:
206         short val_sign = 1; // positive as default
207         switch (buffer[0]) {
208         case '-':
209                 lyx_advance(buffer, 1);
210                 val_sign = -1;
211                 break;
212         case '+':
213                 lyx_advance(buffer, 1);
214                 // fall through
215         default:
216                 // no action
217                 break;
218         }
219         // end of hack
220         
221         int       pattern_index = 0, table_index = 0;
222         char      pattern[20];
223
224         number_index = unit_index = 1;  // entries at index 0 are sentinels
225
226         // construct "pattern" from "data"
227         while (!isEndOfData (buffer)) {
228                 if (pattern_index > 20) return false;
229                 pattern[pattern_index] = nextToken (buffer);
230                 if (pattern[pattern_index] == 'E') return false;
231                 ++pattern_index;
232         }
233         pattern[pattern_index] = '\0';
234
235         // search "pattern" in "table"
236         table_index = 0;
237         while (compare(pattern, table[table_index].pattern)) {
238                 ++table_index;
239                 if (!*table[table_index].pattern) return false;
240         }
241         
242         // Get the values from the appropriate places.  If an index
243         // is zero, the corresponding array value is zero or UNIT_NONE,
244         // so we needn't check this.
245         if (result) {
246                 result->val = number[1] * val_sign;
247                 result->uni = unit[1];
248                 result->plus_val  = number[table[table_index].plus_val_index];
249                 result->minus_val = number[table[table_index].minus_val_index];
250                 result->plus_uni  = unit  [table[table_index].plus_uni_index];
251                 result->minus_uni = unit  [table[table_index].minus_uni_index];
252         }
253         return true;
254 }
255
256
257 bool isValidLength(string const & data, LyXLength * result)
258 {
259         /// This is a trimmed down version of isValidGlueLength.
260         /// The parser may seem overkill for lengths without 
261         /// glue, but since we already have it, using it is
262         /// easier than writing something from scratch.
263         if (data.empty())
264                 return true;
265
266         string   buffer(data);
267         int       pattern_index = 0;
268         char      pattern[3];
269
270         // To make isValidLength recognize negative values
271         // this little hack is needed:
272         short val_sign = 1; // positive as default
273         switch (buffer[0]) {
274         case '-':
275                 lyx_advance(buffer, 1);
276                 val_sign = -1;
277                 break;
278         case '+':
279                 lyx_advance(buffer, 1);
280                 // fall through
281         default:
282                 // no action
283                 break;
284         }
285         // end of hack
286         
287         number_index = unit_index = 1;  // entries at index 0 are sentinels
288
289         // construct "pattern" from "data"
290         while (!isEndOfData (buffer)) {
291                 if (pattern_index > 2) return false;
292                 pattern[pattern_index] = nextToken (buffer);
293                 if (pattern[pattern_index] == 'E') return false;
294                 ++pattern_index;
295         }
296         pattern[pattern_index] = '\0';
297
298         // only the most basic pattern is accepted here
299         if (compare(pattern, "nu") != 0) return false;          
300         
301         // It _was_ a correct length string.  
302         // Store away the values we found.
303         if (result) {
304                 result->val = number[1] * val_sign;
305                 result->uni = unit[1];
306         }
307         return true;
308 }
309
310
311 /// LyXLength class
312
313 LyXLength::LyXLength(string const & data)
314 {
315         LyXLength tmp;
316         
317         if (!isValidLength (data, &tmp))
318                 return; // should raise an exception
319         else {
320                 val = tmp.val;
321                 uni = tmp.uni;
322         }
323 }
324
325
326 string const LyXLength::asString() const
327 {
328         std::ostringstream buffer;
329         buffer << val << unit_name[uni]; // setw?
330         return buffer.str().c_str();
331 }
332
333
334 string const LyXLength::asLatexString() const
335 {
336         std::ostringstream buffer;
337         switch(uni) {
338         case PW:
339         case PE:
340             buffer << "." << abs(static_cast<int>(val)) << "\\columnwidth";
341             break;
342         case PP:
343             buffer << "." << abs(static_cast<int>(val)) << "\\pagewidth";
344             break;
345         case PL:
346             buffer << "." << abs(static_cast<int>(val)) << "\\linewidth";
347             break;
348         default:
349             buffer << val << unit_name[uni]; // setw?
350             break;
351         }
352         return buffer.str().c_str();
353 }
354
355
356 /*  LyXGlueLength class
357  */
358
359 LyXGlueLength::LyXGlueLength (string const & data)
360 {
361         LyXGlueLength tmp(0.0, PT);
362
363         if (!isValidGlueLength (data, &tmp))
364                 return; // should raise an exception
365         else {
366                 val = tmp.val;
367                 uni = tmp.uni;
368                 plus_val = tmp.plus_val;
369                 plus_uni = tmp.plus_uni;
370                 minus_val = tmp.minus_val;
371                 minus_uni = tmp.minus_uni;
372         }
373 }
374
375
376 string const LyXGlueLength::asString() const
377 {
378         std::ostringstream buffer;
379
380         if (plus_val != 0.0)
381                 if (minus_val != 0.0)
382                         if ((uni == plus_uni) && (uni == minus_uni))
383                                 if (plus_val == minus_val)
384                                         buffer << val << "+-"
385                                                << plus_val << unit_name[uni];
386                                 else
387                                         buffer << val
388                                                << '+' << plus_val
389                                                << '-' << minus_val
390                                                << unit_name[uni];
391                         else
392                                 if (plus_uni == minus_uni
393                                     && plus_val == minus_val)
394                                         buffer << val << unit_name[uni]
395                                                << "+-" << plus_val
396                                                << unit_name[plus_uni];
397         
398                                 else
399                                         buffer << val << unit_name[uni]
400                                                << '+' << plus_val
401                                                << unit_name[plus_uni]
402                                                << '-' << minus_val
403                                                << unit_name[minus_uni];
404                 else 
405                         if (uni == plus_uni)
406                                 buffer << val << '+' << plus_val
407                                        << unit_name[uni];
408                         else
409                                 buffer << val << unit_name[uni]
410                                        << '+' << plus_val
411                                        << unit_name[plus_uni];
412         
413         else
414                 if (minus_val != 0.0)
415                         if (uni == minus_uni)
416                                 buffer << val << '-' << minus_val
417                                        << unit_name[uni];
418         
419                         else
420                                 buffer << val << unit_name[uni]
421                                        << '-' << minus_val
422                                        << unit_name[minus_uni];
423                 else
424                         buffer << val << unit_name[uni];
425
426         return buffer.str().c_str();
427 }
428
429
430 string const LyXGlueLength::asLatexString() const
431 {
432         std::ostringstream buffer;
433
434         if (plus_val != 0.0)
435                 if (minus_val != 0.0)
436                         buffer << val << unit_name[uni]
437                                << " plus "
438                                << plus_val << unit_name[plus_uni]
439                                << " minus "
440                                << minus_val << unit_name[minus_uni];
441                 else
442                         buffer << val << unit_name[uni]
443                                << " plus "
444                                << plus_val << unit_name[plus_uni];
445         else
446                 if (minus_val != 0.0)
447                         buffer << val << unit_name[uni]
448                                << " minus "
449                                << minus_val << unit_name[minus_uni];
450                 else
451                         buffer << val << unit_name[uni];
452
453         return buffer.str().c_str();
454 }
455
456
457 /*  VSpace class
458  */
459
460 VSpace::VSpace (string const & data)
461         : kin (NONE), len (0.0, LyXLength::PT) 
462 {
463         kp = false;
464         if (data.empty())
465                 return;
466         float   value;
467         string input  = strip(data);
468
469         int length = input.length();
470
471         if (length > 1 && input[length-1] == '*') {
472                 kp = true;
473                 input.erase(length - 1);
474         }
475
476         if      (prefixIs (input, "defskip"))   kin = DEFSKIP;
477         else if (prefixIs (input, "smallskip")) kin = SMALLSKIP;
478         else if (prefixIs (input, "medskip"))   kin = MEDSKIP;
479         else if (prefixIs (input, "bigskip"))   kin = BIGSKIP;
480         else if (prefixIs (input, "vfill"))     kin = VFILL;
481         else if (isValidGlueLength (input, &len))
482                 kin = LENGTH;
483         else if (sscanf(input.c_str(), "%f", &value) == 1) {
484                 // This last one is for reading old .lyx files
485                 // without units in added_space_top/bottom.
486                 // Let unit default to centimeters here.
487                 kin = LENGTH;
488                 len = LyXGlueLength (value, LyXLength::CM);
489         }
490 }
491
492
493 bool VSpace::operator==(VSpace const & other) const
494 {
495         if (this->kin != other.kin)
496                 return false;
497  
498         if (this->kin != LENGTH)
499                 return this->kp == other.kp;
500  
501         if (!(this->len == other.len))
502                 return false;
503  
504         return this->kp == other.kp;
505 }
506
507
508 string const VSpace::asLyXCommand() const
509 {
510         string result;
511
512         switch (kin) {
513         case NONE:      break;
514         case DEFSKIP:   result = "defskip";      break;
515         case SMALLSKIP: result = "smallskip";    break;
516         case MEDSKIP:   result = "medskip";      break;
517         case BIGSKIP:   result = "bigskip";      break;
518         case VFILL:     result = "vfill";        break;
519         case LENGTH:    result = len.asString(); break;
520         }
521         if (kp && (kin != NONE) && (kin != DEFSKIP))
522                 return result += '*';
523         else
524                 return result;
525 }
526
527
528 string const VSpace::asLatexCommand(BufferParams const & params) const
529 {
530         switch (kin) {
531         case NONE:      return string();
532         case DEFSKIP:   
533                 return params.getDefSkip().asLatexCommand(params);
534         case SMALLSKIP: return kp ? "\\vspace*{\\smallskipamount}"
535                                 : "\\smallskip{}";
536         case MEDSKIP:   return kp ? "\\vspace*{\\medskipamount}"
537                                 : "\\medskip{}";
538         case BIGSKIP:   return kp ? "\\vspace*{\\bigskipamount}"
539                                 : "\\bigskip{}";
540         case VFILL:     return kp ? "\\vspace*{\\fill}"
541                                 : "\\vfill{}";
542         case LENGTH:    return kp ? "\\vspace*{" + len.asLatexString() + '}'
543                                 : "\\vspace{" + len.asLatexString() + '}';
544         }
545         return string();  // should never be reached
546 }
547
548
549 int VSpace::inPixels(BufferView * bv) const
550 {
551         // Height of a normal line in pixels (zoom factor considered)
552         int height = bv->text->DefaultHeight(); // [pixels]
553         int skip = 0;
554         int width = bv->workWidth();
555
556         if (kin == DEFSKIP)
557             skip = bv->buffer()->params.getDefSkip().inPixels(bv);
558
559         return inPixels(height, skip, width);
560 }
561
562 int VSpace::inPixels(int default_height, int default_skip, int default_width) const
563 {
564         // Height of a normal line in pixels (zoom factor considered)
565         int height = default_height; // [pixels]
566         
567         // Zoom factor specified by user in percent
568         float const zoom = lyxrc.zoom / 100.0; // [percent]
569
570         // DPI setting for monitor: pixels/inch
571         float const dpi = lyxrc.dpi; // screen resolution [pixels/inch]
572
573         // We want the result in pixels
574         float result, value;
575
576         switch (kin) {
577         case NONE: return 0;
578
579         case DEFSKIP:
580                 return default_skip;
581
582                 // This is how the skips are normally defined by
583                 // LateX.  But there should be some way to change
584                 // this per document.
585         case SMALLSKIP: return height / 4;
586         case MEDSKIP:   return height / 2;
587         case BIGSKIP:   return height;
588         case VFILL:     return 3 * height;
589                 // leave space for the vfill symbol
590         case LENGTH:
591                 // Pixel values are scaled so that the ratio
592                 // between lengths and font sizes on the screen
593                 // is the same as on paper.
594
595                 // we don't care about sign of value, we
596                 // can't display negative anyway
597                 result = 0.0;
598                 value = len.value();
599                 short val_sign = value < 0.0 ? -1 : 1;
600                 
601                 switch (len.unit()) {
602                 case LyXLength::SP:
603                         // Scaled point: sp = 1/65536 pt
604                         result = zoom * dpi * value
605                                 / (72.27 * 65536); // 4736286.7
606                         break;
607                 case LyXLength::PT:
608                         // Point: 1 pt = 1/72.27 inch
609                         result = zoom * dpi * value
610                                 / 72.27; // 72.27
611                         break;
612                 case LyXLength::BP:
613                         // Big point: 1 bp = 1/72 inch
614                         result = zoom * dpi * value
615                                 / 72; // 72
616                         break;
617                 case LyXLength::DD:
618                         // Didot: 1157dd = 1238 pt?
619                         result = zoom * dpi * value
620                                 / (72.27 / (0.376 * 2.845)); // 67.559735
621                         break;
622                 case LyXLength::MM:
623                         // Millimeter: 1 mm = 1/25.4 inch
624                         result = zoom * dpi * value
625                                 / 25.4; // 25.4
626                         break;
627                 case LyXLength::PC:
628                         // Pica: 1 pc = 12 pt
629                         result = zoom * dpi * value
630                                 / (72.27 / 12); // 6.0225
631                         break;
632                 case LyXLength::CC:
633                         // Cicero: 1 cc = 12 dd
634                         result = zoom * dpi * value
635                                 / (72.27 / (12 * 0.376 * 2.845)); // 5.6299779
636                         break;
637                 case LyXLength::CM:
638                         // Centimeter: 1 cm = 1/2.54 inch
639                         result = zoom * dpi * value
640                                 / 2.54; // 2.54
641                         break;
642                 case LyXLength::IN:
643                         // Inch
644                         result = zoom * dpi * value;
645                         break;
646                 case LyXLength::EX:
647                         // Ex: The height of an "x"
648                         result = zoom * value * height / 2; // what to / width?
649                         break;
650                 case LyXLength::EM: // what to / width?
651                         // Em: The width of an "m"
652                         result = zoom * value * height / 2; // Why 2?
653                         break;
654                 case LyXLength::MU: // This is probably only allowed in
655                         // math mode
656                         result = zoom * value * height;
657                         break;
658                 case LyXLength::PW: // Always % of workarea
659                 case LyXLength::PE:
660                 case LyXLength::PP:
661                 case LyXLength::PL:
662                         result = value * default_width / 100;
663                         break;
664                 case LyXLength::UNIT_NONE:
665                         result = 0;  // this cannot happen
666                         break;  
667                 }
668                 return int (result * val_sign + 0.5);
669         }
670         return 0; // never reached
671 }