Ticket #1725: Do not abort on broken .cpio file
[midnight-commander.git] / src / wtools.c
blobd81b5414c3c253409215524bddef73079dc623aa
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"
38 #include "../src/tty/tty.h"
39 #include "../src/tty/key.h" /* tty_getch() */
41 #include "../src/skin/skin.h" /* INPUT_COLOR */
43 #include "dialog.h"
44 #include "widget.h"
45 #include "wtools.h"
46 #include "background.h" /* parent_call */
47 #include "strutil.h"
50 Listbox *
51 create_listbox_window_centered (int center_y, int center_x, int lines, int cols,
52 const char *title, const char *help)
54 const int listbox_colors[DLG_COLOR_NUM] =
56 MENU_ENTRY_COLOR,
57 MENU_SELECTED_COLOR,
58 MENU_HOT_COLOR,
59 MENU_HOTSEL_COLOR,
62 const int space = 4;
64 int xpos, ypos, len;
65 Listbox *listbox;
67 /* Adjust sizes */
68 lines = min (lines, LINES - 6);
70 if (title != NULL) {
71 len = str_term_width1 (title) + 4;
72 cols = max (cols, len);
75 cols = min (cols, COLS - 6);
77 /* adjust position */
78 if ((center_y < 0) || (center_x < 0)) {
79 ypos = LINES/2;
80 xpos = COLS/2;
81 } else {
82 ypos = center_y;
83 xpos = center_x;
86 ypos -= lines/2;
87 xpos -= cols/2;
89 if (ypos + lines >= LINES)
90 ypos = LINES - lines - space;
91 if (ypos < 0)
92 ypos = 0;
94 if (xpos + cols >= COLS)
95 xpos = COLS - cols - space;
96 if (xpos < 0)
97 xpos = 0;
99 listbox = g_new (Listbox, 1);
101 listbox->dlg =
102 create_dlg (ypos, xpos, lines + space, cols + space,
103 listbox_colors, NULL,
104 help, title, DLG_REVERSE | DLG_TRYUP);
106 listbox->list = listbox_new (2, 2, lines, cols, 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, dlg_msg_t msg, int parm)
135 switch (msg) {
136 case DLG_RESIZE:
138 int xpos = COLS / 2 - h->cols / 2;
139 int ypos = LINES / 3 - (h->lines - 3) / 2;
141 /* set position */
142 dlg_set_position (h, ypos, xpos, ypos + h->lines, xpos + h->cols);
144 return MSG_HANDLED;
146 default:
147 return default_dlg_callback (h, msg, parm);
151 static Dlg_head *last_query_dlg;
153 static int sel_pos = 0;
155 /* Used to ask questions to the user */
157 query_dialog (const char *header, const char *text, int flags, int count, ...)
159 va_list ap;
160 Dlg_head *query_dlg;
161 WButton *button;
162 WButton *defbutton = NULL;
163 int win_len = 0;
164 int i;
165 int result = -1;
166 int cols, lines;
167 char *cur_name;
168 const int *query_colors = (flags & D_ERROR) ?
169 alarm_colors : dialog_colors;
171 if (header == MSG_ERROR)
172 header = _("Error");
174 if (count > 0) {
175 va_start (ap, count);
176 for (i = 0; i < count; i++) {
177 char *cp = va_arg (ap, char *);
178 win_len += str_term_width1 (cp) + 6;
179 if (strchr (cp, '&') != NULL)
180 win_len--;
182 va_end (ap);
185 /* count coordinates */
186 str_msg_term_size (text, &lines, &cols);
187 cols = 6 + max (win_len, max (str_term_width1 (header), cols));
188 lines += 4 + (count > 0 ? 2 : 0);
190 /* prepare dialog */
191 query_dlg =
192 create_dlg (0, 0, lines, cols, query_colors, default_query_callback,
193 "[QueryBox]", header, DLG_NONE);
195 if (count > 0) {
196 cols = (cols - win_len - 2) / 2 + 2;
197 va_start (ap, count);
198 for (i = 0; i < count; i++) {
199 int xpos;
201 cur_name = va_arg (ap, char *);
202 xpos = str_term_width1 (cur_name) + 6;
203 if (strchr (cur_name, '&') != NULL)
204 xpos--;
206 button =
207 button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON,
208 cur_name, 0);
209 add_widget (query_dlg, button);
210 cols += xpos;
211 if (i == sel_pos)
212 defbutton = button;
214 va_end (ap);
216 add_widget (query_dlg, label_new (2, 3, text));
218 /* do resize before running and selecting any widget */
219 default_query_callback (query_dlg, DLG_RESIZE, 0);
221 if (defbutton)
222 dlg_select_widget (defbutton);
224 /* run dialog and make result */
225 switch (run_dlg (query_dlg)) {
226 case B_CANCEL:
227 break;
228 default:
229 result = query_dlg->ret_value - B_USER;
232 /* free used memory */
233 destroy_dlg (query_dlg);
234 } else {
235 add_widget (query_dlg, label_new (2, 3, text));
236 add_widget (query_dlg,
237 button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
238 last_query_dlg = query_dlg;
240 sel_pos = 0;
241 return result;
244 void query_set_sel (int new_sel)
246 sel_pos = new_sel;
250 /* Create message dialog */
251 static struct Dlg_head *
252 do_create_message (int flags, const char *title, const char *text)
254 char *p;
255 Dlg_head *d;
257 /* Add empty lines before and after the message */
258 p = g_strconcat ("\n", text, "\n", (char *) NULL);
259 query_dialog (title, p, flags, 0);
260 d = last_query_dlg;
262 /* do resize before initing and running */
263 default_query_callback (d, DLG_RESIZE, 0);
265 init_dlg (d);
266 g_free (p);
268 return d;
273 * Create message dialog. The caller must call dlg_run_done() and
274 * destroy_dlg() to dismiss it. Not safe to call from background.
276 struct Dlg_head *
277 create_message (int flags, const char *title, const char *text, ...)
279 va_list args;
280 Dlg_head *d;
281 char *p;
283 va_start (args, text);
284 p = g_strdup_vprintf (text, args);
285 va_end (args);
287 d = do_create_message (flags, title, p);
288 g_free (p);
290 return d;
295 * Show message dialog. Dismiss it when any key is pressed.
296 * Not safe to call from background.
298 static void
299 fg_message (int flags, const char *title, const char *text)
301 Dlg_head *d;
303 d = do_create_message (flags, title, text);
304 tty_getch ();
305 dlg_run_done (d);
306 destroy_dlg (d);
310 /* Show message box from background */
311 #ifdef WITH_BACKGROUND
312 static void
313 bg_message (int dummy, int *flags, char *title, const char *text)
315 (void) dummy;
316 title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
317 fg_message (*flags, title, text);
318 g_free (title);
320 #endif /* WITH_BACKGROUND */
323 /* Show message box, background safe */
324 void
325 message (int flags, const char *title, const char *text, ...)
327 char *p;
328 va_list ap;
329 union {
330 void *p;
331 void (*f) (int, int *, char *, const char *);
332 } func;
334 va_start (ap, text);
335 p = g_strdup_vprintf (text, ap);
336 va_end (ap);
338 if (title == MSG_ERROR)
339 title = _("Error");
341 #ifdef WITH_BACKGROUND
342 if (we_are_background) {
343 func.f = bg_message;
344 parent_call (func.p, NULL, 3, sizeof (flags), &flags,
345 strlen (title), title, strlen (p), p);
346 } else
347 #endif /* WITH_BACKGROUND */
348 fg_message (flags, title, p);
350 g_free (p);
354 /* {{{ Quick dialog routines */
358 quick_dialog_skip (QuickDialog *qd, int nskip)
360 #ifdef ENABLE_NLS
361 #define I18N(x) (x = !qd->i18n && x && *x ? _(x): x)
362 #else
363 #define I18N(x) (x = x)
364 #endif
365 Dlg_head *dd;
366 QuickWidget *qw;
367 WInput *in;
368 WRadio *r;
369 int return_val;
371 I18N (qd->title);
373 if ((qd->xpos == -1) || (qd->ypos == -1))
374 dd = create_dlg (0, 0, qd->ylen, qd->xlen,
375 dialog_colors, NULL, qd->help, qd->title,
376 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
377 else
378 dd = create_dlg (qd->ypos, qd->xpos, qd->ylen, qd->xlen,
379 dialog_colors, NULL, qd->help, qd->title,
380 DLG_REVERSE);
382 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
383 int xpos;
384 int ypos;
386 xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
387 ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;
389 switch (qw->widget_type) {
390 case quick_checkbox:
391 qw->widget = (Widget *) check_new (ypos, xpos, *qw->u.checkbox.state, I18N (qw->u.checkbox.text));
392 break;
394 case quick_button:
395 qw->widget = (Widget *) button_new (ypos, xpos, qw->u.button.action,
396 (qw->u.button.action == B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
397 I18N (qw->u.button.text), qw->u.button.callback);
398 break;
400 case quick_input:
401 in = input_new (ypos, xpos, INPUT_COLOR, qw->u.input.len,
402 qw->u.input.text, qw->u.input.histname, INPUT_COMPLETE_DEFAULT);
403 in->is_password = (qw->u.input.flags == 1);
404 in->point = 0;
405 if ((qw->u.input.flags & 2) != 0)
406 in->completion_flags |= INPUT_COMPLETE_CD;
407 qw->widget = (Widget *) in;
408 *qw->u.input.result = NULL;
409 break;
411 case quick_label:
412 qw->widget = (Widget *) label_new (ypos, xpos, I18N (qw->u.label.text));
413 break;
415 case quick_radio:
417 int i;
418 char **items = NULL;
420 /* create the copy of radio_items to avoid mwmory leak */
421 items = g_new0 (char *, qw->u.radio.count + 1);
423 if (!qd->i18n)
424 for (i = 0; i < qw->u.radio.count; i++)
425 items[i] = g_strdup (_(qw->u.radio.items[i]));
426 else
427 for (i = 0; i < qw->u.radio.count; i++)
428 items[i] = g_strdup (qw->u.radio.items[i]);
430 r = radio_new (ypos, xpos, qw->u.radio.count, (const char **) items);
431 r->pos = r->sel = *qw->u.radio.value;
432 qw->widget = (Widget *) r;
433 g_strfreev (items);
434 break;
437 default:
438 qw->widget = NULL;
439 fprintf (stderr, "QuickWidget: unknown widget type\n");
440 break;
443 add_widget (dd, qw->widget);
446 while (nskip-- != 0)
447 dd->current = dd->current->next;
449 return_val = run_dlg (dd);
451 /* Get the data if we found something interesting */
452 if (return_val != B_CANCEL) {
453 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
454 switch (qw->widget_type) {
455 case quick_checkbox:
456 *qw->u.checkbox.state = ((WCheck *) qw->widget)->state & C_BOOL;
457 break;
459 case quick_input:
460 if ((qw->u.input.flags & 2) != 0)
461 *qw->u.input.result = tilde_expand (((WInput *) qw->widget)->buffer);
462 else
463 *qw->u.input.result = g_strdup (((WInput *) qw->widget)->buffer);
464 break;
466 case quick_radio:
467 *qw->u.radio.value = ((WRadio *) qw->widget)->sel;
468 break;
470 default:
471 break;
476 destroy_dlg (dd);
478 return return_val;
479 #undef I18N
483 quick_dialog (QuickDialog *qd)
485 return quick_dialog_skip (qd, 0);
488 /* }}} */
490 /* {{{ Input routines */
493 * Show dialog, not background safe.
495 * If the arguments "header" and "text" should be translated,
496 * that MUST be done by the caller of fg_input_dialog_help().
498 * The argument "history_name" holds the name of a section
499 * in the history file. Data entered in the input field of
500 * the dialog box will be stored there.
503 static char *
504 fg_input_dialog_help (const char *header, const char *text, const char *help,
505 const char *history_name, const char *def_text)
507 char *my_str;
509 QuickWidget quick_widgets[] = {
510 /* 0 */ QUICK_BUTTON (6, 10, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
511 /* 1 */ QUICK_BUTTON (3, 10, 1, 0, N_("&OK"), B_ENTER, NULL),
512 /* 2 */ QUICK_INPUT (4, 80, 0, 0, def_text, 58, 0, NULL, &my_str),
513 /* 3 */ QUICK_LABEL (4, 80, 2, 0, ""),
514 QUICK_END
517 char histname [64] = "inp|";
518 int lines, cols;
519 int len;
520 int i;
521 char *p_text;
522 int ret;
524 if (history_name != NULL && *history_name != '\0') {
525 g_strlcpy (histname + 3, history_name, sizeof (histname) - 3);
526 quick_widgets[2].u.input.histname = histname;
529 msglen (text, &lines, &cols);
530 len = max (max (str_term_width1 (header), cols) + 4, 64);
532 /* The special value of def_text is used to identify password boxes
533 and hide characters with "*". Don't save passwords in history! */
534 if (def_text == INPUT_PASSWORD) {
535 quick_widgets[2].u.input.flags = 1;
536 histname[3] = '\0';
537 quick_widgets[2].u.input.text = "";
540 #ifdef ENABLE_NLS
542 * An attempt to place buttons symmetrically, based on actual i18n
543 * length of the string. It looks nicer with i18n (IMO) - alex
545 quick_widgets[0].u.button.text = _(quick_widgets[0].u.button.text);
546 quick_widgets[1].u.button.text = _(quick_widgets[1].u.button.text);
547 quick_widgets[0].relative_x = len / 2 + 4;
548 quick_widgets[1].relative_x =
549 len / 2 - (str_term_width1 (quick_widgets[1].u.button.text) + 9);
550 quick_widgets[0].x_divisions = quick_widgets[1].x_divisions = len;
551 #endif /* ENABLE_NLS */
553 p_text = g_strstrip (g_strdup (text));
554 quick_widgets[3].u.label.text = p_text;
557 QuickDialog Quick_input =
559 len, lines + 6, -1, -1, header,
560 help, quick_widgets, TRUE
563 for (i = 0; i < 4; i++)
564 quick_widgets[i].y_divisions = Quick_input.ylen;
566 for (i = 0; i < 3; i++)
567 quick_widgets[i].relative_y += 2 + lines;
569 ret = quick_dialog (&Quick_input);
572 g_free (p_text);
574 return (ret != B_CANCEL) ? my_str : NULL;
578 * Show input dialog, background safe.
580 * If the arguments "header" and "text" should be translated,
581 * that MUST be done by the caller of these wrappers.
583 char *
584 input_dialog_help (const char *header, const char *text, const char *help,
585 const char *history_name, const char *def_text)
587 union {
588 void *p;
589 char * (*f) (const char *, const char *, const char *,
590 const char *, const char *);
591 } func;
592 #ifdef WITH_BACKGROUND
593 if (we_are_background)
595 func.f = fg_input_dialog_help;
596 return parent_call_string (func.p, 5,
597 strlen (header), header, strlen (text),
598 text, strlen (help), help,
599 strlen (history_name), history_name,
600 strlen (def_text), def_text);
602 else
603 #endif /* WITH_BACKGROUND */
604 return fg_input_dialog_help (header, text, help, history_name, def_text);
607 /* Show input dialog with default help, background safe */
608 char *input_dialog (const char *header, const char *text,
609 const char *history_name, const char *def_text)
611 return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
614 char *
615 input_expand_dialog (const char *header, const char *text,
616 const char *history_name, const char *def_text)
618 char *result;
619 char *expanded;
621 result = input_dialog (header, text, history_name, def_text);
622 if (result) {
623 expanded = tilde_expand (result);
624 g_free (result);
625 return expanded;
627 return result;
630 /* }}} */
632 /* }}} */
634 Cause emacs to enter folding mode for this file:
635 Local variables:
636 end: