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