2 /* This file is part of
3 * ======================================================
5 * LyX, The Document Processor
7 * Copyright (C) 1995 Matthias Ettrich
8 * Copyright (C) 1995-1998 The LyX Team.
10 *======================================================*/
15 #pragma implementation "vspace.h"
25 #include "BufferView.h"
28 extern BufferView *current_view;
30 // $Id: vspace.C,v 1.1 1999/09/27 18:44:38 larsbj Exp $
32 #if !defined(lint) && !defined(WITH_WARNINGS)
33 static char vcid[] = "$Id: vspace.C,v 1.1 1999/09/27 18:44:38 larsbj Exp $";
39 static const int num_units = int(LyXLength::UNIT_NONE);
41 // I am not sure if "mu" should be possible to select (Lgb)
42 static char const* unit_name[num_units] = { "sp", "pt", "bp", "dd",
43 "mm", "pc", "cc", "cm",
44 "in", "ex", "em", "mu" };
47 LyXLength::UNIT unitFromString (LString const & data)
50 while ((i<num_units) && (data != unit_name[i])) i++;
51 return (LyXLength::UNIT)i;
54 /* The following static items form a simple scanner for
55 * length strings, used by isValid[Glue]Length. See comments there.
57 static float number[4] = { 0, 0, 0, 0 };
58 static LyXLength::UNIT unit[4] = { LyXLength::UNIT_NONE,
61 LyXLength::UNIT_NONE };
62 //static int number_index, unit_index;
63 int number_index, unit_index;
65 static void advance (LString& data, const int n)
67 if (data.length() <= n)
70 data.substring (n, data.length()-1);
73 static bool isEndOfData (LString& data)
75 data.frontStrip (' ');
79 static char nextToken (LString& data)
81 data.frontStrip (' ');
85 else if (data[0] == '+') {
89 else if (data.prefixIs ("plus")) {
93 else if (data[0] == '-') {
97 else if (data.prefixIs ("minus")) {
104 // I really mean assignment ("=") below, not equality!
106 if ((i = strspn (data.c_str(), "0123456789."))) {
107 if (number_index > 3) return 'E'; // Error
108 LString buffer = data;
109 buffer.substring (0, i-1);
110 if (sscanf (buffer.c_str(),
111 "%f", &number[number_index]) == 1) {
117 } else if ((i = strspn (data.c_str(),
118 "abcdefghijklmnopqrstuvwxyz"))) {
119 if (unit_index > 3) return 'E'; // Error
120 LString buffer = data; buffer.substring (0, i-1);
121 unit[unit_index] = unitFromString (buffer);
122 if (unit[unit_index] != LyXLength::UNIT_NONE) {
135 int plus_val_index, minus_val_index,
136 plus_uni_index, minus_uni_index;
137 } table[] = { { "nu", 0, 0, 0, 0 },
138 { "nu+nu", 2, 0, 2, 0 },
139 { "nu+nu-nu", 2, 3, 2, 3 },
140 { "nu+-nu", 2, 2, 2, 2 },
141 { "nu-nu", 0, 2, 0, 2 },
142 { "nu-nu+nu", 3, 2, 3, 2 },
143 { "nu-+nu", 2, 2, 2, 2 },
144 { "n+nu", 2, 0, 1, 0 },
145 { "n+n-nu", 2, 3, 1, 1 },
146 { "n+-nu", 2, 2, 1, 1 },
147 { "n-nu", 0, 2, 0, 1 },
148 { "n-n+nu", 3, 2, 1, 1 },
149 { "n-+nu", 2, 2, 1, 1 },
150 { "", 0, 0, 0, 0 } // sentinel, must be empty
153 bool isValidGlueLength (LString const & data, LyXGlueLength* result)
155 // This parser is table-driven. First, it constructs a "pattern"
156 // that describes the sequence of tokens in "data". For example,
157 // "n-nu" means: number, minus sign, number, unit. As we go along,
158 // numbers and units are stored into static arrays. Then, "pattern"
159 // is searched in the "table". If it is found, the associated
160 // table entries tell us which number and unit should go where
161 // in the LyXLength structure. Example: if "data" has the "pattern"
162 // "nu+nu-nu", the associated table entries are "2, 3, 2, 3".
163 // That means, "plus_val" is the second number that was seen
164 // in the input, "minus_val" is the third number, and "plus_uni"
165 // and "minus_uni" are the second and third units, respectively.
166 // ("val" and "uni" are always the first items seen in "data".)
167 // This is the most elegant solution I could find -- a straight-
168 // forward approach leads to very long, tedious code that would be
169 // much harder to understand and maintain. (AS)
171 LString buffer = data;
172 buffer.frontStrip(' ');
174 // To make isValidGlueLength recognize negative values as
175 // the first number this little hack is needed:
176 short val_sign = 1; // positive as default
191 int pattern_index = 0, table_index = 0;
194 number_index = unit_index = 1; // entries at index 0 are sentinels
196 // construct "pattern" from "data"
197 while (!isEndOfData (buffer)) {
198 if (pattern_index > 20) return false;
199 pattern[pattern_index] = nextToken (buffer);
200 if (pattern[pattern_index] == 'E') return false;
203 pattern[pattern_index] = '\0';
205 // search "pattern" in "table"
207 while (strcmp (pattern, table[table_index].pattern)) {
209 if (!*table[table_index].pattern) return false;
212 // Get the values from the appropriate places. If an index
213 // is zero, the corresponding array value is zero or UNIT_NONE,
214 // so we needn't check this.
216 result->val = number[1] * val_sign;
217 result->uni = unit[1];
218 result->plus_val = number[table[table_index].plus_val_index];
219 result->minus_val = number[table[table_index].minus_val_index];
220 result->plus_uni = unit [table[table_index].plus_uni_index];
221 result->minus_uni = unit [table[table_index].minus_uni_index];
227 bool isValidLength(LString const & data, LyXLength* result)
229 /// This is a trimmed down version of isValidGlueLength.
230 /// The parser may seem overkill for lengths without
231 /// glue, but since we already have it, using it is
232 /// easier than writing something from scratch.
234 LString buffer = data;
235 int pattern_index = 0;
238 // To make isValidLength recognize negative values
239 // this little hack is needed:
240 short val_sign = 1; // positive as default
255 number_index = unit_index = 1; // entries at index 0 are sentinels
257 // construct "pattern" from "data"
258 while (!isEndOfData (buffer)) {
259 if (pattern_index > 2) return false;
260 pattern[pattern_index] = nextToken (buffer);
261 if (pattern[pattern_index] == 'E') return false;
264 pattern[pattern_index] = '\0';
266 // only the most basic pattern is accepted here
267 if (strcmp (pattern, "nu") != 0) return false;
269 // It _was_ a correct length string.
270 // Store away the values we found.
272 result->val = number[1]*val_sign;
273 result->uni = unit[1];
280 LyXLength::LyXLength(LString const & data)
284 if (!isValidLength (data, &tmp))
285 return; // should raise an exception
292 bool LyXLength::operator== (LyXLength other)
294 return (this->val == other.val)
295 && (this->uni == other.uni);
298 LString LyXLength::asString() const
302 sprintf (buffer, "%g%s", val, unit_name[uni]);
303 return LString (buffer);
307 /* LyXGlueLength class
310 LyXGlueLength::LyXGlueLength (LString const & data)
312 LyXGlueLength tmp(0.0, PT);
314 if (!isValidGlueLength (data, &tmp))
315 return; // should raise an exception
319 plus_val =tmp.plus_val;
320 plus_uni =tmp.plus_uni;
321 minus_val=tmp.minus_val;
322 minus_uni=tmp.minus_uni;
327 bool LyXGlueLength::operator== (LyXGlueLength other)
329 return (this->val == other.val)
330 && (this->uni == other.uni)
331 && (this->plus_val == other.plus_val)
332 && (this->plus_uni == other.plus_uni)
333 && (this->minus_val == other.minus_val)
334 && (this->minus_uni == other.minus_uni);
338 LString LyXGlueLength::asString () const
343 if (minus_val != 0.0)
344 if ((uni == plus_uni) && (uni == minus_uni))
345 if (plus_val == minus_val)
346 sprintf (buffer, "%g+-%g%s",
347 val, plus_val, unit_name[uni]);
349 sprintf (buffer, "%g+%g-%g%s",
350 val, plus_val, minus_val,
353 if ((plus_uni == minus_uni) && (plus_val == minus_val))
354 sprintf (buffer, "%g%s+-%g%s",
356 plus_val, unit_name[plus_uni]);
358 sprintf (buffer, "%g%s+%g%s-%g%s",
360 plus_val, unit_name[plus_uni],
361 minus_val, unit_name[minus_uni]);
364 sprintf (buffer, "%g+%g%s",
365 val, plus_val, unit_name[uni]);
367 sprintf (buffer, "%g%s+%g%s",
369 plus_val, unit_name[plus_uni]);
371 if (minus_val != 0.0)
372 if (uni == minus_uni)
373 sprintf (buffer, "%g-%g%s",
374 val, minus_val, unit_name[uni]);
376 sprintf (buffer, "%g%s-%g%s",
378 minus_val, unit_name[minus_uni]);
380 sprintf (buffer, "%g%s", val, unit_name[uni]);
381 return LString (buffer);
385 LString LyXGlueLength::asLatexString() const
390 if (minus_val != 0.0)
391 sprintf (buffer, "%g%s plus %g%s minus %g%s",
393 plus_val, unit_name[plus_uni],
394 minus_val, unit_name[minus_uni]);
396 sprintf (buffer, "%g%s plus %g%s",
398 plus_val, unit_name[plus_uni]);
400 if (minus_val != 0.0)
401 sprintf (buffer, "%g%s minus %g%s",
403 minus_val, unit_name[minus_uni]);
405 sprintf (buffer, "%g%s",
406 val, unit_name[uni]);
407 return LString (buffer);
414 VSpace::VSpace (LString const & data)
415 : kin (NONE), len (0.0, LyXLength::PT)
418 LString input = data;
421 int length = input.length();
423 if (length > 1 && input[length-1] == '*') {
425 input.substring (0, length-2);
429 if (input.prefixIs ("defskip")) kin = DEFSKIP;
430 else if (input.prefixIs ("smallskip")) kin = SMALLSKIP;
431 else if (input.prefixIs ("medskip")) kin = MEDSKIP;
432 else if (input.prefixIs ("bigskip")) kin = BIGSKIP;
433 else if (input.prefixIs ("vfill")) kin = VFILL;
434 else if (isValidGlueLength (input, &len))
436 else if (sscanf (input.c_str(), "%f", &value) == 1) {
437 // This last one is for reading old .lyx files
438 // without units in added_space_top/bottom.
439 // Let unit default to centimeters here.
441 len = LyXGlueLength (value, LyXLength::CM);
446 bool VSpace::operator== (VSpace other)
448 if (this->kin == other.kin)
449 if (this->kin == LENGTH)
450 if (this->len == other.len)
451 return this->kp == other.kp;
455 return this->kp == other.kp;
461 LString VSpace::asLyXCommand() const
467 case DEFSKIP: result = "defskip"; break;
468 case SMALLSKIP: result = "smallskip"; break;
469 case MEDSKIP: result = "medskip"; break;
470 case BIGSKIP: result = "bigskip"; break;
471 case VFILL: result = "vfill"; break;
472 case LENGTH: result = len.asString(); break;
474 if (kp && (kin != NONE) && (kin != DEFSKIP))
475 return result += '*';
481 LString VSpace::asLatexCommand() const
484 case NONE: return LString();
486 return current_view->currentBuffer()->params.getDefSkip().asLatexCommand();
487 case SMALLSKIP: return kp ? "\\vspace*{\\smallskipamount}"
489 case MEDSKIP: return kp ? "\\vspace*{\\medskipamount}"
491 case BIGSKIP: return kp ? "\\vspace*{\\bigskipamount}"
493 case VFILL: return kp ? "\\vspace*{\\fill}"
495 case LENGTH: return kp ? "\\vspace*{" + len.asLatexString() + '}'
496 : "\\vspace{" + len.asLatexString() + '}';
498 return LString(); // should never be reached
502 int VSpace::inPixels() const
504 // Height of a normal line in pixels (zoom factor considered)
505 int height = current_view->currentBuffer()->text->DefaultHeight(); // [pixels]
507 // Zoom factor specified by user in percent
508 float const zoom = lyxrc->zoom / 100.0; // [percent]
510 // DPI setting for monitor: pixels/inch
511 float const dpi = lyxrc->dpi; // screen resolution [pixels/inch]
513 // We want the result in pixels
520 return current_view->currentBuffer()->params.getDefSkip().inPixels();
522 // This is how the skips are normally defined by
523 // LateX. But there should be some way to change
524 // this per document.
525 case SMALLSKIP: return height/4;
526 case MEDSKIP: return height/2;
527 case BIGSKIP: return height;
528 case VFILL: return 3*height;
529 // leave space for the vfill symbol
531 // Pixel values are scaled so that the ratio
532 // between lengths and font sizes on the screen
533 // is the same as on paper.
535 // we don't care about sign of value, we
536 // can't display negative anyway
539 short val_sign = value < 0.0 ? -1 : 1;
541 switch (len.unit()) {
543 // Scaled point: sp = 1/65536 pt
544 result = zoom * dpi * value
545 / (72.27 * 65536); // 4736286.7
548 // Point: 1 pt = 1/72.27 inch
549 result = zoom * dpi * value
553 // Big point: 1 bp = 1/72 inch
554 result = zoom * dpi * value
558 // Didot: 1157dd = 1238 pt?
559 result = zoom * dpi * value
560 / (72.27/(0.376 * 2.845)); // 67.559735
563 // Millimeter: 1 mm = 1/25.4 inch
564 result = zoom * dpi * value
568 // Pica: 1 pc = 12 pt
569 result = zoom * dpi * value
570 / (72.27/12); // 6.0225
573 // Cicero: 1 cc = 12 dd
574 result = zoom * dpi * value
575 / (72.27/ (12*0.376*2.845)); // 5.6299779
578 // Centimeter: 1 cm = 1/2.54 inch
579 result = zoom * dpi * value
584 result = zoom * dpi * value;
587 // Ex: The height of an "x"
588 result = zoom * value * height / 2; // what to / width?
590 case LyXLength::EM: // what to / width?
591 // Em: The width of an "m"
592 result = zoom * value * height / 2; // Why 2?
594 case LyXLength::MU: // This is probably only allowed in
596 result = zoom * value * height;
598 case LyXLength::UNIT_NONE:
599 result = 0; // this cannot happen
602 return int (result * val_sign + 0.5);
604 return 0; // never reached