1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
/*
libuundo - micro-undo, a small, callback based undo/redo list implementation
Copyright (C) 2017 Tibor 'Igor2' Palinkas
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Author: libuundo [+at+] igor2.repo.hu
VCS: svn://repo.hu/libuundo/trunk
Web: http://repo.hu/projects/libuundo
*/
#ifndef UUNDO_H
#define UUNDO_H
#include <stddef.h>
typedef struct uundo_list_s uundo_list_t; /* list of undoable actions */
typedef struct uundo_item_s uundo_item_t; /* an item on the list */
typedef struct uundo_oper_s uundo_oper_t; /* operator: user provided function pointers for handling an item */
/* serial number type */
typedef long uundo_serial_t;
#define UUNDO_SERIAL_INVALID (-1)
struct uundo_oper_s {
const char *cookie; /* plugin cookie; always matched as pointer, not as string */
void (*item_free)(void *udata); /* free fields of the undo slot */
int (*item_undo)(void *udata); /* perform undo (don't free slot fields); return 0 on success */
int (*item_redo)(void *udata); /* perform redo (don't free slot fields); return 0 on success */
void (*item_print)(void *udata, char *dst, size_t dst_len); /* debug: print the operator and operands in dst */
};
struct uundo_item_s {
uundo_serial_t serial;
const uundo_oper_t *oper;
uundo_item_t *prev, *next; /* doubly linked list of items */
char udata[1]; /* "flexible-size array" (C89-style); data for the callbacks start here */
};
struct uundo_list_s {
uundo_serial_t serial;
uundo_serial_t saved_serial;
size_t num_undo; /* number of slots on the list (up to tail) */
size_t num_redo; /* number of redo items on the list (beyond tail) */
uundo_item_t *head; /* oldest item */
uundo_item_t *tail; /* newest item; an undo will undo this; the list may extend beyond this item (for redo) */
unsigned long freeze_serial; /* when non-zero, don't change the serial */
unsigned long freeze_add; /* when non-zero, do not store new items */
uundo_item_t *frozen; /* temporary items added under freeze_add */
};
/*** list management ***/
/* Initialize a previously uninitialized list lst; result is an empty list */
void uundo_list_init(uundo_list_t *lst);
/* Clear a previously initialized list lst (free undo slots); result is an empty list */
void uundo_list_clear(uundo_list_t *lst);
/* Clear a previously initialized list lst (free undo slots) */
void uundo_list_uninit(uundo_list_t *lst);
/* Clear the redo section */
void uundo_list_truncate_redo(uundo_list_t *lst);
/* Clear items from the sfirst serial (inclusive) to the tail of the list;
also clears the redo section. */
void uundo_list_truncate_from(uundo_list_t *lst, uundo_serial_t sfirst);
/* Search the undo list from tail back for a cookie; returns the first serial
that references the cookie or -1 if no item references the cookie */
uundo_serial_t uundo_list_find_cookie(uundo_list_t *lst, const char *cookie);
/*** undo/redo ***/
/* Allocate a new undo item at tail; clears the redo section; returns the new
item's udata, allocated to be data_len bytes long */
void *uundo_append(uundo_list_t *lst, const uundo_oper_t *oper, size_t data_len);
/* Performs undo of all items with the highest serial number */
int uundo_undo(uundo_list_t *lst);
/* Performs undo of all items with serial number higher than s_min */
int uundo_undo_above(uundo_list_t *lst, uundo_serial_t s_min);
/* Performs redo of all items with the lowest redoable serial number */
int uundo_redo(uundo_list_t *lst);
/* Performs redo of all redoable items with serial number lower than s_till */
int uundo_redo_below(uundo_list_t *lst, uundo_serial_t s_till);
/*** serial management ***/
/* Save or restore serial number for bundling actions under a single serial number */
#define uundo_inc_serial(lst) \
do { \
if (!((lst)->freeze_serial)) \
(lst)->serial++; \
} while(0) \
#define uundo_save_serial(lst) \
do { \
if (!((lst)->freeze_serial)) \
(lst)->saved_serial = (lst)->serial; \
} while(0) \
#define uundo_restore_serial(lst) \
do { \
if (!((lst)->freeze_serial)) \
(lst)->serial = (lst)->saved_serial; \
} while(0) \
#define uundo_freeze_serial(lst) (lst)->freeze_serial++
#define uundo_unfreeze_serial(lst) \
do { \
if (((lst)->freeze_serial) > 0) \
(lst)->freeze_serial--; \
} while(0) \
/*** add freeze management ***/
#define uundo_freeze_add(lst) (lst)->freeze_add++
#define uundo_unfreeze_add(lst) \
do { \
if (((lst)->freeze_add) > 0) { \
(lst)->freeze_add--; \
if (((lst)->freeze_add) == 0) \
uundo_flush_frozen(lst); \
} \
} while(0) \
/* Internal call: free all frozen temp storage records */
void uundo_flush_frozen(uundo_list_t *lst);
#endif
|