mirror of https://github.com/MidnightCommander/mc
303 lines
8.5 KiB
C
303 lines
8.5 KiB
C
/*
|
|
Widgets for the Midnight Commander
|
|
|
|
Copyright (C) 1994-2023
|
|
Free Software Foundation, Inc.
|
|
|
|
Authors:
|
|
Radek Doulik, 1994, 1995
|
|
Miguel de Icaza, 1994, 1995
|
|
Jakub Jelinek, 1995
|
|
Andrej Borsenkow, 1996
|
|
Norbert Warmuth, 1997
|
|
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 history.c
|
|
* \brief Source: show history
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "lib/global.h"
|
|
|
|
#include "lib/tty/tty.h" /* LINES, COLS */
|
|
#include "lib/strutil.h"
|
|
#include "lib/widget.h"
|
|
#include "lib/keybind.h" /* CK_* */
|
|
|
|
/*** global variables ****************************************************************************/
|
|
|
|
/*** file scope macro definitions ****************************************************************/
|
|
|
|
#define B_VIEW (B_USER + 1)
|
|
#define B_EDIT (B_USER + 2)
|
|
|
|
/*** file scope type declarations ****************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
int y;
|
|
int x;
|
|
size_t count;
|
|
size_t max_width;
|
|
} history_dlg_data;
|
|
|
|
/*** forward declarations (file scope functions) *************************************************/
|
|
|
|
/*** file scope variables ************************************************************************/
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
/*** file scope functions ************************************************************************/
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
static cb_ret_t
|
|
history_dlg_reposition (WDialog * dlg_head)
|
|
{
|
|
history_dlg_data *data;
|
|
int x = 0, y, he, wi;
|
|
WRect r;
|
|
|
|
/* guard checks */
|
|
if (dlg_head == NULL || dlg_head->data.p == NULL)
|
|
return MSG_NOT_HANDLED;
|
|
|
|
data = (history_dlg_data *) dlg_head->data.p;
|
|
|
|
y = data->y;
|
|
he = data->count + 2;
|
|
|
|
if (he <= y || y > (LINES - 6))
|
|
{
|
|
he = MIN (he, y - 1);
|
|
y -= he;
|
|
}
|
|
else
|
|
{
|
|
y++;
|
|
he = MIN (he, LINES - y);
|
|
}
|
|
|
|
if (data->x > 2)
|
|
x = data->x - 2;
|
|
|
|
wi = data->max_width + 4;
|
|
|
|
if ((wi + x) > COLS)
|
|
{
|
|
wi = MIN (wi, COLS);
|
|
x = COLS - wi;
|
|
}
|
|
|
|
rect_init (&r, y, x, he, wi);
|
|
|
|
return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
static cb_ret_t
|
|
history_dlg_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
|
|
{
|
|
switch (msg)
|
|
{
|
|
case MSG_RESIZE:
|
|
return history_dlg_reposition (DIALOG (w));
|
|
|
|
case MSG_NOTIFY:
|
|
{
|
|
/* message from listbox */
|
|
WDialog *d = DIALOG (w);
|
|
|
|
switch (parm)
|
|
{
|
|
case CK_View:
|
|
d->ret_value = B_VIEW;
|
|
break;
|
|
case CK_Edit:
|
|
d->ret_value = B_EDIT;
|
|
break;
|
|
case CK_Enter:
|
|
d->ret_value = B_ENTER;
|
|
break;
|
|
default:
|
|
return MSG_NOT_HANDLED;
|
|
}
|
|
|
|
dlg_close (d);
|
|
return MSG_HANDLED;
|
|
}
|
|
|
|
default:
|
|
return dlg_default_callback (w, sender, msg, parm, data);
|
|
}
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
static void
|
|
history_create_item (history_descriptor_t * hd, void *data)
|
|
{
|
|
char *text = (char *) data;
|
|
size_t width;
|
|
|
|
width = str_term_width1 (text);
|
|
hd->max_width = MAX (width, hd->max_width);
|
|
|
|
listbox_add_item (hd->listbox, LISTBOX_APPEND_AT_END, 0, text, NULL, TRUE);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
static void *
|
|
history_release_item (history_descriptor_t * hd, WLEntry * le)
|
|
{
|
|
void *text;
|
|
|
|
(void) hd;
|
|
|
|
text = le->text;
|
|
le->text = NULL;
|
|
|
|
return text;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
/*** public functions ****************************************************************************/
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
void
|
|
history_descriptor_init (history_descriptor_t * hd, int y, int x, GList * history, int current)
|
|
{
|
|
hd->list = history;
|
|
hd->y = y;
|
|
hd->x = x;
|
|
hd->current = current;
|
|
hd->action = CK_IgnoreKey;
|
|
hd->text = NULL;
|
|
hd->max_width = 0;
|
|
hd->listbox = listbox_new (1, 1, 2, 2, TRUE, NULL);
|
|
/* in most cases history list contains string only and no any other data */
|
|
hd->create = history_create_item;
|
|
hd->release = history_release_item;
|
|
hd->free = g_free;
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|
|
|
|
void
|
|
history_show (history_descriptor_t * hd)
|
|
{
|
|
GList *z, *hi;
|
|
size_t count;
|
|
WDialog *query_dlg;
|
|
history_dlg_data hist_data;
|
|
int dlg_ret;
|
|
|
|
if (hd == NULL || hd->list == NULL)
|
|
return;
|
|
|
|
hd->max_width = str_term_width1 (_("History")) + 2;
|
|
|
|
for (z = hd->list; z != NULL; z = g_list_previous (z))
|
|
hd->create (hd, z->data);
|
|
/* after this, the order of history items is following: recent at begin, oldest at end */
|
|
|
|
count = listbox_get_length (hd->listbox);
|
|
|
|
hist_data.y = hd->y;
|
|
hist_data.x = hd->x;
|
|
hist_data.count = count;
|
|
hist_data.max_width = hd->max_width;
|
|
|
|
query_dlg =
|
|
dlg_create (TRUE, 0, 0, 4, 4, WPOS_KEEP_DEFAULT, TRUE, dialog_colors, history_dlg_callback,
|
|
NULL, "[History-query]", _("History"));
|
|
query_dlg->data.p = &hist_data;
|
|
|
|
/* this call makes list stick to all sides of dialog, effectively make
|
|
it be resized with dialog */
|
|
group_add_widget_autopos (GROUP (query_dlg), hd->listbox, WPOS_KEEP_ALL, NULL);
|
|
|
|
/* to avoid diplicating of (calculating sizes in two places)
|
|
code, call history_dlg_callback function here, to set dialog and
|
|
controls positions.
|
|
The main idea - create 4x4 dialog and add 2x2 list in
|
|
center of it, and let dialog function resize it to needed size. */
|
|
send_message (query_dlg, NULL, MSG_RESIZE, 0, NULL);
|
|
|
|
if (WIDGET (query_dlg)->rect.y < hd->y)
|
|
{
|
|
/* history is above base widget -- revert order to place recent item at bottom */
|
|
/* revert history direction */
|
|
g_queue_reverse (hd->listbox->list);
|
|
if (hd->current < 0 || (size_t) hd->current >= count)
|
|
listbox_select_last (hd->listbox);
|
|
else
|
|
listbox_set_current (hd->listbox, count - 1 - (size_t) hd->current);
|
|
}
|
|
else
|
|
{
|
|
/* history is below base widget -- keep order to place recent item on top */
|
|
if (hd->current > 0)
|
|
listbox_set_current (hd->listbox, hd->current);
|
|
}
|
|
|
|
dlg_ret = dlg_run (query_dlg);
|
|
if (dlg_ret != B_CANCEL)
|
|
{
|
|
char *q;
|
|
|
|
switch (dlg_ret)
|
|
{
|
|
case B_EDIT:
|
|
hd->action = CK_Edit;
|
|
break;
|
|
case B_VIEW:
|
|
hd->action = CK_View;
|
|
break;
|
|
default:
|
|
hd->action = CK_Enter;
|
|
}
|
|
|
|
listbox_get_current (hd->listbox, &q, NULL);
|
|
hd->text = g_strdup (q);
|
|
}
|
|
|
|
/* get modified history from dialog */
|
|
z = NULL;
|
|
for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi))
|
|
/* history is being reverted here again */
|
|
z = g_list_prepend (z, hd->release (hd, LENTRY (hi->data)));
|
|
|
|
/* restore history direction */
|
|
if (WIDGET (query_dlg)->rect.y < hd->y)
|
|
z = g_list_reverse (z);
|
|
|
|
widget_destroy (WIDGET (query_dlg));
|
|
|
|
hd->list = g_list_first (hd->list);
|
|
g_list_free_full (hd->list, hd->free);
|
|
hd->list = g_list_last (z);
|
|
}
|
|
|
|
/* --------------------------------------------------------------------------------------------- */
|