/* Widget based utility functions. Copyright (C) 1994-2024 Free Software Foundation, Inc. Authors: Miguel de Icaza, 1994, 1995, 1996 Radek Doulik, 1994, 1995 Jakub Jelinek, 1995 Andrej Borsenkow, 1995 Andrew Borodin , 2009-2022 This file is part of the Midnight Commander. The Midnight Commander is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. The Midnight Commander is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ /** \file quick.c * \brief Source: quick dialog engine */ #include #include #include /* fprintf() */ #include "lib/global.h" #include "lib/strutil.h" /* str_term_width1() */ #include "lib/util.h" /* tilde_expand() */ #include "lib/widget.h" /*** global variables ****************************************************************************/ /*** file scope macro definitions ****************************************************************/ #ifdef ENABLE_NLS #define I18N(x) (x = x != NULL && *x != '\0' ? _(x) : x) #else #define I18N(x) (x = x) #endif /*** file scope type declarations ****************************************************************/ typedef struct { Widget *widget; quick_widget_t *quick_widget; } quick_widget_item_t; /*** forward declarations (file scope functions) *************************************************/ /*** file scope variables ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ static WInput * quick_create_input (int y, int x, const quick_widget_t * qw) { WInput *in; in = input_new (y, x, input_colors, 8, qw->u.input.text, qw->u.input.histname, qw->u.input.completion_flags); in->is_password = qw->u.input.is_passwd; in->strip_password = qw->u.input.strip_passwd; return in; } /* --------------------------------------------------------------------------------------------- */ static void quick_create_labeled_input (GArray * widgets, int *y, int x, quick_widget_t * quick_widget, int *width) { quick_widget_item_t in, label; label.quick_widget = g_new0 (quick_widget_t, 1); label.quick_widget->widget_type = quick_label; label.quick_widget->options = quick_widget->options; label.quick_widget->state = quick_widget->state; /* FIXME: this should be turned in depend of label_location */ label.quick_widget->pos_flags = quick_widget->pos_flags; switch (quick_widget->u.input.label_location) { case input_label_above: label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text))); *y += label.widget->rect.lines - 1; g_array_append_val (widgets, label); in.widget = WIDGET (quick_create_input (++(*y), x, quick_widget)); in.quick_widget = quick_widget; g_array_append_val (widgets, in); *width = MAX (label.widget->rect.cols, in.widget->rect.cols); break; case input_label_left: label.widget = WIDGET (label_new (*y, x, I18N (quick_widget->u.input.label_text))); g_array_append_val (widgets, label); in.widget = WIDGET (quick_create_input (*y, x + label.widget->rect.cols + 1, quick_widget)); in.quick_widget = quick_widget; g_array_append_val (widgets, in); *width = label.widget->rect.cols + in.widget->rect.cols + 1; break; case input_label_right: in.widget = WIDGET (quick_create_input (*y, x, quick_widget)); in.quick_widget = quick_widget; g_array_append_val (widgets, in); label.widget = WIDGET (label_new (*y, x + in.widget->rect.cols + 1, I18N (quick_widget->u.input.label_text))); g_array_append_val (widgets, label); *width = label.widget->rect.cols + in.widget->rect.cols + 1; break; case input_label_below: in.widget = WIDGET (quick_create_input (*y, x, quick_widget)); in.quick_widget = quick_widget; g_array_append_val (widgets, in); label.widget = WIDGET (label_new (++(*y), x, I18N (quick_widget->u.input.label_text))); *y += label.widget->rect.lines - 1; g_array_append_val (widgets, label); *width = MAX (label.widget->rect.cols, in.widget->rect.cols); break; default: return; } INPUT (in.widget)->label = LABEL (label.widget); /* cross references */ label.quick_widget->u.label.input = in.quick_widget; in.quick_widget->u.input.label = label.quick_widget; } /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ int quick_dialog_skip (quick_dialog_t * quick_dlg, int nskip) { int len; int blen = 0; int x, y; /* current positions */ int y1 = 0; /* bottom of 1st column in case of two columns */ int y2 = -1; /* start of two columns */ int width1 = 0; /* width of single column */ int width2 = 0; /* width of each of two columns */ gboolean have_groupbox = FALSE; gboolean two_columns = FALSE; gboolean put_buttons = FALSE; /* x position of 1st column is 3 */ const int x1 = 3; /* x position of 2nd column is 4 and it will be fixed later, after creation of all widgets */ int x2 = 4; GArray *widgets; size_t i; quick_widget_t *quick_widget; WGroupbox *g = NULL; WDialog *dd; GList *input_labels = NULL; /* Widgets not directly requested by the user. */ int return_val; len = str_term_width1 (I18N (quick_dlg->title)) + 6; quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len); y = 1; x = x1; /* create widgets */ widgets = g_array_sized_new (FALSE, FALSE, sizeof (quick_widget_item_t), 8); for (quick_widget = quick_dlg->widgets; quick_widget->widget_type != quick_end; quick_widget++) { quick_widget_item_t item = { NULL, quick_widget }; int width = 0; switch (quick_widget->widget_type) { case quick_checkbox: item.widget = WIDGET (check_new (++y, x, *quick_widget->u.checkbox.state, I18N (quick_widget->u.checkbox.text))); g_array_append_val (widgets, item); width = item.widget->rect.cols; if (g != NULL) width += 2; if (two_columns) width2 = MAX (width2, width); else width1 = MAX (width1, width); break; case quick_button: /* single button */ item.widget = WIDGET (button_new (++y, x, quick_widget->u.button.action, quick_widget->u.button.action == B_ENTER ? DEFPUSH_BUTTON : NORMAL_BUTTON, I18N (quick_widget->u.button.text), quick_widget->u.button.callback)); g_array_append_val (widgets, item); width = item.widget->rect.cols; if (g != NULL) width += 2; if (two_columns) width2 = MAX (width2, width); else width1 = MAX (width1, width); break; case quick_input: *quick_widget->u.input.result = NULL; y++; if (quick_widget->u.input.label_location != input_label_none) { quick_create_labeled_input (widgets, &y, x, quick_widget, &width); input_labels = g_list_prepend (input_labels, quick_widget->u.input.label); } else { item.widget = WIDGET (quick_create_input (y, x, quick_widget)); g_array_append_val (widgets, item); width = item.widget->rect.cols; } if (g != NULL) width += 2; if (two_columns) width2 = MAX (width2, width); else width1 = MAX (width1, width); break; case quick_label: item.widget = WIDGET (label_new (++y, x, I18N (quick_widget->u.label.text))); g_array_append_val (widgets, item); y += item.widget->rect.lines - 1; width = item.widget->rect.cols; if (g != NULL) width += 2; if (two_columns) width2 = MAX (width2, width); else width1 = MAX (width1, width); break; case quick_radio: { WRadio *r; char **items = NULL; /* create the copy of radio_items to avoid mwmory leak */ items = g_new (char *, quick_widget->u.radio.count + 1); for (i = 0; i < (size_t) quick_widget->u.radio.count; i++) items[i] = g_strdup (_(quick_widget->u.radio.items[i])); items[i] = NULL; r = radio_new (++y, x, quick_widget->u.radio.count, (const char **) items); r->pos = r->sel = *quick_widget->u.radio.value; g_strfreev (items); item.widget = WIDGET (r); g_array_append_val (widgets, item); y += item.widget->rect.lines - 1; width = item.widget->rect.cols; if (g != NULL) width += 2; if (two_columns) width2 = MAX (width2, width); else width1 = MAX (width1, width); } break; case quick_start_groupbox: I18N (quick_widget->u.groupbox.title); len = str_term_width1 (quick_widget->u.groupbox.title); g = groupbox_new (++y, x, 1, len + 4, quick_widget->u.groupbox.title); item.widget = WIDGET (g); g_array_append_val (widgets, item); have_groupbox = TRUE; break; case quick_stop_groupbox: if (g != NULL) { Widget *w = WIDGET (g); y++; w->rect.lines = y + 1 - w->rect.y; g = NULL; g_array_append_val (widgets, item); } break; case quick_separator: y++; if (quick_widget->u.separator.line) { item.widget = WIDGET (hline_new (y, x, 1)); g_array_append_val (widgets, item); } break; case quick_start_columns: y2 = y; g_array_append_val (widgets, item); two_columns = TRUE; break; case quick_next_column: x = x2; y1 = y; y = y2; break; case quick_stop_columns: x = x1; y = MAX (y1, y); g_array_append_val (widgets, item); two_columns = FALSE; break; case quick_buttons: /* start put several buttons in bottom line */ if (quick_widget->u.separator.space) { y++; if (quick_widget->u.separator.line) item.widget = WIDGET (hline_new (y, 1, -1)); } g_array_append_val (widgets, item); /* several buttons in bottom line */ y++; quick_widget++; for (; quick_widget->widget_type == quick_button; quick_widget++) { item.widget = WIDGET (button_new (y, x++, quick_widget->u.button.action, quick_widget->u.button.action == B_ENTER ? DEFPUSH_BUTTON : NORMAL_BUTTON, I18N (quick_widget->u.button.text), quick_widget->u.button.callback)); item.quick_widget = quick_widget; g_array_append_val (widgets, item); blen += item.widget->rect.cols + 1; } /* stop dialog build here */ blen--; quick_widget->widget_type = quick_end; quick_widget--; break; default: break; } } /* adjust dialog width */ quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, blen + 6); if (have_groupbox) { if (width1 != 0) width1 += 2; if (width2 != 0) width2 += 2; } if (width2 == 0) len = width1 + 6; else { len = width2 * 2 + 7; if (width1 != 0) len = MAX (len, width1 + 6); } quick_dlg->rect.cols = MAX (quick_dlg->rect.cols, len); width1 = quick_dlg->rect.cols - 6; width2 = (quick_dlg->rect.cols - 7) / 2; if (quick_dlg->rect.x == -1 || quick_dlg->rect.y == -1) dd = dlg_create (TRUE, 0, 0, y + 3, quick_dlg->rect.cols, WPOS_CENTER | WPOS_TRYUP, FALSE, dialog_colors, quick_dlg->callback, quick_dlg->mouse_callback, quick_dlg->help, quick_dlg->title); else dd = dlg_create (TRUE, quick_dlg->rect.y, quick_dlg->rect.x, y + 3, quick_dlg->rect.cols, WPOS_KEEP_DEFAULT, FALSE, dialog_colors, quick_dlg->callback, quick_dlg->mouse_callback, quick_dlg->help, quick_dlg->title); /* add widgets into the dialog */ x2 = x1 + width2 + 1; g = NULL; two_columns = FALSE; x = (WIDGET (dd)->rect.cols - blen) / 2; for (i = 0; i < widgets->len; i++) { quick_widget_item_t *item; int column_width; WRect *r; item = &g_array_index (widgets, quick_widget_item_t, i); r = &item->widget->rect; column_width = two_columns ? width2 : width1; /* adjust widget width and x position */ switch (item->quick_widget->widget_type) { case quick_label: { quick_widget_t *input = item->quick_widget->u.label.input; if (input != NULL && input->u.input.label_location == input_label_right) { /* location of this label will be adjusted later */ break; } } MC_FALLTHROUGH; case quick_checkbox: case quick_radio: if (r->x != x1) r->x = x2; if (g != NULL) r->x += 2; break; case quick_button: if (!put_buttons) { if (r->x != x1) r->x = x2; if (g != NULL) r->x += 2; } else { r->x = x; x += r->cols + 1; } break; case quick_input: { Widget *label = WIDGET (INPUT (item->widget)->label); int width = column_width; if (g != NULL) width -= 4; switch (item->quick_widget->u.input.label_location) { case input_label_left: /* label was adjusted before; adjust input line */ r->x = label->rect.x + label->rect.cols + 1 - WIDGET (label->owner)->rect.x; r->cols = width - label->rect.cols - 1; break; case input_label_right: if (r->x != x1) r->x = x2; if (g != NULL) r->x += 2; r->cols = width - label->rect.cols - 1; label->rect.x = r->x + r->cols + 1; break; default: if (r->x != x1) r->x = x2; if (g != NULL) r->x += 2; r->cols = width; break; } /* forced update internal variables of input line */ r->lines = 1; widget_set_size_rect (item->widget, r); } break; case quick_start_groupbox: g = GROUPBOX (item->widget); if (r->x != x1) r->x = x2; r->cols = column_width; break; case quick_stop_groupbox: g = NULL; break; case quick_separator: if (item->widget != NULL) { if (g != NULL) { Widget *wg = WIDGET (g); HLINE (item->widget)->auto_adjust_cols = FALSE; r->x = wg->rect.x + 1 - WIDGET (wg->owner)->rect.x; r->cols = wg->rect.cols; } else if (two_columns) { HLINE (item->widget)->auto_adjust_cols = FALSE; if (r->x != x1) r->x = x2; r->x--; r->cols = column_width + 2; } else HLINE (item->widget)->auto_adjust_cols = TRUE; } break; case quick_start_columns: two_columns = TRUE; break; case quick_stop_columns: two_columns = FALSE; break; case quick_buttons: /* several buttons in bottom line */ put_buttons = TRUE; break; default: break; } if (item->widget != NULL) { unsigned long id; /* add widget into dialog */ item->widget->options |= item->quick_widget->options; /* FIXME: cannot reset flags, setup only */ item->widget->state |= item->quick_widget->state; /* FIXME: cannot reset flags, setup only */ id = group_add_widget_autopos (GROUP (dd), item->widget, item->quick_widget->pos_flags, NULL); if (item->quick_widget->id != NULL) *item->quick_widget->id = id; } } /* skip frame widget */ if (dd->bg != NULL) nskip++; while (nskip-- != 0) group_set_current_widget_next (GROUP (dd)); return_val = dlg_run (dd); /* Get the data if we found something interesting */ if (return_val != B_CANCEL) for (i = 0; i < widgets->len; i++) { quick_widget_item_t *item; item = &g_array_index (widgets, quick_widget_item_t, i); switch (item->quick_widget->widget_type) { case quick_checkbox: *item->quick_widget->u.checkbox.state = CHECK (item->widget)->state; break; case quick_input: if ((item->quick_widget->u.input.completion_flags & INPUT_COMPLETE_CD) != 0) *item->quick_widget->u.input.result = tilde_expand (input_get_ctext (INPUT (item->widget))); else *item->quick_widget->u.input.result = input_get_text (INPUT (item->widget)); break; case quick_radio: *item->quick_widget->u.radio.value = RADIO (item->widget)->sel; break; default: break; } } widget_destroy (WIDGET (dd)); g_list_free_full (input_labels, g_free); /* destroy input labels created before */ g_array_free (widgets, TRUE); return return_val; } /* --------------------------------------------------------------------------------------------- */