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 | |
---|
22 | using namespace std; |
---|
23 | |
---|
24 | #define AWAR_QUESTION "tmp/question" |
---|
25 | |
---|
26 | int 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 | |
---|
250 | bool 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 | |
---|
260 | void 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 | |
---|
278 | void 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 | |
---|
301 | void AW_repeated_question::add_help(const char *help_file) { |
---|
302 | freedup(helpfile, help_file); |
---|
303 | } |
---|
304 | |
---|
305 | int 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 | |
---|