ARB
AW_question.cxx
Go to the documentation of this file.
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) {
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 
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, "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(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  {
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) {
257  return aw_question(unique_id, msg, "Yes,No", true, NULp) == 0;
258 }
259 
260 void aw_popup_ok(const char *msg) {
265  aw_question(NULp, msg, "Ok", true, NULp);
266 }
267 
268 __ATTR__NORETURN void aw_popup_exit(const char *msg) {
273  aw_question(NULp, msg, "EXIT", true, NULp);
274  aw_assert(0); // should not be reached
275  exit(EXIT_FAILURE);
276 }
277 
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 
GB_ERROR GB_check_key(const char *key) __ATTR__USERESULT
Definition: adstring.cxx:85
string result
group_matcher all()
Definition: test_unit.h:1000
void aw_detect_text_size(const char *text, size_t &width, size_t &height)
Definition: AW_button.cxx:403
#define AW_MESSAGE_LISTEN_DELAY
long GB_read_int(GBDATA *gbd)
Definition: arbdb.cxx:699
GBDATA * GB_child(GBDATA *father)
Definition: adquery.cxx:322
long GBS_write_hash(GB_HASH *hs, const char *key, long val)
Definition: adhash.cxx:457
long read_int() const
Definition: AW_awar.cxx:187
const char * GBS_global_string(const char *templat,...)
Definition: arb_msg.cxx:204
void AW_advice(const char *message, AW_Advice_Type type, const char *title, const char *corresponding_help)
Show a message box with an advice for the user.
Definition: AW_advice.cxx:160
STL namespace.
void add_timed_callback_never_disabled(int ms, const TimedCallback &tcb)
Definition: AW_root.cxx:541
char * GBS_string_2_key(const char *str)
Definition: adstring.cxx:52
#define ARB_SIGSEGV(backtrace)
Definition: arb_assert.h:174
char * program_name
Definition: aw_root.hxx:113
unsigned aw_message_timer_listen_event(AW_root *, AW_window *aww)
Definition: AW_modal.cxx:40
#define AWAR_QUESTION
Definition: AW_question.cxx:24
static AW_root * SINGLETON
Definition: aw_root.hxx:102
WindowCallback makeHelpCallback(const char *helpfile)
Definition: aw_window.hxx:106
fflush(stdout)
void message_cb(AW_window *, int result)
Definition: AW_modal.cxx:33
void aw_popup_ok(const char *msg)
#define aw_assert(bed)
Definition: aw_position.hxx:29
void show_modal()
Definition: AW_window.cxx:1801
static void error(const char *msg)
Definition: mkptypes.cxx:96
void process_events()
Definition: AW_root.cxx:42
Definition: arbdb.h:86
void AW_reactivate_all_questions(AW_window *)
#define EXIT_FAILURE
Definition: arb_a2ps.c:157
GB_ERROR GB_write_int(GBDATA *gbd, long i)
Definition: arbdb.cxx:1220
long int flag
Definition: f2c.h:39
int aw_message_cb_result
Definition: AW_modal.cxx:31
char * awar_name
Definition: aw_awar.hxx:103
bool aw_ask_sure(const char *unique_id, const char *msg)
int aw_question(const char *unique_id, const char *question, const char *buttons, bool sameSizeButtons, const char *helpfile)
Definition: AW_question.cxx:26
AW_awar * awar_int(const char *var_name, long default_value=0, AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:580
#define assert_or_exit(cond)
Definition: arb_assert.h:263
bool disable_callbacks
Definition: aw_root.hxx:114
void GBK_dump_backtrace(FILE *out, const char *message)
Definition: arb_msg.cxx:428
void aw_message(const char *msg)
Definition: AW_status.cxx:932
void hide()
Definition: AW_window.cxx:1807
__ATTR__NORETURN void aw_popup_exit(const char *msg)
#define NULp
Definition: cxxforward.h:97
#define __ATTR__NORETURN
Definition: attributes.h:56
GB_ERROR write_string(const char *aw_string)
GBDATA * GB_nextChild(GBDATA *child)
Definition: adquery.cxx:326
GB_transaction ta(gb_var)
AW_awar * awar_string(const char *var_name, const char *default_value="", AW_default default_file=AW_ROOT_DEFAULT)
Definition: AW_root.cxx:570
GBDATA * GB_search(GBDATA *gbd, const char *fieldpath, GB_TYPES create)
Definition: adquery.cxx:531
#define AW_ROOT_DEFAULT
Definition: aw_base.hxx:106
GB_ERROR write_int(long aw_int)
long GBS_read_hash(const GB_HASH *hs, const char *key)
Definition: adhash.cxx:395
char * GBS_global_string_copy(const char *templat,...)
Definition: arb_msg.cxx:195
const char * label
GB_HASH * GBS_create_hash(long estimated_elements, GB_CASE case_sens)
Definition: adhash.cxx:253