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