| 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 | |
|---|