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