added new parameter mcview_eof into ini-file
[free-mc.git] / src / wtools.c
blobb267c875bed3d24b545726300106cee34584a58d
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, Widget *sender,
134 dlg_msg_t msg, int parm, void *data)
136 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) ?
170 alarm_colors : dialog_colors;
172 if (header == MSG_ERROR)
173 header = _("Error");
175 if (count > 0) {
176 va_start (ap, count);
177 for (i = 0; i < count; i++) {
178 char *cp = va_arg (ap, char *);
179 win_len += str_term_width1 (cp) + 6;
180 if (strchr (cp, '&') != NULL)
181 win_len--;
183 va_end (ap);
186 /* count coordinates */
187 str_msg_term_size (text, &lines, &cols);
188 cols = 6 + max (win_len, max (str_term_width1 (header), cols));
189 lines += 4 + (count > 0 ? 2 : 0);
191 /* prepare dialog */
192 query_dlg =
193 create_dlg (0, 0, lines, cols, query_colors, default_query_callback,
194 "[QueryBox]", header, DLG_NONE);
196 if (count > 0) {
197 cols = (cols - win_len - 2) / 2 + 2;
198 va_start (ap, count);
199 for (i = 0; i < count; i++) {
200 int xpos;
202 cur_name = va_arg (ap, char *);
203 xpos = str_term_width1 (cur_name) + 6;
204 if (strchr (cur_name, '&') != NULL)
205 xpos--;
207 button =
208 button_new (lines - 3, cols, B_USER + i, NORMAL_BUTTON,
209 cur_name, 0);
210 add_widget (query_dlg, button);
211 cols += xpos;
212 if (i == sel_pos)
213 defbutton = button;
215 va_end (ap);
217 add_widget (query_dlg, label_new (2, 3, text));
219 /* do resize before running and selecting any widget */
220 default_query_callback (query_dlg, NULL, DLG_RESIZE, 0, NULL);
222 if (defbutton)
223 dlg_select_widget (defbutton);
225 /* run dialog and make result */
226 switch (run_dlg (query_dlg)) {
227 case B_CANCEL:
228 break;
229 default:
230 result = query_dlg->ret_value - B_USER;
233 /* free used memory */
234 destroy_dlg (query_dlg);
235 } else {
236 add_widget (query_dlg, label_new (2, 3, text));
237 add_widget (query_dlg,
238 button_new (0, 0, 0, HIDDEN_BUTTON, "-", 0));
239 last_query_dlg = query_dlg;
241 sel_pos = 0;
242 return result;
245 void query_set_sel (int new_sel)
247 sel_pos = new_sel;
251 /* Create message dialog */
252 static struct Dlg_head *
253 do_create_message (int flags, const char *title, const char *text)
255 char *p;
256 Dlg_head *d;
258 /* Add empty lines before and after the message */
259 p = g_strconcat ("\n", text, "\n", (char *) NULL);
260 query_dialog (title, p, flags, 0);
261 d = last_query_dlg;
263 /* do resize before initing and running */
264 default_query_callback (d, NULL, DLG_RESIZE, 0, NULL);
266 init_dlg (d);
267 g_free (p);
269 return d;
274 * Create message dialog. The caller must call dlg_run_done() and
275 * destroy_dlg() to dismiss it. Not safe to call from background.
277 struct Dlg_head *
278 create_message (int flags, const char *title, const char *text, ...)
280 va_list args;
281 Dlg_head *d;
282 char *p;
284 va_start (args, text);
285 p = g_strdup_vprintf (text, args);
286 va_end (args);
288 d = do_create_message (flags, title, p);
289 g_free (p);
291 return d;
296 * Show message dialog. Dismiss it when any key is pressed.
297 * Not safe to call from background.
299 static void
300 fg_message (int flags, const char *title, const char *text)
302 Dlg_head *d;
304 d = do_create_message (flags, title, text);
305 tty_getch ();
306 dlg_run_done (d);
307 destroy_dlg (d);
311 /* Show message box from background */
312 #ifdef WITH_BACKGROUND
313 static void
314 bg_message (int dummy, int *flags, char *title, const char *text)
316 (void) dummy;
317 title = g_strconcat (_("Background process:"), " ", title, (char *) NULL);
318 fg_message (*flags, title, text);
319 g_free (title);
321 #endif /* WITH_BACKGROUND */
324 /* Show message box, background safe */
325 void
326 message (int flags, const char *title, const char *text, ...)
328 char *p;
329 va_list ap;
330 union {
331 void *p;
332 void (*f) (int, int *, char *, const char *);
333 } func;
335 va_start (ap, text);
336 p = g_strdup_vprintf (text, ap);
337 va_end (ap);
339 if (title == MSG_ERROR)
340 title = _("Error");
342 #ifdef WITH_BACKGROUND
343 if (we_are_background) {
344 func.f = bg_message;
345 parent_call (func.p, NULL, 3, sizeof (flags), &flags,
346 strlen (title), title, strlen (p), p);
347 } else
348 #endif /* WITH_BACKGROUND */
349 fg_message (flags, title, p);
351 g_free (p);
355 /* {{{ Quick dialog routines */
359 quick_dialog_skip (QuickDialog *qd, int nskip)
361 #ifdef ENABLE_NLS
362 #define I18N(x) (x = !qd->i18n && x && *x ? _(x): x)
363 #else
364 #define I18N(x) (x = x)
365 #endif
366 Dlg_head *dd;
367 QuickWidget *qw;
368 WInput *in;
369 WRadio *r;
370 int return_val;
372 I18N (qd->title);
374 if ((qd->xpos == -1) || (qd->ypos == -1))
375 dd = create_dlg (0, 0, qd->ylen, qd->xlen,
376 dialog_colors, NULL, qd->help, qd->title,
377 DLG_CENTER | DLG_TRYUP | DLG_REVERSE);
378 else
379 dd = create_dlg (qd->ypos, qd->xpos, qd->ylen, qd->xlen,
380 dialog_colors, NULL, qd->help, qd->title,
381 DLG_REVERSE);
383 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
384 int xpos;
385 int ypos;
387 xpos = (qd->xlen * qw->relative_x) / qw->x_divisions;
388 ypos = (qd->ylen * qw->relative_y) / qw->y_divisions;
390 switch (qw->widget_type) {
391 case quick_checkbox:
392 qw->widget = (Widget *) check_new (ypos, xpos, *qw->u.checkbox.state, I18N (qw->u.checkbox.text));
393 break;
395 case quick_button:
396 qw->widget = (Widget *) button_new (ypos, xpos, qw->u.button.action,
397 (qw->u.button.action == B_ENTER) ? DEFPUSH_BUTTON : NORMAL_BUTTON,
398 I18N (qw->u.button.text), qw->u.button.callback);
399 break;
401 case quick_input:
402 in = input_new (ypos, xpos, INPUT_COLOR, qw->u.input.len,
403 qw->u.input.text, qw->u.input.histname, INPUT_COMPLETE_DEFAULT);
404 in->is_password = (qw->u.input.flags == 1);
405 in->point = 0;
406 if ((qw->u.input.flags & 2) != 0)
407 in->completion_flags |= INPUT_COMPLETE_CD;
408 qw->widget = (Widget *) in;
409 *qw->u.input.result = NULL;
410 break;
412 case quick_label:
413 qw->widget = (Widget *) label_new (ypos, xpos, I18N (qw->u.label.text));
414 break;
416 case quick_radio:
418 int i;
419 char **items = NULL;
421 /* create the copy of radio_items to avoid mwmory leak */
422 items = g_new0 (char *, qw->u.radio.count + 1);
424 if (!qd->i18n)
425 for (i = 0; i < qw->u.radio.count; i++)
426 items[i] = g_strdup (_(qw->u.radio.items[i]));
427 else
428 for (i = 0; i < qw->u.radio.count; i++)
429 items[i] = g_strdup (qw->u.radio.items[i]);
431 r = radio_new (ypos, xpos, qw->u.radio.count, (const char **) items);
432 r->pos = r->sel = *qw->u.radio.value;
433 qw->widget = (Widget *) r;
434 g_strfreev (items);
435 break;
438 default:
439 qw->widget = NULL;
440 fprintf (stderr, "QuickWidget: unknown widget type\n");
441 break;
444 add_widget (dd, qw->widget);
447 while (nskip-- != 0)
448 dd->current = dd->current->next;
450 return_val = run_dlg (dd);
452 /* Get the data if we found something interesting */
453 if (return_val != B_CANCEL) {
454 for (qw = qd->widgets; qw->widget_type != quick_end; qw++) {
455 switch (qw->widget_type) {
456 case quick_checkbox:
457 *qw->u.checkbox.state = ((WCheck *) qw->widget)->state & C_BOOL;
458 break;
460 case quick_input:
461 if ((qw->u.input.flags & 2) != 0)
462 *qw->u.input.result = tilde_expand (((WInput *) qw->widget)->buffer);
463 else
464 *qw->u.input.result = g_strdup (((WInput *) qw->widget)->buffer);
465 break;
467 case quick_radio:
468 *qw->u.radio.value = ((WRadio *) qw->widget)->sel;
469 break;
471 default:
472 break;
477 destroy_dlg (dd);
479 return return_val;
480 #undef I18N
484 quick_dialog (QuickDialog *qd)
486 return quick_dialog_skip (qd, 0);
489 /* }}} */
491 /* {{{ Input routines */
494 * Show dialog, not background safe.
496 * If the arguments "header" and "text" should be translated,
497 * that MUST be done by the caller of fg_input_dialog_help().
499 * The argument "history_name" holds the name of a section
500 * in the history file. Data entered in the input field of
501 * the dialog box will be stored there.
504 static char *
505 fg_input_dialog_help (const char *header, const char *text, const char *help,
506 const char *history_name, const char *def_text)
508 char *my_str;
510 QuickWidget quick_widgets[] = {
511 /* 0 */ QUICK_BUTTON (6, 10, 1, 0, N_("&Cancel"), B_CANCEL, NULL),
512 /* 1 */ QUICK_BUTTON (3, 10, 1, 0, N_("&OK"), B_ENTER, NULL),
513 /* 2 */ QUICK_INPUT (4, 80, 0, 0, def_text, 58, 0, NULL, &my_str),
514 /* 3 */ QUICK_LABEL (4, 80, 2, 0, ""),
515 QUICK_END
518 char histname [64] = "inp|";
519 int lines, cols;
520 int len;
521 int i;
522 char *p_text;
523 int ret;
525 if (history_name != NULL && *history_name != '\0') {
526 g_strlcpy (histname + 3, history_name, sizeof (histname) - 3);
527 quick_widgets[2].u.input.histname = histname;
530 msglen (text, &lines, &cols);
531 len = max (max (str_term_width1 (header), cols) + 4, 64);
533 /* The special value of def_text is used to identify password boxes
534 and hide characters with "*". Don't save passwords in history! */
535 if (def_text == INPUT_PASSWORD) {
536 quick_widgets[2].u.input.flags = 1;
537 histname[3] = '\0';
538 quick_widgets[2].u.input.text = "";
541 #ifdef ENABLE_NLS
543 * An attempt to place buttons symmetrically, based on actual i18n
544 * length of the string. It looks nicer with i18n (IMO) - alex
546 quick_widgets[0].u.button.text = _(quick_widgets[0].u.button.text);
547 quick_widgets[1].u.button.text = _(quick_widgets[1].u.button.text);
548 quick_widgets[0].relative_x = len / 2 + 4;
549 quick_widgets[1].relative_x =
550 len / 2 - (str_term_width1 (quick_widgets[1].u.button.text) + 9);
551 quick_widgets[0].x_divisions = quick_widgets[1].x_divisions = len;
552 #endif /* ENABLE_NLS */
554 p_text = g_strstrip (g_strdup (text));
555 quick_widgets[3].u.label.text = p_text;
558 QuickDialog Quick_input =
560 len, lines + 6, -1, -1, header,
561 help, quick_widgets, TRUE
564 for (i = 0; i < 4; i++)
565 quick_widgets[i].y_divisions = Quick_input.ylen;
567 for (i = 0; i < 3; i++)
568 quick_widgets[i].relative_y += 2 + lines;
570 ret = quick_dialog (&Quick_input);
573 g_free (p_text);
575 return (ret != B_CANCEL) ? my_str : NULL;
579 * Show input dialog, background safe.
581 * If the arguments "header" and "text" should be translated,
582 * that MUST be done by the caller of these wrappers.
584 char *
585 input_dialog_help (const char *header, const char *text, const char *help,
586 const char *history_name, const char *def_text)
588 union {
589 void *p;
590 char * (*f) (const char *, const char *, const char *,
591 const char *, const char *);
592 } func;
593 #ifdef WITH_BACKGROUND
594 if (we_are_background)
596 func.f = fg_input_dialog_help;
597 return parent_call_string (func.p, 5,
598 strlen (header), header, strlen (text),
599 text, strlen (help), help,
600 strlen (history_name), history_name,
601 strlen (def_text), def_text);
603 else
604 #endif /* WITH_BACKGROUND */
605 return fg_input_dialog_help (header, text, help, history_name, def_text);
608 /* Show input dialog with default help, background safe */
609 char *input_dialog (const char *header, const char *text,
610 const char *history_name, const char *def_text)
612 return input_dialog_help (header, text, "[Input Line Keys]", history_name, def_text);
615 char *
616 input_expand_dialog (const char *header, const char *text,
617 const char *history_name, const char *def_text)
619 char *result;
620 char *expanded;
622 result = input_dialog (header, text, history_name, def_text);
623 if (result) {
624 expanded = tilde_expand (result);
625 g_free (result);
626 return expanded;
628 return result;
631 /* }}} */
633 /* }}} */
635 Cause emacs to enter folding mode for this file:
636 Local variables:
637 end: