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