Manual pages: mcmcdiffmceditmcview

root/lib/widget/history.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. history_dlg_reposition
  2. history_dlg_callback
  3. history_create_item
  4. history_release_item
  5. history_load
  6. history_save
  7. history_descriptor_init
  8. history_show

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Authors:
   8    Radek Doulik, 1994, 1995
   9    Miguel de Icaza, 1994, 1995
  10    Jakub Jelinek, 1995
  11    Andrej Borsenkow, 1996
  12    Norbert Warmuth, 1997
  13    Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  14 
  15    This file is part of the Midnight Commander.
  16 
  17    The Midnight Commander is free software: you can redistribute it
  18    and/or modify it under the terms of the GNU General Public License as
  19    published by the Free Software Foundation, either version 3 of the License,
  20    or (at your option) any later version.
  21 
  22    The Midnight Commander is distributed in the hope that it will be useful,
  23    but WITHOUT ANY WARRANTY; without even the implied warranty of
  24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25    GNU General Public License for more details.
  26 
  27    You should have received a copy of the GNU General Public License
  28    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  29  */
  30 
  31 /** \file history.c
  32  *  \brief Source: show history
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <stdlib.h>
  38 #include <sys/types.h>
  39 
  40 #include "lib/global.h"
  41 
  42 #include "lib/tty/tty.h"  // LINES, COLS
  43 #include "lib/strutil.h"
  44 #include "lib/widget.h"
  45 #include "lib/keybind.h"   // CK_*
  46 #include "lib/fileloc.h"   // MC_HISTORY_FILE
  47 #include "lib/event.h"     // mc_event_raise()
  48 #include "lib/mcconfig.h"  // num_history_items_recorded
  49 
  50 /*** global variables ****************************************************************************/
  51 
  52 /*** file scope macro definitions ****************************************************************/
  53 
  54 #define B_VIEW (B_USER + 1)
  55 #define B_EDIT (B_USER + 2)
  56 
  57 /*** file scope type declarations ****************************************************************/
  58 
  59 typedef struct
  60 {
  61     int y;
  62     int x;
  63     size_t count;
  64     size_t max_width;
  65 } history_dlg_data;
  66 
  67 /*** forward declarations (file scope functions) *************************************************/
  68 
  69 /*** file scope variables ************************************************************************/
  70 
  71 /* --------------------------------------------------------------------------------------------- */
  72 /*** file scope functions ************************************************************************/
  73 /* --------------------------------------------------------------------------------------------- */
  74 
  75 static cb_ret_t
  76 history_dlg_reposition (WDialog *dlg_head)
     /* [previous][next][first][last][top][bottom][index][help]  */
  77 {
  78     history_dlg_data *data;
  79     int x = 0, y, he, wi;
  80     WRect r;
  81 
  82     // guard checks
  83     if (dlg_head == NULL || dlg_head->data.p == NULL)
  84         return MSG_NOT_HANDLED;
  85 
  86     data = (history_dlg_data *) dlg_head->data.p;
  87 
  88     y = data->y;
  89     he = data->count + 2;
  90 
  91     if (he <= y || y > (LINES - 6))
  92     {
  93         he = MIN (he, y - 1);
  94         y -= he;
  95     }
  96     else
  97     {
  98         y++;
  99         he = MIN (he, LINES - y);
 100     }
 101 
 102     if (data->x > 2)
 103         x = data->x - 2;
 104 
 105     wi = data->max_width + 4;
 106 
 107     if ((wi + x) > COLS)
 108     {
 109         wi = MIN (wi, COLS);
 110         x = COLS - wi;
 111     }
 112 
 113     rect_init (&r, y, x, he, wi);
 114 
 115     return dlg_default_callback (WIDGET (dlg_head), NULL, MSG_RESIZE, 0, &r);
 116 }
 117 
 118 /* --------------------------------------------------------------------------------------------- */
 119 
 120 static cb_ret_t
 121 history_dlg_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 122 {
 123     switch (msg)
 124     {
 125     case MSG_RESIZE:
 126         return history_dlg_reposition (DIALOG (w));
 127 
 128     case MSG_NOTIFY:
 129     {
 130         // message from listbox
 131         WDialog *d = DIALOG (w);
 132 
 133         switch (parm)
 134         {
 135         case CK_View:
 136             d->ret_value = B_VIEW;
 137             break;
 138         case CK_Edit:
 139             d->ret_value = B_EDIT;
 140             break;
 141         case CK_Enter:
 142             d->ret_value = B_ENTER;
 143             break;
 144         default:
 145             return MSG_NOT_HANDLED;
 146         }
 147 
 148         dlg_close (d);
 149         return MSG_HANDLED;
 150     }
 151 
 152     default:
 153         return dlg_default_callback (w, sender, msg, parm, data);
 154     }
 155 }
 156 
 157 /* --------------------------------------------------------------------------------------------- */
 158 
 159 static void
 160 history_create_item (history_descriptor_t *hd, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 161 {
 162     char *text = (char *) data;
 163     size_t width;
 164 
 165     width = str_term_width1 (text);
 166     hd->max_width = MAX (width, hd->max_width);
 167 
 168     listbox_add_item (hd->listbox, LISTBOX_APPEND_AT_END, 0, text, NULL, TRUE);
 169 }
 170 
 171 /* --------------------------------------------------------------------------------------------- */
 172 
 173 static void *
 174 history_release_item (history_descriptor_t *hd, WLEntry *le)
     /* [previous][next][first][last][top][bottom][index][help]  */
 175 {
 176     void *text;
 177 
 178     (void) hd;
 179 
 180     text = le->text;
 181     le->text = NULL;
 182 
 183     return text;
 184 }
 185 
 186 /* --------------------------------------------------------------------------------------------- */
 187 /*** public functions ****************************************************************************/
 188 /* --------------------------------------------------------------------------------------------- */
 189 
 190 /**
 191  * Load widget history from the ${XDG_DATA_HOME}/mc/history file
 192  *
 193  * @param h WDialog object, the event group area
 194  * @param w Widget object whose history should be loaded. If NULL, history of all widgets of @h
 195  *                 will be loaded.
 196  */
 197 void
 198 history_load (const WDialog *h, Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 199 {
 200     char *profile;
 201     ev_history_load_save_t event_data;
 202 
 203     if (num_history_items_recorded == 0)  // this is how to disable
 204         return;
 205 
 206     profile = mc_config_get_full_path (MC_HISTORY_FILE);
 207     event_data.cfg = mc_config_init (profile, TRUE);
 208     event_data.receiver = w;
 209 
 210     mc_event_raise (h->event_group, MCEVENT_HISTORY_LOAD, &event_data);
 211 
 212     mc_config_deinit (event_data.cfg);
 213     g_free (profile);
 214 }
 215 
 216 /* --------------------------------------------------------------------------------------------- */
 217 
 218 /**
 219  * Save widget history to the ${XDG_DATA_HOME}/mc/history file
 220  *
 221  * @param h WDialog object, the event group area
 222  * @param w Widget object whose history should be saved. If NULL, history of all widgets of @h
 223  *                 will be saved.
 224  */
 225 void
 226 history_save (const WDialog *h, Widget *w)
     /* [previous][next][first][last][top][bottom][index][help]  */
 227 {
 228     char *profile;
 229     int i;
 230 
 231     if (num_history_items_recorded == 0)  // this is how to disable
 232         return;
 233 
 234     profile = mc_config_get_full_path (MC_HISTORY_FILE);
 235     i = open (profile, O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
 236     if (i != -1)
 237         close (i);
 238 
 239     // Make sure the history is only readable by the user
 240     if (chmod (profile, S_IRUSR | S_IWUSR) != -1 || errno == ENOENT)
 241     {
 242         ev_history_load_save_t event_data;
 243 
 244         event_data.cfg = mc_config_init (profile, FALSE);
 245         event_data.receiver = w;
 246 
 247         mc_event_raise (h->event_group, MCEVENT_HISTORY_SAVE, &event_data);
 248 
 249         mc_config_save_file (event_data.cfg, NULL);
 250         mc_config_deinit (event_data.cfg);
 251     }
 252 
 253     g_free (profile);
 254 }
 255 
 256 /* --------------------------------------------------------------------------------------------- */
 257 
 258 void
 259 history_descriptor_init (history_descriptor_t *hd, int y, int x, GList *history, int current)
     /* [previous][next][first][last][top][bottom][index][help]  */
 260 {
 261     hd->list = history;
 262     hd->y = y;
 263     hd->x = x;
 264     hd->current = current;
 265     hd->action = CK_IgnoreKey;
 266     hd->text = NULL;
 267     hd->max_width = 0;
 268     hd->listbox = listbox_new (1, 1, 2, 2, TRUE, NULL);
 269     // in most cases history list contains string only and no any other data
 270     hd->create = history_create_item;
 271     hd->release = history_release_item;
 272     hd->free = g_free;
 273 }
 274 
 275 /* --------------------------------------------------------------------------------------------- */
 276 
 277 void
 278 history_show (history_descriptor_t *hd)
     /* [previous][next][first][last][top][bottom][index][help]  */
 279 {
 280     GList *z, *hi;
 281     size_t count;
 282     WDialog *query_dlg;
 283     history_dlg_data hist_data;
 284     int dlg_ret;
 285 
 286     if (hd == NULL || hd->list == NULL)
 287         return;
 288 
 289     hd->max_width = str_term_width1 (_ ("History")) + 2;
 290 
 291     for (z = hd->list; z != NULL; z = g_list_previous (z))
 292         hd->create (hd, z->data);
 293     // after this, the order of history items is following: recent at begin, oldest at end
 294 
 295     count = listbox_get_length (hd->listbox);
 296 
 297     hist_data.y = hd->y;
 298     hist_data.x = hd->x;
 299     hist_data.count = count;
 300     hist_data.max_width = hd->max_width;
 301 
 302     query_dlg = dlg_create (TRUE, 0, 0, 4, 4, WPOS_KEEP_DEFAULT, TRUE, dialog_colors,
 303                             history_dlg_callback, NULL, "[History-query]", _ ("History"));
 304     query_dlg->data.p = &hist_data;
 305 
 306     /* this call makes list stick to all sides of dialog, effectively make
 307        it be resized with dialog */
 308     group_add_widget_autopos (GROUP (query_dlg), hd->listbox, WPOS_KEEP_ALL, NULL);
 309 
 310     /* to avoid diplicating of (calculating sizes in two places)
 311        code, call history_dlg_callback function here, to set dialog and
 312        controls positions.
 313        The main idea - create 4x4 dialog and add 2x2 list in
 314        center of it, and let dialog function resize it to needed size. */
 315     send_message (query_dlg, NULL, MSG_RESIZE, 0, NULL);
 316 
 317     if (WIDGET (query_dlg)->rect.y < hd->y)
 318     {
 319         // history is above base widget -- revert order to place recent item at bottom
 320         // revert history direction
 321         g_queue_reverse (hd->listbox->list);
 322         if (hd->current < 0 || (size_t) hd->current >= count)
 323             listbox_select_last (hd->listbox);
 324         else
 325             listbox_set_current (hd->listbox, count - 1 - (size_t) hd->current);
 326     }
 327     else
 328     {
 329         // history is below base widget -- keep order to place recent item on top
 330         if (hd->current > 0)
 331             listbox_set_current (hd->listbox, hd->current);
 332     }
 333 
 334     dlg_ret = dlg_run (query_dlg);
 335     if (dlg_ret != B_CANCEL)
 336     {
 337         char *q;
 338 
 339         switch (dlg_ret)
 340         {
 341         case B_EDIT:
 342             hd->action = CK_Edit;
 343             break;
 344         case B_VIEW:
 345             hd->action = CK_View;
 346             break;
 347         default:
 348             hd->action = CK_Enter;
 349         }
 350 
 351         listbox_get_current (hd->listbox, &q, NULL);
 352         hd->text = g_strdup (q);
 353     }
 354 
 355     // get modified history from dialog
 356     z = NULL;
 357     for (hi = listbox_get_first_link (hd->listbox); hi != NULL; hi = g_list_next (hi))
 358         // history is being reverted here again
 359         z = g_list_prepend (z, hd->release (hd, LENTRY (hi->data)));
 360 
 361     // restore history direction
 362     if (WIDGET (query_dlg)->rect.y < hd->y)
 363         z = g_list_reverse (z);
 364 
 365     widget_destroy (WIDGET (query_dlg));
 366 
 367     hd->list = g_list_first (hd->list);
 368     g_list_free_full (hd->list, hd->free);
 369     hd->list = g_list_last (z);
 370 }
 371 
 372 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */