2 * \file xforms_helpers.C
3 * Copyright 2000-2002 The LyX Team.
4 * See the file COPYING.
6 * \author Angus Leeming, a.leeming@ic.ac.uk
11 #include FORMS_H_LOCATION
13 #include <fstream> // ofstream
17 #pragma implementation
20 #include "xforms_helpers.h"
23 #include "lyxlength.h"
24 #include "lyxgluelength.h"
25 #include "support/LAssert.h"
26 #include "support/FileInfo.h"
27 #include "support/filetools.h"
28 #include "support/lstrings.h" // frontStrip, strip
34 // Extract shortcut from <ident>|<shortcut> string
35 char const * flyx_shortcut_extract(char const * sc)
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;
49 // Extract identifier from <ident>|<shortcut> string
50 char const * flyx_ident_extract(char const * sc)
52 register char const * se = sc;
53 while (se[0]!= 0 && se[0] != '|') ++se;
55 if (se[0] == 0) return sc;
57 char * sb = new char[se - sc + 1];
59 register char const * sd = sc;
69 // Set an FL_OBJECT to activated or deactivated
70 void setEnabled(FL_OBJECT * ob, bool enable)
73 fl_activate_object(ob);
74 fl_set_object_lcol(ob, FL_BLACK);
76 fl_deactivate_object(ob);
77 fl_set_object_lcol(ob, FL_INACTIVE);
82 // Given an fl_choice, create a vector of its entries
83 vector<string> const getVectorFromChoice(FL_OBJECT * ob)
86 if (!ob || ob->objclass != FL_CHOICE)
89 for(int i = 0; i < fl_get_choice_maxitems(ob); ++i) {
90 string const text = fl_get_choice_item_text(ob, i+1);
91 vec.push_back(strip(frontStrip(text)));
98 /// Given an fl_input, return its contents.
99 string const getStringFromInput(FL_OBJECT * ob)
101 if (!ob || ob->objclass != FL_INPUT)
104 char const * tmp = fl_get_input(ob);
105 return (tmp) ? tmp : string();
109 // Given an fl_browser, return the contents of line
110 string const getStringFromBrowser(FL_OBJECT * ob, int line)
112 if (!ob || ob->objclass != FL_BROWSER ||
113 line < 1 || line > fl_get_browser_maxline(ob))
116 char const * tmp = fl_get_browser_line(ob, line);
117 return (tmp) ? tmp : string();
120 // Given an fl_browser, return the contents of the currently
122 // If nothing is selected, return an empty string
123 string const getSelectedStringFromBrowser(FL_OBJECT * ob)
125 if (!ob || ob->objclass != FL_BROWSER)
128 int const line = fl_get_browser(ob);
129 if (line < 1 || line > fl_get_browser_maxline(ob))
132 if (!fl_isselected_browser_line(ob, line))
135 char const * tmp = fl_get_browser_line(ob, line);
136 return (tmp) ? tmp : string();
140 // Given an fl_browser, create a vector of its entries
141 vector<string> const getVectorFromBrowser(FL_OBJECT * ob)
144 if (!ob || ob->objclass != FL_BROWSER)
147 for(int i = 0; i < fl_get_browser_maxline(ob); ++i) {
148 string const text = fl_get_browser_line(ob, i+1);
149 vec.push_back(strip(frontStrip(text)));
156 string getLengthFromWidgets(FL_OBJECT * input, FL_OBJECT * choice)
159 lyx::Assert(input && input->objclass == FL_INPUT &&
160 choice && choice->objclass == FL_CHOICE);
162 string const length = strip(frontStrip(fl_get_input(input)));
166 //don't return unit-from-choice if the input(field) contains a unit
167 if (isValidGlueLength(length))
170 string unit = strip(frontStrip(fl_get_choice_text(choice)));
171 unit = subst(unit, "%%", "%");
173 return length + unit;
178 // old code which can be deleted if the new one, now enabled,
179 // works satisfyingly (JSpitzm, 11/02/2002)
180 // this should definitely be the other way around!!!
181 void updateWidgetsFromLength(FL_OBJECT * input, FL_OBJECT * choice,
182 LyXLength const & len,
183 string const & default_unit)
186 updateWidgetsFromLengthString(input, choice,
187 string(), default_unit);
188 // use input field only for gluelengths
189 else if (!isValidLength(len) && !isStrDbl(len)) {
190 fl_set_input(input, len.c_str());
191 fl_set_choice_text(choice, default_unit.c_str());
194 updateWidgetsFromLengthString(input, choice,
195 len.asString(), default_unit);
200 // Most of the code here is a poor duplication of the parser code
201 // which is in LyXLength. Use that instead
202 void updateWidgetsFromLengthString(FL_OBJECT * input, FL_OBJECT * choice,
204 string const & default_unit)
207 lyx::Assert(input && input->objclass == FL_INPUT &&
208 choice && choice->objclass == FL_CHOICE);
211 fl_set_input(input, "");
212 int unitpos = 1; // xforms has Fortran-style indexing
213 for(int i = 0; i < fl_get_choice_maxitems(choice); ++i) {
214 string const text = fl_get_choice_item_text(choice,i+1);
216 lowercase(strip(frontStrip(text)))) {
221 fl_set_choice(choice, unitpos);
225 // The unit is presumed to begin at the first char a-z
226 // or with the char '%'
227 string const tmp = lowercase(strip(frontStrip(str)));
229 string::const_iterator p = tmp.begin();
230 for (; p != tmp.end(); ++p) {
231 if ((*p >= 'a' && *p <= 'z') || *p == '%')
236 int unitpos = 1; // xforms has Fortran-style indexing
238 if (p == tmp.end()) {
243 string tmplen = string(tmp.begin(), p);
244 if (isStrDbl(tmplen))
246 string unit = string(p, tmp.end());
247 unit = subst(unit, "%", "%%");
249 for(int i = 0; i < fl_get_choice_maxitems(choice); ++i) {
250 string const text = fl_get_choice_item_text(choice,i+1);
251 if (unit == lowercase(strip(frontStrip(text)))) {
258 fl_set_input(input, len.c_str());
259 fl_set_choice(choice, unitpos);
262 void updateWidgetsFromLengthString(FL_OBJECT * input, FL_OBJECT * choice,
264 string const & default_unit)
266 // use input field only for gluelengths
267 if (!isValidLength(str) && !isStrDbl(str)) {
268 fl_set_input(input, str.c_str());
269 fl_set_choice_text(choice, default_unit.c_str());
271 updateWidgetsFromLength(input, choice,
272 LyXLength(str), default_unit);
277 void updateWidgetsFromLength(FL_OBJECT * input, FL_OBJECT * choice,
278 LyXLength const & len,
279 string const & default_unit)
282 lyx::Assert(input && input->objclass == FL_INPUT &&
283 choice && choice->objclass == FL_CHOICE);
286 fl_set_input(input, "");
287 fl_set_choice_text(choice, default_unit.c_str());
289 ostringstream buffer;
290 buffer << len.value();
291 fl_set_input(input, buffer.str().c_str());
292 fl_set_choice_text(choice,
293 subst(stringFromUnit(len.unit()),"%","%%").c_str());
299 // Take a string and add breaks so that it fits into a desired label width, w
300 string formatted(string const & sin, int w, int size, int style)
303 if (sin.empty()) return sout;
306 // FIX: Q: Why cant this be done by a one pass algo? (Lgb)
308 // breaks in up into a vector of individual words
309 vector<string> sentence;
311 for (string::const_iterator sit = sin.begin();
312 sit != sin.end(); ++sit) {
313 if ((*sit) == ' ' || (*sit) == '\n') {
315 sentence.push_back(word);
318 if ((*sit) == '\n') word += '\n';
325 // Flush remaining contents of word
326 if (!word.empty()) sentence.push_back(word);
329 string line_plus_word;
330 for (vector<string>::const_iterator vit = sentence.begin();
331 vit != sentence.end(); ++vit) {
338 line_plus_word.erase();
342 if (!line_plus_word.empty()) line_plus_word += ' ';
343 line_plus_word += word;
345 int const length = fl_get_string_width(style, size,
346 line_plus_word.c_str(),
347 int(line_plus_word.length()));
350 line_plus_word = word;
353 line = line_plus_word;
355 // Flush remaining contents of line
360 if (sout[sout.length() - 1] == '\n')
361 sout.erase(sout.length() - 1);
364 string::size_type curpos = 0;
367 string::size_type const nxtpos1 = sin.find(' ', curpos);
368 string::size_type const nxtpos2 = sin.find('\n', curpos);
369 string::size_type const nxtpos = std::min(nxtpos1, nxtpos1);
371 string const word = nxtpos == string::npos ?
372 sin.substr(curpos) : sin.substr(curpos, nxtpos-curpos);
374 bool const newline = (nxtpos2 != string::npos &&
377 string const line_plus_word =
378 line.empty() ? word : line + ' ' + word;
381 fl_get_string_width(style, size,
382 line_plus_word.c_str(),
383 int(line_plus_word.length()));
394 } else if (newline) {
395 sout += line_plus_word + '\n';
404 if (nxtpos == string::npos) {
418 void setCursorColor(int color)
420 fl_set_cursor_color(FL_DEFAULT_CURSOR, color, FL_WHITE);
421 fl_set_cursor_color(XC_question_arrow, color, FL_WHITE);
422 fl_set_cursor_color(XC_xterm, color, FL_WHITE);
423 fl_set_cursor_color(XC_watch, color, FL_WHITE);
424 fl_set_cursor_color(XC_sb_right_arrow, color, FL_WHITE);
430 // sorted by hand to prevent LyXLex from complaining on read().
431 keyword_item xformTags[] = {
432 { "\\gui_background", FL_COL1 },
433 { "\\gui_buttonbottom", FL_BOTTOM_BCOL },
434 { "\\gui_buttonleft", FL_LEFT_BCOL },
435 { "\\gui_buttonright", FL_RIGHT_BCOL },
436 { "\\gui_buttontop", FL_TOP_BCOL },
437 { "\\gui_inactive", FL_INACTIVE },
438 { "\\gui_pointer", FL_FREE_COL16 },
439 { "\\gui_push_button", FL_YELLOW },
440 { "\\gui_selected", FL_MCOL },
441 { "\\gui_text", FL_BLACK }
445 const int xformCount = sizeof(xformTags) / sizeof(keyword_item);
450 bool XformsColor::read(string const & filename)
452 LyXLex lexrc(xformTags, xformCount);
453 if (!lexrc.setFile(filename))
456 while (lexrc.isOK()) {
457 int const le = lexrc.lex();
460 case LyXLex::LEX_UNDEF:
461 lexrc.printError("Unknown tag `$$Token'");
463 case LyXLex::LEX_FEOF:
468 string const tag = lexrc.getString();
472 if (!lexrc.next()) break;
473 col.r = lexrc.getInteger();
475 if (!lexrc.next()) break;
476 col.g = lexrc.getInteger();
478 if (!lexrc.next()) break;
479 col.b = lexrc.getInteger();
481 fl_mapcolor(le, col.r, col.g, col.b);
483 if (tag == "\\gui_pointer") {
484 setCursorColor(FL_FREE_COL16);
492 bool XformsColor::write(string const & filename)
494 ofstream os(filename.c_str());
498 os << "### This file is part of\n"
499 << "### ========================================================\n"
500 << "### LyX, The Document Processor\n"
502 << "### Copyright 1995 Matthias Ettrich\n"
503 << "### Copyright 1995-2002 The LyX Team.\n"
505 << "### ========================================================\n"
507 << "# This file is written by LyX, if you want to make your own\n"
508 << "# modifications you should do them from inside LyX and save\n"
511 for (int i = 0; i < xformCount; ++i) {
512 string const tag = xformTags[i].tag;
513 int const colorID = xformTags[i].code;
516 fl_getmcolor(colorID, &color.r, &color.g, &color.b);
519 << color.r << " " << color.g << " " << color.b << "\n";
526 string RWInfo::error_message;
528 bool RWInfo::WriteableDir(string const & name)
530 error_message.erase();
532 if (!AbsolutePath(name)) {
533 error_message = N_("The absolute path is required.");
537 FileInfo const tp(name);
538 if (!tp.isOK() || !tp.isDir()) {
539 error_message = N_("Directory does not exist.");
543 if (!tp.writable()) {
544 error_message = N_("Cannot write to this directory.");
552 bool RWInfo::ReadableDir(string const & name)
554 error_message.erase();
556 if (!AbsolutePath(name)) {
557 error_message = N_("The absolute path is required.");
561 FileInfo const tp(name);
562 if (!tp.isOK() || !tp.isDir()) {
563 error_message = N_("Directory does not exist.");
567 if (!tp.readable()) {
568 error_message = N_("Cannot read this directory.");
576 bool RWInfo::WriteableFile(string const & name)
578 // A writeable file is either:
579 // * An existing file to which we have write access, or
580 // * A file that doesn't yet exist but that would exist in a writeable
583 error_message.erase();
586 error_message = N_("No file input.");
590 string const dir = OnlyPath(name);
591 if (!AbsolutePath(dir)) {
592 error_message = N_("The absolute path is required.");
598 if (!d.isOK() || !d.isDir()) {
602 if (!d.isOK() || !d.isDir()) {
603 error_message = N_("Directory does not exist.");
608 error_message = N_("Cannot write to this directory.");
613 if (dir == name || (f.isOK() && f.isDir())) {
614 error_message = N_("A file is required, not a directory.");
618 if (f.isOK() && f.exist() && !f.writable()) {
619 error_message = N_("Cannot write to this file.");
627 bool RWInfo::ReadableFile(string const & name)
629 error_message.erase();
632 error_message = N_("No file input.");
636 string const dir = OnlyPath(name);
637 if (!AbsolutePath(dir)) {
638 error_message = N_("The absolute path is required.");
644 if (!d.isOK() && !d.isDir()) {
648 if (!d.isOK() || !d.isDir()) {
649 error_message = N_("Directory does not exist.");
654 error_message = N_("Cannot read from this directory.");
659 if (dir == name || (f.isOK() && f.isDir())) {
660 error_message = N_("A file is required, not a directory.");
665 error_message = N_("File does not exist.");
670 error_message = N_("Cannot read from this file.");