📄 sfundo.c
字号:
/*================================================================== * sfundo.c - Sound font undo/redo routines * * Smurf Sound Font Editor * Copyright (C) 1999-2001 Josh Green * * This program 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 2 * of the License, or (at your option) any later version. * * This program 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA or point your web browser to http://www.gnu.org. * * To contact the author of this program: * Email: Josh Green <jgreen@users.sourceforge.net> * Smurf homepage: http://smurf.sourceforge.net *==================================================================*/#include "config.h"#include <stdio.h>#include <glib.h>#include "sfundo.h"#include "sfdofunc.h"#include "uif_sfundo.h"static SFDoItem *sfdo_new_item (void);static void sfdo_free_item (SFDoItem *doitem);static void sfdo_new_entry (void);static void sfdo_remove_curpos_entry ();#define ENTRY_CHUNK_OPTIMUM_AREA 64#define ITEM_CHUNK_OPTIMUM_AREA 128static GMemChunk *entry_chunk;static GMemChunk *item_chunk;/* the do tree */SFDoTree sfdo_tree;/* if TRUE then undo system is active, else calls to sfdofuncs are ignored */gboolean sfdo_active = FALSE;gboolean sfdo_running = FALSE;/* initialize sfdo_tree */voidsfdo_init (void){ entry_chunk = g_mem_chunk_create (SFDoEntry, ENTRY_CHUNK_OPTIMUM_AREA, G_ALLOC_AND_FREE); item_chunk = g_mem_chunk_create (SFDoItem, ITEM_CHUNK_OPTIMUM_AREA, G_ALLOC_AND_FREE); sfdo_tree.root = g_node_new (NULL); /* root node is a dummy */ sfdo_tree.curpos = sfdo_tree.root; sfdo_tree.groups = NULL; sfdo_active = TRUE;}/* dump undo tree for debugging purposes */voidsfdo_debug_dump (GNode *start, gint sibling, gboolean traverse, gboolean detail){ GList *l = NULL, *p, *p2; GNode *n; SFDoEntry *entry; SFDoItem *item; gint prev, next; if (!start) start = sfdo_tree.curpos; /* no start specified, set to current pos */ else if (sibling >= 0) { /* start from the 'sibling' index of 'start' */ n = start->parent; /* get parent of 'start' */ if (n) { n = g_node_nth_child (n, sibling); /* find the sibling by index */ if (n) start = n; } } if (traverse) { /* traverse up the tree */ n = start; while (n && n->data) { entry = (SFDoEntry *)(n->data); l = g_list_prepend (l, n); n = n->parent; } /* traverse down the tree via the default redo path */ n = start; if (n) n = n->children; while (n && n->data) { entry = (SFDoEntry *)(n->data); l = g_list_append (l, n); n = n->children; } } else l = g_list_append (l, start); /* !traverse, show just one node */ printf (" %c ROOT\n", (sfdo_tree.curpos == sfdo_tree.root) ? '+' : '/'); p = l; prev = 0; next = 0; while (p) { n = (GNode *)(p->data); p = g_list_next (p); entry = (SFDoEntry *)(n->data); if (n->parent) { prev = g_node_child_position (n->parent, n); next = g_node_n_children (n->parent) - prev - 1; } printf ("%c%-2d%c%2d%c (%lx) %s\n", prev ? '<' : ' ', prev, (n == sfdo_tree.curpos) ? '+' : '|', next, next ? '>' : ' ', (long)(n), entry->descr); if (!detail) continue; p2 = g_list_last (entry->items); if (!p2) printf ("\t<EMPTY>\n"); while (p2) { item = (SFDoItem *)(p2->data); printf ("\t| (state = %lx) %s\n", (long)(item->state), sfdofuncs[item->type].descr); p2 = g_list_previous (p2); } }}/* open curpos entry to group do items into (nested grouping is allowed but they are only visable while open) */voidsfdo_group (gchar *descr){ GList *p; SFDoEntry *entry; if (!sfdo_active || sfdo_running) return; if (sfdo_tree.groups) /* sub group? */ { /* ? grouping already active? */ /* ?: yes, get last GList * SFDoItem of last group */ entry = (SFDoEntry *) (sfdo_tree.curpos->data); p = entry->items; /* first (most recent) group item */ } else /* top level group */ { sfdo_new_entry (); /* ?: no, create new entry */ p = NULL; /* last GList * of previous group is NULL */ /* copy the description to the entry if provided */ if (descr) ((SFDoEntry *)(sfdo_tree.curpos->data))->descr = g_strdup (descr); } sfdo_tree.groups = g_list_prepend (sfdo_tree.groups, p);}/* close group */voidsfdo_done (void){ GList *p; if (!sfdo_active || sfdo_running) return; g_return_if_fail (sfdo_tree.groups != NULL); p = sfdo_tree.groups; /* first (most recent) group item */ /* remove and free group node */ sfdo_tree.groups = g_list_remove_link (sfdo_tree.groups, p); g_list_free_1 (p); /* hack to update user interface after a toplevel group is closed */ if (!sfdo_tree.groups) uido_toplevel_group_done ();}/* add an item to the active open group entry */voidsfdo_add (guint16 type, gpointer state){ SFDoEntry *entry; SFDoItem *item; if (!sfdo_active) return; g_return_if_fail (sfdo_tree.groups != NULL); g_return_if_fail (type != DOFUNC_INVALID); g_return_if_fail (type < DOFUNC_LAST); /* create and load the SFDoItem */ item = sfdo_new_item (); item->type = type; item->state = state; entry = (SFDoEntry *) (sfdo_tree.curpos->data); entry->items = g_list_prepend (entry->items, item);}/* undo all items in current open group and delete it */voidsfdo_retract (void){ GList *p, *p2; SFDoEntry *entry; SFDoItem *doitem; if (!sfdo_active) return; g_return_if_fail (sfdo_tree.groups != NULL); /* fail if no groups? */ sfdo_running = TRUE; /* to stop recursive undo calls */ entry = (SFDoEntry *)(sfdo_tree.curpos->data); /* current entry */ p = entry->items; /* first item of current entry */ /* loop while items and item isn't the last item of the previous group */ while (p && p->data != sfdo_tree.groups->data) { doitem = (SFDoItem *)(p->data); (*sfdofuncs[doitem->type].restore) (doitem); /* call undo function */ if (sfdofuncs[doitem->type].free) /* call free state function */ (*sfdofuncs[doitem->type].free) (doitem); p2 = p; p = g_list_next (p); /* must advance here, as p will be removed */ /* remove SFDoItem GList node */ entry->items = g_list_remove_link (entry->items, p2); g_list_free_1 (p2); sfdo_free_item (doitem); /* free the SFDoItem structure */ } /* free last group SFDoItem pointer, thereby destroying the group */ sfdo_tree.groups = g_list_remove_link (sfdo_tree.groups, sfdo_tree.groups); if (!sfdo_tree.groups) sfdo_remove_curpos_entry (); sfdo_running = FALSE;}/* undo all sub groups in current entry and delete it */voidsfdo_retract_all (void){ if (!sfdo_active) return; g_return_if_fail (sfdo_tree.groups != NULL); /* fail if no groups? */ while (sfdo_tree.groups) sfdo_retract ();}/* undo an entry from the tree Steps: save redo entry state, restore undo entry state, remove undo entry, replace undo entry with redo, and move curpos up the tree */voidsfdo_undo (void){ GList *p; SFDoEntry *entry; SFDoItem *undoitem, *newitem; if (!sfdo_active) return; /* groups should not be active */ g_return_if_fail (sfdo_tree.groups == NULL); /* return if no entries to undo */ if (sfdo_tree.curpos == sfdo_tree.root) return; sfdo_running = TRUE; /* to stop recursive undo calls */ entry = (SFDoEntry *)(sfdo_tree.curpos->data); /* current entry */ p = entry->items; /* first item of current entry */ /* loop while items */ while (p) { undoitem = (SFDoItem *)(p->data); /* item to undo */ newitem = sfdo_new_item (); /* new item to store redo state data */ /* load redo item using undo item as reference */ (*sfdofuncs[undoitem->type].restate) (undoitem, newitem); /* restore state from undo item */ (*sfdofuncs[undoitem->type].restore) (undoitem); /* free old undo item state and structure */ if (sfdofuncs[undoitem->type].free) (*sfdofuncs[undoitem->type].free) (undoitem); sfdo_free_item (undoitem); p->data = newitem; /* replace old undo GList data ptr with redo ptr */ p = g_list_next (p); /* advance forward in list (backwards in time) */ } /* advance curpos up the tree */ sfdo_tree.curpos = sfdo_tree.curpos->parent; sfdo_running = FALSE;}/* redo an entry from the sfdo_tree node is a GNode ptr to a child of curpos or NULL to redo most recent undo Steps: save undo state, restore redo state, free redo item, move curpos to redone node */voidsfdo_redo (GNode *node){ GList *p; GNode *n; SFDoEntry *entry; SFDoItem *redoitem, *newitem; if (!sfdo_active) return; /* groups should not be active */ g_return_if_fail (sfdo_tree.groups == NULL); /* return if no entries to redo */ if (sfdo_tree.curpos->children == NULL) return; n = sfdo_tree.curpos->children; if (node) /* redo node specified? */ { /* look for child node matching "node" */ while (n && n != node) n = g_node_next_sibling (n); g_return_if_fail (n != NULL); /* fail if no match found */ } sfdo_running = TRUE; /* to stop recursive undo calls */ entry = (SFDoEntry *)(n->data); /* entry to redo */ p = g_list_last (entry->items); /* last item of entry to redo */ /* loop while items */ while (p) { redoitem = (SFDoItem *)(p->data); /* item to redo */ newitem = sfdo_new_item (); /* new item to store undo state data */ /* load undo item using redo item as reference */ (*sfdofuncs[redoitem->type].restate) (redoitem, newitem); /* restore state from redo item */ (*sfdofuncs[redoitem->type].restore) (redoitem); /* free old redo item state and structure */ if (sfdofuncs[redoitem->type].free) (*sfdofuncs[redoitem->type].free) (redoitem); sfdo_free_item (redoitem); p->data = newitem; /* replace old redo GList data ptr with undo ptr */ p = g_list_previous (p); /* advance back in list (forwards in time) */ } /* advance curpos to redone node */ sfdo_tree.curpos = n; sfdo_running = FALSE;}/* jump to specified state (undo/redo from curpos to 'node') */voidsfdo_jump (GNode *node){ GNode *n; GSList *srclist = NULL, *dstlist = NULL; GSList *srcp, *dstp; gint i; if (!sfdo_active) return; /* groups should not be active */ g_return_if_fail (sfdo_tree.groups == NULL); n = sfdo_tree.curpos; while (n) /* make a list of curpos ancestry */ { srclist = g_slist_prepend (srclist, n); n = n->parent; } n = node; while (n) /* make a list of destination ancestry */ { dstlist = g_slist_prepend (dstlist, n); n = n->parent; } srcp = srclist; dstp = dstlist; i = 0; while (srcp && dstp) /* skip common ancestry */ { if (srcp->data != dstp->data) break; srcp = g_slist_next (srcp); dstp = g_slist_next (dstp); i++; } while (srcp) /* undo from curpos up to common parent */ { sfdo_undo (); srcp = g_slist_next (srcp); } while (dstp) /* redo from common parent to destination */ { sfdo_redo ((GNode *)(dstp->data)); dstp = g_slist_next (dstp); } g_slist_free (srclist); g_slist_free (dstlist);}/* allocate a new SFDoItem and initialize to a NULL state */static SFDoItem *sfdo_new_item (void){ SFDoItem *item; item = g_chunk_new (SFDoItem, item_chunk); item->type = DOFUNC_INVALID; item->state = NULL; return (item);}static voidsfdo_free_item (SFDoItem *doitem){ g_mem_chunk_free (item_chunk, doitem);}/* appends a new entry onto the curpos node, and makes it the curpos */static voidsfdo_new_entry (void){ SFDoEntry *entry; /* grouping should not be active! */ g_return_if_fail (sfdo_tree.groups == NULL); entry = g_chunk_new (SFDoEntry, entry_chunk); entry->descr = NULL; entry->items = NULL; sfdo_tree.curpos = g_node_prepend_data (sfdo_tree.curpos, entry);}/* removes the current entry which must have NO SFDoItems and NO children (i.e. bottom of the tree) */static voidsfdo_remove_curpos_entry (){ GNode *n; SFDoEntry *entry; /* curpos should be the bottom of the tree (no GNode children!) */ g_return_if_fail (sfdo_tree.curpos->children == NULL); n = sfdo_tree.curpos; /* current position GNode */ entry = (SFDoEntry *)(n->data); /* get current entry */ g_return_if_fail (entry->items == NULL); /* fail if it has SFDoItems */ sfdo_tree.curpos = sfdo_tree.curpos->parent; /* move curpos to parent */ /* if this entry has a description, free it */ if (entry->descr) g_free (entry->descr); g_mem_chunk_free (entry_chunk, entry); /* free the SFDoEntry atom */ g_node_unlink (n); /* unlink the node from the tree */ g_node_destroy (n); /* destroy node */}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -