]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/xforms_helpers.C
Add #include <boost/assert.hpp>.
[lyx.git] / src / frontends / xforms / xforms_helpers.C
1 /**
2  * \file xforms_helpers.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "xforms_helpers.h"
14
15 #include "gettext.h"
16 #include "lyxgluelength.h"
17 #include "lyxlex.h"
18
19 #include "support/FileInfo.h"
20 #include "support/filetools.h"
21 #include "support/lstrings.h" // frontStrip, strip
22 #include "support/tostr.h"
23
24 #include "lyx_forms.h"
25 #include "combox.h"
26
27 #include <boost/assert.hpp>
28
29 #include <fstream>
30
31 using lyx::support::AbsolutePath;
32 using lyx::support::FileInfo;
33 using lyx::support::isStrDbl;
34 using lyx::support::OnlyPath;
35 using lyx::support::subst;
36 using lyx::support::trim;
37
38 using std::make_pair;
39
40 using std::ofstream;
41 using std::vector;
42
43
44 bool isActive(FL_OBJECT * ob)
45 {
46         return ob && ob->active > 0;
47 }
48
49
50 std::pair<string, string> parse_shortcut(string const & str)
51 {
52         string::size_type i = str.find_first_of("&");
53         if (i == string::npos || i == str.length() - 1)
54                 return make_pair(str, string());
55
56         // FIXME: handle &&
57
58         string::value_type c = str[i + 1];
59         return make_pair(str.substr(0, i) + str.substr(i + 1),
60                          string("#") + c);
61 }
62
63
64 // A wrapper for the xforms routine, but this one accepts uint args
65 unsigned long fl_getmcolor(int i,
66                            unsigned int * r, unsigned int * g, unsigned int * b)
67 {
68         int r2, g2, b2;
69         unsigned long ret_val = ::fl_getmcolor(i, &r2, &g2, &b2);
70         *r = r2;
71         *g = g2;
72         *b = b2;
73         return ret_val;
74 }
75
76
77 // Set an FL_OBJECT to activated or deactivated
78 void setEnabled(FL_OBJECT * ob, bool enable)
79 {
80         if (enable) {
81                 fl_activate_object(ob);
82                 fl_set_object_lcol(ob, FL_LCOL);
83         } else {
84                 fl_deactivate_object(ob);
85                 fl_set_object_lcol(ob, FL_INACTIVE);
86         }
87 }
88
89
90 // Given an fl_choice or an fl_browser, create a vector of its entries
91 vector<string> const getVector(FL_OBJECT * ob)
92 {
93         vector <string> vec;
94
95         switch (ob->objclass) {
96         case FL_CHOICE:
97                 for(int i = 0; i < fl_get_choice_maxitems(ob); ++i) {
98                         string const text = fl_get_choice_item_text(ob, i+1);
99                         vec.push_back(trim(text));
100                 }
101                 break;
102         case FL_BROWSER:
103                 for(int i = 0; i < fl_get_browser_maxline(ob); ++i) {
104                         string const text = fl_get_browser_line(ob, i+1);
105                         vec.push_back(trim(text));
106                 }
107                 break;
108         default:
109                 BOOST_ASSERT(false);
110         }
111
112         return vec;
113 }
114
115
116 ///
117 string const getString(FL_OBJECT * ob, int line)
118 {
119         // Negative line value does not make sense.
120         BOOST_ASSERT(line >= 0);
121
122         char const * tmp = 0;
123         switch (ob->objclass) {
124         case FL_INPUT:
125                 tmp = fl_get_input(ob);
126                 break;
127
128         case FL_BROWSER:
129                 if (line == 0)
130                         line = fl_get_browser(ob);
131
132                 if (line >= 1 && line <= fl_get_browser_maxline(ob))
133                         tmp = fl_get_browser_line(ob, line);
134                 break;
135
136         case FL_CHOICE:
137                 if (line == 0)
138                         line = fl_get_choice(ob);
139
140                 if (line >= 1 && line <= fl_get_choice_maxitems(ob))
141                         tmp = fl_get_choice_item_text(ob, line);
142                 break;
143
144         case FL_COMBOX:
145                 tmp = fl_get_combox_text(ob);
146                 break;
147
148         default:
149                 BOOST_ASSERT(false);
150         }
151
152         return tmp ? trim(tmp) : string();
153 }
154
155 string getLengthFromWidgets(FL_OBJECT * input, FL_OBJECT * choice)
156 {
157         // Paranoia check
158         BOOST_ASSERT(input  && input->objclass  == FL_INPUT &&
159                     choice && choice->objclass == FL_CHOICE);
160
161         string const length = trim(fl_get_input(input));
162         if (length.empty())
163                 return string();
164
165         // don't return unit-from-choice if the input(field) contains a unit
166         if (isValidGlueLength(length))
167                 return length;
168
169         string unit = trim(fl_get_choice_text(choice));
170         unit = subst(unit, "%%", "%");
171
172         return length + unit;
173 }
174
175
176 void updateWidgetsFromLengthString(FL_OBJECT * input, FL_OBJECT * choice,
177                                    string const & str,
178                                    string const & default_unit)
179 {
180         // use input field only for gluelengths
181         if (!isValidLength(str) && !isStrDbl(str)) {
182                 fl_set_input(input, str.c_str());
183                 // we assume that "default_unit" is in the choice as "we"
184                 // have control over that!
185                 // No need to check for its presence in the choice, therefore.
186                 fl_set_choice_text(choice, default_unit.c_str());
187         } else {
188                 updateWidgetsFromLength(input, choice,
189                                         LyXLength(str), default_unit);
190         }
191 }
192
193
194 void updateWidgetsFromLength(FL_OBJECT * input, FL_OBJECT * choice,
195                              LyXLength const & len,
196                              string const & default_unit)
197 {
198         // Paranoia check
199         BOOST_ASSERT(input  && input->objclass  == FL_INPUT &&
200                     choice && choice->objclass == FL_CHOICE);
201
202         if (len.empty()) {
203                 fl_set_input(input, "");
204                 fl_set_choice_text(choice, default_unit.c_str());
205         } else {
206                 fl_set_input(input, tostr(len.value()).c_str());
207
208                 // Set the choice to the desired unit, if present in the choice.
209                 // Else set the choice to the default unit.
210                 string const unit = subst(stringFromUnit(len.unit()),"%","%%");
211
212                 vector<string> const vec = getVector(choice);
213                 vector<string>::const_iterator it =
214                         std::find(vec.begin(), vec.end(), unit);
215                 if (it != vec.end()) {
216                         fl_set_choice_text(choice, unit.c_str());
217                 } else {
218                         fl_set_choice_text(choice, default_unit.c_str());
219                 }
220         }
221 }
222
223
224 // Take a string and add breaks so that it fits into a desired label width, w
225 string formatted(string const & sin, int w, int size, int style)
226 {
227         string sout;
228         if (sin.empty())
229                 return sout;
230
231         string::size_type curpos = 0;
232         string line;
233         for(;;) {
234                 string::size_type const nxtpos1 = sin.find(' ',  curpos);
235                 string::size_type const nxtpos2 = sin.find('\n', curpos);
236                 string::size_type const nxtpos = std::min(nxtpos1, nxtpos2);
237
238                 string const word = nxtpos == string::npos ?
239                         sin.substr(curpos) : sin.substr(curpos, nxtpos-curpos);
240
241                 bool const newline = (nxtpos2 != string::npos &&
242                                       nxtpos2 < nxtpos1);
243
244                 string const line_plus_word =
245                         line.empty() ? word : line + ' ' + word;
246
247                 int const length =
248                         fl_get_string_width(style, size,
249                                             line_plus_word.c_str(),
250                                             int(line_plus_word.length()));
251
252                 if (length >= w) {
253                         sout += line + '\n';
254                         if (newline) {
255                                 sout += word + '\n';
256                                 line.erase();
257                         } else {
258                                 line = word;
259                         }
260
261                 } else if (newline) {
262                         sout += line_plus_word + '\n';
263                         line.erase();
264
265                 } else {
266                         if (!line.empty())
267                                 line += ' ';
268                         line += word;
269                 }
270
271                 if (nxtpos == string::npos) {
272                         if (!line.empty())
273                                 sout += line;
274                         break;
275                 }
276
277                 curpos = nxtpos+1;
278         }
279
280         return sout;
281 }
282
283
284 void setCursorColor(int color)
285 {
286         fl_set_cursor_color(FL_DEFAULT_CURSOR, color, FL_WHITE);
287         fl_set_cursor_color(XC_xterm,          color, FL_WHITE);
288         fl_set_cursor_color(XC_watch,          color, FL_WHITE);
289         fl_set_cursor_color(XC_sb_right_arrow, color, FL_WHITE);
290 }
291
292
293 namespace {
294
295 // sorted by hand to prevent LyXLex from complaining on read().
296 keyword_item xformTags[] = {
297         { "\\gui_background",   FL_COL1 },
298         { "\\gui_buttonbottom", FL_BOTTOM_BCOL },
299         { "\\gui_buttonleft",   FL_LEFT_BCOL },
300         { "\\gui_buttonright",  FL_RIGHT_BCOL },
301         { "\\gui_buttontop",    FL_TOP_BCOL },
302         { "\\gui_inactive",     FL_INACTIVE },
303         { "\\gui_pointer",      FL_FREE_COL16 },
304         { "\\gui_push_button",  FL_YELLOW },
305         { "\\gui_selected",     FL_MCOL },
306         { "\\gui_text",         FL_BLACK }
307 };
308
309
310 const int xformCount = sizeof(xformTags) / sizeof(keyword_item);
311
312 } // namespace anon
313
314
315 bool XformsColor::read(string const & filename)
316 {
317         LyXLex lexrc(xformTags, xformCount);
318         if (!lexrc.setFile(filename))
319                 return false;
320
321         while (lexrc.isOK()) {
322                 int const le = lexrc.lex();
323
324                 switch (le) {
325                 case LyXLex::LEX_UNDEF:
326                         lexrc.printError("Unknown tag `$$Token'");
327                         continue;
328                 case LyXLex::LEX_FEOF:
329                         continue;
330                 default: break;
331                 }
332
333                 string const tag = lexrc.getString();
334
335                 RGBColor col;
336
337                 if (!lexrc.next()) break;
338                 col.r = lexrc.getInteger();
339
340                 if (!lexrc.next()) break;
341                 col.g = lexrc.getInteger();
342
343                 if (!lexrc.next()) break;
344                 col.b = lexrc.getInteger();
345
346                 fl_mapcolor(le, col.r, col.g, col.b);
347
348                 if (tag == "\\gui_pointer") {
349                         setCursorColor(FL_FREE_COL16);
350                 }
351         }
352
353         return true;
354 }
355
356
357 bool XformsColor::write(string const & filename)
358 {
359         ofstream os(filename.c_str());
360         if (!os)
361                 return false;
362
363         os << "###"
364            << "### file " << filename << "\n\n"
365            << "### This file is written by LyX, if you want to make your own\n"
366            << "### modifications you should do them from inside LyX and save\n"
367            << '\n';
368
369         for (int i = 0; i < xformCount; ++i) {
370                 string const tag  = xformTags[i].tag;
371                 int const colorID = xformTags[i].code;
372                 RGBColor color;
373
374                 fl_getmcolor(colorID, &color.r, &color.g, &color.b);
375
376                 os << tag << ' '
377                    << color.r << ' ' << color.g << ' ' << color.b << '\n';
378         }
379
380         return true;
381 }
382
383
384 string  RWInfo::error_message;
385
386 bool RWInfo::WriteableDir(string const & name)
387 {
388         error_message.erase();
389
390         if (!AbsolutePath(name)) {
391                 error_message = _("The absolute path is required.");
392                 return false;
393         }
394
395         FileInfo const tp(name);
396         if (!tp.isOK() || !tp.isDir()) {
397                 error_message = _("Directory does not exist.");
398                 return false;
399         }
400
401         if (!tp.writable()) {
402                 error_message = _("Cannot write to this directory.");
403                 return false;
404         }
405
406         return true;
407 }
408
409
410 bool RWInfo::ReadableDir(string const & name)
411 {
412         error_message.erase();
413
414         if (!AbsolutePath(name)) {
415                 error_message = _("The absolute path is required.");
416                 return false;
417         }
418
419         FileInfo const tp(name);
420         if (!tp.isOK() || !tp.isDir()) {
421                 error_message = _("Directory does not exist.");
422                 return false;
423         }
424
425         if (!tp.readable()) {
426                 error_message = _("Cannot read this directory.");
427                 return false;
428         }
429
430         return true;
431 }
432
433
434 bool RWInfo::WriteableFile(string const & name)
435 {
436         // A writeable file is either:
437         // * An existing file to which we have write access, or
438         // * A file that doesn't yet exist but that would exist in a writeable
439         //   directory.
440
441         error_message.erase();
442
443         if (name.empty()) {
444                 error_message = _("No file input.");
445                 return false;
446         }
447
448         string const dir = OnlyPath(name);
449         if (!AbsolutePath(dir)) {
450                 error_message = _("The absolute path is required.");
451                 return false;
452         }
453
454         FileInfo d(name);
455
456         if (!d.isOK() || !d.isDir()) {
457                 d.newFile(dir);
458         }
459
460         if (!d.isOK() || !d.isDir()) {
461                 error_message = _("Directory does not exist.");
462                 return false;
463         }
464
465         if (!d.writable()) {
466                 error_message = _("Cannot write to this directory.");
467                 return false;
468         }
469
470         FileInfo f(name);
471         if (dir == name || (f.isOK() && f.isDir())) {
472                 error_message = _("A file is required, not a directory.");
473                 return false;
474         }
475
476         if (f.isOK() && f.exist() && !f.writable()) {
477                 error_message = _("Cannot write to this file.");
478                 return false;
479         }
480
481         return true;
482 }
483
484
485 bool RWInfo::ReadableFile(string const & name)
486 {
487         error_message.erase();
488
489         if (name.empty()) {
490                 error_message = _("No file input.");
491                 return false;
492         }
493
494         string const dir = OnlyPath(name);
495         if (!AbsolutePath(dir)) {
496                 error_message = _("The absolute path is required.");
497                 return false;
498         }
499
500         FileInfo d(name);
501
502         if (!d.isOK() && !d.isDir()) {
503                 d.newFile(dir);
504         }
505
506         if (!d.isOK() || !d.isDir()) {
507                 error_message = _("Directory does not exist.");
508                 return false;
509         }
510
511         if (!d.readable()) {
512                 error_message = _("Cannot read from this directory.");
513                 return false;
514         }
515
516         FileInfo f(name);
517         if (dir == name || (f.isOK() && f.isDir())) {
518                 error_message = _("A file is required, not a directory.");
519                 return false;
520         }
521
522         if (!f.exist()) {
523                 error_message = _("File does not exist.");
524                 return false;
525         }
526
527         if (!f.readable()) {
528                 error_message = _("Cannot read from this file.");
529                 return false;
530         }
531
532         return true;
533 }