1 //
2 // "$Id: fl_ask.cxx 8616 2011-04-20 14:54:42Z AlbrechtS $"
3 //
4 // Standard dialog functions for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2011 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
17 //
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 // http://www.fltk.org/str.php
26 //
27
28 // Implementation of fl_message, fl_ask, fl_choice, fl_input
29 // The three-message fl_show_x functions are for forms compatibility
30 // mostly. In most cases it is easier to get a multi-line message
31 // by putting newlines in the message.
32
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include "flstring.h"
36
37 #include <FL/Fl.H>
38
39 #include <FL/fl_ask.H>
40
41 #include <FL/Fl_Box.H>
42 #include <FL/Fl_Button.H>
43 #include <FL/Fl_Return_Button.H>
44 #include <FL/Fl_Window.H>
45 #include <FL/Fl_Input.H>
46 #include <FL/Fl_Secret_Input.H>
47 #include <FL/x.H>
48 #include <FL/fl_draw.H>
49
50 static Fl_Window *message_form;
51 static Fl_Box *message;
52 static Fl_Box *icon;
53 static Fl_Button *button[3];
54 static Fl_Input *input;
55 static int ret_val;
56 static const char *iconlabel = "?";
57 static const char *message_title_default;
58 Fl_Font fl_message_font_ = FL_HELVETICA;
59 Fl_Fontsize fl_message_size_ = -1;
60 static int enableHotspot = 1;
61 #ifdef __APPLE__
62 extern "C" void NSBeep(void);
63 #endif
64
65 static char avoidRecursion = 0;
66
67 // Sets the global return value (ret_val) and closes the window.
68 // Note: this is used for the button callbacks and the window
69 // callback (closing the window with the close button or menu).
70 // The first argument (Fl_Widget *) can either be an Fl_Button*
71 // pointer to one of the buttons or an Fl_Window* pointer to the
72 // message window (message_form).
button_cb(Fl_Widget *,void * val)73 static void button_cb(Fl_Widget *, void *val) {
74 ret_val = (fl_intptr_t)val;
75 message_form->hide();
76 }
77
makeform()78 static Fl_Window *makeform() {
79 if (message_form) {
80 message_form->size(410,103);
81 return message_form;
82 }
83 // make sure that the dialog does not become the child of some
84 // current group
85 Fl_Group *previously_current_group = Fl_Group::current();
86 Fl_Group::current(0);
87 // create a new top level window
88 Fl_Window *w = message_form = new Fl_Window(410,103);
89 message_form->callback(button_cb,(void *)0);
90 // w->clear_border();
91 // w->box(FL_UP_BOX);
92 (message = new Fl_Box(60, 25, 340, 20))
93 ->align(FL_ALIGN_LEFT|FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
94 (input = new Fl_Input(60, 37, 340, 23))->hide();
95 {Fl_Box* o = icon = new Fl_Box(10, 10, 50, 50);
96 o->box(FL_THIN_UP_BOX);
97 o->labelfont(FL_TIMES_BOLD);
98 o->labelsize(34);
99 o->color(FL_WHITE);
100 o->labelcolor(FL_BLUE);
101 }
102 w->end(); // don't add the buttons automatically
103 // create the buttons (right to left)
104 for (int b=0, x=310; b<3; b++, x -= 100) {
105 if (b==1)
106 button[b] = new Fl_Return_Button(x, 70, 90, 23);
107 else
108 button[b] = new Fl_Button(x, 70, 90, 23);
109 button[b]->align(FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
110 button[b]->callback(button_cb,(void *)b);
111 }
112 button[0]->shortcut(FL_Escape);
113 // add the buttons (left to right)
114 for (int b=2; b>=0; b--)
115 w->add(button[b]);
116 w->begin();
117 w->resizable(new Fl_Box(60,10,110-60,27));
118 w->end();
119 w->set_modal();
120 Fl_Group::current(previously_current_group);
121 return w;
122 }
123
124 /*
125 * 'resizeform()' - Resize the form and widgets so that they hold everything
126 * that is asked of them...
127 */
128
resizeform()129 void resizeform() {
130 int i;
131 int message_w, message_h;
132 int text_height;
133 int button_w[3], button_h[3];
134 int x, w, h, max_w, max_h;
135 const int icon_size = 50;
136
137 fl_font(message->labelfont(), message->labelsize());
138 message_w = message_h = 0;
139 fl_measure(message->label(), message_w, message_h);
140
141 message_w += 10;
142 message_h += 10;
143 if (message_w < 340)
144 message_w = 340;
145 if (message_h < 30)
146 message_h = 30;
147
148 fl_font(button[0]->labelfont(), button[0]->labelsize());
149
150 memset(button_w, 0, sizeof(button_w));
151 memset(button_h, 0, sizeof(button_h));
152
153 for (max_h = 25, i = 0; i < 3; i ++)
154 if (button[i]->visible())
155 {
156 fl_measure(button[i]->label(), button_w[i], button_h[i]);
157
158 if (i == 1)
159 button_w[1] += 20;
160
161 button_w[i] += 30;
162 button_h[i] += 10;
163
164 if (button_h[i] > max_h)
165 max_h = button_h[i];
166 }
167
168 if (input->visible()) text_height = message_h + 25;
169 else text_height = message_h;
170
171 max_w = message_w + 10 + icon_size;
172 w = button_w[0] + button_w[1] + button_w[2] - 10;
173
174 if (w > max_w)
175 max_w = w;
176
177 message_w = max_w - 10 - icon_size;
178
179 w = max_w + 20;
180 h = max_h + 30 + text_height;
181
182 message_form->size(w, h);
183 message_form->size_range(w, h, w, h);
184
185 message->resize(20 + icon_size, 10, message_w, message_h);
186 icon->resize(10, 10, icon_size, icon_size);
187 icon->labelsize(icon_size - 10);
188 input->resize(20 + icon_size, 10 + message_h, message_w, 25);
189
190 for (x = w, i = 0; i < 3; i ++)
191 if (button_w[i])
192 {
193 x -= button_w[i];
194 button[i]->resize(x, h - 10 - max_h, button_w[i] - 10, max_h);
195
196 // printf("button %d (%s) is %dx%d+%d,%d\n", i, button[i]->label(),
197 // button[i]->w(), button[i]->h(),
198 // button[i]->x(), button[i]->y());
199 }
200 }
201
innards(const char * fmt,va_list ap,const char * b0,const char * b1,const char * b2)202 static int innards(const char* fmt, va_list ap,
203 const char *b0,
204 const char *b1,
205 const char *b2)
206 {
207 Fl::pushed(0); // stop dragging (STR #2159)
208
209 avoidRecursion = 1;
210
211 makeform();
212 char buffer[1024];
213 if (!strcmp(fmt,"%s")) {
214 message->label(va_arg(ap, const char*));
215 } else {
216 ::vsnprintf(buffer, 1024, fmt, ap);
217 message->label(buffer);
218 }
219
220 message->labelfont(fl_message_font_);
221 if (fl_message_size_ == -1)
222 message->labelsize(FL_NORMAL_SIZE);
223 else
224 message->labelsize(fl_message_size_);
225 if (b0) {button[0]->show(); button[0]->label(b0); button[1]->position(210,70);}
226 else {button[0]->hide(); button[1]->position(310,70);}
227 if (b1) {button[1]->show(); button[1]->label(b1);}
228 else button[1]->hide();
229 if (b2) {button[2]->show(); button[2]->label(b2);}
230 else button[2]->hide();
231 const char* prev_icon_label = icon->label();
232 if (!prev_icon_label) icon->label(iconlabel);
233
234 resizeform();
235
236 if (button[1]->visible() && !input->visible())
237 button[1]->take_focus();
238 if (enableHotspot)
239 message_form->hotspot(button[0]);
240 if (b0 && Fl_Widget::label_shortcut(b0))
241 button[0]->shortcut(0);
242 else
243 button[0]->shortcut(FL_Escape);
244
245 // set default window title, if defined and a specific title is not set
246 if (!message_form->label() && message_title_default)
247 message_form->label(message_title_default);
248
249 // deactivate Fl::grab(), because it is incompatible with modal windows
250 Fl_Window* g = Fl::grab();
251 if (g) Fl::grab(0);
252 message_form->show();
253 while (message_form->shown()) Fl::wait();
254 if (g) // regrab the previous popup menu, if there was one
255 Fl::grab(g);
256 icon->label(prev_icon_label);
257 message_form->label(0); // reset window title
258
259 avoidRecursion = 0;
260 return ret_val;
261 }
262
263 /** \addtogroup group_comdlg
264 @{ */
265
266 // pointers you can use to change FLTK to another language:
267 const char* fl_no = "No"; ///< string pointer used in common dialogs, you can change it to another language
268 const char* fl_yes= "Yes"; ///< string pointer used in common dialogs, you can change it to another language
269 const char* fl_ok = "OK"; ///< string pointer used in common dialogs, you can change it to another language
270 const char* fl_cancel= "Cancel"; ///< string pointer used in common dialogs, you can change it to another language
271 const char* fl_close= "Close"; ///< string pointer used in common dialogs, you can change it to another language
272
273 // fltk functions:
274 /**
275 Emits a system beep message.
276 \note \#include <FL/fl_ask.H>
277 */
fl_beep(int type)278 void fl_beep(int type) {
279 #ifdef WIN32
280 switch (type) {
281 case FL_BEEP_QUESTION :
282 case FL_BEEP_PASSWORD :
283 MessageBeep(MB_ICONQUESTION);
284 break;
285 case FL_BEEP_MESSAGE :
286 MessageBeep(MB_ICONASTERISK);
287 break;
288 case FL_BEEP_NOTIFICATION :
289 MessageBeep(MB_ICONASTERISK);
290 break;
291 case FL_BEEP_ERROR :
292 MessageBeep(MB_ICONERROR);
293 break;
294 default :
295 MessageBeep(0xFFFFFFFF);
296 break;
297 }
298 #elif defined(__APPLE__)
299 switch (type) {
300 case FL_BEEP_DEFAULT :
301 case FL_BEEP_ERROR :
302 NSBeep();
303 break;
304 default :
305 break;
306 }
307 #else
308 switch (type) {
309 case FL_BEEP_DEFAULT :
310 case FL_BEEP_ERROR :
311 if (!fl_display) fl_open_display();
312
313 XBell(fl_display, 100);
314 break;
315 default :
316 if (!fl_display) fl_open_display();
317
318 XBell(fl_display, 50);
319 break;
320 }
321 #endif // WIN32
322 }
323
324 /** Shows an information message dialog box.
325
326 \note Common dialog boxes are application modal. No more than one common dialog box
327 can be open at any time. Requests for additional dialog boxes are ignored.
328 \note \#include <FL/fl_ask.H>
329
330
331 \param[in] fmt can be used as an sprintf-like format and variables for the message text
332 */
fl_message(const char * fmt,...)333 void fl_message(const char *fmt, ...) {
334
335 if (avoidRecursion) return;
336
337 va_list ap;
338
339 fl_beep(FL_BEEP_MESSAGE);
340
341 va_start(ap, fmt);
342 iconlabel = "i";
343 innards(fmt, ap, 0, fl_close, 0);
344 va_end(ap);
345 iconlabel = "?";
346 }
347
348 /** Shows an alert message dialog box
349
350 \note Common dialog boxes are application modal. No more than one common dialog box
351 can be open at any time. Requests for additional dialog boxes are ignored.
352 \note \#include <FL/fl_ask.H>
353
354 \param[in] fmt can be used as an sprintf-like format and variables for the message text
355 */
fl_alert(const char * fmt,...)356 void fl_alert(const char *fmt, ...) {
357
358 if (avoidRecursion) return;
359
360 va_list ap;
361
362 fl_beep(FL_BEEP_ERROR);
363
364 va_start(ap, fmt);
365 iconlabel = "!";
366 innards(fmt, ap, 0, fl_close, 0);
367 va_end(ap);
368 iconlabel = "?";
369 }
370 /** Shows a dialog displaying the \p fmt message,
371 this dialog features 2 yes/no buttons
372
373 \note Common dialog boxes are application modal. No more than one common dialog box
374 can be open at any time. Requests for additional dialog boxes are ignored.
375 \note \#include <FL/fl_ask.H>
376
377 \param[in] fmt can be used as an sprintf-like format and variables for the message text
378 \retval 0 if the no button is selected or another dialog box is still open
379 \retval 1 if yes is selected
380 */
fl_ask(const char * fmt,...)381 int fl_ask(const char *fmt, ...) {
382
383 if (avoidRecursion) return 0;
384
385 va_list ap;
386
387 fl_beep(FL_BEEP_QUESTION);
388
389 va_start(ap, fmt);
390 int r = innards(fmt, ap, fl_no, fl_yes, 0);
391 va_end(ap);
392
393 return r;
394 }
395
396 /** Shows a dialog displaying the \p fmt message,
397 this dialog features up to 3 customizable choice buttons
398
399 \note Common dialog boxes are application modal. No more than one common dialog box
400 can be open at any time. Requests for additional dialog boxes are ignored.
401 \note \#include <FL/fl_ask.H>
402
403 \param[in] fmt can be used as an sprintf-like format and variables for the message text
404 \param[in] b0 text label of button 0
405 \param[in] b1 text label of button 1
406 \param[in] b2 text label of button 2
407 \retval 0 if the first button with \p b0 text is selected or another dialog box is still open
408 \retval 1 if the second button with \p b1 text is selected
409 \retval 2 if the third button with \p b2 text is selected
410 */
fl_choice(const char * fmt,const char * b0,const char * b1,const char * b2,...)411 int fl_choice(const char*fmt,const char *b0,const char *b1,const char *b2,...){
412
413 if (avoidRecursion) return 0;
414
415 va_list ap;
416
417 fl_beep(FL_BEEP_QUESTION);
418
419 va_start(ap, b2);
420 int r = innards(fmt, ap, b0, b1, b2);
421 va_end(ap);
422 return r;
423 }
424 /** Gets the Fl_Box icon container of the current default dialog used in
425 many common dialogs like fl_message(), fl_alert(),
426 fl_ask(), fl_choice(), fl_input(), fl_password()
427 \note \#include <FL/fl_ask.H>
428 */
fl_message_icon()429 Fl_Widget *fl_message_icon() {makeform(); return icon;}
430
input_innards(const char * fmt,va_list ap,const char * defstr,uchar type)431 static const char* input_innards(const char* fmt, va_list ap,
432 const char* defstr, uchar type) {
433 makeform();
434 message->position(60,10);
435 input->type(type);
436 input->show();
437 input->value(defstr);
438 input->take_focus();
439
440 int r = innards(fmt, ap, fl_cancel, fl_ok, 0);
441 input->hide();
442 message->position(60,25);
443 return r ? input->value() : 0;
444 }
445
446 /** Shows an input dialog displaying the \p fmt message
447
448 \note Common dialog boxes are application modal. No more than one common dialog box
449 can be open at any time. Requests for additional dialog boxes are ignored.
450 \note \#include <FL/fl_ask.H>
451
452 \param[in] fmt can be used as an sprintf-like format and variables for the message text
453 \param[in] defstr defines the default returned string if no text is entered
454 \return the user string input if OK was pushed, NULL if Cancel was pushed or another dialog box was still open
455 */
fl_input(const char * fmt,const char * defstr,...)456 const char* fl_input(const char *fmt, const char *defstr, ...) {
457
458 if (avoidRecursion) return 0;
459
460 fl_beep(FL_BEEP_QUESTION);
461
462 va_list ap;
463 va_start(ap, defstr);
464 const char* r = input_innards(fmt, ap, defstr, FL_NORMAL_INPUT);
465 va_end(ap);
466 return r;
467 }
468
469 /** Shows an input dialog displaying the \p fmt message.
470
471 Like fl_input() except the input text is not shown,
472 '*' characters are displayed instead.
473
474 \note Common dialog boxes are application modal. No more than one common dialog box
475 can be open at any time. Requests for additional dialog boxes are ignored.
476 \note \#include <FL/fl_ask.H>
477
478 \param[in] fmt can be used as an sprintf-like format and variables for the message text
479 \param[in] defstr defines the default returned string if no text is entered
480 \return the user string input if OK was pushed, NULL if Cancel was pushed or aother dialog box was still open
481 */
fl_password(const char * fmt,const char * defstr,...)482 const char *fl_password(const char *fmt, const char *defstr, ...) {
483
484 if (avoidRecursion) return 0;
485
486 fl_beep(FL_BEEP_PASSWORD);
487
488 va_list ap;
489 va_start(ap, defstr);
490 const char* r = input_innards(fmt, ap, defstr, FL_SECRET_INPUT);
491 va_end(ap);
492 return r;
493 }
494
495 /** Sets whether or not to move the common message box used in
496 many common dialogs like fl_message(), fl_alert(),
497 fl_ask(), fl_choice(), fl_input(), fl_password() to follow
498 the mouse pointer.
499
500 The default is \e enabled, so that the default button is the
501 hotspot and appears at the mouse position.
502 \note \#include <FL/fl_ask.H>
503 \param[in] enable non-zero enables hotspot behavior,
504 0 disables hotspot
505 */
fl_message_hotspot(int enable)506 void fl_message_hotspot(int enable) {
507 enableHotspot = enable ? 1 : 0;
508 }
509
510 /** Gets whether or not to move the common message box used in
511 many common dialogs like fl_message(), fl_alert(),
512 fl_ask(), fl_choice(), fl_input(), fl_password() to follow
513 the mouse pointer.
514 \note \#include <FL/fl_ask.H>
515 \return 0 if disable, non-zero otherwise
516 \see fl_message_hotspot(int)
517 */
fl_message_hotspot(void)518 int fl_message_hotspot(void) {
519 return enableHotspot;
520 }
521
522 /** Sets the title of the dialog window used in many common dialogs.
523
524 This window \p title will be used in the next call of one of the
525 common dialogs like fl_message(), fl_alert(), fl_ask(), fl_choice(),
526 fl_input(), fl_password().
527
528 The \p title string is copied internally, so that you can use a
529 local variable or free the string immediately after this call. It
530 applies only to the \b next call of one of the common dialogs and
531 will be reset to an empty title (the default for all dialogs) after
532 that call.
533
534 \note \#include <FL/fl_ask.H>
535 \param[in] title window label, string copied internally
536 */
fl_message_title(const char * title)537 void fl_message_title(const char *title) {
538 makeform();
539 message_form->copy_label(title);
540 }
541
542 /** Sets the default title of the dialog window used in many common dialogs.
543
544 This window \p title will be used in all subsequent calls of one of the
545 common dialogs like fl_message(), fl_alert(), fl_ask(), fl_choice(),
546 fl_input(), fl_password(), unless a specific title has been set
547 with fl_message_title(const char *title).
548
549 The default is no title. You can override the default title for a
550 single dialog with fl_message_title(const char *title).
551
552 The \p title string is copied internally, so that you can use a
553 local variable or free the string immediately after this call.
554
555 \note \#include <FL/fl_ask.H>
556 \param[in] title default window label, string copied internally
557 */
fl_message_title_default(const char * title)558 void fl_message_title_default(const char *title) {
559 if (message_title_default) {
560 free ((void *)message_title_default);
561 message_title_default = 0;
562 }
563 if (title)
564 message_title_default = strdup(title);
565 }
566
567 /** @} */
568
569 //
570 // End of "$Id: fl_ask.cxx 8616 2011-04-20 14:54:42Z AlbrechtS $".
571 //
572