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