⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sfundo.c

📁 A GTK sound font editor. Sound font files are used to synthesize instruments from audio samples for
💻 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 + -