]> git.lyx.org Git - lyx.git/blob - src/frontends/xforms/xforms_helpers.C
70a3bcb99d214a92610ede2c0e713ce59bf1a683
[lyx.git] / src / frontends / xforms / xforms_helpers.C
1 /**
2  * \file xforms_helpers.C
3  * Copyright 2000-2002 The LyX Team.
4  * See the file COPYING.
5  *
6  * \author Angus Leeming, a.leeming@ic.ac.uk
7  */
8
9 #include <config.h>
10
11 #include FORMS_H_LOCATION
12
13 #include <fstream> // ofstream
14 #include <vector>
15
16 #ifdef __GNUG_
17 #pragma implementation
18 #endif
19  
20 #include "xforms_helpers.h"
21 #include "lyxlex.h"
22 #include "support/FileInfo.h"
23 #include "support/filetools.h"
24 #include "support/lstrings.h" // frontStrip, strip
25 #include "gettext.h"
26 #include "support/LAssert.h"
27 #include "lyxlength.h"
28 #include "lyxgluelength.h"
29
30 using std::ofstream;
31 using std::pair;
32 using std::vector;
33
34 // Extract shortcut from <ident>|<shortcut> string
35 char const * flyx_shortcut_extract(char const * sc)
36 {
37         // Find '|' in the sc and return the string after that.
38         register char const * sd = sc;
39         while (sd[0]!= 0 && sd[0] != '|') ++sd;
40
41         if (sd[0] == '|') {
42                 ++sd;
43                 //lyxerr << sd << endl;
44                 return sd;
45         }
46         return "";
47 }
48
49
50 // Extract identifier from <ident>|<shortcut> string
51 char const * flyx_ident_extract(char const * sc)
52 {
53         register char const * se = sc;
54         while (se[0]!= 0 && se[0] != '|') ++se;
55
56         if (se[0] == 0) return sc;
57         
58         char * sb = new char[se - sc + 1];
59         int index = 0;
60         register char const * sd = sc;
61         while (sd != se) {
62                 sb[index] = sd[0];
63                 ++index; ++sd;
64         }
65         sb[index] = 0;
66         return sb;
67 }
68
69
70 // Set an FL_OBJECT to activated or deactivated
71 void setEnabled(FL_OBJECT * ob, bool enable)
72 {
73         if (enable) {
74                 fl_activate_object(ob);
75                 fl_set_object_lcol(ob, FL_BLACK);
76         } else {
77                 fl_deactivate_object(ob);
78                 fl_set_object_lcol(ob, FL_INACTIVE);
79         }
80 }
81
82         
83 // Given an fl_choice, create a vector of its entries
84 vector<string> const getVectorFromChoice(FL_OBJECT * ob)
85 {
86         vector<string> vec;
87         if (!ob || ob->objclass != FL_CHOICE)
88                 return vec;
89
90         for(int i = 0; i < fl_get_choice_maxitems(ob); ++i) {
91                 string const text = fl_get_choice_item_text(ob, i+1);
92                 vec.push_back(strip(frontStrip(text)));
93         }
94
95         return vec;
96 }
97
98
99 /// Given an fl_input, return its contents.
100 string const getStringFromInput(FL_OBJECT * ob)
101 {
102         if (!ob || ob->objclass != FL_INPUT)
103                 return string();
104
105         char const * tmp = fl_get_input(ob);
106         return (tmp) ? tmp : string();
107 }
108
109
110 // Given an fl_browser, return the contents of line
111 string const getStringFromBrowser(FL_OBJECT * ob, int line)
112 {
113         if (!ob || ob->objclass != FL_BROWSER || 
114             line < 1 || line > fl_get_browser_maxline(ob))
115                 return string();
116
117         char const * tmp = fl_get_browser_line(ob, line);
118         return (tmp) ? tmp : string();
119 }
120
121 // Given an fl_browser, return the contents of the currently
122 // highlighted line.
123 // If nothing is selected, return an empty string
124 string const getSelectedStringFromBrowser(FL_OBJECT * ob)
125 {
126         if (!ob || ob->objclass != FL_BROWSER)
127                 return string();
128
129         int const line = fl_get_browser(ob);
130         if (line < 1 || line > fl_get_browser_maxline(ob))
131                 return string();
132
133         if (!fl_isselected_browser_line(ob, line))
134                 return string();
135
136         char const * tmp = fl_get_browser_line(ob, line);
137         return (tmp) ? tmp : string();
138 }
139
140
141 // Given an fl_browser, create a vector of its entries
142 vector<string> const getVectorFromBrowser(FL_OBJECT * ob)
143 {
144         vector<string> vec;
145         if (!ob || ob->objclass != FL_BROWSER)
146                 return vec;
147
148         for(int i = 0; i < fl_get_browser_maxline(ob); ++i) {
149                 string const text = fl_get_browser_line(ob, i+1);
150                 vec.push_back(strip(frontStrip(text)));
151         }
152
153         return vec;
154 }
155
156
157 string getLengthFromWidgets(FL_OBJECT * input, FL_OBJECT * choice)
158 {
159         // Paranoia check
160         lyx::Assert(input  && input->objclass  == FL_INPUT &&
161                     choice && choice->objclass == FL_CHOICE);
162
163         string const length = strip(frontStrip(fl_get_input(input)));
164         if (length.empty())
165                 return string();
166
167         //don't return unit-from-choice if the input(field) contains a unit
168         if (isValidGlueLength(length))
169                 return length;
170
171         string unit = strip(frontStrip(fl_get_choice_text(choice)));
172         unit = subst(unit, "%%", "%");
173
174         return length + unit;
175 }
176         
177
178 #if 0
179 // old code which can be deleted if the new one, now enabled,
180 // works satisfyingly (JSpitzm, 11/02/2002)
181 // this should definitely be the other way around!!!
182 void updateWidgetsFromLength(FL_OBJECT * input, FL_OBJECT * choice,
183                              LyXLength const & len,
184                              string const & default_unit)
185 {
186         if (len.zero())
187                 updateWidgetsFromLengthString(input, choice,
188                                               string(), default_unit);
189         // use input field only for gluelengths
190         else if (!isValidLength(len) && !isStrDbl(len)) {
191                 fl_set_input(input, len.c_str());
192                 fl_set_choice_text(choice, default_unit.c_str());
193         }
194         else
195                 updateWidgetsFromLengthString(input, choice,
196                                               len.asString(), default_unit);
197
198 }
199
200
201 // Most of the code here is a poor duplication of the parser code
202 // which is in LyXLength. Use that instead
203 void updateWidgetsFromLengthString(FL_OBJECT * input, FL_OBJECT * choice,
204                                    string const & str,
205                                    string const & default_unit)
206 {
207         // Paranoia check
208         lyx::Assert(input  && input->objclass  == FL_INPUT &&
209                     choice && choice->objclass == FL_CHOICE);
210
211         if (str.empty()) {
212                 fl_set_input(input, "");
213                 int unitpos = 1; // xforms has Fortran-style indexing
214                 for(int i = 0; i < fl_get_choice_maxitems(choice); ++i) {
215                         string const text = fl_get_choice_item_text(choice,i+1);
216                         if (default_unit ==
217                             lowercase(strip(frontStrip(text)))) {
218                                 unitpos = i+1;
219                                 break;
220                         }
221                 }
222                 fl_set_choice(choice, unitpos);
223                 return;
224         }
225
226         // The unit is presumed to begin at the first char a-z
227         // or with the char '%'
228         string const tmp = lowercase(strip(frontStrip(str)));
229
230         string::const_iterator p = tmp.begin();
231         for (; p != tmp.end(); ++p) {
232                 if ((*p >= 'a' && *p <= 'z') || *p == '%')
233                         break;
234         }
235
236         string len = "0";
237         int unitpos = 1; // xforms has Fortran-style indexing
238
239         if (p == tmp.end()) {
240                 if (isStrDbl(tmp))
241                         len = tmp;
242
243         } else {
244                 string tmplen = string(tmp.begin(), p);
245                 if (isStrDbl(tmplen))
246                         len = tmplen;
247                 string unit = string(p, tmp.end());
248                 unit = subst(unit, "%", "%%");
249
250                 for(int i = 0; i < fl_get_choice_maxitems(choice); ++i) {
251                         string const text = fl_get_choice_item_text(choice,i+1);
252                         if (unit == lowercase(strip(frontStrip(text)))) {
253                                 unitpos = i+1;
254                                 break;
255                         }
256                 }
257         }
258         
259         fl_set_input(input,   len.c_str());
260         fl_set_choice(choice, unitpos);
261 }
262 #else
263 void updateWidgetsFromLengthString(FL_OBJECT * input, FL_OBJECT * choice,
264                                    string const & str,
265                                    string const & default_unit)
266 {
267         // use input field only for gluelengths
268         if (!isValidLength(str) && !isStrDbl(str)) {
269                 fl_set_input(input, str.c_str());
270                 fl_set_choice_text(choice, default_unit.c_str());
271         } else {
272                 updateWidgetsFromLength(input, choice,
273                                 LyXLength(str), default_unit);
274         }
275 }
276
277
278 void updateWidgetsFromLength(FL_OBJECT * input, FL_OBJECT * choice,
279                              LyXLength const & len,
280                              string const & default_unit)
281 {
282         // Paranoia check
283         lyx::Assert(input  && input->objclass  == FL_INPUT &&
284                     choice && choice->objclass == FL_CHOICE);
285
286         if (len.zero()) {
287                 fl_set_input(input, "");
288                 fl_set_choice_text(choice, default_unit.c_str());
289         } else {
290                 ostringstream buffer;
291                 buffer << len.value();
292                 fl_set_input(input, buffer.str().c_str());
293                 fl_set_choice_text(choice, 
294                     subst(stringFromUnit(len.unit()),"%","%%").c_str());
295         }
296 }
297 #endif
298
299
300 // Take a string and add breaks so that it fits into a desired label width, w
301 string formatted(string const & sin, int w, int size, int style)
302 {
303         // FIX: Q: Why cant this be done by a one pass algo? (Lgb)
304
305         string sout;
306         if (sin.empty()) return sout;
307
308         // breaks in up into a vector of individual words
309         vector<string> sentence;
310         string word;
311         for (string::const_iterator sit = sin.begin();
312              sit != sin.end(); ++sit) {
313                 if ((*sit) == ' ' || (*sit) == '\n') {
314                         if (!word.empty()) {
315                                 sentence.push_back(word);
316                                 word.erase();
317                         }
318                         if ((*sit) == '\n') word += '\n';
319                         
320                 } else {
321                         word += (*sit);
322                 }
323         }
324
325         // Flush remaining contents of word
326         if (!word.empty()) sentence.push_back(word);
327
328         string line;
329         string line_plus_word;
330         for (vector<string>::const_iterator vit = sentence.begin();
331              vit != sentence.end(); ++vit) {
332                 string word(*vit);
333
334                 char c = word[0];
335                 if (c == '\n') {
336                         sout += line + '\n';
337                         word.erase(0,1);
338                         line_plus_word.erase();
339                         line.erase();
340                 }
341
342                 if (!line_plus_word.empty()) line_plus_word += ' ';
343                 line_plus_word += word;
344
345                 int const length = fl_get_string_width(style, size,
346                                                        line_plus_word.c_str(),
347                                                        int(line_plus_word.length()));
348                 if (length >= w) {
349                         sout += line + '\n';
350                         line_plus_word = word;
351                 }
352
353                 line = line_plus_word;
354         }
355         // Flush remaining contents of line
356         if (!line.empty()) {
357                 sout += line;
358         }
359
360         if (sout[sout.length() - 1] == '\n')
361                 sout.erase(sout.length() - 1);
362
363         return sout;
364 }
365
366
367 namespace {
368
369 // sorted by hand to prevent LyXLex from complaining on read().
370 keyword_item xformTags[] = {
371         { "\\gui_background",   FL_COL1 },
372         { "\\gui_buttonbottom", FL_BOTTOM_BCOL },
373         { "\\gui_buttonleft",   FL_LEFT_BCOL },
374         { "\\gui_buttonright",  FL_RIGHT_BCOL },
375         { "\\gui_buttontop",    FL_TOP_BCOL },
376         { "\\gui_inactive",     FL_INACTIVE },
377         { "\\gui_pointer",      FL_FREE_COL16 },
378         { "\\gui_push_button",  FL_YELLOW },
379         { "\\gui_selected",     FL_MCOL },      
380         { "\\gui_text",         FL_BLACK }
381 };
382
383
384 const int xformCount = sizeof(xformTags) / sizeof(keyword_item);
385
386 } // namespace anon
387
388
389 bool XformsColor::read(string const & filename)
390 {
391         LyXLex lexrc(xformTags, xformCount);
392         if (!lexrc.setFile(filename))
393                 return false;
394
395         while (lexrc.isOK()) {
396                 int const le = lexrc.lex();
397
398                 switch (le) {
399                 case LyXLex::LEX_UNDEF:
400                         lexrc.printError("Unknown tag `$$Token'");
401                         continue; 
402                 case LyXLex::LEX_FEOF:
403                         continue;
404                 default: break;
405                 }
406
407                 string const tag = lexrc.getString();
408
409                 RGBColor col;
410
411                 if (!lexrc.next()) break;
412                 col.r = lexrc.getInteger();
413
414                 if (!lexrc.next()) break;
415                 col.g = lexrc.getInteger();
416
417                 if (!lexrc.next()) break;
418                 col.b = lexrc.getInteger();
419
420                 fl_mapcolor(le, col.r, col.g, col.b);
421
422                 if (tag == "\\gui_pointer") {
423                         fl_set_cursor_color(FL_DEFAULT_CURSOR,
424                                             FL_FREE_COL16, FL_WHITE);
425                         fl_set_cursor_color(XC_question_arrow,
426                                             FL_FREE_COL16, FL_WHITE);
427                 }
428         }
429                 
430         return true;
431 }
432
433
434 bool XformsColor::write(string const & filename)
435 {
436         ofstream os(filename.c_str());
437         if (!os)
438                 return false;
439
440         os << "### This file is part of\n"
441            << "### ========================================================\n"
442            << "###          LyX, The Document Processor\n"
443            << "###\n"
444            << "###          Copyright 1995 Matthias Ettrich\n"
445            << "###          Copyright 1995-2002 The LyX Team.\n"
446            << "###\n"
447            << "### ========================================================\n"
448            << "\n"
449            << "# This file is written by LyX, if you want to make your own\n"
450            << "# modifications you should do them from inside LyX and save\n"
451            << "\n";
452
453         for (int i = 0; i < xformCount; ++i) {
454                 string const tag  = xformTags[i].tag;
455                 int const colorID = xformTags[i].code;
456                 RGBColor color;
457
458                 fl_getmcolor(colorID, &color.r, &color.g, &color.b);
459
460                 os << tag << " "
461                    << color.r << " " << color.g << " " << color.b << "\n";
462         }
463
464         return true;
465 }
466
467
468 string  RWInfo::error_message;
469
470 bool RWInfo::WriteableDir(string const & name)
471 {
472         error_message.erase();
473
474         if (!AbsolutePath(name)) {
475                 error_message = N_("The absolute path is required.");
476                 return false;
477         }
478
479         FileInfo const tp(name);
480         if (!tp.isOK() || !tp.isDir()) {
481                 error_message = N_("Directory does not exist.");
482                 return false;
483         }
484
485         if (!tp.writable()) {
486                 error_message = N_("Cannot write to this directory.");
487                 return false;
488         }
489
490         return true;
491 }
492
493
494 bool RWInfo::ReadableDir(string const & name)
495 {
496         error_message.erase();
497
498         if (!AbsolutePath(name)) {
499                 error_message = N_("The absolute path is required.");
500                 return false;
501         }
502
503         FileInfo const tp(name);
504         if (!tp.isOK() || !tp.isDir()) {
505                 error_message = N_("Directory does not exist.");
506                 return false;
507         }
508
509         if (!tp.readable()) {
510                 error_message = N_("Cannot read this directory.");
511                 return false;
512         }
513
514         return true;
515 }
516
517
518 bool RWInfo::WriteableFile(string const & name)
519 {
520         // A writeable file is either:
521         // * An existing file to which we have write access, or
522         // * A file that doesn't yet exist but that would exist in a writeable
523         //   directory.
524
525         error_message.erase();
526
527         if (name.empty()) {
528                 error_message = N_("No file input.");
529                 return false;
530         }
531
532         string const dir = OnlyPath(name);
533         if (!AbsolutePath(dir)) {
534                 error_message = N_("The absolute path is required.");
535                 return false;
536         }
537
538         FileInfo d(name);
539
540         if (!d.isOK() || !d.isDir()) {
541                 d.newFile(dir);
542         }
543
544         if (!d.isOK() || !d.isDir()) {
545                 error_message = N_("Directory does not exist.");
546                 return false;
547         }
548         
549         if (!d.writable()) {
550                 error_message = N_("Cannot write to this directory.");
551                 return false;
552         }
553
554         FileInfo f(name);
555         if (dir == name || (f.isOK() && f.isDir())) {
556                 error_message = N_("A file is required, not a directory.");
557                 return false;
558         }
559
560         if (f.isOK() && f.exist() && !f.writable()) {
561                 error_message = N_("Cannot write to this file.");
562                 return false;
563         }
564         
565         return true;
566 }
567
568
569 bool RWInfo::ReadableFile(string const & name)
570 {
571         error_message.erase();
572
573         if (name.empty()) {
574                 error_message = N_("No file input.");
575                 return false;
576         }
577
578         string const dir = OnlyPath(name);
579         if (!AbsolutePath(dir)) {
580                 error_message = N_("The absolute path is required.");
581                 return false;
582         }
583
584         FileInfo d(name);
585
586         if (!d.isOK() && !d.isDir()) {
587                 d.newFile(dir);
588         }
589
590         if (!d.isOK() || !d.isDir()) {
591                 error_message = N_("Directory does not exist.");
592                 return false;
593         }
594         
595         if (!d.readable()) {
596                 error_message = N_("Cannot read from this directory.");
597                 return false;
598         }
599
600         FileInfo f(name);
601         if (dir == name || (f.isOK() && f.isDir())) {
602                 error_message = N_("A file is required, not a directory.");
603                 return false;
604         }
605
606         if (!f.exist()) {
607                 error_message = N_("File does not exist.");
608                 return false;
609         }
610         
611         if (!f.readable()) {
612                 error_message = N_("Cannot read from this file.");
613                 return false;
614         }
615
616         return true;
617 }