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 *uniqueID, const char *question, const char *buttons, bool fixedSizeButtons, const char *helpfile) { |
---|
27 | // return 0 for first button, 1 for second button, 2 for third button, ... |
---|
28 | // |
---|
29 | // the single buttons are separated by commas (e.g. "YES,NO") |
---|
30 | // If the button-name starts with ^ it starts a new row of buttons |
---|
31 | // (if a button named 'EXIT' is pressed the program terminates using exit(EXIT_FAILURE)) |
---|
32 | // |
---|
33 | // The remaining arguments only apply if 'buttons' is non-zero: |
---|
34 | // |
---|
35 | // If fixedSizeButtons is true all buttons have the same size |
---|
36 | // otherwise the size for every button is set depending on the text length |
---|
37 | // |
---|
38 | // If 'helpfile' is non-zero a HELP button is added. |
---|
39 | |
---|
40 | aw_assert(buttons); |
---|
41 | |
---|
42 | AW_root *root = AW_root::SINGLETON; |
---|
43 | |
---|
44 | char *awar_name_neverAskAgain = NULL; |
---|
45 | int have_auto_answer = 0; |
---|
46 | |
---|
47 | if (uniqueID) { |
---|
48 | GB_ERROR error = GB_check_key(uniqueID); |
---|
49 | if (error) { |
---|
50 | aw_message(error); |
---|
51 | uniqueID = NULL; |
---|
52 | } |
---|
53 | else { |
---|
54 | awar_name_neverAskAgain = GBS_global_string_copy("answers/%s", uniqueID); |
---|
55 | AW_awar *awar_neverAskAgain = root->awar_int(awar_name_neverAskAgain, 0, AW_ROOT_DEFAULT); |
---|
56 | have_auto_answer = awar_neverAskAgain->read_int(); |
---|
57 | } |
---|
58 | } |
---|
59 | |
---|
60 | if (have_auto_answer>0) { |
---|
61 | aw_message_cb_result = have_auto_answer-1; |
---|
62 | } |
---|
63 | else { |
---|
64 | char *button_list = strdup(buttons ? buttons : "OK"); |
---|
65 | if (button_list[0] == 0) { |
---|
66 | freedup(button_list, "Maybe ok,EXIT"); |
---|
67 | GBK_dump_backtrace(stderr, "Empty buttonlist"); |
---|
68 | question = GBS_global_string_copy("%s\n" |
---|
69 | "(Program error - Unsure what happens when you click ok\n" |
---|
70 | " Check console for backtrace and report error)", |
---|
71 | question); |
---|
72 | } |
---|
73 | |
---|
74 | AW_awar *awar_quest = root->awar_string(AWAR_QUESTION); |
---|
75 | if (!question) question = "<oops - no question?!>"; |
---|
76 | awar_quest->write_string(question); |
---|
77 | |
---|
78 | size_t question_length, question_lines; |
---|
79 | aw_detect_text_size(question, question_length, question_lines); |
---|
80 | |
---|
81 | // hash key to find matching window |
---|
82 | char *hindex = GBS_global_string_copy("%s$%s$%zu$%zu$%i$%s", |
---|
83 | button_list, uniqueID ? uniqueID : "<NOID>", |
---|
84 | question_length, question_lines, int(fixedSizeButtons), |
---|
85 | helpfile ? helpfile : ""); |
---|
86 | |
---|
87 | static GB_HASH *hash_windows = 0; |
---|
88 | if (!hash_windows) hash_windows = GBS_create_hash(256, GB_MIND_CASE); |
---|
89 | AW_window_message *aw_msg = (AW_window_message *)GBS_read_hash(hash_windows, hindex); |
---|
90 | |
---|
91 | #if defined(DEBUG) |
---|
92 | printf("hindex='%s'\n", hindex); |
---|
93 | #endif // DEBUG |
---|
94 | |
---|
95 | if (!aw_msg) { |
---|
96 | aw_msg = new AW_window_message; |
---|
97 | GBS_write_hash(hash_windows, hindex, (long)aw_msg); |
---|
98 | |
---|
99 | aw_msg->init(root, "QUESTION BOX", false); |
---|
100 | aw_msg->recalc_size_atShow(AW_RESIZE_DEFAULT); // force size recalc (ignores user size) |
---|
101 | |
---|
102 | aw_msg->label_length(10); |
---|
103 | |
---|
104 | aw_msg->at(10, 10); |
---|
105 | aw_msg->auto_space(10, 10); |
---|
106 | |
---|
107 | aw_msg->button_length(question_length+3); |
---|
108 | aw_msg->button_height(question_lines+1); |
---|
109 | |
---|
110 | aw_msg->create_button(0, AWAR_QUESTION); |
---|
111 | |
---|
112 | aw_msg->button_height(0); |
---|
113 | |
---|
114 | aw_msg->at_newline(); |
---|
115 | |
---|
116 | if (fixedSizeButtons) { |
---|
117 | size_t max_button_length = helpfile ? 4 : 0; |
---|
118 | char *pos = button_list; |
---|
119 | |
---|
120 | while (1) { |
---|
121 | char *comma = strchr(pos, ','); |
---|
122 | if (!comma) comma = strchr(pos, 0); |
---|
123 | |
---|
124 | size_t len = comma-pos; |
---|
125 | if (len>max_button_length) max_button_length = len; |
---|
126 | |
---|
127 | if (!comma[0]) break; |
---|
128 | pos = comma+1; |
---|
129 | } |
---|
130 | |
---|
131 | aw_msg->button_length(max_button_length+2); |
---|
132 | } |
---|
133 | else { |
---|
134 | aw_msg->button_length(0); |
---|
135 | } |
---|
136 | |
---|
137 | // insert the buttons: |
---|
138 | char *ret = strtok(button_list, ","); |
---|
139 | bool help_button_done = false; |
---|
140 | int counter = 0; |
---|
141 | |
---|
142 | while (ret) { |
---|
143 | if (ret[0] == '^') { |
---|
144 | if (helpfile && !help_button_done) { |
---|
145 | aw_msg->callback(makeHelpCallback(helpfile)); |
---|
146 | aw_msg->create_button("HELP", "HELP", "H"); |
---|
147 | help_button_done = true; |
---|
148 | } |
---|
149 | aw_msg->at_newline(); |
---|
150 | ++ret; |
---|
151 | } |
---|
152 | if (strcmp(ret, "EXIT") == 0) { |
---|
153 | aw_msg->callback(makeWindowCallback(message_cb, -1)); |
---|
154 | } |
---|
155 | else { |
---|
156 | aw_msg->callback(makeWindowCallback(message_cb, counter++)); |
---|
157 | } |
---|
158 | |
---|
159 | if (fixedSizeButtons) { |
---|
160 | aw_msg->create_button(0, ret); |
---|
161 | } |
---|
162 | else { |
---|
163 | aw_msg->create_autosize_button(0, ret); |
---|
164 | } |
---|
165 | ret = strtok(NULL, ","); |
---|
166 | } |
---|
167 | |
---|
168 | if (helpfile && !help_button_done) { // if not done above |
---|
169 | aw_msg->callback(makeHelpCallback(helpfile)); |
---|
170 | aw_msg->create_button("HELP", "HELP", "H"); |
---|
171 | help_button_done = true; |
---|
172 | } |
---|
173 | |
---|
174 | if (uniqueID) { |
---|
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_name_neverAskAgain); |
---|
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 (awar_name_neverAskAgain) { |
---|
209 | AW_awar *awar_neverAskAgain = root->awar(awar_name_neverAskAgain); |
---|
210 | |
---|
211 | if (awar_neverAskAgain->read_int()) { // user checked "Never ask again" |
---|
212 | int givenAnswer = aw_message_cb_result >= 0 ? aw_message_cb_result+1 : 0; |
---|
213 | awar_neverAskAgain->write_int(givenAnswer); // store given answer for "never asking again" |
---|
214 | |
---|
215 | if (givenAnswer && strchr(buttons, ',') != 0) { |
---|
216 | const char *appname = root->program_name; |
---|
217 | char *advice = GBS_global_string_copy("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 | "When you restart %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 doing that might be\n" |
---|
225 | "helpful or obstructive.\n" |
---|
226 | "Disabled questions can be reactivated from the properties menu.\n", |
---|
227 | appname, appname); |
---|
228 | AW_advice(advice, AW_ADVICE_TOGGLE, "Disabling questions", NULL); |
---|
229 | free(advice); |
---|
230 | } |
---|
231 | } |
---|
232 | } |
---|
233 | } |
---|
234 | |
---|
235 | free(awar_name_neverAskAgain); |
---|
236 | |
---|
237 | switch (aw_message_cb_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 | return aw_message_cb_result; |
---|
247 | } |
---|
248 | |
---|
249 | bool aw_ask_sure(const char *uniqueID, const char *msg) { |
---|
250 | return aw_question(uniqueID, msg, "Yes,No", true, NULL) == 0; |
---|
251 | } |
---|
252 | void aw_popup_ok(const char *msg) { |
---|
253 | aw_question(NULL, msg, "Ok", true, NULL); |
---|
254 | } |
---|
255 | void aw_popup_exit(const char *msg) { |
---|
256 | aw_question(NULL, msg, "EXIT", true, NULL); |
---|
257 | aw_assert(0); // should not be reached |
---|
258 | exit(EXIT_FAILURE); |
---|
259 | } |
---|
260 | |
---|
261 | void AW_reactivate_all_questions(AW_window*) { |
---|
262 | GB_transaction ta(AW_ROOT_DEFAULT); |
---|
263 | GBDATA *gb_neverAskedAgain = GB_search(AW_ROOT_DEFAULT, "answers", GB_FIND); |
---|
264 | const char *msg = "No questions were disabled yet."; |
---|
265 | |
---|
266 | if (gb_neverAskedAgain) { |
---|
267 | int reactivated = 0; |
---|
268 | for (GBDATA *gb_q = GB_child(gb_neverAskedAgain); gb_q; gb_q = GB_nextChild(gb_q)) { |
---|
269 | if (GB_read_int(gb_q)) { |
---|
270 | GB_write_int(gb_q, 0); |
---|
271 | reactivated++; |
---|
272 | } |
---|
273 | } |
---|
274 | if (reactivated) { |
---|
275 | msg = GBS_global_string("Reactivated %i questions (for this session)\n" |
---|
276 | "To reactivate them for future sessions, save properties.", |
---|
277 | reactivated); |
---|
278 | } |
---|
279 | } |
---|
280 | aw_message(msg); |
---|
281 | } |
---|
282 | |
---|
283 | |
---|
284 | void AW_repeated_question::add_help(const char *help_file) { |
---|
285 | freedup(helpfile, help_file); |
---|
286 | } |
---|
287 | |
---|
288 | int AW_repeated_question::get_answer(const char *uniqueID, const char *question, const char *buttons, const char *to_all, bool add_abort) |
---|
289 | { |
---|
290 | if (!buttons_used) { |
---|
291 | buttons_used = strdup(buttons); |
---|
292 | } |
---|
293 | else { |
---|
294 | // do not use the same instance of AW_repeated_question with different buttons! |
---|
295 | assert_or_exit(strcmp(buttons_used, buttons) == 0); |
---|
296 | } |
---|
297 | |
---|
298 | if (answer == -1 || !dont_ask_again) { |
---|
299 | |
---|
300 | char *all = GBS_global_string_copy(" (%s)", to_all); |
---|
301 | int all_len = strlen(all); |
---|
302 | size_t but_len = strlen(buttons); |
---|
303 | size_t new_buttons_len = but_len*3+1+(add_abort ? 6 : 0)+all_len*3; |
---|
304 | char *new_buttons = (char*)malloc(new_buttons_len); |
---|
305 | int button_count = 0; // number of buttons in 'buttons' |
---|
306 | |
---|
307 | { // transform "YES,NO" -> "YES,YES (to_all),^NO,NO (to_all)" or "YES (to_all),NO (to_all)" |
---|
308 | char *w = new_buttons; |
---|
309 | const char *r = buttons; |
---|
310 | |
---|
311 | while (1) { |
---|
312 | const char *comma = strchr(r, ','); |
---|
313 | if (!comma) comma = strchr(r, 0); |
---|
314 | int len = comma-r; |
---|
315 | |
---|
316 | if (!dont_ask_again) { |
---|
317 | if (w>new_buttons) *w++ = '^'; // not in front of first button |
---|
318 | memcpy(w, r, len); w += len; |
---|
319 | *w++ = ','; |
---|
320 | } |
---|
321 | memcpy(w, r, len); w += len; |
---|
322 | memcpy(w, all, all_len); w += all_len; |
---|
323 | *w++ = ','; |
---|
324 | |
---|
325 | button_count++; |
---|
326 | |
---|
327 | if (!comma[0]) break; |
---|
328 | r = comma+1; |
---|
329 | } |
---|
330 | if (add_abort) { |
---|
331 | const char *abort = "^ABORT"; |
---|
332 | strcpy(w, abort); w += strlen(abort); |
---|
333 | } |
---|
334 | else { |
---|
335 | --w; // delete comma at end |
---|
336 | } |
---|
337 | w[0] = 0; |
---|
338 | |
---|
339 | aw_assert(size_t(w-new_buttons) < new_buttons_len); // oops buffer overflow |
---|
340 | |
---|
341 | free(all); |
---|
342 | } |
---|
343 | |
---|
344 | int user_answer = aw_question(uniqueID, question, new_buttons, true, helpfile); |
---|
345 | |
---|
346 | if (dont_ask_again) { // ask question as normal when called first (dont_ask_again later) |
---|
347 | answer = user_answer; |
---|
348 | } |
---|
349 | else { |
---|
350 | answer = user_answer/2; |
---|
351 | dont_ask_again = (user_answer%2) || (user_answer == (button_count*2)); |
---|
352 | } |
---|
353 | |
---|
354 | free(new_buttons); |
---|
355 | |
---|
356 | aw_assert(answer<(button_count+(add_abort ? 1 : 0))); |
---|
357 | } |
---|
358 | |
---|
359 | aw_assert(answer != -1); |
---|
360 | |
---|
361 | return answer; |
---|
362 | } |
---|
363 | |
---|