source: tags/arb-6.0-rc1/WINDOW/AW_modal.cxx

Last change on this file was 11948, checked in by westram, 11 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.0 KB
Line 
1// ================================================================ //
2//                                                                  //
3//   File      : AW_modal.cxx                                       //
4//   Purpose   : Modal dialogs                                      //
5//                                                                  //
6//   Institute of Microbiology (Technical University Munich)        //
7//   http://www.arb-home.de/                                        //
8//                                                                  //
9// ================================================================ //
10
11#include <aw_window.hxx>
12#include <aw_global.hxx>
13#include <aw_file.hxx>
14#include <aw_awar.hxx>
15#include "aw_root.hxx"
16#include "aw_question.hxx"
17#include "aw_advice.hxx"
18#include "aw_msg.hxx"
19#include "aw_select.hxx"
20#include "aw_window_Xm.hxx"
21
22#include <arbdbt.h>
23#include <arb_strarray.h>
24
25#include <deque>
26#include <string>
27#include <algorithm>
28
29using namespace std;
30
31int aw_message_cb_result;
32
33void message_cb(AW_window*, int result) {
34    if (result == -1) { // exit
35        exit(EXIT_FAILURE);
36    }
37    aw_message_cb_result = result;
38}
39
40unsigned aw_message_timer_listen_event(AW_root *, AW_window *aww) {
41#if defined(TRACE_STATUS_MORE)
42    fprintf(stderr, "in aw_message_timer_listen_event\n"); fflush(stdout);
43#endif // TRACE_STATUS_MORE
44
45    if (aww->is_shown()) {
46        return AW_MESSAGE_LISTEN_DELAY;
47    }
48    return 0;
49}
50
51// -----------------
52//      aw_input
53
54static char *aw_input_cb_result        = 0;
55static int   aw_string_selected_button = -2;
56
57int aw_string_selection_button() {
58    aw_assert(aw_string_selected_button != -2);
59    return aw_string_selected_button;
60}
61
62#define AW_INPUT_AWAR       "tmp/input/string"
63#define AW_INPUT_TITLE_AWAR "tmp/input/title"
64
65#define AW_FILE_SELECT_BASE        "tmp/file_select"
66#define AW_FILE_SELECT_DIR_AWAR    AW_FILE_SELECT_BASE "/directory"
67#define AW_FILE_SELECT_FILE_AWAR   AW_FILE_SELECT_BASE "/file_name"
68#define AW_FILE_SELECT_FILTER_AWAR AW_FILE_SELECT_BASE "/filter"
69#define AW_FILE_SELECT_TITLE_AWAR  AW_FILE_SELECT_BASE "/title"
70
71static void create_input_awars(AW_root *aw_root) {
72    aw_root->awar_string(AW_INPUT_TITLE_AWAR, "", AW_ROOT_DEFAULT);
73    aw_root->awar_string(AW_INPUT_AWAR,       "", AW_ROOT_DEFAULT);
74}
75
76static void create_fileSelection_awars(AW_root *aw_root) {
77    aw_root->awar_string(AW_FILE_SELECT_TITLE_AWAR, "", AW_ROOT_DEFAULT);
78    aw_root->awar_string(AW_FILE_SELECT_DIR_AWAR, "", AW_ROOT_DEFAULT);
79    aw_root->awar_string(AW_FILE_SELECT_FILE_AWAR, "", AW_ROOT_DEFAULT);
80    aw_root->awar_string(AW_FILE_SELECT_FILTER_AWAR, "", AW_ROOT_DEFAULT);
81}
82
83
84// -------------------------
85//      aw_input history
86
87static deque<string> input_history; // front contains newest entries
88
89#if defined(DEBUG)
90// # define TRACE_HISTORY
91#endif // DEBUG
92
93
94#if defined(TRACE_HISTORY)
95static void dumpHistory(const char *where) {
96    printf("History [%s]:\n", where);
97    for (deque<string>::iterator h = input_history.begin(); h != input_history.end(); ++h) {
98        printf("'%s'\n", h->c_str());
99    }
100}
101#endif // TRACE_HISTORY
102
103static void input_history_insert(const char *str, bool front) {
104    string s(str);
105
106    if (input_history.empty()) {
107        input_history.push_front(""); // insert an empty string into history
108    }
109    else {
110        deque<string>::iterator found = find(input_history.begin(), input_history.end(), s);
111        if (found != input_history.end()) {
112            input_history.erase(found);
113        }
114    }
115    if (front) {
116        input_history.push_front(s);
117    }
118    else {
119        input_history.push_back(s);
120    }
121
122#if defined(TRACE_HISTORY)
123    dumpHistory(GBS_global_string("input_history_insert('%s', front=%i)", str, front));
124#endif // TRACE_HISTORY
125}
126
127void input_history_cb(AW_window *aw, int mode) {
128    // mode: -1 = '<' +1 = '>'
129    AW_root *aw_root = aw->get_root();
130    AW_awar *awar    = aw_root->awar(AW_INPUT_AWAR);
131    char    *content = awar->read_string();
132
133    if (content) input_history_insert(content, mode == 1);
134
135    if (!input_history.empty()) {
136        if (mode == -1) {
137            string s = input_history.front();
138            awar->write_string(s.c_str());
139            input_history.pop_front();
140            input_history.push_back(s);
141        }
142        else {
143            string s = input_history.back();
144            awar->write_string(s.c_str());
145            input_history.pop_back();
146            input_history.push_front(s);
147        }
148    }
149
150#if defined(TRACE_HISTORY)
151    dumpHistory(GBS_global_string("input_history_cb(mode=%i)", mode));
152#endif // TRACE_HISTORY
153
154    free(content);
155}
156
157void input_cb(AW_window *aw, int buttonNr) {
158    // any previous contents were passed to client (who is responsible to free the resources)
159    // so DON'T free aw_input_cb_result here:
160    aw_input_cb_result        = 0;
161    aw_string_selected_button = buttonNr;
162
163    if (buttonNr >= 0) { // <0 = cancel button -> no result
164        // create heap-copy of result -> client will get the owner
165        aw_input_cb_result = aw->get_root()->awar(AW_INPUT_AWAR)->read_as_string();
166    }
167}
168
169void file_selection_cb(AW_window *aw, int ok_cancel_flag) {
170    // any previous contents were passed to client (who is responsible to free the resources)
171    // so DON'T free aw_input_cb_result here:
172    aw_input_cb_result        = 0;
173    aw_string_selected_button = ok_cancel_flag;
174
175    if (ok_cancel_flag >= 0) { // <0 = cancel button -> no result
176        // create heap-copy of result -> client will get the owner
177        aw_input_cb_result = aw->get_root()->awar(AW_FILE_SELECT_FILE_AWAR)->read_as_string();
178    }
179}
180
181#define INPUT_SIZE 50           // size of input prompts in aw_input and aw_string_selection
182
183static AW_window_message *new_input_window(AW_root *root, const char *title, const char *buttons) {
184    // helper for aw_input and aw_string_selection
185    //
186    // 'buttons' comma separated list of button names (buttons starting with \n force a newline)
187
188    AW_window_message *aw_msg = new AW_window_message;
189
190    aw_msg->init(root, title, false);
191
192    aw_msg->label_length(0);
193    aw_msg->auto_space(10, 10);
194
195    aw_msg->at(10, 10);
196    aw_msg->button_length(INPUT_SIZE+1);
197    aw_msg->create_button(0, AW_INPUT_TITLE_AWAR);
198
199    aw_msg->at_newline();
200    aw_msg->create_input_field(AW_INPUT_AWAR, INPUT_SIZE);
201
202    size_t        butCount = 2;                     // ok and cancel
203    ConstStrArray button_names;
204    int           maxlen   = 6;                     // use as min.length for buttons (for 'CANCEL')
205
206    if (buttons) {
207        GBT_split_string(button_names, buttons, ',');
208        butCount = button_names.size();
209
210        for (size_t b = 0; b<butCount; b++) {
211            int len = strlen(button_names[b]);
212            if (len>maxlen) maxlen = len;
213        }
214
215    }
216
217    aw_msg->button_length(maxlen+1);
218
219#define MAXBUTTONSPERLINE 5
220
221    aw_msg->at_newline();
222    aw_msg->callback(makeWindowCallback(input_history_cb, -1)); aw_msg->create_button("bwd", "<<", 0);
223    aw_msg->callback(makeWindowCallback(input_history_cb,  1)); aw_msg->create_button("fwd", ">>", 0);
224    size_t thisLine = 2;
225
226    // @@@ add a history button (opening a window with elements from history)
227
228    if (butCount>(MAXBUTTONSPERLINE-thisLine) && butCount <= MAXBUTTONSPERLINE) { // approx. 5 buttons (2+3) fit into one line
229        aw_msg->at_newline();
230        thisLine = 0;
231    }
232
233    if (buttons) {
234        for (size_t b = 0; b<butCount; b++) {
235            const char *name    = button_names[b];
236            bool        forceLF = name[0] == '\n';
237
238            if (thisLine >= MAXBUTTONSPERLINE || forceLF) {
239                aw_msg->at_newline();
240                thisLine = 0;
241                if (forceLF) name++;
242            }
243            aw_msg->callback(makeWindowCallback(input_cb, int(b))); // use b == 0 as result for 1st button, 1 for 2nd button, etc.
244            aw_msg->create_button(name, name, "");
245            thisLine++;
246        }
247    }
248    else {
249        aw_msg->callback(makeWindowCallback(input_cb,  0)); aw_msg->create_button("OK", "OK", "O");
250        aw_msg->callback(makeWindowCallback(input_cb, -1)); aw_msg->create_button("CANCEL", "CANCEL", "C");
251    }
252
253    return aw_msg;
254}
255
256char *aw_input(const char *title, const char *prompt, const char *default_input) {
257    // prompt user to enter a string
258    //
259    // title         = title of window
260    // prompt        = question
261    // default_input = default for answer (NULL -> "")
262    //
263    // result is NULL, if cancel was pressed
264    // otherwise result contains the user input (maybe an empty string)
265
266    static AW_window_message *aw_msg = 0;
267
268    AW_root *root = AW_root::SINGLETON;
269    if (!aw_msg) create_input_awars(root); // first call -> create awars
270
271    root->awar(AW_INPUT_TITLE_AWAR)->write_string(prompt);
272    aw_assert(strlen(prompt) <= INPUT_SIZE);
273
274    AW_awar *inAwar = root->awar(AW_INPUT_AWAR);
275    if (default_input) {
276        input_history_insert(default_input, true); // insert default into history
277        inAwar->write_string(default_input);
278    }
279    else {
280        inAwar->write_string("");
281    }
282
283    aw_assert(GB_get_transaction_level(inAwar->gb_var) <= 0); // otherwise history would not work
284
285    if (!aw_msg) aw_msg = new_input_window(root, title, NULL);
286    else aw_msg->set_window_title(title);
287
288    aw_msg->window_fit();
289    aw_msg->show_modal();
290    char dummy[]       = "";
291    aw_input_cb_result = dummy;
292
293    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
294    {
295        LocallyModify<bool> flag(root->disable_callbacks, true);
296        while (aw_input_cb_result == dummy) {
297            root->process_events();
298        }
299    }
300    aw_msg->hide();
301
302    if (aw_input_cb_result) input_history_insert(aw_input_cb_result, true);
303    return aw_input_cb_result;
304}
305
306char *aw_input(const char *prompt, const char *default_input) {
307    return aw_input("Enter string", prompt, default_input);
308}
309
310static char *aw_input2awar(const char *title, const char *prompt, const char *awar_name) {
311    AW_root *aw_root       = AW_root::SINGLETON;
312    AW_awar *awar          = aw_root->awar(awar_name);
313    char    *default_value = awar->read_string();
314    char    *result        = aw_input(title, prompt, default_value);
315
316    awar->write_string(result);
317    free(default_value);
318
319    return result;
320}
321
322char *aw_input2awar(const char *prompt, const char *awar_name) {
323    return aw_input2awar("Enter string", prompt, awar_name);
324}
325
326
327char *aw_string_selection(const char *title, const char *prompt, const char *default_input,
328                          const char *value_list, const char *buttons, char *(*check_fun)(const char*)) {
329    // A modal input window. A String may be entered by hand or selected from value_list
330    //
331    //      title           window title
332    //      prompt          prompt at input field
333    //      default_input   default value (if NULL => "").
334    //      value_list      Existing selections (separated by ';') or NULL if no selection exists
335    //      buttons         String containing answer button names separated by ',' (default is "OK,Cancel")
336    //                      Use aw_string_selected_button() to detect which has been pressed.
337    //      check_fun       function to correct input (or NULL for no check). The function may return NULL to indicate no correction
338    //
339    // returns the value of the inputfield
340
341    static GB_HASH *str_sels = 0; // for each 'buttons' store window + selection list
342
343    if (!str_sels) str_sels = GBS_create_hash(100, GB_MIND_CASE);
344
345    struct str_sel_data {
346        AW_window_message *aw_msg;
347        AW_selection_list *sel;
348    };
349
350    const char   *bkey = buttons ? buttons : ",default,";
351    str_sel_data *sd      = (str_sel_data*)GBS_read_hash(str_sels, bkey);
352    if (!sd) {
353        sd         = new str_sel_data;
354        sd->aw_msg = 0;
355        sd->sel    = 0;
356
357        GBS_write_hash(str_sels, bkey, (long)sd);
358    }
359
360    AW_window_message *& aw_msg = sd->aw_msg;
361    AW_selection_list *& sel    = sd->sel;
362
363    AW_root *root = AW_root::SINGLETON;
364    if (!aw_msg) create_input_awars(root); // first call -> create awars
365
366    root->awar(AW_INPUT_TITLE_AWAR)->write_string(prompt);
367    aw_assert(strlen(prompt) <= INPUT_SIZE);
368
369    AW_awar *inAwar = root->awar(AW_INPUT_AWAR);
370    if (default_input) {
371        input_history_insert(default_input, true); // insert default into history
372        inAwar->write_string(default_input);
373    }
374    else {
375        inAwar->write_string("");
376    }
377
378    aw_assert(GB_get_transaction_level(inAwar->gb_var) <= 0); // otherwise history would not work
379
380    if (!aw_msg) {
381        aw_msg = new_input_window(root, title, buttons);
382
383        aw_msg->at_newline();
384        sel = aw_msg->create_selection_list(AW_INPUT_AWAR, INPUT_SIZE, 10);
385        sel->insert_default("", "");
386        sel->update();
387    }
388    else {
389        aw_msg->set_window_title(title);
390    }
391    aw_msg->window_fit();
392
393    // update the selection box :
394    aw_assert(sel);
395    sel->clear();
396    if (value_list) {
397        char *values = strdup(value_list);
398        char *word;
399
400        for (word = strtok(values, ";"); word; word = strtok(0, ";")) {
401            sel->insert(word, word);
402        }
403        free(values);
404    }
405    sel->insert_default("<new>", "");
406    sel->update();
407
408    // do modal loop :
409    aw_msg->show_modal();
410    char dummy[] = "";
411    aw_input_cb_result = dummy;
412
413    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
414    {
415        LocallyModify<bool> flag(root->disable_callbacks, true);
416
417        char *last_input = root->awar(AW_INPUT_AWAR)->read_string();
418        while (aw_input_cb_result == dummy) {
419            root->process_events();
420
421            char *this_input = root->awar(AW_INPUT_AWAR)->read_string();
422            if (strcmp(this_input, last_input) != 0) {
423                if (check_fun) {
424                    char *corrected_input = check_fun(this_input);
425                    if (corrected_input) {
426                        if (strcmp(corrected_input, this_input) != 0) {
427                            root->awar(AW_INPUT_AWAR)->write_string(corrected_input);
428                        }
429                        free(corrected_input);
430                    }
431                }
432                reassign(last_input, this_input);
433            }
434            free(this_input);
435
436            if (!aw_msg->is_shown()) { // somebody hided/closed the window
437                input_cb(aw_msg, -1); // CANCEL
438                break;
439            }
440        }
441
442        free(last_input);
443    }
444    aw_msg->hide();
445
446    return aw_input_cb_result;
447}
448
449char *aw_string_selection2awar(const char *title, const char *prompt, const char *awar_name, const char *value_list, const char *buttons, char *(*check_fun)(const char*)) {
450    // params see aw_string_selection
451    // default_value is taken from and result is written back to awar 'awar_name'
452
453    AW_root *aw_root       = AW_root::SINGLETON;
454    AW_awar *awar          = aw_root->awar(awar_name);
455    char    *default_value = awar->read_string();
456    char    *result        = aw_string_selection(title, prompt, default_value, value_list, buttons, check_fun);
457
458    awar->write_string(result);
459    free(default_value);
460
461    return result;
462}
463
464// --------------------------
465//      aw_file_selection
466
467char *aw_file_selection(const char *title, const char *dir, const char *def_name, const char *suffix) {
468    AW_root *root = AW_root::SINGLETON;
469
470    static AW_window_simple *aw_msg = 0;
471    if (!aw_msg) create_fileSelection_awars(root);
472
473    {
474        char *edir      = GBS_eval_env(dir);
475        char *edef_name = GBS_eval_env(def_name);
476
477        root->awar(AW_FILE_SELECT_TITLE_AWAR) ->write_string(title);
478        root->awar(AW_FILE_SELECT_DIR_AWAR)   ->write_string(edir);
479        root->awar(AW_FILE_SELECT_FILE_AWAR)  ->write_string(edef_name);
480        root->awar(AW_FILE_SELECT_FILTER_AWAR)->write_string(suffix);
481
482        free(edef_name);
483        free(edir);
484    }
485
486    if (!aw_msg) {
487        aw_msg = new AW_window_simple;
488
489        aw_msg->init(root, "AW_FILE_SELECTION", "File selection");
490        aw_msg->allow_delete_window(false); // disable closing the window
491
492        aw_msg->load_xfig("fileselect.fig");
493
494        aw_msg->at("title");
495        aw_msg->create_button(0, AW_FILE_SELECT_TITLE_AWAR);
496
497        AW_create_standard_fileselection(aw_msg, AW_FILE_SELECT_BASE);
498
499        aw_msg->button_length(7);
500
501        aw_msg->at("ok");
502        aw_msg->callback(makeWindowCallback(file_selection_cb, 0));
503        aw_msg->create_button("OK", "OK", "O");
504
505        aw_msg->at("cancel");
506        aw_msg->callback(makeWindowCallback(file_selection_cb, -1));
507        aw_msg->create_button("CANCEL", "CANCEL", "C");
508
509        aw_msg->window_fit();
510    }
511
512    aw_msg->show_modal();
513    char dummy[] = "";
514    aw_input_cb_result = dummy;
515
516    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
517    {
518        LocallyModify<bool> flag(root->disable_callbacks, true);
519        while (aw_input_cb_result == dummy) {
520            root->process_events();
521        }
522    }
523    aw_msg->hide();
524
525    return aw_input_cb_result;
526}
527
528
Note: See TracBrowser for help on using the repository browser.