mc/lib/widget/quick.c
Andrew Borodin 6718b3ec26 Indentation using GNU indent-2.2.13.
Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
2024-06-02 12:49:13 +03:00

627 lines
20 KiB
C

/*
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 <aborodin@vmail.ru>, 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 <http://www.gnu.org/licenses/>.
*/
/** \file quick.c
* \brief Source: quick dialog engine
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h> /* 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;
}
/* --------------------------------------------------------------------------------------------- */