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

Last change on this file was 12267, checked in by westram, 10 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.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
187    // - a buttonname starting with - marks the abort button (only one possible)
188    // - buttonnames starting with \n force a newline
189    // (has to be '-\nNAME' if combined)
190
191    aw_assert(buttons);
192
193    AW_window_message *aw_msg = new AW_window_message;
194
195    aw_msg->init(root, title, false);
196
197    aw_msg->label_length(0);
198    aw_msg->auto_space(10, 10);
199
200    aw_msg->at(10, 10);
201    aw_msg->button_length(INPUT_SIZE+1);
202    aw_msg->create_button(0, AW_INPUT_TITLE_AWAR);
203
204    aw_msg->at_newline();
205    aw_msg->create_input_field(AW_INPUT_AWAR, INPUT_SIZE);
206
207    ConstStrArray button_names;
208    int           maxlen = 0; // track max. button length (used as min.length for buttons)
209
210    GBT_split_string(button_names, buttons, ',');
211    size_t butCount = button_names.size();
212
213    int abortButton = -1; // index of abort button (-1 means 'none')
214    for (size_t b = 0; b<butCount; b++) {
215        if (button_names[b][0] == '-') {
216            aw_assert(abortButton<0); // only one abort button possible!
217            abortButton        = b;
218            button_names.replace(b, button_names[b]+1); // point behind '-'
219        }
220        int len = strlen(button_names[b]);
221        if (len>maxlen) maxlen = len;
222    }
223
224    aw_msg->button_length(maxlen+1);
225
226#define MAXBUTTONSPERLINE 5
227
228    aw_msg->at_newline();
229    aw_msg->callback(makeWindowCallback(input_history_cb, -1)); aw_msg->create_button("bwd", "<<", 0);
230    aw_msg->callback(makeWindowCallback(input_history_cb,  1)); aw_msg->create_button("fwd", ">>", 0);
231    size_t thisLine = 2;
232
233    if (butCount>(MAXBUTTONSPERLINE-thisLine) && butCount <= MAXBUTTONSPERLINE) { // approx. 5 buttons (2+3) fit into one line
234        aw_msg->at_newline();
235        thisLine = 0;
236    }
237
238    for (size_t b = 0; b<butCount; b++) {
239        const char *name    = button_names[b];
240        bool        forceLF = name[0] == '\n';
241
242        if (thisLine >= MAXBUTTONSPERLINE || forceLF) {
243            aw_msg->at_newline();
244            thisLine = 0;
245            if (forceLF) name++;
246        }
247
248        // use 0 as result for 1st button, 1 for 2nd button, etc.
249        // use -1 for abort button
250        int resultCode = b == abortButton ? -1 : b;
251        aw_msg->callback(makeWindowCallback(input_cb, resultCode));
252        aw_msg->create_button(name, name, "");
253        thisLine++;
254    }
255
256    return aw_msg;
257}
258
259char *aw_input(const char *title, const char *prompt, const char *default_input) {
260    // prompt user to enter a string
261    //
262    // title         = title of window
263    // prompt        = question
264    // default_input = default for answer (NULL -> "")
265    //
266    // result is NULL, if cancel was pressed
267    // otherwise result contains the user input (maybe an empty string)
268
269    static AW_window_message *aw_msg = 0;
270
271    AW_root *root = AW_root::SINGLETON;
272    if (!aw_msg) create_input_awars(root); // first call -> create awars
273
274    root->awar(AW_INPUT_TITLE_AWAR)->write_string(prompt);
275    aw_assert(strlen(prompt) <= INPUT_SIZE);
276
277    AW_awar *inAwar = root->awar(AW_INPUT_AWAR);
278    if (default_input) {
279        input_history_insert(default_input, true); // insert default into history
280        inAwar->write_string(default_input);
281    }
282    else {
283        inAwar->write_string("");
284    }
285
286    aw_assert(GB_get_transaction_level(inAwar->gb_var) <= 0); // otherwise history would not work
287
288    if (!aw_msg) aw_msg = new_input_window(root, title, "Ok,-Abort");
289    else aw_msg->set_window_title(title);
290
291    aw_msg->window_fit();
292    aw_msg->show_modal();
293    char dummy[]       = "";
294    aw_input_cb_result = dummy;
295
296    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
297    {
298        LocallyModify<bool> flag(root->disable_callbacks, true);
299        while (aw_input_cb_result == dummy) {
300            root->process_events();
301        }
302    }
303    aw_msg->hide();
304
305    if (aw_input_cb_result) input_history_insert(aw_input_cb_result, true);
306    return aw_input_cb_result;
307}
308
309char *aw_input(const char *prompt, const char *default_input) {
310    return aw_input("Enter string", prompt, default_input);
311}
312
313char *aw_string_selection(const char *title, const char *prompt, const char *default_input, const char *value_list, const char *buttons) {
314    // A modal input window. A String may be entered by hand or selected from value_list
315    //
316    //      title           window title
317    //      prompt          prompt at input field
318    //      default_input   default value (if NULL => "").
319    //      value_list      Existing selections (separated by ';') or NULL if no selection exists
320    //      buttons         String containing answer button names separated by ',' (default is "Ok,-Abort")
321    //                      Use aw_string_selected_button() to detect which has been pressed.
322    //
323    // returns the value of the inputfield
324
325    static GB_HASH *str_sels = 0; // for each 'buttons' store window + selection list
326
327    if (!str_sels) str_sels = GBS_create_hash(100, GB_MIND_CASE);
328
329    struct str_sel_data {
330        AW_window_message *aw_msg;
331        AW_selection_list *sel;
332    };
333
334    // create one window for each button combination
335    const char   *bkey = buttons ? buttons : ",default,";
336    str_sel_data *sd   = (str_sel_data*)GBS_read_hash(str_sels, bkey);
337    if (!sd) {
338        sd         = new str_sel_data;
339        sd->aw_msg = 0;
340        sd->sel    = 0;
341
342        GBS_write_hash(str_sels, bkey, (long)sd);
343    }
344
345    AW_window_message *& aw_msg = sd->aw_msg;
346    AW_selection_list *& sel    = sd->sel;
347
348    AW_root *root = AW_root::SINGLETON;
349    if (!aw_msg) create_input_awars(root); // first call -> create awars
350
351    root->awar(AW_INPUT_TITLE_AWAR)->write_string(prompt);
352    aw_assert(strlen(prompt) <= INPUT_SIZE);
353
354    AW_awar *inAwar = root->awar(AW_INPUT_AWAR);
355    if (default_input) {
356        input_history_insert(default_input, true); // insert default into history
357        inAwar->write_string(default_input);
358    }
359    else {
360        inAwar->write_string("");
361    }
362
363    aw_assert(GB_get_transaction_level(inAwar->gb_var) <= 0); // otherwise history would not work
364
365    if (!aw_msg) {
366        aw_msg = new_input_window(root, title, buttons ? buttons : "Ok,-Abort");
367
368        aw_msg->at_newline();
369        sel = aw_msg->create_selection_list(AW_INPUT_AWAR, INPUT_SIZE, 10, false);
370        sel->insert_default("", "");
371        sel->update();
372    }
373    else {
374        aw_msg->set_window_title(title);
375    }
376    aw_msg->window_fit();
377
378    // update the selection box :
379    aw_assert(sel);
380    sel->clear();
381    if (value_list) {
382        char *values = strdup(value_list);
383        char *word;
384
385        for (word = strtok(values, ";"); word; word = strtok(0, ";")) {
386            sel->insert(word, word);
387        }
388        free(values);
389    }
390    sel->insert_default("<new>", "");
391    sel->update();
392
393    // do modal loop :
394    aw_msg->show_modal();
395    char dummy[] = "";
396    aw_input_cb_result = dummy;
397
398    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
399    {
400        LocallyModify<bool> flag(root->disable_callbacks, true);
401
402        char *last_input = root->awar(AW_INPUT_AWAR)->read_string();
403        while (aw_input_cb_result == dummy) {
404            root->process_events();
405
406            char *this_input = root->awar(AW_INPUT_AWAR)->read_string();
407            reassign(last_input, this_input);
408
409            if (!aw_msg->is_shown()) { // somebody hided/closed the window
410                input_cb(aw_msg, -1); // CANCEL
411                break;
412            }
413        }
414
415        free(last_input);
416    }
417    aw_msg->hide();
418
419    return aw_input_cb_result;
420}
421
422char *aw_string_selection2awar(const char *title, const char *prompt, const char *awar_name, const char *value_list, const char *buttons) {
423    // params see aw_string_selection
424    //
425    // default for string is taken from awar 'awar_name'.
426    // result is written back to awar.
427    // if abort button is pressed, old value is written back to awar.
428
429    AW_root *aw_root   = AW_root::SINGLETON;
430    AW_awar *awar      = aw_root->awar(awar_name);
431    char    *old_value = awar->read_string();
432    char    *result    = aw_string_selection(title, prompt, old_value, value_list, buttons);
433
434    awar->write_string(result ? result : old_value);
435    free(old_value);
436
437    return result;
438}
439
440// --------------------------
441//      aw_file_selection
442
443char *aw_file_selection(const char *title, const char *dir, const char *def_name, const char *suffix) {
444    AW_root *root = AW_root::SINGLETON;
445
446    static AW_window_simple *aw_msg = 0;
447    if (!aw_msg) create_fileSelection_awars(root);
448
449    {
450        char *edir      = GBS_eval_env(dir);
451        char *edef_name = GBS_eval_env(def_name);
452
453        root->awar(AW_FILE_SELECT_TITLE_AWAR) ->write_string(title);
454        root->awar(AW_FILE_SELECT_DIR_AWAR)   ->write_string(edir);
455        root->awar(AW_FILE_SELECT_FILE_AWAR)  ->write_string(edef_name);
456        root->awar(AW_FILE_SELECT_FILTER_AWAR)->write_string(suffix);
457
458        free(edef_name);
459        free(edir);
460    }
461
462    if (!aw_msg) {
463        aw_msg = new AW_window_simple;
464
465        aw_msg->init(root, "AW_FILE_SELECTION", "File selection");
466        aw_msg->allow_delete_window(false); // disable closing the window
467
468        aw_msg->load_xfig("fileselect.fig");
469
470        aw_msg->at("title");
471        aw_msg->create_button(0, AW_FILE_SELECT_TITLE_AWAR);
472
473        AW_create_standard_fileselection(aw_msg, AW_FILE_SELECT_BASE);
474
475        aw_msg->button_length(7);
476
477        aw_msg->at("ok");
478        aw_msg->callback(makeWindowCallback(file_selection_cb, 0));
479        aw_msg->create_button("OK", "OK", "O");
480
481        aw_msg->at("cancel");
482        aw_msg->callback(makeWindowCallback(file_selection_cb, -1));
483        aw_msg->create_button("CANCEL", "CANCEL", "C");
484
485        aw_msg->window_fit();
486    }
487
488    aw_msg->show_modal();
489    char dummy[] = "";
490    aw_input_cb_result = dummy;
491
492    root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
493    {
494        LocallyModify<bool> flag(root->disable_callbacks, true);
495        while (aw_input_cb_result == dummy) {
496            root->process_events();
497        }
498    }
499    aw_msg->hide();
500
501    return aw_input_cb_result;
502}
503
504
Note: See TracBrowser for help on using the repository browser.