Ticket #1649: Prepare for prerelease mc-4.7.0-pre3 (code cleanup)
[midnight-commander.git] / src / wtools.c
blobce0d3d0e722cb0aaf0b8a05931bd7b3069e73457
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/skin/skin.h" /* INPUT_COLOR */
40 #include "../src/tty/key.h" /* tty_getch() */
42 #include "dialog.h"
43 #include "widget.h"
44 #include "wtools.h"
45 #include "background.h" /* parent_call */
46 #include "strutil.h"
49 Listbox *
50 create_listbox_window_delta (int delta_x, int delta_y, int cols, int lines,
51 const char *title, const char *help)
53 const int listbox_colors[DLG_COLOR_NUM] =
55 MENU_ENTRY_COLOR,
56 MENU_SELECTED_COLOR,
57 MENU_HOT_COLOR,
58 MENU_HOTSEL_COLOR,
61 int xpos, ypos, len;
62 Listbox *listbox;
64 /* Adjust sizes */
65 lines = min (lines, LINES - 6);
67 if (title != NULL) {
68 len = str_term_width1 (title) + 4;
69 cols = max (cols, len);
72 cols = min (cols, COLS - 6);
74 /* adjust position */
75 xpos = (COLS - cols + delta_x) / 2;
76 ypos = (LINES - lines + delta_y) / 2 - 2;
78 listbox = g_new (Listbox, 1);
80 listbox->dlg =
81 create_dlg (ypos, xpos, lines + 4, cols + 4, listbox_colors, NULL,
82 help, title, DLG_REVERSE);
84 listbox->list = listbox_new (2, 2, lines, cols, NULL);
85 add_widget (listbox->dlg, listbox->list);
87 return listbox;
90 Listbox *
91 create_listbox_window (int cols, int lines, const char *title, const char *help)
93 return create_listbox_window_delta (0, 0, cols, lines, title, help);
96 /* Returns the number of the item selected */
97 int
98 run_listbox (Listbox *l)
100 int val = -1;
102 if (run_dlg (l->dlg) != B_CANCEL)
103 val = l->list->pos;
104 destroy_dlg (l->dlg);
105 g_free (l);
106 return val;
109 /* default query callback, used to reposition query */
110 static cb_ret_t
111 default_query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
113 switch (msg) {
114 case DLG_RESIZE:
116 int xpos = COLS / 2 - h->cols / 2;
117 int ypos = LINES / 3 - (h->lines - 3) / 2;
119 /* set position */
120 dlg_set_position (h, ypos, xpos, ypos + h->lines, xpos + h->cols);
122 return MSG_HANDLED;
124 default:
125 return default_dlg_callback (h, msg, parm);
129 static Dlg_head *last_query_dlg;
131 static int sel_pos = 0;
133 /* Used to ask questions to the user */
135 query_dialog (const char *header, const char *text, int flags, int count, ...)
137 va_list ap;
138 Dlg_head *query_dlg;
139 WButton *button;
140 WButton *defbutton = NULL;
141 int win_len = 0;
142 int i;
143 int result = -1;
144 int cols, lines;
145 char *cur_name;
146 const int *query_colors = (flags & D_ERROR) ?
147 alarm_colors : dialog_colors;
149 if (header == MSG_ERROR)
150 header = _("Error");
152 if (count > 0) {
153 va_start (ap, count);
154 for (i = 0; i < count; i++) {
155 char *cp = va_arg (ap, char *);
156 win_len += str_term_width1 (cp) + 6;
157 if (strchr (cp, '&') != NULL)
158 win_len--;
160 va_end (ap);
163 /* count coordinates */
164 str_msg_term_size (text, &lines, &cols);
165 cols = 6 + max (win_len, max (str_term_width1 (header), cols));
166 lines += 4 + (count > 0 ? 2 : 0);
168 /* prepare dialog */
169 query_dlg =
170 create_dlg (0, 0, lines, cols, query_colors, default_query_callback,
171 "[QueryBox]", header, DLG_NONE);
173 if (count > 0) {
174 cols = (cols - win_len - 2) / 2 + 2;
175 va_start (ap, count);
176 for (i = 0; i < count; i++) {
177 int xpos;
179 cur_name = va_arg (ap, char *);
180 xpos = str_term_width1 (cur_name) + 6;
181 if (strchr (cur_name, '&') != NULL)
182 xpos--;
184 button =
185 button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON,
186 cur_name, 0);
187 add_widget (query_dlg, button);
188 cols += xpos;
189 if (i == sel_pos)
190 defbutton = button;
192 va_end (ap);
194 add_widget (query_dlg, label_new (2, 3, text));
196 /* do resize before running and selecting any widget */
197 default_query_callback (query_dlg, DLG_RESIZE, 0);
199 if (defbutton)
200 dlg_select_widget (defbutton);
202 /* run dialog and make result */
203 switch (run_dlg (query_dlg)) {
204 case B_CANCEL:
205 break;
206 default:
207 result = query_dlg->ret_value - B_USER;
210 /* free used memory */
211 destroy_dlg (query_dlg);
212 } else {
213 add_widget (query_dlg, label_new (2, 3, text));
214 add_widget (query_dlg,
215 button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
216 last_query_dlg = query_dlg;
218 sel_pos = 0;
219 return result;
222 void query_set_sel (int new_sel)
224 sel_pos = new_sel;
228 /* Create message dialog */
229 static struct Dlg_head *
230 do_create_message (int flags, const char *title, const char *text)
232 char *p;
233 Dlg_head *d;
235 /* Add empty lines before and after the message */
236 p = g_strconcat ("\n", text, "\n", (char *) NULL);
237 query_dialog (title, p, flags, 0);
238 d = last_query_dlg;
240 /* do resize before initing and running */
241 default_query_callback (d, DLG_RESIZE, 0);
243 init_dlg (d);
244 g_free (p);
246 return d;
251 * Create message dialog. The caller must call dlg_run_done() and
252 * destroy_dlg() to dismiss it. Not safe to call from background.
254 struct Dlg_head *
255 create_message (int flags, const char *title, const char *text, ...)
257 va_list args;
258 Dlg_head *d;
259 char *p;
261 va_start (args, text);
262 p = g_strdup_vprintf (text, args);
263 va_end (args);
265 d = do_create_message (flags, title, p);
266 g_free (p);
268 return d;
273 * Show message dialog. Dismiss it when any key is pressed.
274 * Not safe to call from background.
276 static void
277 fg_message (int flags, const char *title, const char *text)
279 Dlg_head *d;
281 d = do_create_message (flags, title, text);
282 tty_getch ();
283 dlg_run_done (d);
284 destroy_dlg (d);
288 /* Show message box from background */
289 #ifdef WITH_BACKGROUND
290 static void
291 bg_message (int dummy, int *flags, char *title, const char *text)
293 (void) dummy;
294 title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
295 fg_message (*flags, title, text);
296 g_free (title);
298 #endif /* WITH_BACKGROUND */
301 /* Show message box, background safe */
302 void
303 message (int flags, const char *title, const char *text, ...)
305 char *p;
306 va_list ap;
307 union {
308 void *p;
309 void (*f) (int, int *, char *, const char *);
310 } func;
312 va_start (ap, text);
313 p = g_strdup_vprintf (text, ap);
314 va_end (ap);
316 if (title == MSG_ERROR)
317 title = _("Error");
319 #ifdef WITH_BACKGROUND
320 if (we_are_background) {
321 func.f = bg_message;
322 parent_call (func.p, NULL, 3, sizeof (flags), &flags,
323 strlen (title), title, strlen (p), p);
324 } else
325 #endif /* WITH_BACKGROUND */
326 fg_message (flags, title, p);
328 g_free (p);
332 /* {{{ Quick dialog routines */
336 quick_dialog_skip (QuickDialog *qd, int nskip)
338 #ifdef ENABLE_NLS
339 #define I18N(x) (x = !qd->i18n && x && *x ? _(x): x)
340 #else
341 #define I18N(x) (x = x)
342 #endif
343 Dlg_head *dd;
344 QuickWidget *qw;
345 WInput *in;
346 WRadio *r;
347 int return_val;
349 I18N (qd->title);
351 if ((qd->xpos == -1) || (qd->ypos == -1))
352 dd = create_dlg (0, 0, qd->ylen, qd->xlen,
353 dialog_colors, NULL, qd->help, qd->title,
354 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
355 else
356 dd = create_dlg (qd->ypos, qd->xpos, qd->ylen, qd->xlen,
357 dialog_colors, NULL, qd->help, qd->title,
358 DLG_REVERSE);
360 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
361 int xpos;
362 int ypos;
364 xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
365 ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;
367 switch (qw->widget_type) {
368 case quick_checkbox:
369 qw->widget = (Widget *) check_new (ypos, xpos, *qw->u.checkbox.state, I18N (qw->u.checkbox.text));
370 break;
372 case quick_button:
373 qw->widget = (Widget *) button_new (ypos, xpos, qw->u.button.action,
374 (qw->u.button.action == B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
375 I18N (qw->u.button.text), qw->u.button.callback);
376 break;
378 case quick_input:
379 in = input_new (ypos, xpos, INPUT_COLOR, qw->u.input.len,
380 qw->u.input.text, qw->u.input.histname, INPUT_COMPLETE_DEFAULT);
381 in->is_password = (qw->u.input.flags == 1);
382 in->point = 0;
383 if ((qw->u.input.flags & 2) != 0)
384 in->completion_flags |= INPUT_COMPLETE_CD;
385 qw->widget = (Widget *) in;
386 *qw->u.input.result = NULL;
387 break;
389 case quick_label:
390 qw->widget = (Widget *) label_new (ypos, xpos, I18N (qw->u.label.text));
391 break;
393 case quick_radio:
395 int i;
396 char **items = NULL;
398 /* create the copy of radio_items to avoid mwmory leak */
399 items = g_new0 (char *, qw->u.radio.count + 1);
401 if (!qd->i18n)
402 for (i = 0; i < qw->u.radio.count; i++)
403 items[i] = g_strdup (_(qw->u.radio.items[i]));
404 else
405 for (i = 0; i < qw->u.radio.count; i++)
406 items[i] = g_strdup (qw->u.radio.items[i]);
408 r = radio_new (ypos, xpos, qw->u.radio.count, (const char **) items);
409 r->pos = r->sel = *qw->u.radio.value;
410 qw->widget = (Widget *) r;
411 g_strfreev (items);
412 break;
415 default:
416 qw->widget = NULL;
417 fprintf (stderr, "QuickWidget: unknown widget type\n");
418 break;
421 add_widget (dd, qw->widget);
424 while (nskip-- != 0)
425 dd->current = dd->current->next;
427 return_val = run_dlg (dd);
429 /* Get the data if we found something interesting */
430 if (return_val != B_CANCEL) {
431 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
432 switch (qw->widget_type) {
433 case quick_checkbox:
434 *qw->u.checkbox.state = ((WCheck *) qw->widget)->state & C_BOOL;
435 break;
437 case quick_input:
438 if ((qw->u.input.flags & 2) != 0)
439 *qw->u.input.result = tilde_expand (((WInput *) qw->widget)->buffer);
440 else
441 *qw->u.input.result = g_strdup (((WInput *) qw->widget)->buffer);
442 break;
444 case quick_radio:
445 *qw->u.radio.value = ((WRadio *) qw->widget)->sel;
446 break;
448 default:
449 break;
454 destroy_dlg (dd);
456 return return_val;
457 #undef I18N
461 quick_dialog (QuickDialog *qd)
463 return quick_dialog_skip (qd, 0);
466 /* }}} */
468 /* {{{ Input routines */
471 * Show dialog, not background safe.
473 * If the arguments "header" and "text" should be translated,
474 * that MUST be done by the caller of fg_input_dialog_help().
476 * The argument "history_name" holds the name of a section
477 * in the history file. Data entered in the input field of
478 * the dialog box will be stored there.
481 static char *
482 fg_input_dialog_help (const char *header, const char *text, const char *help,
483 const char *history_name, const char *def_text)
485 char *my_str;
487 QuickWidget quick_widgets[] = {
488 /* 0 */ QUICK_BUTTON (6, 10, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
489 /* 1 */ QUICK_BUTTON (3, 10, 1, 0, N_("&OK"), B_ENTER, NULL),
490 /* 2 */ QUICK_INPUT (4, 80, 0, 0, def_text, 58, 0, NULL, &my_str),
491 /* 3 */ QUICK_LABEL (4, 80, 2, 0, ""),
492 QUICK_END
495 char histname [64] = "inp|";
496 int lines, cols;
497 int len;
498 int i;
499 char *p_text;
500 int ret;
502 if (history_name != NULL && *history_name != '\0') {
503 g_strlcpy (histname + 3, history_name, sizeof (histname) - 3);
504 quick_widgets[2].u.input.histname = histname;
507 msglen (text, &lines, &cols);
508 len = max (max (str_term_width1 (header), cols) + 4, 64);
510 /* The special value of def_text is used to identify password boxes
511 and hide characters with "*". Don't save passwords in history! */
512 if (def_text == INPUT_PASSWORD) {
513 quick_widgets[2].u.input.flags = 1;
514 histname[3] = '\0';
515 quick_widgets[2].u.input.text = "";
518 #ifdef ENABLE_NLS
520 * An attempt to place buttons symmetrically, based on actual i18n
521 * length of the string. It looks nicer with i18n (IMO) - alex
523 quick_widgets[0].u.button.text = _(quick_widgets[0].u.button.text);
524 quick_widgets[1].u.button.text = _(quick_widgets[1].u.button.text);
525 quick_widgets[0].relative_x = len / 2 + 4;
526 quick_widgets[1].relative_x =
527 len / 2 - (str_term_width1 (quick_widgets[1].u.button.text) + 9);
528 quick_widgets[0].x_divisions = quick_widgets[1].x_divisions = len;
529 #endif /* ENABLE_NLS */
531 p_text = g_strstrip (g_strdup (text));
532 quick_widgets[3].u.label.text = p_text;
535 QuickDialog Quick_input =
537 len, lines + 6, -1, -1, header,
538 help, quick_widgets, TRUE
541 for (i = 0; i < 4; i++)
542 quick_widgets[i].y_divisions = Quick_input.ylen;
544 for (i = 0; i < 3; i++)
545 quick_widgets[i].relative_y += 2 + lines;
547 ret = quick_dialog (&Quick_input);
550 g_free (p_text);
552 return (ret != B_CANCEL) ? my_str : NULL;
556 * Show input dialog, background safe.
558 * If the arguments "header" and "text" should be translated,
559 * that MUST be done by the caller of these wrappers.
561 char *
562 input_dialog_help (const char *header, const char *text, const char *help,
563 const char *history_name, const char *def_text)
565 union {
566 void *p;
567 char * (*f) (const char *, const char *, const char *,
568 const char *, const char *);
569 } func;
570 #ifdef WITH_BACKGROUND
571 if (we_are_background)
573 func.f = fg_input_dialog_help;
574 return parent_call_string (func.p, 5,
575 strlen (header), header, strlen (text),
576 text, strlen (help), help,
577 strlen (history_name), history_name,
578 strlen (def_text), def_text);
580 else
581 #endif /* WITH_BACKGROUND */
582 return fg_input_dialog_help (header, text, help, history_name, def_text);
585 /* Show input dialog with default help, background safe */
586 char *input_dialog (const char *header, const char *text,
587 const char *history_name, const char *def_text)
589 return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
592 char *
593 input_expand_dialog (const char *header, const char *text,
594 const char *history_name, const char *def_text)
596 char *result;
597 char *expanded;
599 result = input_dialog (header, text, history_name, def_text);
600 if (result) {
601 expanded = tilde_expand (result);
602 g_free (result);
603 return expanded;
605 return result;
608 /* }}} */
610 /* }}} */
612 Cause emacs to enter folding mode for this file:
613 Local variables:
614 end: