3 * Copyright 1995-2002 the LyX Team
4 * Read the file COPYING
6 * \author Matthias Ettrich
12 #pragma implementation
16 #include "lengthcommon.h"
19 #include "BufferView.h"
20 #include "support/LAssert.h"
22 #include "support/lstrings.h"
26 #ifndef CXX_GLOBAL_CSTD
32 /// used to return numeric values in parsing vspace
33 double number[4] = { 0, 0, 0, 0 };
34 /// used to return unit types in parsing vspace
35 LyXLength::UNIT unit[4] = { LyXLength::UNIT_NONE,
38 LyXLength::UNIT_NONE };
40 /// the current position in the number array
42 /// the current position in the unit array
45 /// skip n characters of input
47 void lyx_advance(string & data, string::size_type n)
52 /// return true when the input is at the end
54 bool isEndOfData(string const & data)
56 return ltrim(data).empty();
60 * nextToken - return the next token in the input
61 * @param data input string
62 * @return a char representing the type of token returned
64 * The possible return values are :
65 * + stretch indicator for glue length
66 * - shrink indicator for glue length
67 * n a numeric value (stored in number array)
68 * u a unit type (stored in unit array)
71 char nextToken(string & data)
76 else if (data[0] == '+') {
79 } else if (prefixIs(data, "plus")) {
82 } else if (data[0] == '-') {
85 } else if (prefixIs(data, "minus")) {
89 string::size_type i = data.find_first_not_of("0123456789.");
92 if (number_index > 3) return 'E';
96 // we have found some number
97 if (i == string::npos) {
101 buffer = data.substr(0, i);
103 lyx_advance(data, i);
105 if (isStrDbl(buffer)) {
106 number[number_index] = strToDbl(buffer);
112 i = data.find_first_not_of("abcdefghijklmnopqrstuvwxyz%");
114 if (unit_index > 3) return 'E';
118 // we have found some alphabetical string
119 if (i == string::npos) {
123 buffer = data.substr(0, i);
125 // possibly we have "mmplus" string or similar
126 if (buffer.size() > 5 && (buffer.substr(2,4) == string("plus") || buffer.substr(2,5) == string("minus"))) {
127 lyx_advance(data, 2);
128 unit[unit_index] = unitFromString(buffer.substr(0, 2));
130 lyx_advance(data, i);
131 unit[unit_index] = unitFromString(buffer);
134 if (unit[unit_index] != LyXLength::UNIT_NONE) {
137 } else return 'E'; // Error
144 /// latex representation of a vspace
146 char const * pattern;
154 /// the possible formats for a vspace string
155 LaTeXLength table[] = {
156 { "nu", 0, 0, 0, 0 },
157 { "nu+nu", 2, 0, 2, 0 },
158 { "nu+nu-nu", 2, 3, 2, 3 },
159 { "nu+-nu", 2, 2, 2, 2 },
160 { "nu-nu", 0, 2, 0, 2 },
161 { "nu-nu+nu", 3, 2, 3, 2 },
162 { "nu-+nu", 2, 2, 2, 2 },
163 { "n+nu", 2, 0, 1, 0 },
164 { "n+n-nu", 2, 3, 1, 1 },
165 { "n+-nu", 2, 2, 1, 1 },
166 { "n-nu", 0, 2, 0, 1 },
167 { "n-n+nu", 3, 2, 1, 1 },
168 { "n-+nu", 2, 2, 1, 1 },
169 { "", 0, 0, 0, 0 } // sentinel, must be empty
175 const char * stringFromUnit(int unit)
177 if (unit < 0 || unit >= num_units)
179 return unit_name[unit];
183 bool isValidGlueLength(string const & data, LyXGlueLength * result)
185 // This parser is table-driven. First, it constructs a "pattern"
186 // that describes the sequence of tokens in "data". For example,
187 // "n-nu" means: number, minus sign, number, unit. As we go along,
188 // numbers and units are stored into static arrays. Then, "pattern"
189 // is searched in the "table". If it is found, the associated
190 // table entries tell us which number and unit should go where
191 // in the LyXLength structure. Example: if "data" has the "pattern"
192 // "nu+nu-nu", the associated table entries are "2, 3, 2, 3".
193 // That means, "plus_val" is the second number that was seen
194 // in the input, "minus_val" is the third number, and "plus_uni"
195 // and "minus_uni" are the second and third units, respectively.
196 // ("val" and "uni" are always the first items seen in "data".)
197 // This is the most elegant solution I could find -- a straight-
198 // forward approach leads to very long, tedious code that would be
199 // much harder to understand and maintain. (AS)
203 string buffer = ltrim(data);
205 // To make isValidGlueLength recognize negative values as
206 // the first number this little hack is needed:
207 int val_sign = 1; // positive as default
210 lyx_advance(buffer, 1);
214 lyx_advance(buffer, 1);
222 int pattern_index = 0;
227 unit_index = 1; // entries at index 0 are sentinels
229 // construct "pattern" from "data"
230 while (!isEndOfData (buffer)) {
231 if (pattern_index > 20) return false;
232 pattern[pattern_index] = nextToken (buffer);
233 if (pattern[pattern_index] == 'E') return false;
236 pattern[pattern_index] = '\0';
238 // search "pattern" in "table"
240 while (compare(pattern, table[table_index].pattern)) {
242 if (!*table[table_index].pattern)
246 // Get the values from the appropriate places. If an index
247 // is zero, the corresponding array value is zero or UNIT_NONE,
248 // so we needn't check this.
250 result->len_.value (number[1] * val_sign);
251 result->len_.unit (unit[1]);
252 result->plus_.value (number[table[table_index].plus_val_index]);
253 result->plus_.unit (unit [table[table_index].plus_uni_index]);
254 result->minus_.value(number[table[table_index].minus_val_index]);
255 result->minus_.unit (unit [table[table_index].minus_uni_index]);
261 bool isValidLength(string const & data, LyXLength * result)
263 // This is a trimmed down version of isValidGlueLength.
264 // The parser may seem overkill for lengths without
265 // glue, but since we already have it, using it is
266 // easier than writing something from scratch.
270 string buffer = data;
271 int pattern_index = 0;
274 // To make isValidLength recognize negative values
275 // this little hack is needed:
276 int val_sign = 1; // positive as default
279 lyx_advance(buffer, 1);
283 lyx_advance(buffer, 1);
291 number_index = unit_index = 1; // entries at index 0 are sentinels
293 // construct "pattern" from "data"
294 while (!isEndOfData (buffer)) {
295 if (pattern_index > 2)
297 pattern[pattern_index] = nextToken (buffer);
298 if (pattern[pattern_index] == 'E')
302 pattern[pattern_index] = '\0';
304 // only the most basic pattern is accepted here
305 if (compare(pattern, "nu") != 0) return false;
307 // It _was_ a correct length string.
308 // Store away the values we found.
310 result->val_ = number[1] * val_sign;
311 result->unit_ = unit[1];
322 : kind_(NONE), len_(), keep_(false)
326 VSpace::VSpace(vspace_kind k)
327 : kind_(k), len_(), keep_(false)
331 VSpace::VSpace(LyXLength const & l)
332 : kind_(LENGTH), len_(l), keep_(false)
336 VSpace::VSpace(LyXGlueLength const & l)
337 : kind_(LENGTH), len_(l), keep_(false)
341 VSpace::VSpace(string const & data)
342 : kind_(NONE), len_(), keep_(false)
347 string input = rtrim(data);
349 string::size_type const length = input.length();
351 if (length > 1 && input[length - 1] == '*') {
353 input.erase(length - 1);
356 if (prefixIs (input, "defskip")) kind_ = DEFSKIP;
357 else if (prefixIs (input, "smallskip")) kind_ = SMALLSKIP;
358 else if (prefixIs (input, "medskip")) kind_ = MEDSKIP;
359 else if (prefixIs (input, "bigskip")) kind_ = BIGSKIP;
360 else if (prefixIs (input, "vfill")) kind_ = VFILL;
361 else if (isValidGlueLength(input, &len_)) kind_ = LENGTH;
362 else if (sscanf(input.c_str(), "%lf", &value) == 1) {
363 // This last one is for reading old .lyx files
364 // without units in added_space_top/bottom.
365 // Let unit default to centimeters here.
367 len_ = LyXGlueLength(LyXLength(value, LyXLength::CM));
372 VSpace::vspace_kind VSpace::kind() const
378 LyXGlueLength VSpace::length() const
384 bool VSpace::keep() const
390 void VSpace::setKeep(bool val)
396 bool VSpace::operator==(VSpace const & other) const
398 if (kind_ != other.kind_)
402 return this->keep_ == other.keep_;
404 if (len_ != other.len_)
407 return keep_ == other.keep_;
411 string const VSpace::asLyXCommand() const
416 case DEFSKIP: result = "defskip"; break;
417 case SMALLSKIP: result = "smallskip"; break;
418 case MEDSKIP: result = "medskip"; break;
419 case BIGSKIP: result = "bigskip"; break;
420 case VFILL: result = "vfill"; break;
421 case LENGTH: result = len_.asString(); break;
423 if (keep_ && kind_ != NONE && kind_ != DEFSKIP)
429 string const VSpace::asLatexCommand(BufferParams const & params) const
437 ret = params.getDefSkip().asLatexCommand(params);
440 ret = keep_ ? "\\vspace*{\\smallskipamount}"
444 ret = keep_ ? "\\vspace*{\\medskipamount}"
448 ret = keep_ ? "\\vspace*{\\bigskipamount}"
452 ret = keep_ ? "\\vspace*{\\fill}"
457 string const lenstr = len_.asLatexString();
459 ret = keep_ ? "\\vspace*{" + lenstr + '}'
460 : "\\vspace{" + lenstr + '}';
471 int VSpace::inPixels(BufferView const * bv) const
473 // Height of a normal line in pixels (zoom factor considered)
474 int const default_height = bv->defaultHeight(); // [pixels]
481 // value for this is already set
485 retval = bv->buffer()->params.getDefSkip().inPixels(bv);
488 // This is how the skips are normally defined by LateX.
489 // But there should be some way to change this per document.
491 retval = default_height / 4;
495 retval = default_height / 2;
499 retval = default_height;
503 // leave space for the vfill symbol
504 retval = 3 * default_height;
508 retval = len_.len().inPixels(bv->workWidth());