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