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