/* Widgets for the Midnight Commander Copyright (C) 1994-2024 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 , 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 history.c * \brief Source: show history */ #include #include #include #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); } /* --------------------------------------------------------------------------------------------- */