source: tags/arb_5.0/AWT/AWT_input_mask.cxx

Last change on this file was 6143, checked in by westram, 15 years ago
  • backport [6141] (parts changing code, but only strings and comments)
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 90.7 KB
Line 
1//  ==================================================================== //
2//                                                                       //
3//    File      : AWT_input_mask.cxx                                     //
4//    Purpose   : General input masks                                    //
5//                                                                       //
6//                                                                       //
7//  Coded by Ralf Westram (coder@reallysoft.de) in August 2001           //
8//  Copyright Department of Microbiology (Technical University Munich)   //
9//                                                                       //
10//  Visit our web site at: http://www.arb-home.de/                       //
11//                                                                       //
12//                                                                       //
13//  ==================================================================== //
14
15#include "awt_input_mask_internal.hxx"
16
17#include <arbdbt.h>
18#include <awt_www.hxx>
19
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <dirent.h>
23#include <climits>
24
25using namespace std;
26
27const char *awt_itemtype_names[AWT_IT_TYPES+1] =
28{
29 "Unknown",
30 "Species", "Organism", "Gene", "Experiment",
31 "<overflow>"
32};
33
34#define SEC_XBORDER    3        // space left/right of NEW_SECTION line
35#define SEC_YBORDER    4        // space above/below
36#define SEC_LINE_WIDTH 1        // line width
37
38#define MIN_TEXTFIELD_SIZE 1
39#define MAX_TEXTFIELD_SIZE 1000
40
41#define AWT_SCOPE_LOCAL  0
42#define AWT_SCOPE_GLOBAL 1
43
44awt_input_mask_id_list awt_input_mask_global::global_ids; // stores global ids
45
46// ---------------------
47//      global awars
48// ---------------------
49
50#define AWAR_INPUT_MASK_BASE   "tmp/inputMask"
51#define AWAR_INPUT_MASK_NAME   AWAR_INPUT_MASK_BASE"/name"
52#define AWAR_INPUT_MASK_ITEM   AWAR_INPUT_MASK_BASE"/item"
53#define AWAR_INPUT_MASK_SCOPE  AWAR_INPUT_MASK_BASE"/scope"
54#define AWAR_INPUT_MASK_HIDDEN AWAR_INPUT_MASK_BASE"/hidden"
55
56#define AWAR_INPUT_MASKS_EDIT_ENABLED AWAR_INPUT_MASK_BASE"/edit_enabled"
57
58static bool global_awars_created = false;
59
60static void create_global_awars(AW_root *awr) {
61    awt_assert(!global_awars_created);
62    awr->awar_string(AWAR_INPUT_MASK_NAME, "new");
63    awr->awar_string(AWAR_INPUT_MASK_ITEM, awt_itemtype_names[AWT_IT_SPECIES]);
64    awr->awar_int(AWAR_INPUT_MASK_SCOPE, 0);
65    awr->awar_int(AWAR_INPUT_MASK_HIDDEN, 0);
66    awr->awar_int(AWAR_INPUT_MASKS_EDIT_ENABLED, 0);
67    global_awars_created = true;
68}
69
70// ------------------------------------------
71//      Callbacks from database and awars
72// ------------------------------------------
73
74static bool in_item_changed_callback  = false;
75static bool in_field_changed_callback = false;
76static bool in_awar_changed_callback  = false;
77
78static void item_changed_cb(GBDATA */*gb_item*/, int *cl_awt_linked_to_item, GB_CB_TYPE type) {
79    if (!in_item_changed_callback) { // avoid deadlock
80        in_item_changed_callback      = true;
81        awt_linked_to_item *item_link = (awt_linked_to_item*)cl_awt_linked_to_item;
82
83        if (type&GB_CB_DELETE) { // handled child was deleted
84            item_link->relink();
85        }
86        else if ((type&(GB_CB_CHANGED|GB_CB_SON_CREATED)) == (GB_CB_CHANGED|GB_CB_SON_CREATED)) {
87            // child was created (not only changed)
88            item_link->relink();
89        }
90        else if (type&GB_CB_CHANGED) { // only changed
91            item_link->general_item_change();
92        }
93
94        in_item_changed_callback = false;
95    }
96}
97
98static void field_changed_cb(GBDATA */*gb_item*/, int *cl_awt_input_handler, GB_CB_TYPE type) {
99    if (!in_field_changed_callback) { // avoid deadlock
100        in_field_changed_callback  = true;
101        awt_input_handler *handler = (awt_input_handler*)cl_awt_input_handler;
102
103        if (type&GB_CB_DELETE) { // field was deleted from db -> relink this item
104            handler->relink();
105        }
106        else if (type&GB_CB_CHANGED) {
107            handler->db_changed();  // database entry was changed
108        }
109        in_field_changed_callback = false;
110    }
111}
112
113static void awar_changed_cb(AW_root */*awr*/, AW_CL cl_awt_mask_awar_item) {
114    if (!in_awar_changed_callback) { // avoid deadlock
115        in_awar_changed_callback   = true;
116        awt_mask_awar_item *item = (awt_mask_awar_item*)cl_awt_mask_awar_item;
117        awt_assert(item);
118        if (item) item->awar_changed();
119        in_awar_changed_callback   = false;
120    }
121}
122
123string awt_input_mask_global::generate_id(const string& mask_name_)
124{
125    string result;
126    result.reserve(mask_name_.length());
127    for (string::const_iterator p = mask_name_.begin(); p != mask_name_.end(); ++p) {
128        if (isalnum(*p)) result.append(1, *p);
129        else result.append(1, '_');
130    }
131    return result;
132}
133
134bool awt_input_mask_global::edit_allowed() const {
135    return !test_edit_enabled ||
136        get_root()->awar(AWAR_INPUT_MASKS_EDIT_ENABLED)->read_int() == 1;
137}
138
139void awt_input_mask_global::no_item_selected() const {
140    aw_message(GBS_global_string("This had no effect, because no %s is selected",
141                                 awt_itemtype_names[get_itemtype()]));
142}
143
144GB_ERROR awt_input_mask_id_list::add(const string& name, awt_mask_item *item) {
145    awt_mask_item *existing = lookup(name);
146    if (existing) return GB_export_errorf("ID '%s' already exists", name.c_str());
147
148    id[name] = item;
149    return 0;
150}
151GB_ERROR awt_input_mask_id_list::remove(const string& name) {
152    if (!lookup(name)) return GB_export_errorf("ID '%s' does not exist", name.c_str());
153    id.erase(name);
154    return 0;
155}
156
157awt_mask_item::awt_mask_item(awt_input_mask_global *global_)
158    : global(global_)
159{
160}
161
162awt_mask_item::~awt_mask_item() {
163    awt_assert(!has_name()); // you forgot to call remove_name()
164}
165
166GB_ERROR awt_mask_item::set_name(const string& name_, bool is_global)
167{
168    GB_ERROR error = 0;
169    if (has_name()) {
170        error = GB_export_errorf("Element already has name (%s)", get_name().c_str());
171    }
172    else {
173        name = new string(name_);
174        if (is_global) {
175            if (!mask_global()->has_global_id(*name)) { // do not add if variable already defined elsewhere
176                error = mask_global()->add_global_id(*name, this);
177            }
178        }
179        else {
180            error = mask_global()->add_local_id(*name, this);
181        }
182    }
183    return error;
184}
185
186GB_ERROR awt_mask_item::remove_name()
187{
188    GB_ERROR error = 0;
189    if (has_name()) {
190        error = mask_global()->remove_id(*name);
191        name.SetNull();
192    }
193    return error;
194}
195
196awt_mask_awar_item::awt_mask_awar_item(awt_input_mask_global *global_, const string& awar_base, const string& default_value, bool saved_with_properties)
197    : awt_mask_item(global_)
198{
199    const char *root_name;
200
201    if (saved_with_properties) root_name = "/input_masks";
202    else root_name = "/tmp/input_masks"; // awars starting with /tmp are not saved
203
204    awarName = GBS_global_string("%s/%s", root_name, awar_base.c_str());
205#if defined(DEBUG)
206    printf("awarName='%s'\n", awarName.c_str());
207#endif // DEBUG
208    mask_global()->get_root()->awar_string(awarName.c_str(), default_value.c_str()); // create the awar
209    add_awar_callbacks();
210}
211
212void awt_mask_awar_item::add_awar_callbacks() {
213    AW_awar *var = awar();
214    awt_assert(var);
215    if (var) var->add_callback(awar_changed_cb, AW_CL(this));
216}
217void awt_mask_awar_item::remove_awar_callbacks() {
218    AW_awar *var = awar();
219    awt_assert(var);
220    if (var) var->remove_callback((AW_RCB)awar_changed_cb, AW_CL(this), AW_CL(0));
221}
222
223awt_variable::awt_variable(awt_input_mask_global *global_, const string& id, bool is_global_, const string& default_value, GB_ERROR& error)
224    : awt_mask_awar_item(global_, generate_baseName(global_, id, is_global_), default_value, true)
225    , is_global(is_global_)
226{
227    error = set_name(id, is_global);
228}
229
230awt_variable::~awt_variable()
231{
232}
233
234string awt_script::get_value() const
235{
236    string                        result;
237    AW_root                      *root     = mask_global()->get_root();
238    const awt_item_type_selector *selector = mask_global()->get_selector();
239    GBDATA                       *gbd      = selector->current(root);
240
241    if (gbd) {
242        char           *species_name    = root->awar(selector->get_self_awar())->read_string();
243        GBDATA         *gb_main = mask_global()->get_gb_main();
244        GB_transaction  tscope(gb_main);
245
246        char *val = GB_command_interpreter(gb_main, species_name, script.c_str(), gbd, 0);
247        if (!val) {
248            aw_message(GB_await_error());
249            result = "<error>";
250        }
251        else {
252            result = val;
253            free(val);
254        }
255        free(species_name);
256    }
257    else {
258        result = "<undefined>";
259    }
260
261
262    return result;
263}
264
265GB_ERROR awt_script::set_value(const string& /*new_value*/)
266{
267    return GBS_global_string("You cannot assign a value to script '%s'", has_name() ? get_name().c_str() : "<unnamed>");
268}
269
270GB_ERROR awt_linked_to_item::add_db_callbacks()
271{
272    GB_ERROR error = 0;
273    if (gb_item) error = GB_add_callback(gb_item, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), item_changed_cb, (int*)this);
274    return error;
275}
276
277void awt_linked_to_item::remove_db_callbacks() {
278    GB_remove_callback(gb_item, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), item_changed_cb, (int*)this);
279}
280
281awt_script_viewport::awt_script_viewport(awt_input_mask_global *global_, const awt_script *script_, const string& label_, long field_width_)
282    : awt_viewport(global_, generate_baseName(global_), "", false, label_)
283    , script(script_)
284    , field_width(field_width_)
285{
286}
287
288awt_script_viewport::~awt_script_viewport()
289{
290    unlink();
291}
292
293GB_ERROR awt_script_viewport::link_to(GBDATA *gb_new_item)
294{
295    GB_ERROR       error = 0;
296    GB_transaction dummy(mask_global()->get_gb_main());
297
298    remove_awar_callbacks();    // unbind awar callbacks temporarily
299
300    if (item()) {
301        remove_db_callbacks(); // ignore result (if handled db-entry was deleted, it returns an error)
302        set_item(0);
303    }
304
305    if (gb_new_item) {
306        set_item(gb_new_item);
307        db_changed();
308        error = add_db_callbacks();
309    }
310
311    add_awar_callbacks();       // rebind awar callbacks
312
313    return error;
314}
315
316void awt_script_viewport::build_widget(AW_window *aws)
317{
318    const string& lab = get_label();
319    if (lab.length()) aws->label(lab.c_str());
320
321    aws->create_input_field(awar_name().c_str(), field_width);
322}
323
324void awt_script_viewport::db_changed()
325{
326    GB_ERROR error = 0;
327    awt_assert(script);
328    string current_value = script->get_value();
329    error                = awt_mask_awar_item::set_value(current_value);
330
331    if (error) aw_message(error);
332}
333
334void awt_script_viewport::awar_changed() {
335    aw_message("It makes no sense to change the result of a script");
336}
337
338GB_ERROR awt_input_handler::add_db_callbacks() {
339    GB_ERROR error = awt_linked_to_item::add_db_callbacks();
340    if (item() && gbd) error = GB_add_callback(gbd, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), field_changed_cb, (int*)this);
341    return error;
342}
343void awt_input_handler::remove_db_callbacks() {
344    awt_linked_to_item::remove_db_callbacks();
345    if (item() && gbd) GB_remove_callback(gbd, (GB_CB_TYPE)(GB_CB_CHANGED|GB_CB_DELETE), field_changed_cb, (int*)this);
346}
347
348awt_input_handler::awt_input_handler(awt_input_mask_global *global_, const string& child_path_, GB_TYPES type_, const string& label_)
349    : awt_viewport(global_, generate_baseName(global_, child_path_), "", false, label_)
350    , gbd(0)
351    , child_path(child_path_)
352    , db_type(type_)
353    , in_destructor(false)
354{
355}
356
357awt_input_handler::~awt_input_handler() {
358    in_destructor = true;
359    unlink();
360}
361
362GB_ERROR awt_input_handler::link_to(GBDATA *gb_new_item) {
363    GB_ERROR       error = 0;
364    GB_transaction dummy(mask_global()->get_gb_main());
365
366    remove_awar_callbacks(); // unbind awar callbacks temporarily
367
368    if (item()) {
369        remove_db_callbacks();  // ignore result (if handled db-entry was deleted, it returns an error)
370        set_item(0);
371        gbd = 0;
372    }
373    else {
374        awt_assert(!gbd);
375    }
376
377    if (!gb_new_item && !in_destructor) { // crashes if we are in ~awt_input_handler
378        db_changed();
379    }
380
381    if (!error && gb_new_item) {
382        set_item(gb_new_item);
383        gbd = GB_search(item(), child_path.c_str(), GB_FIND);
384
385        db_changed();           // synchronize AWAR with DB-entry
386
387        error = add_db_callbacks();
388    }
389
390    add_awar_callbacks(); // rebind awar callbacks
391
392    return error;
393}
394
395void awt_string_handler::awar_changed() {
396    GBDATA   *gbdata    = data();
397    GBDATA   *gb_main   = mask_global()->get_gb_main();
398    bool      relink_me = false;
399    GB_ERROR  error     = 0;
400
401    GB_push_transaction(gb_main);
402
403    if (!mask_global()->edit_allowed()) error = "Editing is disabled. Check the 'Enable edit' switch!";
404
405    if (!error && !gbdata) {
406        const char *child   = get_child_path().c_str();
407        const char *keypath = mask_global()->get_selector()->getKeyPath();
408
409        if (item()) {
410            gbdata = GB_search(item(), child, GB_FIND);
411
412            if (!gbdata) {
413                GB_TYPES found_typ = GBT_get_type_of_changekey(gb_main, child, keypath);
414                if (found_typ != GB_NONE) set_type(found_typ); // fix type if different
415
416                gbdata = GB_search(item(), child, type()); // here new items are created
417                if (found_typ == GB_NONE) GBT_add_new_changekey_to_keypath(gb_main, child, type(), keypath);
418                relink_me = true; //  @@@ only if child was created!!
419            }
420        }
421        else {
422            mask_global()->no_item_selected();
423            aw_message(GBS_global_string("This had no effect, because no %s is selected",
424                                         awt_itemtype_names[mask_global()->get_itemtype()]));
425        }
426    }
427
428    if (!error && gbdata) {
429        char     *content   = awar()->read_string();
430        GB_TYPES  found_typ = GB_read_type(gbdata);
431        if (found_typ != type()) set_type(found_typ); // fix type if different
432
433        error = GB_write_as_string(gbdata, awar2db(content).c_str());
434
435        free(content);
436    }
437
438    if (error) {
439        aw_message(error);
440        GB_abort_transaction(gb_main);
441        db_changed(); // write back old value
442    }
443    else {
444        GB_pop_transaction(gb_main);
445    }
446
447    if (relink_me) relink();
448}
449
450void awt_string_handler::db_changed() {
451    GBDATA *gbdata = data();
452    if (gbdata) { // gbdata may be zero, if field does not exist
453        GB_transaction  dummy(mask_global()->get_gb_main());
454        char           *content = GB_read_as_string(gbdata);
455        awar()->write_string(db2awar(content).c_str());
456        free(content);
457    }
458    else {
459        awar()->write_string(default_value.c_str());
460    }
461}
462
463// ----------------
464//      Widgets
465// ----------------
466
467void awt_input_field::build_widget(AW_window *aws) {
468    const string& lab = get_label();
469    if (lab.length()) aws->label(lab.c_str());
470
471    aws->create_input_field(awar_name().c_str(), field_width);
472}
473
474void awt_text_viewport::build_widget(AW_window *aws)
475{
476    const string& lab = get_label();
477    if (lab.length()) aws->label(lab.c_str());
478
479    aws->create_input_field(awar_name().c_str(), field_width);
480}
481
482void awt_check_box::build_widget(AW_window *aws) {
483    const string& lab = get_label();
484    if (lab.length()) aws->label(lab.c_str());
485
486    aws->create_toggle(awar_name().c_str());
487}
488void awt_radio_button::build_widget(AW_window *aws) {
489    const string& lab = get_label();
490    if (lab.length()) aws->label(lab.c_str());
491
492    aws->create_toggle_field(awar_name().c_str(), vertical ? 0 : 1);
493
494    vector<string>::const_iterator b   = buttons.begin();
495    vector<string>::const_iterator v   = values.begin();
496    int                            pos = 0;
497
498    for (; b != buttons.end() && v != values.end(); ++b, ++v, ++pos) {
499        void (AW_window::*ins_togg)(AW_label, const char*, const char*);
500
501        if (pos == default_position) ins_togg = &AW_window::insert_default_toggle;
502        else ins_togg                         = &AW_window::insert_toggle;
503
504        (aws->*ins_togg)(b->c_str(), mask_global()->hotkey(*b), b->c_str());
505    }
506
507    awt_assert(b == buttons.end() && v == values.end());
508
509    aws->update_toggle_field();
510}
511
512// -----------------------------------------
513//      Special AWAR <-> DB translations
514// -----------------------------------------
515
516string awt_check_box::awar2db(const string& awar_content) const {
517    GB_TYPES typ = type();
518
519    if (awar_content == "yes") {
520        if (typ == GB_STRING) return "yes";
521        return "1";
522    }
523    else {
524        if (typ == GB_STRING) return "no";
525        return "0";
526    }
527}
528string awt_check_box::db2awar(const string& db_content) const {
529    if (db_content == "yes" || db_content == "1") return "yes";
530    if (db_content == "no" || db_content == "0") return "no";
531    return atoi(db_content.c_str()) ? "yes" : "no";
532}
533
534string awt_radio_button::awar2db(const string& awar_content) const {
535    vector<string>::const_iterator b   = buttons.begin();
536    vector<string>::const_iterator v   = values.begin();
537    for (; b != buttons.end() && v != values.end(); ++b, ++v) {
538        if (*b == awar_content) {
539            return *v;
540        }
541    }
542
543    return string("Unknown awar_content '")+awar_content+"'";
544}
545
546string awt_radio_button::db2awar(const string& db_content) const {
547    vector<string>::const_iterator b   = buttons.begin();
548    vector<string>::const_iterator v   = values.begin();
549    for (; b != buttons.end() && v != values.end(); ++b, ++v) {
550        if (*v == db_content) {
551            return *b;
552        }
553    }
554    return buttons[default_position];
555}
556
557string awt_numeric_input_field::awar2db(const string& awar_content) const {
558    long val = strtol(awar_content.c_str(), 0, 10);
559
560    // correct numeric value :
561    if (val<min) val = min;
562    if (val>max) val = max;
563
564    return GBS_global_string("%li", val);
565}
566
567// -----------------------------------------
568//      Routines to parse user-mask file
569// -----------------------------------------
570
571static GB_ERROR readLine(FILE *in, string& line, size_t& lineNo) {
572    const int  BUFSIZE = 8000;
573    char       buffer[BUFSIZE];
574    char      *res     = fgets(&buffer[0], BUFSIZE-1, in);
575    GB_ERROR   error   = 0;
576
577    if (int err = ferror(in)) {
578        error = strerror(err);
579    }
580    else if (res == 0) {
581        error = "Unexpected end of file (@MASK_BEGIN or @MASK_END missing?)";
582    }
583    else {
584        awt_assert(res == buffer);
585        res += strlen(buffer);
586        if (res>buffer) {
587            size_t last = res-buffer;
588            if (buffer[last-1] == '\n') {
589                buffer[last-1] = 0;
590            }
591        }
592        line = buffer;
593        lineNo++; // increment lineNo
594
595        size_t last = line.find_last_not_of(" \t");
596        if (last != string::npos && line[last] == '\\') {
597            string next;
598            error = readLine(in, next, lineNo);
599            line  = line.substr(0, last)+' '+next;
600        }
601    }
602
603    if (error) line = "";
604
605    return error;
606}
607
608inline size_t next_non_white(const string& line, size_t start) {
609    if (start == string::npos) return string::npos;
610    return line.find_first_not_of(" \t", start);
611}
612
613static bool was_last_parameter = false;
614
615inline size_t eat_para_separator(const string& line, size_t start, GB_ERROR& error) {
616    size_t para_sep = next_non_white(line, start);
617
618    if (para_sep == string::npos) {
619        error = "',' or ')' expected after parameter";
620    }
621    else {
622        switch (line[para_sep]) {
623            case ')' :
624                was_last_parameter = true;
625                break;
626
627            case ',' :
628                break;
629
630            default :
631                error = "',' or ')' expected after parameter";
632                break;
633        }
634        if (!error) para_sep++;
635    }
636
637    return para_sep;
638}
639
640static void check_last_parameter(GB_ERROR& error, const string& command) {
641    if (!error && !was_last_parameter) {
642        error = GBS_global_string("Superfluous arguments to '%s'", command.c_str());
643    }
644}
645
646static void check_no_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, const string& command) {
647    size_t start = next_non_white(line, scan_pos);
648    scan_pos     = start;
649
650    if (start == string::npos) {
651        error = "')' expected";
652    }
653    else if (line[start] != ')') {
654        was_last_parameter = false;
655        check_last_parameter(error, command);
656    }
657    else { // ok
658        scan_pos++;
659    }
660}
661
662inline GB_ERROR decode_escapes(string& s) {
663    string::iterator f = s.begin();
664    string::iterator t = s.begin();
665
666    for (; f != s.end(); ++f, ++t) {
667        if (*f == '\\') {
668            ++f;
669            if (f == s.end()) return GBS_global_string("Trailing \\ in '%s'", s.c_str());
670        }
671        *t = *f;
672    }
673
674    s.erase(t, f);
675
676    return 0;
677}
678
679static string scan_string_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, bool allow_escaped = false) {
680    string result;
681    size_t open_quote = next_non_white(line, scan_pos);
682    scan_pos          = open_quote;
683
684    if (open_quote == string::npos || line[open_quote] != '\"') {
685        error = "string parameter expected";
686    }
687    else {
688        size_t close_quote = string::npos;
689
690        if (allow_escaped) {
691            size_t start = open_quote+1;
692
693            while (1) {
694                close_quote = line.find_first_of("\\\"", start);
695
696                if (close_quote == string::npos) break; // error
697                if (line[close_quote] == '\"') break; // found closing quote
698
699                if (line[close_quote] == '\\') { // escape next char
700                    close_quote++;
701                }
702                start = close_quote+1;
703            }
704        }
705        else {
706            close_quote = line.find('\"', open_quote+1);
707        }
708
709        if (close_quote == string::npos) {
710            error = "string parameter missing closing '\"'";
711        }
712        else {
713            result = line.substr(open_quote+1, close_quote-open_quote-1);
714            if (allow_escaped) {
715                awt_assert(!error);
716                error = decode_escapes(result);
717            }
718            if (!error) scan_pos = eat_para_separator(line, close_quote+1, error);
719        }
720    }
721
722    return result;
723}
724
725string list_keywords(const char **allowed_keywords) {
726    string result;
727    for (int i = 0; allowed_keywords[i]; ++i) {
728        if (i) {
729            if (allowed_keywords[i+1]) result += ", ";
730            else result                       += " or ";
731        }
732        result += allowed_keywords[i];
733    }
734    return result;
735}
736
737inline int isKeyword(const char *current, const char *keyword) {
738    int pos = 0;
739    for (; keyword[pos]; ++pos) {
740        if (!current[pos] || std::tolower(current[pos]) != std::tolower(keyword[pos])) {
741            return 0;
742        }
743    }
744    return pos;
745}
746
747static int scan_keyword_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, const char **allowed_keywords) {
748    // return index of keyword (or -1)
749    // allowed_keywords has to be 0-terminated
750    int    result = -1;
751    size_t start  = next_non_white(line, scan_pos);
752    scan_pos      = start;
753
754    awt_assert(!error);
755
756    if (start == string::npos) {
757EXPECTED :
758        string keywords = list_keywords(allowed_keywords);
759        error           = GBS_global_string("%s expected", keywords.c_str());
760    }
761    else {
762        const char *current = line.c_str()+start;
763
764        int i = 0;
765        for (; allowed_keywords[i]; ++i) {
766            int found_keyword_size = isKeyword(current, allowed_keywords[i]);
767            if (found_keyword_size) {
768                result    = i;
769                scan_pos += found_keyword_size;
770                break;
771            }
772        }
773        if (!allowed_keywords[i]) goto EXPECTED;
774        awt_assert(!error);
775        scan_pos = eat_para_separator(line, scan_pos, error);
776    }
777    return result;
778}
779
780static void scan_string_or_keyword_parameter(const string& line, size_t& scan_pos, GB_ERROR& error,
781                                             string& string_para, int& keyword_index, // result parameters
782                                             const char **allowed_keywords) {
783    // if keyword_index != -1 -> keyword found
784    //                  == -1 -> string_para contains string-parameter
785
786    awt_assert(!error);
787
788    string_para = scan_string_parameter(line, scan_pos, error);
789    if (!error) {
790        keyword_index = -1;
791    }
792    else {                      // no string
793        error         = 0;      // ignore error - test for keywords
794        keyword_index = scan_keyword_parameter(line, scan_pos, error, allowed_keywords);
795        if (keyword_index == -1) { // no keyword
796            error = GBS_global_string("string parameter or %s", error);
797        }
798    }
799}
800
801static long scan_long_parameter(const string& line, size_t& scan_pos, GB_ERROR& error) {
802    int    result = 0;
803    size_t start  = next_non_white(line, scan_pos);
804    bool   neg    = false;
805
806    awt_assert(!error);
807
808    while (start != string::npos) {
809        char c = line[start];
810        if (c != '+' && c != '-') break;
811
812        start             = next_non_white(line, start+1);
813        if (c == '-') neg = !neg;
814        continue;
815    }
816
817    if (start == string::npos || !isdigit(line[start]) ) {
818        scan_pos = start;
819        error    = "digits (or+-) expected";
820    }
821    else {
822        size_t end = line.find_first_not_of("0123456789", start);
823        scan_pos   = eat_para_separator(line, end, error);
824        if (!error) {
825            awt_assert(end != string::npos);
826            result = atoi(line.substr(start, end-start+1).c_str());
827        }
828    }
829
830    return neg ? -result : result;
831}
832static long scan_long_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, long min, long max) {
833    // with range-check
834    awt_assert(!error);
835    size_t old_scan_pos = scan_pos;
836    long   result       = scan_long_parameter(line, scan_pos, error);
837    if (!error) {
838        if (result<min || result>max) {
839            scan_pos = old_scan_pos;
840            error    = GBS_global_string("value %li is outside allowed range (%li-%li)", result, min, max);
841        }
842    }
843    return result;
844}
845
846static long scan_optional_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, long if_empty) {
847    awt_assert(!error);
848    size_t old_scan_pos = scan_pos;
849    long   result       = scan_long_parameter(line, scan_pos, error);
850    if (error) {
851        error    = 0;           // ignore and test for empty parameter
852        scan_pos = old_scan_pos;
853        scan_pos = eat_para_separator(line, scan_pos, error);
854
855        if (error) {
856            error    = "expected number or empty parameter";
857            scan_pos = old_scan_pos;
858        }
859        else {
860            result = if_empty;
861        }
862    }
863    return result;
864}
865
866static int scan_flag_parameter(const string& line, size_t& scan_pos, GB_ERROR& error, const string& allowed_flags) {
867    // return 0..n-1 ( = position in 'allowed_flags')
868    // or error is set
869    awt_assert(!error);
870    int    result = 0;
871    size_t start  = next_non_white(line, scan_pos);
872    scan_pos      = start;
873
874    if (start == string::npos) {
875        error    = GBS_global_string("One of '%s' expected", allowed_flags.c_str());
876    }
877    else {
878        char   found       = line[start];
879        char   upper_found = std::toupper(found);
880        size_t pos         = allowed_flags.find(upper_found);
881
882        if (pos != string::npos) {
883            result   = pos;
884            scan_pos = eat_para_separator(line, start+1, error);
885        }
886        else {
887            error = GBS_global_string("One of '%s' expected (found '%c')", allowed_flags.c_str(), found);
888        }
889    }
890    return result;
891}
892static bool scan_bool_parameter(const string& line, size_t& scan_pos, GB_ERROR& error) {
893    awt_assert(!error);
894    size_t old_scan_pos = scan_pos;
895    long   result       = scan_long_parameter(line, scan_pos, error);
896
897    if (!error && (result != 0) && (result != 1)) {
898        scan_pos = old_scan_pos;
899        error    = "'0' or '1' expected";
900    }
901    return result != 0;
902}
903
904static string scan_identifier(const string& line, size_t& scan_pos, GB_ERROR& error) {
905    string id;
906    size_t start = next_non_white(line, scan_pos);
907    if (start == string::npos) {
908        error = "identifier expected";
909    }
910    else {
911        size_t end = start;
912        while (end<line.length()) {
913            char c                 = line[end];
914            if (!(isalnum(c) || c == '_')) break;
915            ++end;
916        }
917        id         = line.substr(start, end-start);
918        scan_pos   = eat_para_separator(line, end, error);
919    }
920    return id;
921}
922
923inline const char *inputMaskDir(bool local) {
924    if (local) {
925        static char *local_mask_dir;
926        if (!local_mask_dir) local_mask_dir = AWT_unfold_path(".arb_prop/inputMasks", "HOME");
927        return local_mask_dir;
928    }
929
930    static char *global_mask_dir;
931    if (!global_mask_dir) global_mask_dir = AWT_unfold_path("lib/inputMasks", "ARBHOME");
932    return global_mask_dir;
933}
934
935inline string inputMaskFullname(const string& mask_name, bool local) {
936    const char *dir = inputMaskDir(local);
937    return string(dir)+'/'+mask_name;
938}
939
940#define ARB_INPUT_MASK_ID "ARB-Input-Mask"
941
942static awt_input_mask_descriptor *quick_scan_input_mask(const string& mask_name, const string& filename, bool local) {
943    FILE     *in     = fopen(filename.c_str(), "rt");
944    size_t    lineNo = 0;
945    GB_ERROR  error  = 0;
946    int       hidden = 0; // defaults to 'not hidden'
947
948    if (in) {
949        string   line;
950        error = readLine(in, line, lineNo);
951
952        if (!error && line == ARB_INPUT_MASK_ID) {
953            bool   done = false;
954            string title;
955            string itemtype;
956
957            while (!feof(in)) {
958                error = readLine(in, line, lineNo);
959                if (error) break;
960
961                if (line[0] == '#') continue; // ignore comments
962                if (line == "@MASK_BEGIN") { done = true; break; }
963
964                size_t at = line.find('@');
965                size_t eq = line.find('=', at);
966
967                if (at == string::npos || eq == string::npos)  {
968                    continue; // ignore all other lines
969                }
970                else {
971                    string parameter = line.substr(at+1, eq-at-1);
972                    string value     = line.substr(eq+1);
973
974                    if (parameter == "ITEMTYPE") itemtype = value;
975                    else if (parameter == "TITLE") title  = value;
976                    else if (parameter == "HIDE") hidden  = atoi(value.c_str());
977                }
978            }
979
980            if (!error && done) {
981                if (itemtype == "") {
982                    error = "No itemtype defined";
983                }
984                else {
985                    if (title == "") title = mask_name;
986                    return new awt_input_mask_descriptor(title.c_str(), mask_name.c_str(), itemtype.c_str(), local, hidden);
987                }
988            }
989        }
990    }
991
992    if (error) {
993        aw_message(GBS_global_string("%s (while scanning user-mask '%s')", error, filename.c_str()));
994    }
995
996#if defined(DEBUG)
997    printf("Skipping '%s' (not a input mask)\n", filename.c_str());
998#endif // DEBUG
999    return 0;
1000}
1001
1002static void AWT_edit_input_mask(AW_window *, AW_CL cl_mask_name, AW_CL cl_local) {
1003    const string *mask_name = (const string *)cl_mask_name;
1004    string        fullmask  = inputMaskFullname(*mask_name, (bool)cl_local);
1005   
1006    AWT_edit(fullmask.c_str()); // @@@ add callback and automatically reload input mask
1007}
1008
1009//  ---------------------------------
1010//      input mask container :
1011//  ---------------------------------
1012typedef SmartPtr<awt_input_mask>        awt_input_mask_ptr;
1013typedef map<string, awt_input_mask_ptr> InputMaskList; // contains all active masks
1014static InputMaskList                    input_mask_list;
1015
1016static void awt_input_mask_awar_changed_cb(AW_root */*root*/, AW_CL cl_mask) {
1017    awt_input_mask *mask = (awt_input_mask*)(cl_mask);
1018    mask->relink();
1019}
1020static void link_mask_to_database(awt_input_mask_ptr mask) {
1021    awt_input_mask_global         *global = mask->mask_global();
1022    const awt_item_type_selector  *sel    = global->get_selector();
1023    AW_root                       *root   = global->get_root();
1024
1025    sel->add_awar_callbacks(root, awt_input_mask_awar_changed_cb, (AW_CL)(&*mask));
1026    awt_input_mask_awar_changed_cb(root, (AW_CL)(&*mask));
1027}
1028static void unlink_mask_from_database(awt_input_mask_ptr mask) {
1029    awt_input_mask_global        *global = mask->mask_global();
1030    const awt_item_type_selector *sel    = global->get_selector();
1031    AW_root                      *root   = global->get_root();
1032
1033    sel->remove_awar_callbacks(root, awt_input_mask_awar_changed_cb, (AW_CL)(&*mask));
1034}
1035
1036inline bool isInternalMaskName(const string& s) {
1037    return s[0] == '0' || s[0] == '1';
1038}
1039
1040static void awt_open_input_mask(AW_window *aww, AW_CL cl_internal_mask_name, AW_CL cl_mask_to_open, bool reload, bool hide_current) {
1041    const string            *internal_mask_name = (const string *)cl_internal_mask_name;
1042    const string            *mask_to_open       = (const string *)cl_mask_to_open;
1043    InputMaskList::iterator  mask_iter          = input_mask_list.find(*internal_mask_name);
1044
1045    awt_assert(internal_mask_name && isInternalMaskName(*internal_mask_name));
1046    awt_assert(mask_to_open && isInternalMaskName(*mask_to_open));
1047
1048    if (mask_iter != input_mask_list.end()) {
1049        awt_input_mask_ptr     mask   = mask_iter->second;
1050        awt_input_mask_global *global = mask->mask_global();
1051
1052        printf("aww=%p root=%p ; global=%p root=%p\n", aww, aww->get_root(), global, global->get_root());
1053        awt_assert(aww->get_root() == global->get_root());
1054
1055        if (reload) mask->set_reload_on_reinit(true);
1056        if (hide_current) mask->hide();
1057        // @@@ hier sollte nicht der Selector der alten Maske verwendet werden, sondern anhand des Typs ein
1058        // Selector ausgewaehlt werden. Dazu muessen jedoch alle Selectoren registriert werden.
1059        GB_ERROR error = AWT_initialize_input_mask(global->get_root(), global->get_gb_main(), global->get_selector(), mask_to_open->c_str(), global->is_local_mask());
1060        // CAUTION: AWT_initialize_input_mask invalidates mask and mask_iter
1061        if (error && hide_current) {
1062            mask_iter = input_mask_list.find(*internal_mask_name);
1063            awt_assert(mask_iter != input_mask_list.end());
1064            mask_iter->second->show();
1065        }
1066    }
1067#if defined(DEBUG)
1068    else {
1069        string mask_name = internal_mask_name->substr(1);
1070        printf("'%s' (no such mask in input_mask_list)\n", mask_name.c_str());
1071        awt_assert(0);
1072    }
1073#endif // DEBUG
1074}
1075
1076static void AWT_reload_input_mask(AW_window *aww, AW_CL cl_internal_mask_name) {
1077    awt_open_input_mask(aww, cl_internal_mask_name, cl_internal_mask_name, true, true);
1078}
1079static void AWT_open_input_mask(AW_window *aww, AW_CL cl_internal_mask_name, AW_CL cl_mask_to_open) {
1080    awt_open_input_mask(aww, cl_internal_mask_name, cl_mask_to_open, false, false);
1081}
1082static void AWT_change_input_mask(AW_window *aww, AW_CL cl_internal_mask_name, AW_CL cl_mask_to_open) {
1083    awt_open_input_mask(aww, cl_internal_mask_name, cl_mask_to_open, false, true);
1084}
1085
1086
1087
1088//  ------------------------------
1089//      class awt_mask_action
1090//  ------------------------------
1091// something that is performed i.e. when user pressed a mask button
1092// used as callback parameter
1093class awt_mask_action {
1094private:
1095    virtual GB_ERROR action() = 0;
1096protected:
1097    awt_input_mask_ptr mask;
1098public:
1099    awt_mask_action(awt_input_mask_ptr mask_) : mask(mask_) {}
1100    virtual ~awt_mask_action() {}
1101
1102    void perform_action() {
1103        GB_ERROR error = action();
1104        if (error) aw_message(error);
1105    }
1106};
1107
1108
1109//  ------------------------------------------------------
1110//      class awt_assignment : public awt_mask_action
1111//  ------------------------------------------------------
1112class awt_assignment : public awt_mask_action {
1113private:
1114    string id_dest;
1115    string id_source;
1116
1117    virtual GB_ERROR action() {
1118        GB_ERROR             error       = 0;
1119        const awt_mask_item *item_source = mask->mask_global()->get_identified_item(id_source, error);
1120        awt_mask_item       *item_dest   = mask->mask_global()->get_identified_item(id_dest, error);
1121
1122        if (!error) error = item_dest->set_value(item_source->get_value());
1123
1124        return error;
1125    }
1126public:
1127    awt_assignment(awt_input_mask_ptr mask_, const string& id_dest_, const string& id_source_)
1128        : awt_mask_action(mask_)
1129        , id_dest(id_dest_)
1130        , id_source(id_source_)
1131    {}
1132    virtual ~awt_assignment() {}
1133};
1134
1135static void AWT_input_mask_perform_action(AW_window */*aww*/, AW_CL cl_awt_mask_action, AW_CL) {
1136    awt_mask_action *action = (awt_mask_action*)cl_awt_mask_action;
1137    action->perform_action();
1138}
1139
1140static void AWT_input_mask_browse_url(AW_window *aww, AW_CL cl_url_srt, AW_CL cl_mask_ptr) {
1141    AW_root                      *root     = aww->get_root();
1142    const string                 *url_srt  = (const string *)cl_url_srt;
1143    const awt_input_mask         *mask     = (const awt_input_mask *)cl_mask_ptr;
1144    const awt_input_mask_global  *global   = mask->mask_global();
1145    const awt_item_type_selector *selector = global->get_selector();
1146    GBDATA                       *gbd      = selector->current(root);
1147
1148    if (!gbd) {
1149        aw_message(GBS_global_string("You have to select a %s first", awt_itemtype_names[selector->get_item_type()]));
1150    }
1151    else {
1152        char     *name  = root->awar(selector->get_self_awar())->read_string();
1153        GB_ERROR  error = awt_open_ACISRT_URL_by_gbd(root, global->get_gb_main(), gbd, name, url_srt->c_str());
1154        if (error) aw_message(error);
1155        free(name);
1156    }
1157}
1158
1159
1160// ---------------------------
1161//      User Mask Commands
1162// ---------------------------
1163
1164enum MaskCommand  {
1165    CMD_TEXTFIELD,
1166    CMD_NUMFIELD,
1167    CMD_CHECKBOX,
1168    CMD_RADIO,
1169    CMD_OPENMASK,
1170    CMD_CHANGEMASK,
1171    CMD_TEXT,
1172    CMD_SELF,
1173    CMD_WWW,
1174    CMD_NEW_LINE,
1175    CMD_NEW_SECTION,
1176    CMD_ID,
1177    CMD_GLOBAL,
1178    CMD_LOCAL,
1179    CMD_SHOW,
1180    CMD_ASSIGN,
1181    CMD_SCRIPT,
1182
1183    MASK_COMMANDS,
1184    CMD_UNKNOWN = MASK_COMMANDS
1185};
1186
1187struct MaskCommandDefinition {
1188    const char  *cmd_name;
1189    MaskCommand  cmd;
1190};
1191
1192static struct MaskCommandDefinition mask_command[MASK_COMMANDS+1] =
1193{
1194 { "TEXTFIELD", CMD_TEXTFIELD },
1195 { "NUMFIELD", CMD_NUMFIELD },
1196 { "CHECKBOX", CMD_CHECKBOX },
1197 { "RADIO", CMD_RADIO },
1198 { "OPENMASK", CMD_OPENMASK },
1199 { "CHANGEMASK", CMD_CHANGEMASK },
1200 { "TEXT", CMD_TEXT },
1201 { "SELF", CMD_SELF },
1202 { "NEW_LINE", CMD_NEW_LINE },
1203 { "NEW_SECTION", CMD_NEW_SECTION },
1204 { "WWW", CMD_WWW },
1205 { "ID", CMD_ID },
1206 { "GLOBAL", CMD_GLOBAL },
1207 { "LOCAL", CMD_LOCAL },
1208 { "SHOW", CMD_SHOW },
1209 { "ASSIGN", CMD_ASSIGN },
1210 { "SCRIPT", CMD_SCRIPT },
1211
1212 { 0, CMD_UNKNOWN}
1213};
1214
1215inline MaskCommand findCommand(const string& cmd_name) {
1216    int mc = 0;
1217
1218    for (; mask_command[mc].cmd != CMD_UNKNOWN; ++mc) {
1219        if (mask_command[mc].cmd_name == cmd_name) {
1220            return mask_command[mc].cmd;
1221        }
1222    }
1223    return CMD_UNKNOWN;
1224}
1225
1226static void parse_CMD_RADIO(string& line, size_t& scan_pos, GB_ERROR& error, const string& command,
1227                            awt_mask_item_ptr& handler1, awt_mask_item_ptr& handler2, awt_input_mask_global *global)
1228{
1229    string         label, data_path;
1230    int            default_position = -1, orientation = -1;
1231    vector<string> buttons;
1232    vector<string> values;
1233    bool           allow_edit       = false;
1234    int            width            = -1;
1235    int            edit_position    = -1;
1236
1237    label                        = scan_string_parameter(line, scan_pos, error);
1238    if (!error) data_path        = scan_string_parameter(line, scan_pos, error);
1239    if (!error) default_position = scan_long_parameter(line, scan_pos, error);
1240    if (!error) {
1241        orientation = scan_flag_parameter(line, scan_pos, error, "HVXY");
1242        orientation = orientation&1;
1243    }
1244    while (!error && !was_last_parameter) {
1245        string but = scan_string_parameter(line, scan_pos, error);
1246        string val = "";
1247        if (!error) {
1248            int keyword_index;
1249            const char *allowed_keywords[] = { "ALLOW_EDIT", 0};
1250            scan_string_or_keyword_parameter(line, scan_pos, error, val, keyword_index, allowed_keywords);
1251
1252            if (!error) {
1253                if (keyword_index != -1) { // found keyword
1254                    if (allow_edit) error = "ALLOW_EDIT is allowed only once for each RADIO";
1255
1256                    if (!error) width = scan_long_parameter(line, scan_pos, error, MIN_TEXTFIELD_SIZE, MAX_TEXTFIELD_SIZE);
1257                    if (!error) val   = scan_string_parameter(line, scan_pos, error);
1258
1259                    if (!error) {
1260                        allow_edit    = true;
1261                        edit_position = buttons.size()+1; // positions are 1..n
1262                        buttons.push_back(but);
1263                        values.push_back(val);
1264                    }
1265                }
1266                else { // found string
1267                    buttons.push_back(but);
1268                    values.push_back(val);
1269                }
1270            }
1271        }
1272    }
1273    check_last_parameter(error, command);
1274
1275    if (!error && (buttons.size()<2)) error = "You have to define at least 2 buttons.";
1276
1277    if (!error && allow_edit && edit_position != default_position) {
1278        error = GBS_global_string("Invalid default %i (must be index of ALLOW_EDIT: %i )", default_position, edit_position);
1279    }
1280    if (!error && (default_position<1 || size_t(default_position)>buttons.size())) {
1281        error = GBS_global_string("Invalid default %i (valid: 1..%zu)", default_position, buttons.size());
1282    }
1283
1284    if (!error) {
1285        if (allow_edit) {
1286            // create radio-button + textfield
1287            handler1 = new awt_radio_button(global, data_path, label, default_position-1, orientation, buttons, values);
1288            handler2 = new awt_input_field(global, data_path, "", width, "", GB_STRING);
1289        }
1290        else {
1291            handler1 = new awt_radio_button(global, data_path, label, default_position-1, orientation, buttons, values);
1292        }
1293    }
1294}
1295
1296static string find_internal_name(const string& mask_name, bool search_in_local) {
1297    const char *internal_local  = 0;
1298    const char *internal_global = 0;
1299
1300    for (int id = 0; ; ++id) {
1301        const awt_input_mask_descriptor *descriptor = AWT_look_input_mask(id);
1302        if (!descriptor) break;
1303
1304        const char *internal_name = descriptor->get_internal_maskname();
1305
1306        if (strcmp(internal_name+1, mask_name.c_str()) == 0) {
1307            if (descriptor->is_local_mask()) {
1308                awt_assert(internal_local == 0);
1309                internal_local = internal_name;
1310            }
1311            else {
1312                awt_assert(internal_global == 0);
1313                internal_global = internal_name;
1314            }
1315        }
1316    }
1317
1318    return (search_in_local && internal_local) ? internal_local : (internal_global ? internal_global : "");
1319}
1320
1321// ----------------------------------
1322//      class awt_marked_checkbox
1323// ----------------------------------
1324
1325class awt_marked_checkbox : public awt_viewport, public awt_linked_to_item {
1326private:
1327
1328    string generate_baseName(awt_input_mask_global *global_) {
1329        return GBS_global_string("%s/marked", global_->get_maskid().c_str());
1330    }
1331
1332public:
1333    awt_marked_checkbox(awt_input_mask_global *global_, const std::string& label_)
1334        : awt_viewport(global_, generate_baseName(global_), "0", false, label_)
1335        , awt_linked_to_item()
1336    {}
1337    virtual ~awt_marked_checkbox() {}
1338
1339    virtual GB_ERROR link_to(GBDATA *gb_new_item); // link to a new item
1340    virtual GB_ERROR relink() { return link_to(mask_global()->get_selector()->current(mask_global()->get_root())); }
1341    virtual void awar_changed();
1342    virtual void db_changed();
1343    virtual void general_item_change() { db_changed(); } // called if item was changed (somehow)
1344    virtual void build_widget(AW_window *aws); // builds the widget at the current position
1345};
1346
1347GB_ERROR awt_marked_checkbox::link_to(GBDATA *gb_new_item) { // link to a new item
1348    GB_ERROR       error = 0;
1349    GB_transaction dummy(mask_global()->get_gb_main());
1350
1351    remove_awar_callbacks();    // unbind awar callbacks temporarily
1352
1353    if (item()) {
1354        remove_db_callbacks(); // ignore result (if handled db-entry was deleted, it returns an error)
1355        set_item(0);
1356    }
1357
1358    if (gb_new_item) {
1359        set_item(gb_new_item);
1360        db_changed();
1361        error = add_db_callbacks();
1362    }
1363
1364    add_awar_callbacks();       // rebind awar callbacks
1365    return error;
1366}
1367
1368void awt_marked_checkbox::awar_changed() { // called when awar changes
1369    if (item()) {
1370        string         value  = get_value();
1371        bool           marked = value == "yes";
1372        GB_transaction dummy(mask_global()->get_gb_main());
1373        GB_write_flag(item(), marked);
1374    }
1375    else {
1376        mask_global()->no_item_selected();
1377    }
1378}
1379
1380void awt_marked_checkbox::db_changed() {
1381    bool marked = false;
1382
1383    if (item()) {
1384        GB_transaction dummy(mask_global()->get_gb_main());
1385        if (GB_read_flag(item())) marked = true;
1386        set_value(marked ? "yes" : "no"); // @@@ TEST: moved into if (was below)
1387    }
1388}
1389
1390void awt_marked_checkbox::build_widget(AW_window *aws) { // builds the widget at the current position
1391    const string& lab = get_label();
1392    if (lab.length()) aws->label(lab.c_str());
1393
1394    aws->create_toggle(awar_name().c_str());
1395}
1396
1397static GB_ERROR writeDefaultMaskfile(const string& fullname, const string& maskname, const string& itemtypename, bool hidden) {
1398    FILE *out = fopen(fullname.c_str(), "wt");
1399    if (!out) return GBS_global_string("Can't open '%s'", fullname.c_str());
1400
1401    fprintf(out,
1402            "%s\n"
1403            "# New mask '%s'\n\n"
1404            "# What kind of item to edit:\n"
1405            "@ITEMTYPE=%s\n\n"
1406            "# Window title:\n"
1407            "@TITLE=%s\n\n", ARB_INPUT_MASK_ID, maskname.c_str(), itemtypename.c_str(), maskname.c_str());
1408
1409    fprintf(out,
1410            "# Should mask appear in 'User mask' menu\n"
1411            "@HIDE=%i\n\n", int(hidden));
1412
1413    fputs("# Spacing in window:\n"
1414          "@X_SPACING=5\n"
1415          "@Y_SPACING=3\n\n"
1416          "# Show edit/reload button?\n"
1417          "@EDIT=1\n"
1418          "# Show 'edit enable' toggle?\n"
1419          "@EDIT_ENABLE=1\n"
1420          "# Show 'marked' toggle?\n"
1421          "@SHOW_MARKED=1\n"
1422          "\n# ---------------------------\n"
1423          "# The definition of the mask:\n\n"
1424          "@MASK_BEGIN\n\n"
1425          "    TEXT(\"You are editing\") SELF()\n"
1426          "    NEW_SECTION()\n"
1427          "    TEXTFIELD(\"Full name\", \"full_name\", 30)\n\n"
1428          "@MASK_END\n\n", out);
1429
1430    fclose(out);
1431    return 0;
1432}
1433
1434static awt_input_mask_ptr awt_create_input_mask(AW_root *root, GBDATA *gb_main, const awt_item_type_selector *sel,
1435                                                const string& mask_name, bool local, GB_ERROR& error, bool reloading) {
1436    size_t             lineNo = 0;
1437    awt_input_mask_ptr mask;
1438
1439    error = 0;
1440
1441    FILE *in = 0;
1442    {
1443        string  fullfile = inputMaskFullname(mask_name, local);
1444        in               = fopen(fullfile.c_str(), "rt");
1445        if (!in) error   = GBS_global_string("Can't open '%s'", fullfile.c_str());
1446    }
1447
1448    if (!error) {
1449        bool   mask_began = false;
1450        bool   mask_ended = false;
1451
1452        // data to be scanned :
1453        string        title;
1454        string        itemtypename;
1455        int           x_spacing   = 5;
1456        int           y_spacing   = 3;
1457        bool          edit_reload = false;
1458        bool          edit_enable = true;
1459        bool          show_marked = true;
1460
1461        string line;
1462        size_t err_pos = 0;     // 0 = unknown; string::npos = at end of line; else position+1;
1463        error          = readLine(in, line, lineNo);
1464
1465        if (!error && line != ARB_INPUT_MASK_ID) {
1466            error = "'" ARB_INPUT_MASK_ID "' expected";
1467        }
1468
1469        while (!error && !mask_began && !feof(in)) {
1470            error = readLine(in, line, lineNo);
1471            if (error) break;
1472
1473            if (line[0] == '#') continue; // ignore comments
1474
1475            if (line == "@MASK_BEGIN") mask_began = true;
1476            else  {
1477                size_t at = line.find('@');
1478                size_t eq = line.find('=', at);
1479
1480                if (at == string::npos || eq == string::npos) {
1481                    continue;
1482                }
1483                else {
1484                    string parameter = line.substr(at+1, eq-at-1);
1485                    string value     = line.substr(eq+1);
1486
1487                    if (parameter == "ITEMTYPE")            itemtypename = value;
1488                    else if (parameter == "TITLE")          title        = value;
1489                    else if (parameter == "X_SPACING")      x_spacing    = atoi(value.c_str());
1490                    else if (parameter == "Y_SPACING")      y_spacing    = atoi(value.c_str());
1491                    else if (parameter == "EDIT")           edit_reload  = atoi(value.c_str()) != 0;
1492                    else if (parameter == "EDIT_ENABLE")    edit_enable  = atoi(value.c_str()) != 0;
1493                    else if (parameter == "SHOW_MARKED")    show_marked  = atoi(value.c_str()) != 0;
1494                    else if (parameter == "HIDE") ; // ignore parameter here
1495                    else {
1496                        error = GBS_global_string("Unknown parameter '%s'", parameter.c_str());
1497                    }
1498                }
1499            }
1500        }
1501
1502        if (!error && !mask_began) error = "@MASK_BEGIN expected";
1503
1504        // check data :
1505        if (!error) {
1506            if (title == "") title = string("Untitled (")+mask_name+")";
1507            awt_item_type itemtype = AWT_getItemType(itemtypename);
1508
1509            if (itemtype == AWT_IT_UNKNOWN)         error = GBS_global_string("Unknown @ITEMTYPE '%s'", itemtypename.c_str());
1510            if (itemtype != sel->get_item_type())   error = GBS_global_string("Mask is designed for @ITEMTYPE '%s' (current @ITEMTYPE '%s')",
1511                                                                              itemtypename.c_str(), awt_itemtype_names[sel->get_item_type()]);
1512
1513            // create mask
1514            if (!error) mask = new awt_input_mask(root, gb_main, mask_name, itemtype, local, sel, edit_enable);
1515        }
1516
1517        // create window
1518        if (!error) {
1519            awt_assert(!mask.Null());
1520            AW_window_simple*& aws = mask->get_window();
1521            aws                    = new AW_window_simple;
1522
1523            // do not use callback ids for reloaded masks
1524#define ID(id) (reloading ? NULL : id)
1525
1526            {
1527                char *window_id = GBS_global_string_copy("INPUT_MASK_%s", mask->mask_global()->get_maskid().c_str()); // create a unique id for each mask
1528                aws->init(root, window_id, title.c_str());
1529                free(window_id);
1530            }
1531            aws->load_xfig(0, true);
1532
1533            aws->recalc_size_at_show = 1; // ignore user size!
1534
1535            aws->auto_space(x_spacing, y_spacing);
1536            aws->at_newline();
1537
1538            aws->callback((AW_CB0)AW_POPDOWN);                          aws->create_button(ID("CLOSE"), "CLOSE", "C");
1539            aws->callback( AW_POPUP_HELP,(AW_CL)"input_mask.hlp");      aws->create_button(ID("HELP"),"HELP","H");
1540
1541            if (edit_reload) {
1542                aws->callback(AWT_edit_input_mask, (AW_CL)&mask->mask_global()->get_maskname(), (AW_CL)mask->mask_global()->is_local_mask());   aws->create_button(0, "EDIT","E");
1543                aws->callback(AWT_reload_input_mask, (AW_CL)&mask->mask_global()->get_internal_maskname());                                     aws->create_button(0, "RELOAD","R");
1544            }
1545
1546            if (edit_reload && edit_enable && show_marked) aws->at_newline();
1547
1548            if (edit_enable) {
1549                aws->label("Enable edit?");
1550                aws->create_toggle(AWAR_INPUT_MASKS_EDIT_ENABLED);
1551            }
1552
1553            if (show_marked) {
1554                awt_mask_item_ptr handler = new awt_marked_checkbox(mask->mask_global(), "Marked");
1555                mask->add_handler(handler);
1556                if (handler->is_viewport()) handler->to_viewport()->build_widget(aws);
1557
1558            }
1559
1560            aws->at_newline();
1561
1562            vector<int> horizontal_lines; // y-positions of horizontal lines
1563            map<string, size_t> referenced_ids; // all ids that where referenced by the script (size_t contains lineNo of last reference)
1564            map<string, size_t> declared_ids; // all ids that where declared by the script (size_t contains lineNo of declaration)
1565
1566            awt_mask_item_ptr lasthandler;
1567
1568            // read mask itself :
1569            while (!error && !mask_ended && !feof(in)) {
1570                error = readLine(in, line, lineNo);
1571                if (error) break;
1572
1573                if (line.empty()) continue;
1574                if (line[0] == '#') continue;
1575
1576                if (line == "@MASK_END") {
1577                    mask_ended = true;
1578                }
1579                else  {
1580                PARSE_REST_OF_LINE:
1581                    was_last_parameter = false;
1582                    size_t start       = next_non_white(line, 0);
1583                    if (start != string::npos) { // line contains sth
1584                        size_t after_command = line.find_first_of(string(" \t("), start);
1585                        if (after_command == string::npos) {
1586                            string command = line.substr(start);
1587                            error          = GBS_global_string("arguments missing after '%s'", command.c_str());
1588                        }
1589                        else {
1590                            string command    = line.substr(start, after_command-start);
1591                            size_t paren_open = line.find('(', after_command);
1592                            if (paren_open == string::npos) {
1593                                error = GBS_global_string("'(' expected after '%s'", command.c_str());
1594                            }
1595                            else {
1596                                size_t            scan_pos = paren_open+1;
1597                                awt_mask_item_ptr handler;
1598                                awt_mask_item_ptr radio_edit_handler;
1599                                MaskCommand       cmd      = findCommand(command);
1600
1601                                //  --------------------------------------
1602                                //      code for different commands :
1603                                //  --------------------------------------
1604
1605                                if (cmd == CMD_TEXTFIELD) {
1606                                    string label, data_path;
1607                                    long   width          = -1;
1608                                    label                 = scan_string_parameter(line, scan_pos, error);
1609                                    if (!error) data_path = scan_string_parameter(line, scan_pos, error);
1610                                    if (!error) width     = scan_long_parameter(line, scan_pos, error, MIN_TEXTFIELD_SIZE, MAX_TEXTFIELD_SIZE);
1611                                    check_last_parameter(error, command);
1612
1613                                    if (!error) handler = new awt_input_field(mask->mask_global(), data_path, label, width, "", GB_STRING);
1614                                }
1615                                else if (cmd == CMD_NUMFIELD) {
1616                                    string label, data_path;
1617                                    long   width = -1;
1618
1619                                    long min = LONG_MIN;
1620                                    long max = LONG_MAX;
1621
1622                                    label                 = scan_string_parameter(line, scan_pos, error);
1623                                    if (!error) data_path = scan_string_parameter(line, scan_pos, error);
1624                                    if (!error) width     = scan_long_parameter(line, scan_pos, error, MIN_TEXTFIELD_SIZE, MAX_TEXTFIELD_SIZE);
1625                                    if (!was_last_parameter) {
1626                                        if (!error) min = scan_optional_parameter(line, scan_pos, error, LONG_MIN);
1627                                        if (!error) max = scan_optional_parameter(line, scan_pos, error, LONG_MAX);
1628                                    }
1629                                    check_last_parameter(error, command);
1630
1631                                    if (!error) handler = new awt_numeric_input_field(mask->mask_global(), data_path, label, width, 0, min, max);
1632                                }
1633                                else if (cmd == CMD_CHECKBOX) {
1634                                    string label, data_path;
1635                                    bool   checked        = false;
1636                                    label                 = scan_string_parameter(line, scan_pos, error);
1637                                    if (!error) data_path = scan_string_parameter(line, scan_pos, error);
1638                                    if (!error) checked   = scan_bool_parameter(line, scan_pos, error);
1639                                    check_last_parameter(error, command);
1640
1641                                    if (!error) handler = new awt_check_box(mask->mask_global(), data_path, label, checked);
1642                                }
1643                                else if (cmd == CMD_RADIO) {
1644                                    parse_CMD_RADIO(line, scan_pos, error, command, handler, radio_edit_handler, mask->mask_global());
1645                                }
1646                                else if (cmd == CMD_SCRIPT) {
1647                                    string id, script;
1648                                    id                 = scan_identifier(line, scan_pos, error);
1649                                    if (!error) script = scan_string_parameter(line, scan_pos, error, true);
1650                                    check_last_parameter(error, command);
1651
1652                                    if (!error) {
1653                                        handler          = new awt_script(mask->mask_global(), script);
1654                                        error            = handler->set_name(id, false);
1655                                        declared_ids[id] = lineNo;
1656                                    }
1657                                }
1658                                else if (cmd == CMD_GLOBAL || cmd == CMD_LOCAL) {
1659                                    string id, def_value;
1660
1661                                    id                 = scan_identifier(line, scan_pos, error);
1662                                    bool global_exists = mask->mask_global()->has_global_id(id);
1663                                    bool local_exists  = mask->mask_global()->has_local_id(id);
1664
1665                                    if ((cmd == CMD_GLOBAL && local_exists) || (cmd == CMD_LOCAL && global_exists)) {
1666                                        error = GB_export_errorf("ID '%s' already declared as %s ID (rename your local id)",
1667                                                                 id.c_str(), cmd == CMD_LOCAL ? "global" : "local");
1668                                    }
1669                                    else if (cmd == CMD_LOCAL && local_exists) {
1670                                        error = GB_export_errorf("ID '%s' declared twice", id.c_str());
1671                                    }
1672
1673                                    if (!error) def_value = scan_string_parameter(line, scan_pos, error);
1674                                    if (!error) {
1675                                        if (cmd == CMD_GLOBAL) {
1676                                            if (!mask->mask_global()->has_global_id(id)) { // do not create globals more than once
1677                                                // and never free them -> so we don't need pointer
1678                                                new awt_variable(mask->mask_global(), id, true, def_value, error);
1679                                            }
1680                                            awt_assert(handler.Null());
1681                                        }
1682                                        else {
1683                                            handler = new awt_variable(mask->mask_global(), id, false, def_value, error);
1684                                        }
1685                                        declared_ids[id] = lineNo;
1686                                    }
1687                                }
1688                                else if (cmd == CMD_ID) {
1689                                    string id = scan_identifier(line, scan_pos, error);
1690                                    check_last_parameter(error, command);
1691
1692                                    if (!error) {
1693                                        if (lasthandler.Null()) {
1694                                            error = "ID() may only be used BEHIND another element";
1695                                        }
1696                                        else {
1697                                            error            = lasthandler->set_name(id, false);
1698                                            declared_ids[id] = lineNo;
1699                                        }
1700                                    }
1701                                }
1702                                else if (cmd == CMD_SHOW) {
1703                                    string         label, id;
1704                                    long           width = -1;
1705                                    awt_mask_item *item  = 0;
1706
1707                                    label             = scan_string_parameter(line, scan_pos, error);
1708                                    if (!error) id    = scan_identifier(line, scan_pos, error);
1709                                    if (!error) item  = (awt_mask_item*)mask->mask_global()->get_identified_item(id, error);
1710                                    if (!error) width = scan_long_parameter(line, scan_pos, error, MIN_TEXTFIELD_SIZE, MAX_TEXTFIELD_SIZE);
1711                                    check_last_parameter(error, command);
1712
1713                                    if (!error) {
1714                                        awt_mask_awar_item *awar_item = dynamic_cast<awt_mask_awar_item*>(item);
1715
1716                                        if (awar_item) { // item has an awar -> create a viewport using that awar
1717                                            handler = new awt_text_viewport(awar_item, label, width);
1718                                        }
1719                                        else { // item has no awar -> test if it's a script
1720                                            awt_script *script  = dynamic_cast<awt_script*>(item);
1721                                            if (script) handler = new awt_script_viewport(mask->mask_global(), script, label, width);
1722                                            else        error   = "SHOW cannot be used on this ID-type";
1723                                        }
1724
1725                                        referenced_ids[id] = lineNo;
1726                                    }
1727                                }
1728                                else if (cmd == CMD_OPENMASK || cmd == CMD_CHANGEMASK) {
1729                                    string label, mask_to_start;
1730                                    label                     = scan_string_parameter(line, scan_pos, error);
1731                                    if (!error) mask_to_start = scan_string_parameter(line, scan_pos, error);
1732                                    check_last_parameter(error, command);
1733
1734                                    if (!error) {
1735                                        char   *key                    = ID(GBS_string_2_key(label.c_str()));
1736                                        AW_CB   cb                     = cmd == CMD_OPENMASK ? AWT_open_input_mask : AWT_change_input_mask;
1737                                        string  mask_to_start_internal = find_internal_name(mask_to_start, local);
1738
1739                                        if (mask_to_start_internal.length() == 0) {
1740                                            error = "Can't detect which mask to load";
1741                                        }
1742                                        else {
1743                                            string *cl_arg1 = new string(mask->mask_global()->get_internal_maskname());
1744                                            string *cl_arg2 = new string(mask_to_start_internal);
1745
1746                                            awt_assert(cl_arg1);
1747                                            awt_assert(cl_arg2);
1748
1749                                            aws->callback( cb, (AW_CL)cl_arg1, (AW_CL)cl_arg2);
1750                                            aws->button_length(label.length()+2);
1751                                            aws->create_button(key, label.c_str());
1752                                        }
1753
1754                                        free(key);
1755                                    }
1756                                }
1757                                else if (cmd == CMD_WWW) {
1758                                    string button_text, url_srt;
1759                                    button_text         = scan_string_parameter(line, scan_pos, error);
1760                                    if (!error) url_srt = scan_string_parameter(line, scan_pos, error, true);
1761                                    check_last_parameter(error, command);
1762
1763                                    if (!error) {
1764                                        char *key = ID(GBS_string_2_key(button_text.c_str()));
1765
1766                                        aws->callback(AWT_input_mask_browse_url, (AW_CL)new string(url_srt), (AW_CL)&*mask);
1767                                        aws->button_length(button_text.length()+2);
1768                                        aws->create_button(key, button_text.c_str());
1769
1770                                        free(key);
1771                                    }
1772                                }
1773                                else if (cmd == CMD_ASSIGN) {
1774                                    string id_dest, id_source, button_text;
1775
1776                                    id_dest                 = scan_identifier(line, scan_pos, error);
1777                                    if (!error) id_source   = scan_identifier(line, scan_pos, error);
1778                                    if (!error) button_text = scan_string_parameter(line, scan_pos, error);
1779
1780                                    if (!error) {
1781                                        referenced_ids[id_source] = lineNo;
1782                                        referenced_ids[id_dest]   = lineNo;
1783
1784                                        char *key = ID(GBS_string_2_key(button_text.c_str()));
1785
1786                                        aws->callback(AWT_input_mask_perform_action, (AW_CL)new awt_assignment(mask, id_dest, id_source), 0);
1787                                        aws->button_length(button_text.length()+2);
1788                                        aws->create_button(key, button_text.c_str());
1789
1790                                        free(key);
1791                                    }
1792                                }
1793                                else if (cmd == CMD_TEXT) {
1794                                    string text;
1795                                    text = scan_string_parameter(line, scan_pos, error);
1796                                    check_last_parameter(error, command);
1797
1798                                    if (!error) {
1799                                        char *key = ID(GBS_string_2_key(text.c_str()));
1800                                        aws->button_length(text.length()+2);
1801                                        aws->create_button(key, text.c_str());
1802                                        free(key);
1803                                    }
1804                                }
1805                                else if (cmd == CMD_SELF) {
1806                                    check_no_parameter(line, scan_pos, error, command);
1807                                    if (!error) {
1808                                        const awt_item_type_selector *selector         = mask->mask_global()->get_selector();
1809                                        string                        button_awar_name = selector->get_self_awar();
1810                                        char                         *key              = ID(GBS_string_2_key(button_awar_name.c_str()));
1811
1812                                        aws->button_length(selector->get_self_awar_content_length());
1813                                        aws->create_button(key, button_awar_name.c_str());
1814                                        free(key);
1815                                    }
1816                                }
1817                                else if (cmd == CMD_NEW_LINE) {
1818                                    check_no_parameter(line, scan_pos, error, command);
1819                                    if (!error) {
1820                                        int width, height;
1821                                        aws->get_window_size(width, height);
1822                                        aws->at_shift(0, 2*SEC_YBORDER+SEC_LINE_WIDTH);
1823                                    }
1824                                }
1825                                else if (cmd == CMD_NEW_SECTION) {
1826                                    check_no_parameter(line, scan_pos, error, command);
1827                                    if (!error) {
1828                                        int width, height;
1829                                        aws->get_window_size(width, height);
1830                                        horizontal_lines.push_back(height);
1831                                        aws->at_shift(0, 2*SEC_YBORDER+SEC_LINE_WIDTH);
1832                                    }
1833                                }
1834                                else if (cmd == CMD_UNKNOWN) {
1835                                    error = GBS_global_string("Unknown command '%s'", command.c_str());
1836                                }
1837                                else {
1838                                    error = GBS_global_string("No handler for '%s'", command.c_str());
1839                                    awt_assert(0);
1840                                }
1841
1842                                //  --------------------------
1843                                //      insert handler(s)
1844                                //  --------------------------
1845
1846                                if (!handler.Null() && !error) {
1847                                    if (!radio_edit_handler.Null()) { // special radio handler
1848                                        const awt_radio_button *radio = dynamic_cast<const awt_radio_button*>(&*handler);
1849                                        awt_assert(radio);
1850
1851                                        int x_start, y_start;
1852
1853                                        aws->get_at_position(&x_start, &y_start);
1854
1855                                        mask->add_handler(handler);
1856                                        handler->to_viewport()->build_widget(aws);
1857
1858                                        int x_end, y_end, dummy;
1859                                        aws->get_at_position(&x_end, &dummy);
1860                                        aws->at_newline();
1861                                        aws->get_at_position(&dummy, &y_end);
1862
1863                                        int height    = y_end-y_start;
1864                                        int each_line = height/radio->no_of_toggles();
1865                                        int y_offset  = each_line*(radio->default_toggle());
1866
1867                                        aws->at(x_end+x_spacing, y_start+y_offset);
1868
1869                                        mask->add_handler(radio_edit_handler);
1870                                        radio_edit_handler->to_viewport()->build_widget(aws);
1871
1872                                        radio_edit_handler.SetNull();
1873                                    }
1874                                    else {
1875                                        mask->add_handler(handler);
1876                                        if (handler->is_viewport()) handler->to_viewport()->build_widget(aws);
1877                                    }
1878                                    lasthandler = handler; // store handler (used by CMD_ID)
1879                                }
1880
1881                                // parse rest of line or abort
1882                                if (!error) {
1883                                    line = line.substr(scan_pos);
1884                                    goto PARSE_REST_OF_LINE;
1885                                }
1886                                err_pos = scan_pos;
1887                                if (err_pos != string::npos) err_pos++; // because 0 means unknown
1888                            }
1889                        }
1890                    }
1891                    else { // reached the end of the current line
1892                        aws->at_newline();
1893                    }
1894                }
1895            }
1896
1897            // check declarations and references
1898            if (!error) {
1899                for (map<string, size_t>::const_iterator r = referenced_ids.begin(); r != referenced_ids.end(); ++r) {
1900                    if (declared_ids.find(r->first) == declared_ids.end()) {
1901                        error = GB_export_errorf("ID '%s' used in line #%zu was not declared", r->first.c_str(), r->second);
1902                        aw_message(error);
1903                    }
1904                }
1905
1906                for (map<string, size_t>::const_iterator d = declared_ids.begin(); d != declared_ids.end(); ++d) {
1907                    if (referenced_ids.find(d->first) == referenced_ids.end()) {
1908                        const char *warning = GBS_global_string("ID '%s' declared in line #%zu is never used in %s",
1909                                                                d->first.c_str(), d->second, mask_name.c_str());
1910                        aw_message(warning);
1911                    }
1912                }
1913
1914                if (error) error = "Inconsistent IDs";
1915            }
1916
1917            if (!error) {
1918                if (!horizontal_lines.empty()) { // draw all horizontal lines
1919                    int width, height;
1920                    aws->get_window_size(width, height);
1921                    for (vector<int>::const_iterator yi = horizontal_lines.begin(); yi != horizontal_lines.end(); ++yi) {
1922                        int y = (*yi)+SEC_YBORDER;
1923                        aws->draw_line(SEC_XBORDER, y, width-SEC_XBORDER, y, SEC_LINE_WIDTH, true);
1924                    }
1925                }
1926
1927                // fit the window
1928                aws->window_fit();
1929            }
1930
1931            // link to database
1932            if (!error) link_mask_to_database(mask);
1933
1934#undef ID
1935        }
1936
1937        if (error) {
1938            if (lineNo == 0) {
1939                ; // do not change error
1940            }
1941            else if (err_pos == 0) { // don't knows exact error position
1942                error = GBS_global_string("%s in line #%zu", error, lineNo);
1943            }
1944            else if (err_pos == string::npos) {
1945                error = GBS_global_string("%s at end of line #%zu", error, lineNo);
1946            }
1947            else {
1948                int    wanted         = 35;
1949                size_t end            = line.length();
1950                string context;
1951                context.reserve(wanted);
1952                bool   last_was_space = false;
1953
1954                for (size_t ex = err_pos-1; ex<end && wanted>0; ++ex) {
1955                    char ch            = line[ex];
1956                    bool this_is_space = ch == ' ';
1957
1958                    if (!this_is_space || !last_was_space) {
1959                        context.append(1, ch);
1960                        --wanted;
1961                    }
1962                    last_was_space = this_is_space;
1963                }
1964
1965                error = GBS_global_string("%s in line #%zu at '%s...'", error, lineNo, context.c_str());
1966            }
1967        }
1968
1969        fclose(in);
1970    }
1971
1972    if (error) mask.SetNull();
1973
1974    return mask;
1975}
1976
1977GB_ERROR AWT_initialize_input_mask(AW_root *root, GBDATA *gb_main, const awt_item_type_selector *sel, const char* internal_mask_name, bool local) {
1978    const char              *mask_name  = internal_mask_name+1;
1979    InputMaskList::iterator  mask_iter  = input_mask_list.find(internal_mask_name);
1980    GB_ERROR                 error      = 0;
1981    awt_input_mask_ptr       old_mask;
1982    bool                     unlink_old = false;
1983
1984    static list<awt_input_mask_ptr> mask_collector; // here old (aka reloaded) masks are kept
1985    // (freeing masks is not possible, because of too many nested callbacks)
1986
1987    // erase mask (so it loads again from scratch)
1988    if (mask_iter != input_mask_list.end() && mask_iter->second->reload_on_reinit()) // reload wanted ?
1989    {
1990        old_mask  = mask_iter->second;
1991        input_mask_list.erase(mask_iter);
1992        mask_iter = input_mask_list.end();
1993
1994        old_mask->hide();
1995        mask_collector.push_back(old_mask);
1996        unlink_old = true;
1997    }
1998
1999    if (mask_iter == input_mask_list.end()) { // mask not loaded yet
2000        awt_input_mask_ptr newMask = awt_create_input_mask(root, gb_main, sel, mask_name, local, error, unlink_old);
2001        if (error) {
2002            error = GBS_global_string("Error reading %s (%s)", mask_name, error);
2003            if (!old_mask.Null()) { // are we doing a reload or changemask ?
2004                input_mask_list[internal_mask_name] = old_mask; // error loading modified mask -> put old one back to mask-list
2005                unlink_old                          = false;
2006            }
2007        }
2008        else {                                      // new mask has been generated
2009            input_mask_list[internal_mask_name] = newMask;
2010        }
2011        mask_iter = input_mask_list.find(internal_mask_name);
2012    }
2013
2014    if (!error) {
2015        awt_assert(mask_iter != input_mask_list.end());
2016        mask_iter->second->get_window()->activate();
2017    }
2018
2019    if (unlink_old) {
2020        old_mask->relink(true); // unlink old mask from database ()
2021        unlink_mask_from_database(old_mask);
2022    }
2023
2024    if (error) aw_message(error);
2025    return error;
2026}
2027
2028// start of implementation of class awt_input_mask:
2029
2030awt_input_mask::~awt_input_mask() {
2031    relink(true); // unlink from DB
2032    for (awt_mask_item_list::iterator h = handlers.begin(); h != handlers.end(); ++h) {
2033        (*h)->remove_name();
2034    }
2035}
2036
2037void awt_input_mask::relink(bool unlink) {
2038    // this functions links/unlinks all registered item handlers to/from the database
2039    const awt_item_type_selector *sel     = global.get_selector();
2040    GBDATA                       *gb_item = unlink ? 0 : sel->current(global.get_root());
2041
2042    for (awt_mask_item_list::iterator h = handlers.begin(); h != handlers.end(); ++h) {
2043         if ((*h)->is_linked_item()) (*h)->to_linked_item()->link_to(gb_item);
2044//         if ((*h)->is_input_handler()) (*h)->to_input_handler()->link_to(gb_item);
2045//         else if ((*h)->is_script_viewport()) (*h)->to_script_viewport()->link_to(gb_item);
2046    }
2047}
2048
2049// -end- of implementation of class awt_input_mask.
2050
2051
2052awt_input_mask_descriptor::awt_input_mask_descriptor(const char *title_, const char *maskname_, const char *itemtypename_, bool local, bool hidden_) {
2053    title                = strdup(title_);
2054    internal_maskname    = (char*)malloc(strlen(maskname_)+2);
2055    internal_maskname[0] = local ? '0' : '1';
2056    strcpy(internal_maskname+1, maskname_);
2057    //     maskname      = strdup(maskname_);
2058    itemtypename         = strdup(itemtypename_);
2059    local_mask           = local;
2060    hidden               = hidden_;
2061}
2062awt_input_mask_descriptor::~awt_input_mask_descriptor() {
2063    free(itemtypename);
2064    free(internal_maskname);
2065    free(title);
2066}
2067awt_input_mask_descriptor::awt_input_mask_descriptor(const awt_input_mask_descriptor& other) {
2068    title             = strdup(other.title);
2069    internal_maskname = strdup(other.internal_maskname);
2070    itemtypename      = strdup(other.itemtypename);
2071    local_mask        = other.local_mask;
2072    hidden            = other.hidden;
2073}
2074awt_input_mask_descriptor& awt_input_mask_descriptor::operator = (const awt_input_mask_descriptor& other) {
2075    if (this != &other) {
2076        free(itemtypename);
2077        free(internal_maskname);
2078        free(title);
2079
2080        title             = strdup(other.title);
2081        internal_maskname = strdup(other.internal_maskname);
2082        itemtypename      = strdup(other.itemtypename);
2083        local_mask        = other.local_mask;
2084        hidden            = other.hidden;
2085    }
2086
2087    return *this;
2088}
2089
2090static bool scanned_existing_input_masks = false;
2091static vector<awt_input_mask_descriptor> existing_masks;
2092
2093static void add_new_input_mask(const string& maskname, const string& fullname, bool local) {
2094    awt_input_mask_descriptor *descriptor = quick_scan_input_mask(maskname, fullname, local);
2095    if (descriptor) {
2096        existing_masks.push_back(*descriptor);
2097        delete descriptor;
2098    }
2099}
2100static void scan_existing_input_masks() {
2101    awt_assert(!scanned_existing_input_masks);
2102
2103    for (int scope = 0; scope <= 1; ++scope) {
2104        const char *dirname = inputMaskDir(scope == AWT_SCOPE_LOCAL);
2105
2106        if (!GB_is_directory(dirname)) {
2107            if (scope == AWT_SCOPE_LOCAL) {         // in local scope
2108                GB_ERROR warning = GB_create_directory(dirname); // try to create directory
2109                if (warning) fprintf(stderr, "Warning: %s\n", warning);
2110            }
2111        }
2112
2113        DIR *dirp = opendir(dirname);
2114        if (!dirp) {
2115            fprintf(stderr, "Warning: No such directory '%s'\n", dirname);
2116        }
2117        else {
2118            struct dirent *dp;
2119            for (dp = readdir(dirp); dp; dp = readdir(dirp)) {
2120                struct stat st;
2121                string      maskname = dp->d_name;
2122                string      fullname = inputMaskFullname(maskname, scope == AWT_SCOPE_LOCAL);
2123
2124                if (stat(fullname.c_str(), &st)) continue;
2125                if (!S_ISREG(st.st_mode)) continue;
2126                // now we have a regular file
2127
2128                size_t ext_pos = maskname.find(".mask");
2129
2130                if (ext_pos != string::npos && maskname.substr(ext_pos) == ".mask") {
2131                    awt_input_mask_descriptor *descriptor = quick_scan_input_mask(maskname, fullname, scope == AWT_SCOPE_LOCAL);
2132                    if (descriptor) { // we found a input mask file
2133                        existing_masks.push_back(*descriptor);
2134                        delete descriptor;
2135                    }
2136                }
2137            }
2138            closedir(dirp);
2139        }
2140    }
2141    scanned_existing_input_masks = true;
2142}
2143
2144const awt_input_mask_descriptor *AWT_look_input_mask(int id) {
2145    if (!scanned_existing_input_masks) scan_existing_input_masks();
2146
2147    if (id<0 || size_t(id) >= existing_masks.size()) return 0;
2148
2149    const awt_input_mask_descriptor *descriptor = &existing_masks[id];
2150    return descriptor;
2151}
2152
2153awt_item_type AWT_getItemType(const string& itemtype_name) {
2154    awt_item_type type = AWT_IT_UNKNOWN;
2155
2156    for (int i = AWT_IT_UNKNOWN+1; i<AWT_IT_TYPES; ++i) {
2157        if (itemtype_name == awt_itemtype_names[i]) {
2158            type = awt_item_type(i);
2159            break;
2160        }
2161    }
2162
2163    return type;
2164}
2165
2166// -----------------------------
2167//      Registered Itemtypes
2168// -----------------------------
2169
2170typedef void (*AWT_OpenMaskWindowCallback)(AW_window* aww, AW_CL cl_id, AW_CL);
2171
2172//  --------------------------------------
2173//      class AWT_registered_itemtype
2174//  --------------------------------------
2175// stores information about so-far-used item types
2176class AWT_registered_itemtype {
2177private:
2178    AW_window_menu_modes       *awm; // the main window responsible for opening windows
2179    AWT_OpenMaskWindowCallback  open_window_cb; // callback to open the window
2180
2181public:
2182    AWT_registered_itemtype() : awm(0), open_window_cb(0) {}
2183    AWT_registered_itemtype(AW_window_menu_modes *awm_, AWT_OpenMaskWindowCallback open_window_cb_)
2184        : awm(awm_)
2185        , open_window_cb(open_window_cb_)
2186    {}
2187    virtual ~AWT_registered_itemtype() {}
2188
2189    AW_window_menu_modes *getWindow() const { return awm; }
2190    AWT_OpenMaskWindowCallback getOpenCb() const { return open_window_cb; }
2191};
2192
2193static map<awt_item_type, AWT_registered_itemtype> registeredTypes;
2194
2195static GB_ERROR openMaskWindowByType(int mask_id, awt_item_type type) {
2196    map<awt_item_type, AWT_registered_itemtype>::const_iterator registered = registeredTypes.find(type);
2197    GB_ERROR                                                    error      = 0;
2198
2199    if (registered == registeredTypes.end()) error = GBS_global_string("Type '%s' not registered (yet)", awt_itemtype_names[type]);
2200    else registered->second.getOpenCb()(registered->second.getWindow(), (AW_CL)mask_id, (AW_CL)0);
2201
2202    return error;
2203}
2204
2205static void registerType(awt_item_type type, AW_window_menu_modes *awm, AWT_OpenMaskWindowCallback open_window_cb) {
2206    map<awt_item_type, AWT_registered_itemtype>::const_iterator alreadyRegistered = registeredTypes.find(type);
2207    if (alreadyRegistered == registeredTypes.end()) {
2208        registeredTypes[type] = AWT_registered_itemtype(awm, open_window_cb);
2209    }
2210#if defined(DEBUG)
2211    else {
2212//         awt_assert(alreadyRegistered->second.getWindow() == awm);
2213        awt_assert(alreadyRegistered->second.getOpenCb() == open_window_cb);
2214    }
2215#endif // DEBUG
2216}
2217
2218// ----------------------------------------------
2219//      Create a new input mask (interactive)
2220// ----------------------------------------------
2221
2222static void create_new_mask_cb(AW_window *aww) {
2223    AW_root *awr = aww->get_root();
2224
2225    string maskname = awr->awar(AWAR_INPUT_MASK_NAME)->read_string();
2226    {
2227        size_t ext = maskname.find(".mask");
2228
2229        if (ext == string::npos) maskname = maskname+".mask";
2230        else maskname                     = maskname.substr(0, ext)+".mask";
2231
2232        awr->awar(AWAR_INPUT_MASK_NAME)->write_string(maskname.c_str());
2233    }
2234
2235
2236    string       itemname     = awr->awar(AWAR_INPUT_MASK_ITEM)->read_string();
2237    int          scope        = awr->awar(AWAR_INPUT_MASK_SCOPE)->read_int();
2238    int          hidden       = awr->awar(AWAR_INPUT_MASK_HIDDEN)->read_int();
2239    bool         local        = scope == AWT_SCOPE_LOCAL;
2240    string       maskfullname = inputMaskFullname(maskname, local);
2241    bool         openMask     = false;
2242    bool         closeWindow  = false;
2243    const char  *error        = 0;
2244    struct stat  st;
2245
2246    if (stat(maskfullname.c_str(), &st) == 0) { // file exists
2247        int answer = aw_question("File does already exist", "Open mask,Cancel");
2248        switch (answer) {
2249            case 0:
2250                openMask   = true;
2251                break;
2252            case 1: break;
2253            default: awt_assert(0); break;
2254        }
2255    }
2256    else {                      // file does not exist
2257        error = GB_create_directory(inputMaskDir(local));
2258        if (!error) {
2259            error = writeDefaultMaskfile(maskfullname, maskname, itemname, hidden);
2260        }
2261        if (!error) {
2262            add_new_input_mask(maskname, maskfullname, local);
2263            openMask    = true;
2264            closeWindow = true;
2265        }
2266    }
2267
2268    if (!error && openMask) {
2269        int           mask_id;
2270        awt_item_type item_type = AWT_IT_UNKNOWN;
2271
2272        for (mask_id = 0; ; ++mask_id) {
2273            const awt_input_mask_descriptor *descriptor = AWT_look_input_mask(mask_id);
2274            if (!descriptor) {
2275                error = GBS_global_string("Can't find descriptor for mask '%s'", maskname.c_str());
2276                break;
2277            }
2278
2279            if (strcmp(descriptor->get_maskname(), maskname.c_str()) == 0 &&
2280                descriptor->is_local_mask() == local)
2281            {
2282                item_type = AWT_getItemType(descriptor->get_itemtypename());
2283                break;          // found wanted mask id
2284            }
2285        }
2286
2287        if (!error) {
2288            error = openMaskWindowByType(mask_id, item_type);
2289        }
2290    }
2291
2292    if (error) aw_message(error);
2293}
2294
2295static void create_new_input_mask(AW_window *aww, AW_CL cl_item_type, AW_CL) { // create new user mask (interactively)
2296    static AW_window_simple *aws = 0;
2297
2298    if (!aws) {
2299        aws = new AW_window_simple;
2300
2301        aws->init(aww->get_root(), "CREATE_USER_MASK", "Create new input mask:");
2302
2303        aws->auto_space(10, 10);
2304
2305        aws->button_length(10);
2306        aws->callback(AW_POPDOWN);
2307        aws->create_button("CLOSE", "CLOSE", 0);
2308        aws->callback(AW_POPUP_HELP,(AW_CL)"input_mask_new.hlp");
2309        aws->create_button("HELP", "HELP","H");
2310
2311        aws->at_newline();
2312
2313        aws->label("Name of new input mask");
2314        aws->create_input_field(AWAR_INPUT_MASK_NAME, 20);
2315
2316        aws->at_newline();
2317
2318        aws->label("Item type");
2319        aws->create_option_menu(AWAR_INPUT_MASK_ITEM, "", "");
2320        for (int i = AWT_IT_UNKNOWN+1; i<AWT_IT_TYPES; ++i) {
2321            aws->insert_option(awt_itemtype_names[i], "", awt_itemtype_names[i]);
2322        }
2323        aws->update_option_menu();
2324
2325        aws->at_newline();
2326
2327        aws->label("Scope");
2328        aws->create_toggle_field(AWAR_INPUT_MASK_SCOPE, 1);
2329        aws->insert_toggle("Local", "L", AWT_SCOPE_LOCAL);
2330        aws->insert_toggle("Global", "G", AWT_SCOPE_GLOBAL);
2331        aws->update_toggle_field();
2332
2333        aws->at_newline();
2334
2335        aws->label("Visibility");
2336        aws->create_toggle_field(AWAR_INPUT_MASK_HIDDEN, 1);
2337        aws->insert_toggle("Normal", "N", 0);
2338        aws->insert_toggle("Hidden", "H", 1);
2339        aws->update_toggle_field();
2340
2341        aws->at_newline();
2342
2343        aws->callback(create_new_mask_cb);
2344        aws->create_button("CREATE", "CREATE","C");
2345
2346        aws->window_fit();
2347    }
2348
2349    aws->activate();
2350    aww->get_root()->awar(AWAR_INPUT_MASK_ITEM)->write_string(awt_itemtype_names[int(cl_item_type)]);
2351}
2352
2353// -----------------------------------------------------
2354//      Create User-Mask-Submenu for any application
2355// -----------------------------------------------------
2356
2357void AWT_create_mask_submenu(AW_window_menu_modes *awm, awt_item_type wanted_item_type, void (*open_window_cb)(AW_window* aww, AW_CL cl_id, AW_CL)) {
2358    // add a user mask submenu at current position
2359    AW_root *awr = awm->get_root();
2360
2361    if (!global_awars_created) create_global_awars(awr);
2362
2363    awm->insert_sub_menu("User Masks", "k");
2364
2365    for (int scope = 0; scope <= 1; ++scope) {
2366        bool entries_made = false;
2367
2368        for (int id = 0; ;++id) {
2369            const awt_input_mask_descriptor *descriptor = AWT_look_input_mask(id);
2370
2371            if (!descriptor) break;
2372            if (descriptor->is_local_mask() != (scope == AWT_SCOPE_LOCAL)) continue; // wrong scope type
2373
2374            awt_item_type item_type = AWT_getItemType(descriptor->get_itemtypename());
2375
2376            if (item_type == wanted_item_type) {
2377                if (!descriptor->is_hidden()) { // do not show masks with hidden-flag
2378                    entries_made = true;
2379                    char *macroname2key = GBS_string_2_key(descriptor->get_internal_maskname());
2380#if defined(DEBUG) && 0
2381                    printf("added user-mask '%s' with id=%i\n", descriptor->get_maskname(), id);
2382#endif // DEBUG
2383                    awm->insert_menu_topic(macroname2key, descriptor->get_title(), "", "input_mask.hlp", AWM_ALL, open_window_cb, (AW_CL)id, (AW_CL)0);
2384                    free(macroname2key);
2385                }
2386                registerType(item_type, awm, open_window_cb);
2387            }
2388            else if (item_type == AWT_IT_UNKNOWN) {
2389                aw_message(GBS_global_string("Unknown @ITEMTYPE '%s' in '%s'", descriptor->get_itemtypename(), descriptor->get_internal_maskname()));
2390            }
2391        }
2392        if (entries_made) awm->insert_separator();
2393    }
2394
2395    {
2396        const char *itemname            = awt_itemtype_names[wanted_item_type];
2397        char       *new_item_mask_id    = GBS_global_string_copy("new_%s_mask", itemname);
2398        char       *new_item_mask_label = GBS_global_string_copy("New %s mask..", itemname);
2399
2400        awm->insert_menu_topic(new_item_mask_id, new_item_mask_label, "N", "input_mask_new.hlp", AWM_ALL, create_new_input_mask, (AW_CL)wanted_item_type, (AW_CL)0);
2401
2402        free(new_item_mask_label);
2403        free(new_item_mask_id);
2404    }   
2405    awm->close_sub_menu();
2406}
2407
2408void AWT_destroy_input_masks() {
2409    input_mask_list.clear();
2410}
2411
Note: See TracBrowser for help on using the repository browser.