Merge branch '2344_fix_line_jump'
[midnight-commander.git] / src / wtools.c
blob7f0cd74088cc4affed151cf8f2ada2b700ccecf7
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 "lib/global.h"
37 #include "lib/tty/tty.h"
38 #include "lib/tty/key.h" /* tty_getch() */
39 #include "lib/skin.h" /* INPUT_COLOR */
40 #include "lib/strutil.h"
42 #include "dialog.h"
43 #include "widget.h"
44 #include "wtools.h"
45 #include "background.h" /* parent_call */
48 Listbox *
49 create_listbox_window_centered (int center_y, int center_x, int lines, int cols,
50 const char *title, const char *help)
52 const int listbox_colors[DLG_COLOR_NUM] = {
53 MENU_ENTRY_COLOR,
54 MENU_SELECTED_COLOR,
55 MENU_HOT_COLOR,
56 MENU_HOTSEL_COLOR,
59 const int space = 4;
61 int xpos, ypos, len;
62 Listbox *listbox;
64 /* Adjust sizes */
65 lines = min (lines, LINES - 6);
67 if (title != NULL)
69 len = str_term_width1 (title) + 4;
70 cols = max (cols, len);
73 cols = min (cols, COLS - 6);
75 /* adjust position */
76 if ((center_y < 0) || (center_x < 0))
78 ypos = LINES / 2;
79 xpos = COLS / 2;
81 else
83 ypos = center_y;
84 xpos = center_x;
87 ypos -= lines / 2;
88 xpos -= cols / 2;
90 if (ypos + lines >= LINES)
91 ypos = LINES - lines - space;
92 if (ypos < 0)
93 ypos = 0;
95 if (xpos + cols >= COLS)
96 xpos = COLS - cols - space;
97 if (xpos < 0)
98 xpos = 0;
100 listbox = g_new (Listbox, 1);
102 listbox->dlg =
103 create_dlg (TRUE, ypos, xpos, lines + space, cols + space,
104 listbox_colors, NULL, help, title, DLG_REVERSE | DLG_TRYUP);
106 listbox->list = listbox_new (2, 2, lines, cols, FALSE, NULL);
107 add_widget (listbox->dlg, listbox->list);
109 return listbox;
112 Listbox *
113 create_listbox_window (int lines, int cols, const char *title, const char *help)
115 return create_listbox_window_centered (-1, -1, lines, cols, title, help);
118 /* Returns the number of the item selected */
120 run_listbox (Listbox * l)
122 int val = -1;
124 if (run_dlg (l->dlg) != B_CANCEL)
125 val = l->list->pos;
126 destroy_dlg (l->dlg);
127 g_free (l);
128 return val;
131 /* default query callback, used to reposition query */
132 static cb_ret_t
133 default_query_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
135 switch (msg)
137 case DLG_RESIZE:
139 int xpos = COLS / 2 - h->cols / 2;
140 int ypos = LINES / 3 - (h->lines - 3) / 2;
142 /* set position */
143 dlg_set_position (h, ypos, xpos, ypos + h->lines, xpos + h->cols);
145 return MSG_HANDLED;
147 default:
148 return default_dlg_callback (h, sender, msg, parm, data);
152 static Dlg_head *last_query_dlg;
154 static int sel_pos = 0;
156 /* Used to ask questions to the user */
158 query_dialog (const char *header, const char *text, int flags, int count, ...)
160 va_list ap;
161 Dlg_head *query_dlg;
162 WButton *button;
163 WButton *defbutton = NULL;
164 int win_len = 0;
165 int i;
166 int result = -1;
167 int cols, lines;
168 char *cur_name;
169 const int *query_colors = (flags & D_ERROR) ? alarm_colors : dialog_colors;
171 if (header == MSG_ERROR)
172 header = _("Error");
174 if (count > 0)
176 va_start (ap, count);
177 for (i = 0; i < count; i++)
179 char *cp = va_arg (ap, char *);
180 win_len += str_term_width1 (cp) + 6;
181 if (strchr (cp, '&') != NULL)
182 win_len--;
184 va_end (ap);
187 /* count coordinates */
188 str_msg_term_size (text, &lines, &cols);
189 cols = 6 + max (win_len, max (str_term_width1 (header), cols));
190 lines += 4 + (count > 0 ? 2 : 0);
192 /* prepare dialog */
193 query_dlg =
194 create_dlg (TRUE, 0, 0, lines, cols, query_colors, default_query_callback,
195 "[QueryBox]", header, DLG_NONE);
197 if (count > 0)
199 cols = (cols - win_len - 2) / 2 + 2;
200 va_start (ap, count);
201 for (i = 0; i < count; i++)
203 int xpos;
205 cur_name = va_arg (ap, char *);
206 xpos = str_term_width1 (cur_name) + 6;
207 if (strchr (cur_name, '&') != NULL)
208 xpos--;
210 button = button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON, cur_name, 0);
211 add_widget (query_dlg, button);
212 cols += xpos;
213 if (i == sel_pos)
214 defbutton = button;
216 va_end (ap);
218 add_widget (query_dlg, label_new (2, 3, text));
220 /* do resize before running and selecting any widget */
221 default_query_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
223 if (defbutton)
224 dlg_select_widget (defbutton);
226 /* run dialog and make result */
227 switch (run_dlg (query_dlg))
229 case B_CANCEL:
230 break;
231 default:
232 result = query_dlg->ret_value - B_USER;
235 /* free used memory */
236 destroy_dlg (query_dlg);
238 else
240 add_widget (query_dlg, label_new (2, 3, text));
241 add_widget (query_dlg, button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
242 last_query_dlg = query_dlg;
244 sel_pos = 0;
245 return result;
248 void
249 query_set_sel (int new_sel)
251 sel_pos = new_sel;
255 /* Create message dialog */
256 static struct Dlg_head *
257 do_create_message (int flags, const char *title, const char *text)
259 char *p;
260 Dlg_head *d;
262 /* Add empty lines before and after the message */
263 p = g_strconcat ("\n", text, "\n", (char *) NULL);
264 query_dialog (title, p, flags, 0);
265 d = last_query_dlg;
267 /* do resize before initing and running */
268 default_query_callback (d, NULL, DLG_RESIZE, 0, NULL);
270 init_dlg (d);
271 g_free (p);
273 return d;
278 * Create message dialog. The caller must call dlg_run_done() and
279 * destroy_dlg() to dismiss it. Not safe to call from background.
281 struct Dlg_head *
282 create_message (int flags, const char *title, const char *text, ...)
284 va_list args;
285 Dlg_head *d;
286 char *p;
288 va_start (args, text);
289 p = g_strdup_vprintf (text, args);
290 va_end (args);
292 d = do_create_message (flags, title, p);
293 g_free (p);
295 return d;
300 * Show message dialog. Dismiss it when any key is pressed.
301 * Not safe to call from background.
303 static void
304 fg_message (int flags, const char *title, const char *text)
306 Dlg_head *d;
308 d = do_create_message (flags, title, text);
309 tty_getch ();
310 dlg_run_done (d);
311 destroy_dlg (d);
315 /* Show message box from background */
316 #ifdef WITH_BACKGROUND
317 static void
318 bg_message (int dummy, int *flags, char *title, const char *text)
320 (void) dummy;
321 title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
322 fg_message (*flags, title, text);
323 g_free (title);
325 #endif /* WITH_BACKGROUND */
328 /* Show message box, background safe */
329 void
330 message (int flags, const char *title, const char *text, ...)
332 char *p;
333 va_list ap;
334 union
336 void *p;
337 void (*f) (int, int *, char *, const char *);
338 } func;
340 va_start (ap, text);
341 p = g_strdup_vprintf (text, ap);
342 va_end (ap);
344 if (title == MSG_ERROR)
345 title = _("Error");
347 #ifdef WITH_BACKGROUND
348 if (we_are_background)
350 func.f = bg_message;
351 parent_call (func.p, NULL, 3, sizeof (flags), &flags, strlen (title), title, strlen (p), p);
353 else
354 #endif /* WITH_BACKGROUND */
355 fg_message (flags, title, p);
357 g_free (p);
361 /* {{{ Quick dialog routines */
365 quick_dialog_skip (QuickDialog * qd, int nskip)
367 #ifdef ENABLE_NLS
368 #define I18N(x) (x = !qd->i18n && x && *x ? _(x): x)
369 #else
370 #define I18N(x) (x = x)
371 #endif
372 Dlg_head *dd;
373 QuickWidget *qw;
374 WInput *in;
375 WRadio *r;
376 int return_val;
378 const int input_colors[3] = {
379 INPUT_COLOR,
380 INPUT_UNCHANGED_COLOR,
381 INPUT_MARK_COLOR
384 I18N (qd->title);
386 if ((qd->xpos == -1) || (qd->ypos == -1))
387 dd = create_dlg (TRUE, 0, 0, qd->ylen, qd->xlen,
388 dialog_colors, qd->callback, qd->help, qd->title,
389 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
390 else
391 dd = create_dlg (TRUE, qd->ypos, qd->xpos, qd->ylen, qd->xlen,
392 dialog_colors, qd->callback, qd->help, qd->title, DLG_REVERSE);
394 for (qw = qd->widgets; qw->widget_type != quick_end; qw++)
396 int xpos;
397 int ypos;
399 xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
400 ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;
402 switch (qw->widget_type)
404 case quick_checkbox:
405 qw->widget =
406 (Widget *) check_new (ypos, xpos, *qw->u.checkbox.state,
407 I18N (qw->u.checkbox.text));
408 break;
410 case quick_button:
411 qw->widget = (Widget *) button_new (ypos, xpos, qw->u.button.action,
412 (qw->u.button.action ==
413 B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
414 I18N (qw->u.button.text), qw->u.button.callback);
415 break;
417 case quick_input:
418 in = input_new (ypos, xpos, (int *) input_colors,
419 qw->u.input.len, qw->u.input.text, qw->u.input.histname,
420 INPUT_COMPLETE_DEFAULT);
421 in->is_password = (qw->u.input.flags == 1);
422 if ((qw->u.input.flags & 2) != 0)
423 in->completion_flags |= INPUT_COMPLETE_CD;
424 qw->widget = (Widget *) in;
425 *qw->u.input.result = NULL;
426 break;
428 case quick_label:
429 qw->widget = (Widget *) label_new (ypos, xpos, I18N (qw->u.label.text));
430 break;
432 case quick_groupbox:
433 qw->widget = (Widget *) groupbox_new (ypos, xpos,
434 qw->u.groupbox.height,
435 qw->u.groupbox.width,
436 I18N (qw->u.groupbox.title));
437 break;
439 case quick_radio:
441 int i;
442 char **items = NULL;
444 /* create the copy of radio_items to avoid mwmory leak */
445 items = g_new0 (char *, qw->u.radio.count + 1);
447 if (!qd->i18n)
448 for (i = 0; i < qw->u.radio.count; i++)
449 items[i] = g_strdup (_(qw->u.radio.items[i]));
450 else
451 for (i = 0; i < qw->u.radio.count; i++)
452 items[i] = g_strdup (qw->u.radio.items[i]);
454 r = radio_new (ypos, xpos, qw->u.radio.count, (const char **) items);
455 r->pos = r->sel = *qw->u.radio.value;
456 qw->widget = (Widget *) r;
457 g_strfreev (items);
458 break;
461 default:
462 qw->widget = NULL;
463 fprintf (stderr, "QuickWidget: unknown widget type\n");
464 break;
467 if (qw->widget != NULL)
469 qw->widget->options |= qw->options; /* FIXME: cannot reset flags, setup only */
470 add_widget (dd, qw->widget);
474 while (nskip-- != 0)
476 dd->current = g_list_next (dd->current);
477 if (dd->current == NULL)
478 dd->current = dd->widgets;
481 return_val = run_dlg (dd);
483 /* Get the data if we found something interesting */
484 if (return_val != B_CANCEL)
486 for (qw = qd->widgets; qw->widget_type != quick_end; qw++)
488 switch (qw->widget_type)
490 case quick_checkbox:
491 *qw->u.checkbox.state = ((WCheck *) qw->widget)->state & C_BOOL;
492 break;
494 case quick_input:
495 if ((qw->u.input.flags & 2) != 0)
496 *qw->u.input.result = tilde_expand (((WInput *) qw->widget)->buffer);
497 else
498 *qw->u.input.result = g_strdup (((WInput *) qw->widget)->buffer);
499 break;
501 case quick_radio:
502 *qw->u.radio.value = ((WRadio *) qw->widget)->sel;
503 break;
505 default:
506 break;
511 destroy_dlg (dd);
513 return return_val;
514 #undef I18N
518 quick_dialog (QuickDialog * qd)
520 return quick_dialog_skip (qd, 0);
523 /* }}} */
525 /* {{{ Input routines */
528 * Show dialog, not background safe.
530 * If the arguments "header" and "text" should be translated,
531 * that MUST be done by the caller of fg_input_dialog_help().
533 * The argument "history_name" holds the name of a section
534 * in the history file. Data entered in the input field of
535 * the dialog box will be stored there.
538 static char *
539 fg_input_dialog_help (const char *header, const char *text, const char *help,
540 const char *history_name, const char *def_text)
542 char *my_str;
544 QuickWidget quick_widgets[] = {
545 /* 0 */ QUICK_BUTTON (6, 64, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
546 /* 1 */ QUICK_BUTTON (3, 64, 1, 0, N_("&OK"), B_ENTER, NULL),
547 /* 2 */ QUICK_INPUT (3, 64, 0, 0, def_text, 58, 0, NULL, &my_str),
548 /* 3 */ QUICK_LABEL (3, 64, 2, 0, ""),
549 QUICK_END
552 int b0_len, b1_len, b_len, gap;
553 char histname[64] = "inp|";
554 int lines, cols;
555 int len;
556 int i;
557 char *p_text;
558 int ret;
560 /* buttons */
561 #ifdef ENABLE_NLS
562 quick_widgets[0].u.button.text = _(quick_widgets[0].u.button.text);
563 quick_widgets[1].u.button.text = _(quick_widgets[1].u.button.text);
564 #endif /* ENABLE_NLS */
566 b0_len = str_term_width1 (quick_widgets[0].u.button.text) + 3;
567 b1_len = str_term_width1 (quick_widgets[1].u.button.text) + 5; /* default button */
568 b_len = b0_len + b1_len + 2; /* including gap */
570 /* input line */
571 if (history_name != NULL && *history_name != '\0')
573 g_strlcpy (histname + 3, history_name, sizeof (histname) - 3);
574 quick_widgets[2].u.input.histname = histname;
577 /* The special value of def_text is used to identify password boxes
578 and hide characters with "*". Don't save passwords in history! */
579 if (def_text == INPUT_PASSWORD)
581 quick_widgets[2].u.input.flags = 1;
582 histname[3] = '\0';
583 quick_widgets[2].u.input.text = "";
586 /* text */
587 p_text = g_strstrip (g_strdup (text));
588 msglen (p_text, &lines, &cols);
589 quick_widgets[3].u.label.text = p_text;
591 /* dialog width */
592 len = max (max (str_term_width1 (header), cols) + 4, 64);
593 len = min (max (len, b_len + 6), COLS);
595 /* button locations */
596 gap = (len - 8 - b_len) / 3;
597 quick_widgets[1].relative_x = 3 + gap;
598 quick_widgets[0].relative_x = quick_widgets[1].relative_x + b1_len + gap + 2;
601 QuickDialog Quick_input = {
602 len, lines + 6, -1, -1, header,
603 help, quick_widgets, NULL, TRUE
606 for (i = 0; i < 4; i++)
608 quick_widgets[i].x_divisions = Quick_input.xlen;
609 quick_widgets[i].y_divisions = Quick_input.ylen;
612 for (i = 0; i < 3; i++)
613 quick_widgets[i].relative_y += 2 + lines;
615 /* input line length */
616 quick_widgets[2].u.input.len = Quick_input.xlen - 6;
618 ret = quick_dialog (&Quick_input);
621 g_free (p_text);
623 return (ret != B_CANCEL) ? my_str : NULL;
627 * Show input dialog, background safe.
629 * If the arguments "header" and "text" should be translated,
630 * that MUST be done by the caller of these wrappers.
632 char *
633 input_dialog_help (const char *header, const char *text, const char *help,
634 const char *history_name, const char *def_text)
636 union
638 void *p;
639 char *(*f) (const char *, const char *, const char *, const char *, const char *);
640 } func;
641 #ifdef WITH_BACKGROUND
642 if (we_are_background)
644 func.f = fg_input_dialog_help;
645 return parent_call_string (func.p, 5,
646 strlen (header), header, strlen (text),
647 text, strlen (help), help,
648 strlen (history_name), history_name,
649 strlen (def_text), def_text);
651 else
652 #endif /* WITH_BACKGROUND */
653 return fg_input_dialog_help (header, text, help, history_name, def_text);
656 /* Show input dialog with default help, background safe */
657 char *
658 input_dialog (const char *header, const char *text, const char *history_name, const char *def_text)
660 return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
663 char *
664 input_expand_dialog (const char *header, const char *text,
665 const char *history_name, const char *def_text)
667 char *result;
668 char *expanded;
670 result = input_dialog (header, text, history_name, def_text);
671 if (result)
673 expanded = tilde_expand (result);
674 g_free (result);
675 return expanded;
677 return result;
680 /* }}} */
682 /* }}} */
684 Cause emacs to enter folding mode for this file:
685 Local variables:
686 end: