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