mc/lib/widget/quick.c
Andrew Borodin 7257f794d2 Update template for .c files.
Add section for forward declarations of local functions. This section is
located before file scope variables because functions can be used in
strucutres (see find.c for example):

/*** forward declarations (file scope functions) *************************************************/

/* button callbacks */
static int start_stop (WButton * button, int action);
static int find_do_view_file (WButton * button, int action);
static int find_do_edit_file (WButton * button, int action);

/*** file scope variables ************************************************************************/

static struct
{
    ...
    bcback_fn callback;
} fbuts[] =
{
    ...
    { B_STOP, NORMAL_BUTTON, N_("S&uspend"), 0, 0, NULL, start_stop },
    ...
    { B_VIEW, NORMAL_BUTTON, N_("&View - F3"), 0, 0, NULL, find_do_view_file },
    { B_VIEW, NORMAL_BUTTON, N_("&Edit - F4"), 0, 0, NULL, find_do_edit_file }
};

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
2023-03-19 20:34:24 +03:00

627 lines
20 KiB
C

/*
Widget based utility functions.
Copyright (C) 1994-2023
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;
}
/* --------------------------------------------------------------------------------------------- */