Editor: sync with new global config location (user menu and syntax files).
[midnight-commander.git] / src / wtools.c
blob4a2c95d4731a22c53aa39cdaa4cb8cca430daffb
1 /* Widget based utility functions.
2 Copyright (C) 1994, 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
3 2005, 2006, 2007 Free Software Foundation, Inc.
5 Authors: 1994, 1995, 1996 Miguel de Icaza
6 1994, 1995 Radek Doulik
7 1995 Jakub Jelinek
8 1995 Andrej Borsenkow
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 /** \file wtools.c
27 * \brief Source: widget based utility functions
30 #include <config.h>
32 #include <stdarg.h>
33 #include <stdio.h>
34 #include <string.h>
36 #include "global.h"
37 #include "tty.h"
38 #include "color.h" /* dialog_colors */
39 #include "dialog.h"
40 #include "widget.h"
41 #include "wtools.h"
42 #include "key.h" /* mi_getch() */
43 #include "background.h" /* parent_call */
44 #include "strutil.h"
47 Listbox *
48 create_listbox_window_delta (int delta_x, int delta_y, int cols, int lines, const char *title, const char *help)
50 int xpos, ypos, len;
51 Listbox *listbox = g_new (Listbox, 1);
52 const char *cancel_string = _("&Cancel");
54 /* Adjust sizes */
55 lines = (lines > LINES - 6) ? LINES - 6 : lines;
57 if (title && (cols < (len = str_term_width1 (title) + 2)))
58 cols = len;
60 /* no &, but 4 spaces around button for brackets and such */
61 if (cols < (len = str_term_width1 (cancel_string) + 3))
62 cols = len;
64 cols = cols > COLS - 6 ? COLS - 6 : cols;
65 xpos = (COLS - cols + delta_x) / 2;
66 ypos = (LINES - lines + delta_y) / 2 - 2;
67 /* Create components */
68 listbox->dlg =
69 create_dlg (ypos, xpos, lines + 6, cols + 4, dialog_colors, NULL,
70 help, title, DLG_REVERSE);
72 listbox->list = listbox_new (2, 2, lines, cols, NULL);
74 add_widget (listbox->dlg,
75 button_new (lines + 3, (cols / 2 + 2) - len / 2, B_CANCEL,
76 NORMAL_BUTTON, cancel_string, 0));
77 add_widget (listbox->dlg, listbox->list);
79 return listbox;
82 Listbox *
83 create_listbox_window (int cols, int lines, const char *title, const char *help)
85 return create_listbox_window_delta (0, 0, cols, lines, title, help);
88 /* Returns the number of the item selected */
89 int run_listbox (Listbox *l)
91 int val;
93 run_dlg (l->dlg);
94 if (l->dlg->ret_value == B_CANCEL)
95 val = -1;
96 else
97 val = l->list->pos;
98 destroy_dlg (l->dlg);
99 g_free (l);
100 return val;
104 static Dlg_head *last_query_dlg;
106 static int sel_pos = 0;
108 /* Used to ask questions to the user */
110 query_dialog (const char *header, const char *text, int flags, int count, ...)
112 va_list ap;
113 Dlg_head *query_dlg;
114 WButton *button;
115 WButton *defbutton = NULL;
116 int win_len = 0;
117 int i;
118 int result = -1;
119 int xpos, ypos;
120 int cols, lines;
121 char *cur_name;
122 static const int *query_colors;
124 /* set dialog colors */
125 if (flags & D_ERROR)
126 query_colors = alarm_colors;
127 else
128 query_colors = dialog_colors;
130 if (header == MSG_ERROR)
131 header = _("Error");
133 if (count > 0) {
134 va_start (ap, count);
135 for (i = 0; i < count; i++) {
136 char *cp = va_arg (ap, char *);
137 win_len += str_term_width1 (cp) + 6;
138 if (strchr (cp, '&') != NULL)
139 win_len--;
141 va_end (ap);
144 /* count coordinates */
145 str_msg_term_size (text, &lines, &cols);
146 cols = 6 + max (win_len, max (str_term_width1 (header), cols));
147 lines += 4 + (count > 0 ? 2 : 0);
148 xpos = COLS / 2 - cols / 2;
149 ypos = LINES / 3 - (lines - 3) / 2;
151 /* prepare dialog */
152 query_dlg =
153 create_dlg (ypos, xpos, lines, cols, query_colors, NULL,
154 "[QueryBox]", header, DLG_NONE);
156 if (count > 0) {
157 cols = (cols - win_len - 2) / 2 + 2;
158 va_start (ap, count);
159 for (i = 0; i < count; i++) {
160 cur_name = va_arg (ap, char *);
161 xpos = str_term_width1 (cur_name) + 6;
162 if (strchr (cur_name, '&') != NULL)
163 xpos--;
165 button =
166 button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON,
167 cur_name, 0);
168 add_widget (query_dlg, button);
169 cols += xpos;
170 if (i == sel_pos)
171 defbutton = button;
173 va_end (ap);
175 add_widget (query_dlg, label_new (2, 3, text));
177 if (defbutton)
178 dlg_select_widget (defbutton);
180 /* run dialog and make result */
181 run_dlg (query_dlg);
182 switch (query_dlg->ret_value) {
183 case B_CANCEL:
184 break;
185 default:
186 result = query_dlg->ret_value - B_USER;
189 /* free used memory */
190 destroy_dlg (query_dlg);
191 } else {
192 add_widget (query_dlg, label_new (2, 3, text));
193 add_widget (query_dlg,
194 button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
195 last_query_dlg = query_dlg;
197 sel_pos = 0;
198 return result;
201 void query_set_sel (int new_sel)
203 sel_pos = new_sel;
207 /* Create message dialog */
208 static struct Dlg_head *
209 do_create_message (int flags, const char *title, const char *text)
211 char *p;
212 Dlg_head *d;
214 /* Add empty lines before and after the message */
215 p = g_strconcat ("\n", text, "\n", (char *) NULL);
216 query_dialog (title, p, flags, 0);
217 d = last_query_dlg;
218 init_dlg (d);
219 g_free (p);
221 return d;
226 * Create message dialog. The caller must call dlg_run_done() and
227 * destroy_dlg() to dismiss it. Not safe to call from background.
229 struct Dlg_head *
230 create_message (int flags, const char *title, const char *text, ...)
232 va_list args;
233 Dlg_head *d;
234 char *p;
236 va_start (args, text);
237 p = g_strdup_vprintf (text, args);
238 va_end (args);
240 d = do_create_message (flags, title, p);
241 g_free (p);
243 return d;
248 * Show message dialog. Dismiss it when any key is pressed.
249 * Not safe to call from background.
251 static void
252 fg_message (int flags, const char *title, const char *text)
254 Dlg_head *d;
256 d = do_create_message (flags, title, text);
257 mi_getch ();
258 dlg_run_done (d);
259 destroy_dlg (d);
263 /* Show message box from background */
264 #ifdef WITH_BACKGROUND
265 static void
266 bg_message (int dummy, int *flags, char *title, const char *text)
268 (void) dummy;
269 title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
270 fg_message (*flags, title, text);
271 g_free (title);
273 #endif /* WITH_BACKGROUND */
276 /* Show message box, background safe */
277 void
278 message (int flags, const char *title, const char *text, ...)
280 char *p;
281 va_list ap;
282 union {
283 void *p;
284 void (*f) (int, int *, char *, const char *);
285 } func;
287 va_start (ap, text);
288 p = g_strdup_vprintf (text, ap);
289 va_end (ap);
291 if (title == MSG_ERROR)
292 title = _("Error");
294 #ifdef WITH_BACKGROUND
295 if (we_are_background) {
296 func.f = bg_message;
297 parent_call (func.p, NULL, 3, sizeof (flags), &flags,
298 strlen (title), title, strlen (p), p);
299 } else
300 #endif /* WITH_BACKGROUND */
301 fg_message (flags, title, p);
303 g_free (p);
307 /* {{{ Quick dialog routines */
309 #define I18N(x) (do_int && *x ? (x = _(x)): x)
312 quick_dialog_skip (QuickDialog *qd, int nskip)
314 Dlg_head *dd;
316 WRadio *r;
317 int xpos;
318 int ypos;
319 int return_val;
320 WInput *input;
321 QuickWidget *qw;
322 int do_int;
324 if (!qd->i18n) {
325 qd->i18n = 1;
326 do_int = 1;
327 if (*qd->title)
328 qd->title = _(qd->title);
329 } else
330 do_int = 0;
332 if (qd->xpos == -1)
333 dd = create_dlg (0, 0, qd->ylen, qd->xlen, dialog_colors, NULL,
334 qd->help, qd->title,
335 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
336 else
337 dd = create_dlg (qd->ypos, qd->xpos, qd->ylen, qd->xlen,
338 dialog_colors, NULL, qd->help, qd->title,
339 DLG_REVERSE);
341 for (qw = qd->widgets; qw->widget_type; qw++) {
342 xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
343 ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;
345 switch (qw->widget_type) {
346 case quick_checkbox:
347 qw->widget = (Widget *)check_new (ypos, xpos, *qw->result, I18N (qw->text));
348 break;
350 case quick_radio:
351 r = radio_new (ypos, xpos, qw->hotkey_pos, const_cast(const char **, qw->str_result));
352 r->pos = r->sel = qw->value;
353 qw->widget = (Widget *)r;
354 break;
356 case quick_button:
357 qw->widget = (Widget *)
358 button_new (ypos, xpos, qw->value,
359 (qw->value ==
360 B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
361 I18N (qw->text), (bcback) qw->cb);
362 break;
364 /* We use the hotkey pos as the field length */
365 case quick_input:
366 input =
367 input_new (ypos, xpos, INPUT_COLOR, qw->hotkey_pos,
368 qw->text, qw->histname, INPUT_COMPLETE_DEFAULT);
369 input->is_password = qw->value == 1;
370 input->point = 0;
371 if (qw->value & 2)
372 input->completion_flags |= INPUT_COMPLETE_CD;
373 qw->widget = (Widget *)input;
374 break;
376 case quick_label:
377 qw->widget = (Widget *)label_new (ypos, xpos, I18N (qw->text));
378 break;
380 default:
381 qw->widget = 0;
382 fprintf (stderr, "QuickWidget: unknown widget type\n");
383 break;
385 add_widget (dd, qw->widget);
388 while (nskip--)
389 dd->current = dd->current->next;
391 run_dlg (dd);
393 /* Get the data if we found something interesting */
394 if (dd->ret_value != B_CANCEL) {
395 for (qw = qd->widgets; qw->widget_type; qw++) {
396 Widget *w = qw->widget;
398 switch (qw->widget_type) {
399 case quick_checkbox:
400 *qw->result = ((WCheck *) w)->state & C_BOOL;
401 break;
403 case quick_radio:
404 *qw->result = ((WRadio *) w)->sel;
405 break;
407 case quick_input:
408 if (qw->value & 2)
409 *qw->str_result =
410 tilde_expand (((WInput *) w)->buffer);
411 else
412 *qw->str_result = g_strdup (((WInput *) w)->buffer);
413 break;
417 return_val = dd->ret_value;
418 destroy_dlg (dd);
420 return return_val;
423 int quick_dialog (QuickDialog *qd)
425 return quick_dialog_skip (qd, 0);
428 /* }}} */
430 /* {{{ Input routines */
432 #define INPUT_INDEX 2
435 * Show dialog, not background safe.
437 * If the arguments "header" and "text" should be translated,
438 * that MUST be done by the caller of fg_input_dialog_help().
440 * The argument "history_name" holds the name of a section
441 * in the history file. Data entered in the input field of
442 * the dialog box will be stored there.
445 static char *
446 fg_input_dialog_help (const char *header, const char *text, const char *help,
447 const char *history_name, const char *def_text)
449 QuickDialog Quick_input;
450 QuickWidget quick_widgets[] = {
451 {quick_button, 6, 10, 1, 0, N_("&Cancel"), 0, B_CANCEL, 0, 0,
452 NULL, NULL, NULL},
453 {quick_button, 3, 10, 1, 0, N_("&OK"), 0, B_ENTER, 0, 0, NULL, NULL, NULL},
454 {quick_input, 4, 80, 0, 0, "", 58, 0, 0, 0, NULL, NULL, NULL},
455 {quick_label, 4, 80, 2, 0, "", 0, 0, 0, 0, NULL, NULL, NULL},
456 NULL_QuickWidget
459 int len;
460 int i;
461 int lines, cols;
462 int ret;
463 char *my_str;
464 char histname[64] = "inp|";
465 char *p_text;
467 if (history_name != NULL && *history_name != '\0') {
468 g_strlcpy (histname + 3, history_name, 61);
469 quick_widgets[2].histname = histname;
472 msglen (text, &lines, &cols);
473 len = max (str_term_width1 (header), cols) + 4;
474 len = max (len, 64);
476 /* The special value of def_text is used to identify password boxes
477 and hide characters with "*". Don't save passwords in history! */
478 if (def_text == INPUT_PASSWORD) {
479 quick_widgets[INPUT_INDEX].value = 1;
480 histname[3] = 0;
481 def_text = "";
482 } else {
483 quick_widgets[INPUT_INDEX].value = 0;
486 #ifdef ENABLE_NLS
488 * An attempt to place buttons symmetrically, based on actual i18n
489 * length of the string. It looks nicer with i18n (IMO) - alex
491 quick_widgets[0].text = _(quick_widgets[0].text);
492 quick_widgets[1].text = _(quick_widgets[1].text);
493 quick_widgets[0].relative_x = len / 2 + 4;
494 quick_widgets[1].relative_x =
495 len / 2 - (str_term_width1 (quick_widgets[1].text) + 9);
496 quick_widgets[0].x_divisions = quick_widgets[1].x_divisions = len;
497 #endif /* ENABLE_NLS */
499 Quick_input.xlen = len;
500 Quick_input.xpos = -1;
501 Quick_input.title = header;
502 Quick_input.help = help;
503 Quick_input.i18n = 1; /* The dialog is already translated. */
504 p_text = g_strstrip (g_strdup (text));
505 quick_widgets[INPUT_INDEX + 1].text = p_text;
506 quick_widgets[INPUT_INDEX].text = def_text;
508 for (i = 0; i < 4; i++)
509 quick_widgets[i].y_divisions = lines + 6;
510 Quick_input.ylen = lines + 6;
512 for (i = 0; i < 3; i++)
513 quick_widgets[i].relative_y += 2 + lines;
515 quick_widgets[INPUT_INDEX].str_result = &my_str;
517 Quick_input.widgets = quick_widgets;
518 ret = quick_dialog (&Quick_input);
519 g_free (p_text);
521 if (ret != B_CANCEL) {
522 return my_str;
523 } else
524 return 0;
528 * Show input dialog, background safe.
530 * If the arguments "header" and "text" should be translated,
531 * that MUST be done by the caller of these wrappers.
533 char *
534 input_dialog_help (const char *header, const char *text, const char *help,
535 const char *history_name, const char *def_text)
537 union {
538 void *p;
539 char * (*f) (const char *, const char *, const char *,
540 const char *, const char *);
541 } func;
542 #ifdef WITH_BACKGROUND
543 if (we_are_background)
545 func.f = fg_input_dialog_help;
546 return parent_call_string (func.p, 5,
547 strlen (header), header, strlen (text),
548 text, strlen (help), help,
549 strlen (history_name), history_name,
550 strlen (def_text), def_text);
552 else
553 #endif /* WITH_BACKGROUND */
554 return fg_input_dialog_help (header, text, help, history_name, def_text);
557 /* Show input dialog with default help, background safe */
558 char *input_dialog (const char *header, const char *text,
559 const char *history_name, const char *def_text)
561 return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
564 char *
565 input_expand_dialog (const char *header, const char *text,
566 const char *history_name, const char *def_text)
568 char *result;
569 char *expanded;
571 result = input_dialog (header, text, history_name, def_text);
572 if (result) {
573 expanded = tilde_expand (result);
574 g_free (result);
575 return expanded;
577 return result;
580 /* }}} */
582 /* }}} */
584 Cause emacs to enter folding mode for this file:
585 Local variables:
586 end: