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

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