source: tags/ms_r16q3/WINDOW/AW_question.cxx

Last change on this file was 15176, checked in by westram, 8 years ago
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1//  ==================================================================== //
2//                                                                       //
3//    File      : AW_question.cxx                                        //
4//    Purpose   :                                                        //
5//                                                                       //
6//  Coded by Ralf Westram (coder@reallysoft.de) in January 2002          //
7//  Copyright Department of Microbiology (Technical University Munich)   //
8//                                                                       //
9//  Visit our web site at: http://www.arb-home.de/                       //
10//  ==================================================================== //
11
12#include <arbdb.h>
13#include <aw_msg.hxx>
14#include <aw_question.hxx>
15#include "aw_root.hxx"
16#include "aw_awar.hxx"
17#include "aw_global.hxx"
18#include "aw_window.hxx"
19#include "aw_window_Xm.hxx"
20#include "aw_advice.hxx"
21
22using namespace std;
23
24#define AWAR_QUESTION "tmp/question"
25
26int aw_question(const char *unique_id, const char *question, const char *buttons, bool sameSizeButtons, const char *helpfile) {
27    /*! Ask the user a question. Blocks all UI input until the answer is provided.
28     *
29     * @param  unique_id       Unique ID to identify the question. Must be valid hkey.
30     * @param  question        The question.
31     * @param  buttons         Comma separated list of button names. A button named starting
32     *                         with "^" will begin a new row of buttons. A button named "EXIT"
33     *                         will cause abnormal (EXIT_FAILURE) termination of program.
34     * @param  sameSizeButtons Make all buttons have the same size.
35     * @param  helpfile        Adds a "HELP" button. May be NULL. (currently ignored)
36     * @return the index of the selected answer
37     */
38
39    aw_assert(buttons);
40
41    AW_awar *awar_neverAskAgain = NULL;
42    if (unique_id) {
43        GB_ERROR error = GB_check_key(unique_id);
44        if (error) {
45            aw_message(error);
46            unique_id = NULL;
47        }
48        else {
49            awar_neverAskAgain = AW_root::SINGLETON->awar_int(GBS_global_string("answers/%s", unique_id), 0, AW_ROOT_DEFAULT);
50        }
51    }
52
53    int result = awar_neverAskAgain ? awar_neverAskAgain->read_int() : 0;
54    if (result>0) { // have auto-answer
55        --result;
56    }
57    else { // no auto-answer
58        if (!question) question = "No question?! Please report this as a bug.";
59
60        char *button_list = strdup(buttons ? buttons : "OK");
61        if (button_list[0] == 0) {
62            freedup(button_list, "Maybe ok,EXIT");
63            GBK_dump_backtrace(stderr, "Empty buttonlist");
64            question = GBS_global_string_copy("%s\n"
65                                              "(Program error - Unsure what happens when you click ok\n"
66                                              " Check console for backtrace and report error)",
67                                              question);
68        }
69
70        AW_root *root       = AW_root::SINGLETON;
71        AW_awar *awar_quest = root->awar_string(AWAR_QUESTION);
72        awar_quest->write_string(question);
73
74        size_t question_length, question_lines;
75        aw_detect_text_size(question, question_length, question_lines);
76
77        // hash key to find matching window
78        char *hindex = GBS_global_string_copy("%s$%s$%zu$%zu$%i$%s",
79                                              button_list, unique_id ? unique_id : "<NOID>",
80                                              question_length, question_lines, int(sameSizeButtons),
81                                              helpfile ? helpfile : "");
82
83        static GB_HASH    *hash_windows = 0;
84        if (!hash_windows) hash_windows = GBS_create_hash(256, GB_MIND_CASE);
85        AW_window_message *aw_msg       = (AW_window_message *)GBS_read_hash(hash_windows, hindex);
86
87#if defined(DEBUG)
88        printf("hindex='%s'\n", hindex);
89#endif // DEBUG
90
91        if (!aw_msg) {
92            aw_msg = new AW_window_message;
93            GBS_write_hash(hash_windows, hindex, (long)aw_msg);
94            {
95                char *wid = GBS_string_2_key(GBS_global_string("QUESTION BOX %s", unique_id));
96                aw_msg->init(root, wid, "QUESTION BOX", false);
97                free(wid);
98            }
99            aw_msg->recalc_size_atShow(AW_RESIZE_DEFAULT); // force size recalc (ignores user size)
100
101            aw_msg->label_length(10);
102
103            aw_msg->at(10, 10);
104            aw_msg->auto_space(10, 10);
105
106            aw_msg->button_length(question_length+3);
107            aw_msg->button_height(question_lines+1);
108
109            aw_msg->create_button(0, AWAR_QUESTION);
110
111            aw_msg->button_height(0);
112
113            aw_msg->at_newline();
114
115            if (sameSizeButtons) {
116                size_t  max_button_length = helpfile ? 4 : 0;
117                char   *pos               = button_list;
118
119                while (1) {
120                    char *comma       = strchr(pos, ',');
121                    if (!comma) comma = strchr(pos, 0);
122
123                    size_t len                                   = comma-pos;
124                    if (len>max_button_length) max_button_length = len;
125
126                    if (!comma[0]) break;
127                    pos = comma+1;
128                }
129
130                aw_msg->button_length(max_button_length+2);
131            }
132            else {
133                aw_msg->button_length(0);
134            }
135
136            // insert the buttons:
137            char *ret              = strtok(button_list, ",");
138            bool  help_button_done = false;
139            int   counter          = 0;
140
141            while (ret) {
142                if (ret[0] == '^') {
143                    if (helpfile && !help_button_done) {
144                        aw_msg->callback(makeHelpCallback(helpfile));
145                        aw_msg->create_button("HELP", "HELP", "H");
146                        help_button_done = true;
147                    }
148                    aw_msg->at_newline();
149                    ++ret;
150                }
151                if (strcmp(ret, "EXIT") == 0) {
152                    aw_msg->callback(makeWindowCallback(message_cb, -1));
153                }
154                else {
155                    aw_msg->callback(makeWindowCallback(message_cb, counter++));
156                }
157
158                if (sameSizeButtons) {
159                    aw_msg->create_button(0, ret);
160                }
161                else {
162                    aw_msg->create_autosize_button(0, ret);
163                }
164                ret = strtok(NULL, ",");
165            }
166
167            if (helpfile && !help_button_done) { // if not done above
168                aw_msg->callback(makeHelpCallback(helpfile));
169                aw_msg->create_button("HELP", "HELP", "H");
170                help_button_done = true;
171            }
172
173            // create no-repeat checkbox if we have a unique-id
174            if (awar_neverAskAgain) {
175                aw_msg->at_newline();
176                const char *label = counter>1 ? "Never ask again" : "Never notify me again";
177                aw_msg->label_length(strlen(label));
178                aw_msg->label(label);
179                aw_msg->create_toggle(awar_neverAskAgain->awar_name);
180            }
181
182            aw_msg->window_fit();
183        }
184        else {
185#if defined(DEBUG)
186            printf("[Reusing existing aw_question-window]\n");
187#endif
188        }
189        free(hindex);
190        aw_msg->show_modal();
191
192        free(button_list);
193        aw_message_cb_result = -13;
194
195#if defined(TRACE_STATUS_MORE)
196        fprintf(stderr, "add aw_message_timer_listen_event with delay = %i\n", AW_MESSAGE_LISTEN_DELAY); fflush(stdout);
197#endif // TRACE_STATUS_MORE
198        root->add_timed_callback_never_disabled(AW_MESSAGE_LISTEN_DELAY, makeTimedCallback(aw_message_timer_listen_event, static_cast<AW_window*>(aw_msg)));
199
200        {
201            LocallyModify<bool> flag(root->disable_callbacks, true);
202            while (aw_message_cb_result == -13) {
203                root->process_events();
204            }
205        }
206        aw_msg->hide();
207
208        // if we have an awar and no-repeat got checked,
209        // store the result and warn user about what he's done.
210        if (awar_neverAskAgain && awar_neverAskAgain->read_int()) {
211            int answerCode = aw_message_cb_result >= 0 ? aw_message_cb_result+1 : 0;
212            awar_neverAskAgain->write_int(answerCode);
213
214            if (answerCode>0) {
215                const char *appname = AW_root::SINGLETON->program_name;
216                char       *advice  = GBS_global_string_copy
217                    ("You will not be asked that question again in this session.\n"
218                     "%s will always assume the answer you just gave.\n"
219                     "\n"
220                     "After restarting %s that question will be asked again.\n"
221                     "To disable that question permanently for future sessions,\n"
222                     "you need to save properties.\n"
223                     "\n"
224                     "Depending on the type of question, disabling it might be\n"
225                     "helpful or obstructive.\n"
226                     "Disabled questions can be reactivated from the properties menu.\n",
227                     appname, appname);
228
229                AW_advice(advice, AW_ADVICE_TOGGLE, "Disabling questions", "questions.hlp");
230                free(advice);
231            }
232        }
233
234        result = aw_message_cb_result;
235    }
236
237    switch (result) {
238        case -1:                // exit with core
239            fprintf(stderr, "Core dump requested\n");
240            ARB_SIGSEGV(1);
241            break;
242        case -2:                // exit without core
243            exit(-1);
244            break;
245    }
246
247    return result;
248}
249
250bool aw_ask_sure(const char *unique_id, const char *msg) {
251    /*!
252     * pop up a modal yes/no question
253     * @param  unique_id If given, the dialog will get an "do not show again" checkbox
254     * @param  msg       The question.
255     * @return True if the answer was "Yes"
256     */
257    return aw_question(unique_id, msg, "Yes,No", true, NULL) == 0;
258}
259
260void aw_popup_ok(const char *msg) {
261    /*!
262     * Pop up a modal message with an Ok button
263     * @param msg The message.
264     */
265    aw_question(NULL, msg, "Ok", true, NULL);
266}
267
268__ATTR__NORETURN void aw_popup_exit(const char *msg) {
269    /*!
270     * Pop up a modal message with an Exit button.
271     * Won't return but exit with "EXIT_FAILURE"
272     */
273    aw_question(NULL, msg, "EXIT", true, NULL);
274    aw_assert(0); // should not be reached
275    exit(EXIT_FAILURE);
276}
277
278void AW_reactivate_all_questions(AW_window*) {
279    GB_transaction  ta(AW_ROOT_DEFAULT);
280    GBDATA         *gb_neverAskedAgain = GB_search(AW_ROOT_DEFAULT, "answers", GB_FIND);
281    const char     *msg                = "No questions were disabled yet.";
282
283    if (gb_neverAskedAgain) {
284        int reactivated = 0;
285        for (GBDATA *gb_q = GB_child(gb_neverAskedAgain); gb_q; gb_q = GB_nextChild(gb_q)) {
286            if (GB_read_int(gb_q)) {
287                GB_write_int(gb_q, 0);
288                reactivated++;
289            }
290        }
291        if (reactivated) {
292            msg = GBS_global_string("Reactivated %i questions (for this session)\n"
293                                    "To reactivate them for future sessions, save properties.",
294                                    reactivated);
295        }
296    }
297    aw_message(msg);
298}
299
300
301void AW_repeated_question::add_help(const char *help_file) {
302    freedup(helpfile, help_file);
303}
304
305int AW_repeated_question::get_answer(const char *unique_id, const char *question, const char *buttons, const char *to_all, bool add_abort)
306{
307    if (!buttons_used) {
308        buttons_used = strdup(buttons);
309    }
310    else {
311        // do not use the same instance of AW_repeated_question with different buttons!
312        assert_or_exit(strcmp(buttons_used, buttons) == 0);
313    }
314
315    if (answer == -1 || !dont_ask_again) {
316
317        char   *all             = GBS_global_string_copy(" (%s)", to_all);
318        int     all_len         = strlen(all);
319        size_t  but_len         = strlen(buttons);
320        size_t  new_buttons_len = but_len*3+1+(add_abort ? 6 : 0)+all_len*3;
321        char   *new_buttons     = ARB_alloc<char>(new_buttons_len);
322        int     button_count    = 0; // number of buttons in 'buttons'
323
324        { // transform "YES,NO"  ->   "YES,YES (to_all),^NO,NO (to_all)" or "YES (to_all),NO (to_all)"
325            char       *w       = new_buttons;
326            const char *r       = buttons;
327
328            while (1) {
329                const char *comma = strchr(r, ',');
330                if (!comma) comma = strchr(r, 0);
331                int         len   = comma-r;
332
333                if (!dont_ask_again) {
334                    if (w>new_buttons) *w++ = '^'; // not in front of first button
335                    memcpy(w, r, len); w += len;
336                    *w++ = ',';
337                }
338                memcpy(w, r, len); w       += len;
339                memcpy(w, all, all_len); w += all_len;
340                *w++ = ',';
341
342                button_count++;
343
344                if (!comma[0]) break;
345                r = comma+1;
346            }
347            if (add_abort) {
348                const char *abort      = "^ABORT";
349                strcpy(w, abort); w += strlen(abort);
350            }
351            else {
352                --w; // delete comma at end
353            }
354            w[0] = 0;
355
356            aw_assert(size_t(w-new_buttons) < new_buttons_len); // oops buffer overflow
357
358            free(all);
359        }
360
361        int user_answer = aw_question(unique_id, question, new_buttons, true, helpfile);
362
363        if (dont_ask_again) {   // ask question as normal when called first (dont_ask_again later)
364            answer = user_answer;
365        }
366        else {
367            answer         = user_answer/2;
368            dont_ask_again = (user_answer%2) || (user_answer == (button_count*2));
369        }
370
371        free(new_buttons);
372
373        aw_assert(answer<(button_count+(add_abort ? 1 : 0)));
374    }
375
376    aw_assert(answer != -1);
377
378    return answer;
379}
380
Note: See TracBrowser for help on using the repository browser.