source: trunk/WINDOW/AW_question.cxx

Last change on this file was 18463, checked in by westram, 5 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 NULp. (currently ignored)
36     * @return the index of the selected answer
37     */
38
39    aw_assert(buttons);
40
41    AW_awar *awar_neverAskAgain = NULp;
42    if (unique_id) {
43        GB_ERROR error = GB_check_key(unique_id);
44        if (error) {
45            aw_message(error);
46            unique_id = NULp;
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                                              null2empty(helpfile));
82
83        static GB_HASH    *hash_windows = NULp;
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, "ARB prompt", 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(NULp, 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(NULp, ret);
160                }
161                else {
162                    aw_msg->create_autosize_button(NULp, ret);
163                }
164                ret = strtok(NULp, ",");
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, NULp) == 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(NULp, msg, "Ok", true, NULp);
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(NULp, msg, "EXIT", true, NULp);
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    if (!buttons_used) {
307        buttons_used = strdup(buttons);
308    }
309    else {
310        // do not use the same instance of AW_repeated_question with different buttons!
311        assert_or_exit(strcmp(buttons_used, buttons) == 0);
312    }
313
314    if (answer == -1 || !dont_ask_again) {
315
316        char   *all             = GBS_global_string_copy(" (%s)", to_all);
317        int     all_len         = strlen(all);
318        size_t  but_len         = strlen(buttons);
319        size_t  new_buttons_len = but_len*3+1+(add_abort ? 6 : 0)+all_len*3;
320        char   *new_buttons     = ARB_alloc<char>(new_buttons_len);
321        int     button_count    = 0; // number of buttons in 'buttons'
322
323        { // transform "YES,NO"  ->   "YES,YES (to_all),^NO,NO (to_all)" or "YES (to_all),NO (to_all)"
324            char       *w       = new_buttons;
325            const char *r       = buttons;
326
327            while (1) {
328                const char *comma = strchr(r, ',');
329                if (!comma) comma = strchr(r, 0);
330                int         len   = comma-r;
331
332                if (!dont_ask_again) {
333                    if (w>new_buttons) *w++ = '^'; // not in front of first button
334                    memcpy(w, r, len); w += len;
335                    *w++ = ',';
336                }
337                memcpy(w, r, len); w       += len;
338                memcpy(w, all, all_len); w += all_len;
339                *w++ = ',';
340
341                button_count++;
342
343                if (!comma[0]) break;
344                r = comma+1;
345            }
346            if (add_abort) {
347                const char *abort      = "^ABORT";
348                strcpy(w, abort); w += strlen(abort);
349            }
350            else {
351                --w; // delete comma at end
352            }
353            w[0] = 0;
354
355            aw_assert(size_t(w-new_buttons) < new_buttons_len); // oops buffer overflow
356
357            free(all);
358        }
359
360        int user_answer = aw_question(unique_id, question, new_buttons, true, helpfile);
361
362        if (dont_ask_again) {   // ask question as normal when called first (dont_ask_again later)
363            answer = user_answer;
364        }
365        else {
366            answer         = user_answer/2;
367            dont_ask_again = (user_answer%2) || (user_answer == (button_count*2));
368        }
369
370        free(new_buttons);
371
372        aw_assert(answer<(button_count+(add_abort ? 1 : 0)));
373    }
374
375    aw_assert(answer != -1);
376
377    return answer;
378}
379
Note: See TracBrowser for help on using the repository browser.