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