aboutsummaryrefslogtreecommitdiff
path: root/shared/hammer
diff options
context:
space:
mode:
Diffstat (limited to 'shared/hammer')
-rw-r--r--shared/hammer/Makefile4
-rw-r--r--shared/hammer/Makefile.objects0
-rw-r--r--shared/hammer/Makefile.sources4
-rw-r--r--shared/hammer/file.c402
-rw-r--r--shared/hammer/file.h43
-rw-r--r--shared/hammer/gui.c438
-rw-r--r--shared/hammer/gui.h30
-rw-r--r--shared/hammer/tree.c482
-rw-r--r--shared/hammer/tree.h37
9 files changed, 1440 insertions, 0 deletions
diff --git a/shared/hammer/Makefile b/shared/hammer/Makefile
new file mode 100644
index 0000000..5dcb2c8
--- /dev/null
+++ b/shared/hammer/Makefile
@@ -0,0 +1,4 @@
+ROOT_DIR = ../..
+include $(ROOT_DIR)/Makefile.common
+
+all: $(OBJECTS)
diff --git a/shared/hammer/Makefile.objects b/shared/hammer/Makefile.objects
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/shared/hammer/Makefile.objects
diff --git a/shared/hammer/Makefile.sources b/shared/hammer/Makefile.sources
new file mode 100644
index 0000000..5a6d99a
--- /dev/null
+++ b/shared/hammer/Makefile.sources
@@ -0,0 +1,4 @@
+OTHER_SOURCES = \
+file.c \
+gui.c \
+tree.c
diff --git a/shared/hammer/file.c b/shared/hammer/file.c
new file mode 100644
index 0000000..6a94c92
--- /dev/null
+++ b/shared/hammer/file.c
@@ -0,0 +1,402 @@
+/* Copyright (c) 2002-2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* The three uses of the 'hammerfile' proxy class are:
+ 1. providing `embedding' facility -- storing master object's state
+ in a .pd file,
+ 2. encapsulating openpanel/savepanel management,
+ 3. extending the gui of Pd with a simple text editor window.
+
+ A master class which needs embedding feature (like coll), passes
+ a nonzero flag to the hammerfile setup routine, and a nonzero embedfn
+ function pointer to the hammerfile constructor. If a master needs
+ access to the panels (like collcommon), then it passes nonzero readfn
+ and/or writefn callback pointers to the constructor. A master which has
+ an associated text editor, AND wants to update object's state after
+ edits, passes a nonzero updatefn callback in a call to the constructor. */
+
+#include <stdio.h>
+#include <string.h>
+#include "m_pd.h"
+#include "g_canvas.h"
+/* need this for t_class::c_wb field access, LATER find a better way... */
+#include "unstable/pd_imp.h"
+#include "hammer/file.h"
+
+static t_class *hammerfile_class = 0;
+static t_hammerfile *hammerfile_proxies;
+static t_symbol *ps__C;
+
+static t_hammerfile *hammerfile_getproxy(t_pd *master)
+{
+ t_hammerfile *f;
+ for (f = hammerfile_proxies; f; f = f->f_next)
+ if (f->f_master == master)
+ return (f);
+ return (0);
+}
+
+/* FIXME somehow plug the "save changes" dialog into close-by-wm */
+/* FIXME dirty condition */
+static void hammereditor_guidefs(void)
+{
+ sys_gui("proc hammereditor_open {name geometry title} {\n");
+ sys_gui(" if {[winfo exists $name]} {\n");
+ sys_gui(" $name.text delete 1.0 end\n");
+ sys_gui(" } else {\n");
+ sys_gui(" toplevel $name\n");
+ sys_gui(" wm title $name $title\n");
+ sys_gui(" wm geometry $name $geometry\n");
+ sys_gui(" text $name.text -relief raised -bd 2 \\\n");
+ sys_gui(" -font -*-courier-medium--normal--12-* \\\n");
+ sys_gui(" -yscrollcommand \"$name.scroll set\" -background lightgrey\n");
+ sys_gui(" scrollbar $name.scroll -command \"$name.text yview\"\n");
+ sys_gui(" pack $name.scroll -side right -fill y\n");
+ sys_gui(" pack $name.text -side left -fill both -expand 1\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammereditor_doclose {name} {\n");
+ sys_gui(" destroy $name\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammereditor_append {name contents} {\n");
+ sys_gui(" if {[winfo exists $name]} {\n");
+ sys_gui(" $name.text insert end $contents\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+
+ /* FIXME make it more reliable */
+ sys_gui("proc hammereditor_send {name} {\n");
+ sys_gui(" if {[winfo exists $name]} {\n");
+ sys_gui(" set ii [$name.text index [concat end - 1 lines]]\n");
+ sys_gui(" pd [concat miXed$name clear \\;]\n");
+ sys_gui(" for {set i 1} \\\n");
+ sys_gui(" {[$name.text compare $i.end < $ii]} \\\n");
+ sys_gui(" {incr i 1} {\n");
+ sys_gui(" set lin [$name.text get $i.0 $i.end]\n");
+ sys_gui(" if {$lin != \"\"} {\n");
+ /* LATER rethink semi/comma mapping */
+ sys_gui(" regsub -all \\; $lin \" _semi_ \" tmplin\n");
+ sys_gui(" regsub -all \\, $tmplin \" _comma_ \" lin\n");
+ sys_gui(" pd [concat miXed$name addline $lin \\;]\n");
+ sys_gui(" }\n");
+ sys_gui(" }\n");
+ sys_gui(" pd [concat miXed$name end \\;]\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammereditor_close {name ask} {\n");
+ sys_gui(" if {[winfo exists $name]} {\n");
+ sys_gui(" set dirty $ask\n"); /* FIXME */
+ sys_gui(" if {$dirty == 0} {hammereditor_doclose $name} else {\n");
+ sys_gui(" set title [wm title $name]\n");
+ sys_gui(" set answer [tk_messageBox \\-type yesnocancel \\\n");
+ sys_gui(" \\-icon question \\\n");
+ sys_gui(" \\-message [concat Save changes to $title?]]\n");
+ sys_gui(" if {$answer == \"yes\"} {hammereditor_send $name}\n");
+ sys_gui(" if {$answer != \"cancel\"} {hammereditor_doclose $name}\n");
+ sys_gui(" }\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+}
+
+void hammereditor_open(t_hammerfile *f, char *title)
+{
+ if (!title) title = class_getname(*f->f_master);
+ sys_vgui("hammereditor_open .%x %dx%d {%s}\n", (int)f, 600, 340, title);
+}
+
+static void hammereditor_tick(t_hammerfile *f)
+{
+ sys_vgui("hammereditor_close .%x %d\n", (int)f, 1);
+}
+
+void hammereditor_close(t_hammerfile *f, int ask)
+{
+ if (ask)
+ /* hack: deferring modal dialog creation in order to allow for
+ a message box redraw to happen -- LATER investigate */
+ clock_delay(f->f_editorclock, 0);
+ else
+ sys_vgui("hammereditor_close .%x %d\n", (int)f, 0);
+}
+
+void hammereditor_append(t_hammerfile *f, char *contents)
+{
+ if (!contents) contents = "";
+ sys_vgui("hammereditor_append .%x {%s}\n", (int)f, contents);
+}
+
+static void hammereditor_clear(t_hammerfile *f)
+{
+ if (f->f_editorfn)
+ {
+ if (f->f_binbuf)
+ binbuf_clear(f->f_binbuf);
+ else
+ f->f_binbuf = binbuf_new();
+ }
+}
+
+static void hammereditor_addline(t_hammerfile *f,
+ t_symbol *s, int ac, t_atom *av)
+{
+ if (f->f_editorfn)
+ {
+ int i;
+ t_atom *ap;
+ for (i = 0, ap = av; i < ac; i++, ap++)
+ {
+ if (ap->a_type == A_SYMBOL)
+ {
+ /* LATER rethink semi/comma mapping */
+ if (!strcmp(ap->a_w.w_symbol->s_name, "_semi_"))
+ SETSEMI(ap);
+ else if (!strcmp(ap->a_w.w_symbol->s_name, "_comma_"))
+ SETCOMMA(ap);
+ }
+ }
+ binbuf_add(f->f_binbuf, ac, av);
+ }
+}
+
+static void hammereditor_end(t_hammerfile *f)
+{
+ if (f->f_editorfn)
+ {
+ (*f->f_editorfn)(f->f_master, 0, binbuf_getnatom(f->f_binbuf),
+ binbuf_getvec(f->f_binbuf));
+ binbuf_clear(f->f_binbuf);
+ }
+}
+
+static void hammerpanel_guidefs(void)
+{
+ sys_gui("proc hammerpanel_save {target inidir inifile} {\n");
+ sys_gui(" if {$inifile != \"\"} {\n");
+ sys_gui(" set filename [tk_getSaveFile \\\n");
+ sys_gui(" -initialdir $inidir -initialfile $inifile]\n");
+ sys_gui(" } else {\n");
+ sys_gui(" set filename [tk_getSaveFile]\n");
+ sys_gui(" }\n");
+ sys_gui(" if {$filename != \"\"} {\n");
+ sys_gui(" pd [concat $target symbol [pdtk_enquote $filename] \\;]\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+}
+
+static void hammerpanel_symbol(t_hammerfile *f, t_symbol *s)
+{
+ if (s && s != &s_ && f->f_panelfn)
+ (*f->f_panelfn)(f->f_master, s, 0, 0);
+}
+
+static void hammerpanel_tick(t_hammerfile *f)
+{
+ if (f->f_savepanel)
+ sys_vgui("pdtk_openpanel %s\n", f->f_bindname->s_name);
+ else
+ sys_vgui("hammerpanel_save %s {%s} {%s}\n", f->f_bindname->s_name,
+ f->f_inidir->s_name, f->f_inifile->s_name);
+}
+
+/* these are hacks: deferring modal dialog creation in order to allow for
+ a message box redraw to happen -- LATER investigate */
+void hammerpanel_open(t_hammerfile *f)
+{
+ clock_delay(f->f_panelclock, 0);
+}
+
+void hammerpanel_save(t_hammerfile *f, t_symbol *inidir, t_symbol *inifile)
+{
+ /* LATER ask if we can rely on s_ pointing to "" */
+ f->f_savepanel->f_inidir = (inidir ? inidir : &s_);
+ f->f_savepanel->f_inifile = (inifile ? inifile : &s_);
+ clock_delay(f->f_savepanel->f_panelclock, 0);
+}
+
+/* Currently embeddable hammer classes do not use the 'saveto' method.
+ In order to use it, any embeddable class would have to add a creation
+ method to pd_canvasmaker -- then saving could be done with a 'proper'
+ sequence: #N <master> <args>; #X <whatever>; ...; #X restore <x> <y>;
+ However, this works only for -lib externals. So, we choose a sequence:
+ #X obj <x> <y> <master> <args>; #C <whatever>; ...; #C restore;
+ Since the first message in this sequence is a valid creation message
+ on its own, we have to distinguish loading from a .pd file, and other
+ cases (editing). */
+
+static void hammerembed_gc(t_pd *x, t_symbol *s, int expected)
+{
+ t_pd *garbage;
+ int count = 0;
+ while (garbage = pd_findbyclass(s, *x)) pd_unbind(garbage, s), count++;
+ if (count != expected)
+ bug("hammerembed_gc (%d garbage bindings)", count);
+}
+
+static void hammerembed_restore(t_pd *master)
+{
+ hammerembed_gc(master, ps__C, 1);
+}
+
+void hammerembed_save(t_gobj *master, t_binbuf *bb)
+{
+ t_hammerfile *f = hammerfile_getproxy((t_pd *)master);
+ t_text *t = (t_text *)master;
+ binbuf_addv(bb, "ssii", &s__X, gensym("obj"),
+ (int)t->te_xpix, (int)t->te_ypix);
+ binbuf_addbinbuf(bb, t->te_binbuf);
+ binbuf_addsemi(bb);
+ if (f && f->f_embedfn)
+ (*f->f_embedfn)(f->f_master, bb, ps__C);
+ binbuf_addv(bb, "ss;", ps__C, gensym("restore"));
+}
+
+int hammerfile_ismapped(t_hammerfile *f)
+{
+ return (f->f_canvas->gl_mapped);
+}
+
+int hammerfile_isloading(t_hammerfile *f)
+{
+ return (f->f_canvas->gl_loading);
+}
+
+/* LATER find a better way */
+int hammerfile_ispasting(t_hammerfile *f)
+{
+ int result = 0;
+ t_canvas *cv = f->f_canvas;
+ if (!cv->gl_loading)
+ {
+ t_pd *z = s__X.s_thing;
+ if (z == (t_pd *)cv)
+ {
+ pd_popsym(z);
+ if (s__X.s_thing == (t_pd *)cv) result = 1;
+ pd_pushsym(z);
+ }
+ else if (z) result = 1;
+ }
+#if 0
+ if (result) post("pasting");
+#endif
+ return (result);
+}
+
+void hammerfile_free(t_hammerfile *f)
+{
+ t_hammerfile *prev, *next;
+ hammereditor_close(f, 0);
+ if (f->f_embedfn)
+ /* just in case of missing 'restore' */
+ hammerembed_gc(f->f_master, ps__C, 0);
+ if (f->f_savepanel)
+ {
+ pd_unbind((t_pd *)f->f_savepanel, f->f_savepanel->f_bindname);
+ pd_free((t_pd *)f->f_savepanel);
+ }
+ if (f->f_bindname) pd_unbind((t_pd *)f, f->f_bindname);
+ if (f->f_panelclock) clock_free(f->f_panelclock);
+ if (f->f_editorclock) clock_free(f->f_editorclock);
+ for (prev = 0, next = hammerfile_proxies;
+ next; prev = next, next = next->f_next)
+ if (next == f)
+ break;
+ if (prev)
+ prev->f_next = f->f_next;
+ else if (f == hammerfile_proxies)
+ hammerfile_proxies = f->f_next;
+ pd_free((t_pd *)f);
+}
+
+t_hammerfile *hammerfile_new(t_pd *master, t_hammerembedfn embedfn,
+ t_hammerfilefn readfn, t_hammerfilefn writefn,
+ t_hammerfilefn updatefn)
+{
+ t_hammerfile *result = (t_hammerfile *)pd_new(hammerfile_class);
+ result->f_master = master;
+ result->f_next = hammerfile_proxies;
+ hammerfile_proxies = result;
+ if (!(result->f_canvas = canvas_getcurrent()))
+ {
+ bug("hammerfile_new: out of context");
+ return (result);
+ }
+
+ /* 1. embedding */
+ if (result->f_embedfn = embedfn)
+ {
+ /* just in case of missing 'restore' */
+ hammerembed_gc(master, ps__C, 0);
+ if (hammerfile_isloading(result) || hammerfile_ispasting(result))
+ pd_bind(master, ps__C);
+ }
+
+ /* 2. the panels */
+ if (readfn || writefn)
+ {
+ t_hammerfile *f;
+ char buf[64];
+ sprintf(buf, "miXed.%x", (int)result);
+ result->f_bindname = gensym(buf);
+ pd_bind((t_pd *)result, result->f_bindname);
+ result->f_panelfn = readfn;
+ result->f_panelclock = clock_new(result, (t_method)hammerpanel_tick);
+ f = (t_hammerfile *)pd_new(hammerfile_class);
+ f->f_master = master;
+ sprintf(buf, "miXed.%x", (int)f);
+ f->f_bindname = gensym(buf);
+ pd_bind((t_pd *)f, f->f_bindname);
+ f->f_panelfn = writefn;
+ f->f_panelclock = clock_new(f, (t_method)hammerpanel_tick);
+ result->f_savepanel = f;
+ }
+ else result->f_savepanel = 0;
+
+ /* 3. editor */
+ if (result->f_editorfn = updatefn)
+ {
+ result->f_editorclock = clock_new(result, (t_method)hammereditor_tick);
+ if (!result->f_bindname)
+ {
+ char buf[64];
+ sprintf(buf, "miXed.%x", (int)result);
+ result->f_bindname = gensym(buf);
+ pd_bind((t_pd *)result, result->f_bindname);
+ }
+ }
+ return (result);
+}
+
+void hammerfile_setup(t_class *c, int embeddable)
+{
+ if (embeddable)
+ {
+ t_widgetbehavior *newwb = getbytes(sizeof(*newwb)); /* never freed */
+ *newwb = *c->c_wb;
+ newwb->w_savefn = hammerembed_save;
+ class_setwidget(c, newwb);
+ class_addmethod(c, (t_method)hammerembed_restore,
+ gensym("restore"), 0);
+ }
+ if (!hammerfile_class)
+ {
+ ps__C = gensym("#C");
+ hammerfile_class = class_new(gensym("_hammerfile"), 0, 0,
+ sizeof(t_hammerfile),
+ CLASS_PD | CLASS_NOINLET, 0);
+ class_addsymbol(hammerfile_class, hammerpanel_symbol);
+ class_addmethod(hammerfile_class, (t_method)hammereditor_clear,
+ gensym("clear"), 0);
+ class_addmethod(hammerfile_class, (t_method)hammereditor_addline,
+ gensym("addline"), A_GIMME, 0);
+ class_addmethod(hammerfile_class, (t_method)hammereditor_end,
+ gensym("end"), 0);
+ /* LATER find a way of ensuring that these are not defined yet... */
+ hammereditor_guidefs();
+ hammerpanel_guidefs();
+ }
+}
diff --git a/shared/hammer/file.h b/shared/hammer/file.h
new file mode 100644
index 0000000..d0f6526
--- /dev/null
+++ b/shared/hammer/file.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2002-2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#ifndef __HAMMERFILE_H__
+#define __HAMMERFILE_H__
+
+typedef void (*t_hammerfilefn)(t_pd *, t_symbol *, int, t_atom *);
+typedef void (*t_hammerembedfn)(t_pd *, t_binbuf *, t_symbol *);
+
+typedef struct _hammerfile
+{
+ t_pd f_pd;
+ t_pd *f_master;
+ t_canvas *f_canvas;
+ t_symbol *f_bindname;
+ t_symbol *f_inidir;
+ t_symbol *f_inifile;
+ t_hammerfilefn f_panelfn;
+ t_hammerfilefn f_editorfn;
+ t_hammerembedfn f_embedfn;
+ t_binbuf *f_binbuf;
+ t_clock *f_panelclock;
+ t_clock *f_editorclock;
+ struct _hammerfile *f_savepanel;
+ struct _hammerfile *f_next;
+} t_hammerfile;
+
+void hammereditor_open(t_hammerfile *f, char *title);
+void hammereditor_close(t_hammerfile *f, int ask);
+void hammereditor_append(t_hammerfile *f, char *contents);
+void hammerpanel_open(t_hammerfile *f);
+void hammerpanel_save(t_hammerfile *f, t_symbol *inidir, t_symbol *inifile);
+int hammerfile_ismapped(t_hammerfile *f);
+int hammerfile_isloading(t_hammerfile *f);
+int hammerfile_ispasting(t_hammerfile *f);
+void hammerfile_free(t_hammerfile *f);
+t_hammerfile *hammerfile_new(t_pd *master, t_hammerembedfn embedfn,
+ t_hammerfilefn readfn, t_hammerfilefn writefn,
+ t_hammerfilefn updatefn);
+void hammerfile_setup(t_class *c, int embeddable);
+
+#endif
diff --git a/shared/hammer/gui.c b/shared/hammer/gui.c
new file mode 100644
index 0000000..5e98ff8
--- /dev/null
+++ b/shared/hammer/gui.c
@@ -0,0 +1,438 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* FIXME use guiconnect */
+
+#include <stdio.h>
+#include "m_pd.h"
+#include "g_canvas.h"
+#include "hammer/gui.h"
+
+//#define HAMMERGUI_DEBUG
+
+static t_class *hammergui_class = 0;
+static t_hammergui *sink = 0;
+static t_symbol *ps__up;
+static t_symbol *ps__focus;
+static t_symbol *ps__vised;
+
+static void hammergui_anything(t_hammergui *snk,
+ t_symbol *s, int ac, t_atom *av)
+{
+ /* Dummy method, filtering out messages from gui to the masters. This is
+ needed in order to keep Pd's message system happy in a ``gray period''
+ -- after last master is unbound, and before gui bindings are cleared. */
+#ifdef HAMMERGUI_DEBUG
+ startpost("%s", s->s_name);
+ postatom(ac, av);
+ endpost();
+#endif
+}
+
+/* filtering out redundant "_up" messages */
+static void hammergui__up(t_hammergui *snk, t_floatarg f)
+{
+#ifdef HAMMERGUI_DEBUG
+ post("_up %g", f);
+#endif
+ if ((int)f)
+ {
+ if (!snk->g_up)
+ {
+ snk->g_up = 1;
+ if (snk->g_mouse->s_thing)
+ {
+ t_atom at;
+ SETFLOAT(&at, 1);
+ pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at);
+ }
+ }
+ }
+ else
+ {
+ if (snk->g_up)
+ {
+ snk->g_up = 0;
+ if (snk->g_mouse->s_thing)
+ {
+ t_atom at;
+ SETFLOAT(&at, 0);
+ pd_typedmess(snk->g_mouse->s_thing, ps__up, 1, &at);
+ }
+ }
+ }
+}
+
+static void hammergui__focus(t_hammergui *snk, t_symbol *s, t_floatarg f)
+{
+#ifdef HAMMERGUI_DEBUG
+ if (s) post("_focus %s %g", s->s_name, f);
+#endif
+ if (snk->g_focus->s_thing)
+ {
+ t_atom at[2];
+ SETSYMBOL(&at[0], s);
+ SETFLOAT(&at[1], f);
+ pd_typedmess(snk->g_focus->s_thing, ps__focus, 2, at);
+ }
+}
+
+static void hammergui__vised(t_hammergui *snk, t_symbol *s, t_floatarg f)
+{
+#ifdef HAMMERGUI_DEBUG
+ if (s) post("_vised %s %g", s->s_name, f);
+#endif
+ if (snk->g_vised->s_thing)
+ {
+ t_atom at[2];
+ SETSYMBOL(&at[0], s);
+ SETFLOAT(&at[1], f);
+ pd_typedmess(snk->g_vised->s_thing, ps__vised, 2, at);
+ }
+#if 0
+ /* How to be notified about changes of button state, prior to gui objects
+ in a canvas? LATER find a reliable way -- delete if failed */
+ sys_vgui("bindtags %s {hammertag %s Canvas . all}\n",
+ s->s_name, s->s_name);
+#endif
+}
+
+
+static void hammergui_dobindmouse(t_hammergui *snk)
+{
+#if 0
+ /* How to be notified about changes of button state, prior to gui objects
+ in a canvas? LATER find a reliable way -- delete if failed */
+ sys_vgui("bind hammertag <<hammerdown>> {pd [concat %s _up 0 \\;]}\n",
+ snk->g_gui->s_name);
+ sys_vgui("bind hammertag <<hammerup>> {pd [concat %s _up 1 \\;]}\n",
+ snk->g_gui->s_name);
+#endif
+ sys_vgui("bind all <<hammerdown>> {pd [concat %s _up 0 \\;]}\n",
+ snk->g_gui->s_name);
+ sys_vgui("bind all <<hammerup>> {pd [concat %s _up 1 \\;]}\n",
+ snk->g_gui->s_name);
+}
+
+static void hammergui__remouse(t_hammergui *snk)
+{
+ if (snk->g_mouse->s_thing)
+ {
+ /* if a new master was bound in a gray period, we need to
+ restore gui bindings */
+#if 1
+ post("rebinding mouse...");
+#endif
+ hammergui_dobindmouse(snk);
+ }
+}
+
+static void hammergui_dobindfocus(t_hammergui *snk)
+{
+ sys_vgui("bind Canvas <<hammerfocusin>> \
+ {pd [concat %s _focus %%W 1 \\;]}\n", snk->g_gui->s_name);
+ sys_vgui("bind Canvas <<hammerfocusout>> \
+ {pd [concat %s _focus %%W 0 \\;]}\n", snk->g_gui->s_name);
+}
+
+static void hammergui__refocus(t_hammergui *snk)
+{
+ if (snk->g_focus->s_thing)
+ {
+ /* if a new master was bound in a gray period, we need to
+ restore gui bindings */
+#if 1
+ post("rebinding focus...");
+#endif
+ hammergui_dobindfocus(snk);
+ }
+}
+
+static void hammergui_dobindvised(t_hammergui *snk)
+{
+ sys_vgui("bind Canvas <<hammervised>> \
+ {pd [concat %s _vised %%W 1 \\;]}\n", snk->g_gui->s_name);
+ sys_vgui("bind Canvas <<hammerunvised>> \
+ {pd [concat %s _vised %%W 0 \\;]}\n", snk->g_gui->s_name);
+}
+
+static void hammergui__revised(t_hammergui *snk)
+{
+ if (snk->g_vised->s_thing)
+ {
+ /* if a new master was bound in a gray period, we need to
+ restore gui bindings */
+#if 1
+ post("rebinding vised events...");
+#endif
+ hammergui_dobindvised(snk);
+ }
+}
+
+static void hammergui_setup(void)
+{
+ hammergui_class = class_new(gensym("_hammergui"), 0, 0,
+ sizeof(t_hammergui),
+ CLASS_PD | CLASS_NOINLET, 0);
+ class_addanything(hammergui_class, hammergui_anything);
+ class_addmethod(hammergui_class, (t_method)hammergui__remouse,
+ gensym("_remouse"), 0);
+ class_addmethod(hammergui_class, (t_method)hammergui__refocus,
+ gensym("_refocus"), 0);
+ class_addmethod(hammergui_class, (t_method)hammergui__revised,
+ gensym("_revised"), 0);
+ ps__up = gensym("_up");
+ class_addmethod(hammergui_class, (t_method)hammergui__up,
+ ps__up, A_FLOAT, 0);
+ ps__focus = gensym("_focus");
+ class_addmethod(hammergui_class, (t_method)hammergui__focus,
+ ps__focus, A_SYMBOL, A_FLOAT, 0);
+ ps__vised = gensym("_vised");
+ class_addmethod(hammergui_class, (t_method)hammergui__vised,
+ ps__vised, A_SYMBOL, A_FLOAT, 0);
+
+ sys_gui("proc hammergui_remouse {} {\n");
+ sys_gui(" bind all <<hammerdown>> {}\n");
+ sys_gui(" bind all <<hammerup>> {}\n");
+ sys_gui(" pd [concat #hammergui _remouse \\;]\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammergui_mousexy {target} {\n");
+ sys_gui(" set x [winfo pointerx .]\n");
+ sys_gui(" set y [winfo pointery .]\n");
+ sys_gui(" pd [concat #hammermouse $target $x $y \\;]\n");
+ sys_gui("}\n");
+
+ /* visibility hack for msw, LATER rethink */
+ sys_gui("global hammergui_ispolling\n");
+ sys_gui("global hammergui_x\n");
+ sys_gui("global hammergui_y\n");
+ sys_gui("set hammergui_ispolling 0\n");
+ sys_gui("set hammergui_x 0\n");
+ sys_gui("set hammergui_y 0\n");
+
+ sys_gui("proc hammergui_poll {} {\n");
+ sys_gui(" global hammergui_ispolling\n");
+ sys_gui(" global hammergui_x\n");
+ sys_gui(" global hammergui_y\n");
+ sys_gui(" if {$hammergui_ispolling == 1} {\n");
+ sys_gui(" set x [winfo pointerx .]\n");
+ sys_gui(" set y [winfo pointery .]\n");
+ sys_gui(" if {$hammergui_x != $x || $hammergui_y != $y} {\n");
+ sys_gui(" pd [concat #hammermouse _poll $x $y \\;]\n");
+ sys_gui(" set hammergui_x $x\n");
+ sys_gui(" set hammergui_y $y\n");
+ sys_gui(" }\n");
+ sys_gui(" after 50 hammergui_poll\n");
+ sys_gui(" }\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammergui_refocus {} {\n");
+ sys_gui(" bind Canvas <<hammerfocusin>> {}\n");
+ sys_gui(" bind Canvas <<hammerfocusout>> {}\n");
+ sys_gui(" pd [concat #hammergui _refocus \\;]\n");
+ sys_gui("}\n");
+
+ sys_gui("proc hammergui_revised {} {\n");
+ sys_gui(" bind Canvas <<hammervised>> {}\n");
+ sys_gui(" bind Canvas <<hammerunvised>> {}\n");
+ sys_gui(" pd [concat #hammergui _revised \\;]\n");
+ sys_gui("}\n");
+}
+
+static int hammergui_validate(int dosetup)
+{
+ if (dosetup)
+ {
+ if (!hammergui_class) hammergui_setup();
+ if (!sink)
+ {
+ sink = (t_hammergui *)pd_new(hammergui_class);
+ sink->g_gui = gensym("#hammergui");
+ pd_bind((t_pd *)sink, sink->g_gui);
+ }
+ }
+ if (hammergui_class && sink)
+ return (1);
+ else
+ {
+ bug("hammergui_validate");
+ return (0);
+ }
+}
+
+static int hammergui_mousevalidate(int dosetup)
+{
+ if (dosetup && !sink->g_mouse)
+ {
+ sink->g_mouse = gensym("#hammermouse");
+ sys_gui("event add <<hammerdown>> <ButtonPress>\n");
+ sys_gui("event add <<hammerup>> <ButtonRelease>\n");
+ }
+ if (sink->g_mouse)
+ return (1);
+ else
+ {
+ bug("hammergui_mousevalidate");
+ return (0);
+ }
+}
+
+static int hammergui_pollvalidate(int dosetup)
+{
+ if (dosetup && !sink->g_poll)
+ {
+ sink->g_poll = gensym("#hammerpoll");
+ pd_bind((t_pd *)sink, sink->g_poll); /* never unbound */
+ }
+ if (sink->g_poll)
+ return (1);
+ else
+ {
+ bug("hammergui_pollvalidate");
+ return (0);
+ }
+}
+
+static int hammergui_focusvalidate(int dosetup)
+{
+ if (dosetup && !sink->g_focus)
+ {
+ sink->g_focus = gensym("#hammerfocus");
+ sys_gui("event add <<hammerfocusin>> <FocusIn>\n");
+ sys_gui("event add <<hammerfocusout>> <FocusOut>\n");
+ }
+ if (sink->g_focus)
+ return (1);
+ else
+ {
+ bug("hammergui_focusvalidate");
+ return (0);
+ }
+}
+
+static int hammergui_visedvalidate(int dosetup)
+{
+ if (dosetup && !sink->g_vised)
+ {
+ sink->g_vised = gensym("#hammervised");
+ /* subsequent map events have to be filtered out at the caller's side,
+ LATER investigate */
+ sys_gui("event add <<hammervised>> <Map>\n");
+ sys_gui("event add <<hammerunvised>> <Destroy>\n");
+ }
+ if (sink->g_vised)
+ return (1);
+ else
+ {
+ bug("hammergui_visedvalidate");
+ return (0);
+ }
+}
+
+void hammergui_bindmouse(t_pd *master)
+{
+ hammergui_validate(1);
+ hammergui_mousevalidate(1);
+ if (!sink->g_mouse->s_thing)
+ hammergui_dobindmouse(sink);
+ pd_bind(master, sink->g_mouse);
+}
+
+void hammergui_unbindmouse(t_pd *master)
+{
+ if (hammergui_validate(0) && hammergui_mousevalidate(0)
+ && sink->g_mouse->s_thing)
+ {
+ pd_unbind(master, sink->g_mouse);
+ if (!sink->g_mouse->s_thing)
+ sys_gui("hammergui_remouse\n");
+ }
+ else bug("hammergui_unbindmouse");
+}
+
+void hammergui_mousexy(t_symbol *s)
+{
+ if (hammergui_validate(0))
+ sys_vgui("hammergui_mousexy %s\n", s->s_name);
+}
+
+void hammergui_willpoll(void)
+{
+ hammergui_validate(1);
+ hammergui_pollvalidate(1);
+}
+
+void hammergui_startpolling(t_pd *master)
+{
+ if (hammergui_validate(0) && hammergui_pollvalidate(0))
+ {
+ int doinit = (sink->g_poll->s_thing == (t_pd *)sink);
+ pd_bind(master, sink->g_poll);
+ if (doinit)
+ {
+ /* visibility hack for msw, LATER rethink */
+ sys_gui("global hammergui_ispolling\n");
+ sys_gui("set hammergui_ispolling 1\n");
+ sys_gui("hammergui_poll\n");
+ }
+ }
+}
+
+void hammergui_stoppolling(t_pd *master)
+{
+ if (hammergui_validate(0) && hammergui_pollvalidate(0))
+ {
+ pd_unbind(master, sink->g_poll);
+ if (sink->g_poll->s_thing == (t_pd *)sink)
+ {
+ sys_gui("after cancel hammergui_poll\n");
+ /* visibility hack for msw, LATER rethink */
+ sys_gui("global hammergui_ispolling\n");
+ sys_gui("set hammergui_ispolling 0\n");
+ }
+ }
+}
+
+void hammergui_bindfocus(t_pd *master)
+{
+ hammergui_validate(1);
+ hammergui_focusvalidate(1);
+ if (!sink->g_focus->s_thing)
+ hammergui_dobindfocus(sink);
+ pd_bind(master, sink->g_focus);
+}
+
+void hammergui_unbindfocus(t_pd *master)
+{
+ if (hammergui_validate(0) && hammergui_focusvalidate(0)
+ && sink->g_focus->s_thing)
+ {
+ pd_unbind(master, sink->g_focus);
+ if (!sink->g_focus->s_thing)
+ sys_gui("hammergui_refocus\n");
+ }
+ else bug("hammergui_unbindfocus");
+}
+
+void hammergui_bindvised(t_pd *master)
+{
+ hammergui_validate(1);
+ hammergui_visedvalidate(1);
+ if (!sink->g_vised->s_thing)
+ hammergui_dobindvised(sink);
+ pd_bind(master, sink->g_vised);
+}
+
+void hammergui_unbindvised(t_pd *master)
+{
+ if (hammergui_validate(0) && hammergui_visedvalidate(0)
+ && sink->g_vised->s_thing)
+ {
+ pd_unbind(master, sink->g_vised);
+ if (!sink->g_vised->s_thing)
+ sys_gui("hammergui_revised\n");
+ }
+ else bug("hammergui_unbindvised");
+}
diff --git a/shared/hammer/gui.h b/shared/hammer/gui.h
new file mode 100644
index 0000000..13afd0a
--- /dev/null
+++ b/shared/hammer/gui.h
@@ -0,0 +1,30 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#ifndef __HAMMERGUI_H__
+#define __HAMMERGUI_H__
+
+typedef struct _hammergui
+{
+ t_pd g_pd;
+ t_symbol *g_gui;
+ t_symbol *g_mouse;
+ t_symbol *g_poll;
+ t_symbol *g_focus;
+ t_symbol *g_vised;
+ int g_up;
+} t_hammergui;
+
+void hammergui_bindmouse(t_pd *master);
+void hammergui_unbindmouse(t_pd *master);
+void hammergui_mousexy(t_symbol *s);
+void hammergui_willpoll(void);
+void hammergui_startpolling(t_pd *master);
+void hammergui_stoppolling(t_pd *master);
+void hammergui_bindfocus(t_pd *master);
+void hammergui_unbindfocus(t_pd *master);
+void hammergui_bindvised(t_pd *master);
+void hammergui_unbindvised(t_pd *master);
+
+#endif
diff --git a/shared/hammer/tree.c b/shared/hammer/tree.c
new file mode 100644
index 0000000..549dd09
--- /dev/null
+++ b/shared/hammer/tree.c
@@ -0,0 +1,482 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include "hammer/tree.h"
+
+/* Since there is no sentinel node, the deletion routine has to have
+ a few extra checks. LATER rethink. */
+
+/* LATER freelist */
+
+#ifdef HAMMERTREE_DEBUG
+/* returns bh or 0 if failed */
+static int hammernode_verify(t_hammernode *np)
+{
+ if (np)
+ {
+ int bhl, bhr;
+ if (((bhl = hammernode_verify(np->n_left)) == 0) ||
+ ((bhr = hammernode_verify(np->n_right)) == 0))
+ return (0);
+ if (bhl != bhr)
+ {
+ /* failure: two paths rooted in the same node
+ contain different number of black nodes */
+ bug("hammernode_verify: not balanced");
+ return (0);
+ }
+ if (np->n_black)
+ return (bhl + 1);
+ else
+ {
+ if ((np->n_left && !np->n_left->n_black) ||
+ (np->n_right && !np->n_right->n_black))
+ {
+ bug("hammernode_verify: adjacent red nodes");
+ return (0);
+ }
+ return (bhl);
+ }
+ }
+ else return (1);
+}
+
+/* returns bh or 0 if failed */
+static int hammertree_verify(t_hammertree *tree)
+{
+ return (hammernode_verify(tree->t_root));
+}
+
+static void hammernode_post(t_hammernode *np)
+{
+ startpost("%d %g %d (", np->n_index, np->n_value, np->n_black);
+ if (np->n_left)
+ startpost("%d, ", np->n_left->n_index);
+ else
+ startpost("nul, ");
+ if (np->n_right)
+ post("%d)", np->n_right->n_index);
+ else
+ post("nul)");
+}
+
+/* this is a standard stackless traversal, not the best one, obviously...
+ (used only for debugging) */
+static int hammertree_traverse(t_hammertree *tree, int postit)
+{
+ t_hammernode *np = tree->t_root;
+ int count = 0;
+ while (np)
+ {
+ t_hammernode *prev = np->n_left;
+ if (prev)
+ {
+ while (prev->n_right && prev->n_right != np) prev = prev->n_right;
+ if (prev->n_right)
+ {
+ prev->n_right = 0;
+ if (postit) hammernode_post(np);
+ count++;
+ np = np->n_right;
+ }
+ else
+ {
+ prev->n_right = np;
+ np = np->n_left;
+ }
+ }
+ else
+ {
+ if (postit) hammernode_post(np);
+ count++;
+ np = np->n_right;
+ }
+ }
+ return (count);
+}
+
+static int hammernode_height(t_hammernode *np)
+{
+ if (np)
+ {
+ int lh = hammernode_height(np->n_left);
+ int rh = hammernode_height(np->n_right);
+ return (lh > rh ? lh + 1 : rh + 1);
+ }
+ else return (0);
+}
+
+void hammertree_debug(t_hammertree *tree, int level)
+{
+ t_hammernode *np;
+ int count;
+ post("------------------------");
+ count = hammertree_traverse(tree, level);
+ if (level > 1)
+ {
+ post("***");
+ for (np = tree->t_last; np; np = np->n_prev)
+ startpost("%d ", np->n_index);
+ endpost();
+ }
+ post("count %d, height %d, root %d:",
+ count, hammernode_height(tree->t_root),
+ (tree->t_root ? tree->t_root->n_index : 0));
+ post("...verified (black-height is %d)", hammertree_verify(tree));
+ post("------------------------");
+}
+#endif
+
+/* assuming that target node (np->n_right) exists */
+static void hammertree_lrotate(t_hammertree *tree, t_hammernode *np)
+{
+ t_hammernode *target = np->n_right;
+ if (np->n_right = target->n_left)
+ np->n_right->n_parent = np;
+ if (!(target->n_parent = np->n_parent))
+ tree->t_root = target;
+ else if (np == np->n_parent->n_left)
+ np->n_parent->n_left = target;
+ else
+ np->n_parent->n_right = target;
+ target->n_left = np;
+ np->n_parent = target;
+}
+
+/* assuming that target node (np->n_left) exists */
+static void hammertree_rrotate(t_hammertree *tree, t_hammernode *np)
+{
+ t_hammernode *target = np->n_left;
+ if (np->n_left = target->n_right)
+ np->n_left->n_parent = np;
+ if (!(target->n_parent = np->n_parent))
+ tree->t_root = target;
+ else if (np == np->n_parent->n_left)
+ np->n_parent->n_left = target;
+ else
+ np->n_parent->n_right = target;
+ target->n_right = np;
+ np->n_parent = target;
+}
+
+/* returns a newly inserted or already existing node
+ (or 0 if allocation failed) */
+t_hammernode *hammertree_insert(t_hammertree *tree, int ndx)
+{
+ t_hammernode *np, *parent, *result;
+ if (!(np = tree->t_root))
+ {
+ if (!(np = getbytes(sizeof(*np))))
+ return (0);
+ np->n_index = ndx;
+ np->n_black = 1;
+ tree->t_root = tree->t_first = tree->t_last = np;
+ return (np);
+ }
+
+ do
+ if (np->n_index == ndx)
+ return (np);
+ else
+ parent = np;
+ while (np = (ndx < np->n_index ? np->n_left : np->n_right));
+
+ if (!(np = getbytes(sizeof(*np))))
+ return (0);
+ np->n_index = ndx;
+ np->n_parent = parent;
+ if (ndx < parent->n_index)
+ {
+ parent->n_left = np;
+ /* update the auxiliary linked list structure */
+ np->n_next = parent;
+ if (np->n_prev = parent->n_prev)
+ np->n_prev->n_next = np;
+ else
+ tree->t_first = np;
+ parent->n_prev = np;
+ }
+ else
+ {
+ parent->n_right = np;
+ /* update the auxiliary linked list structure */
+ np->n_prev = parent;
+ if (np->n_next = parent->n_next)
+ np->n_next->n_prev = np;
+ else
+ tree->t_last = np;
+ parent->n_next = np;
+ }
+ result = np;
+
+ /* balance the tree -- LATER clean this if possible... */
+ np->n_black = 0;
+ while (np != tree->t_root && !np->n_parent->n_black)
+ {
+ t_hammernode *uncle;
+ /* np->n_parent->n_parent exists (we always paint root node in black) */
+ if (np->n_parent == np->n_parent->n_parent->n_left)
+ {
+ uncle = np->n_parent->n_parent->n_right;
+ if (!uncle /* (sentinel not used) */
+ || uncle->n_black)
+ {
+ if (np == np->n_parent->n_right)
+ {
+ np = np->n_parent;
+ hammertree_lrotate(tree, np);
+ }
+ np->n_parent->n_black = 1;
+ np->n_parent->n_parent->n_black = 0;
+ hammertree_rrotate(tree, np->n_parent->n_parent);
+ }
+ else
+ {
+ np->n_parent->n_black = 1;
+ uncle->n_black = 1;
+ np = np->n_parent->n_parent;
+ np->n_black = 0;
+ }
+ }
+ else
+ {
+ uncle = np->n_parent->n_parent->n_left;
+ if (!uncle /* (sentinel not used) */
+ || uncle->n_black)
+ {
+ if (np == np->n_parent->n_left)
+ {
+ np = np->n_parent;
+ hammertree_rrotate(tree, np);
+ }
+ np->n_parent->n_black = 1;
+ np->n_parent->n_parent->n_black = 0;
+ hammertree_lrotate(tree, np->n_parent->n_parent);
+ }
+ else
+ {
+ np->n_parent->n_black = 1;
+ uncle->n_black = 1;
+ np = np->n_parent->n_parent;
+ np->n_black = 0;
+ }
+ }
+ }
+ tree->t_root->n_black = 1;
+ return (result);
+}
+
+/* assuming that requested node exists */
+void hammertree_delete(t_hammertree *tree, t_hammernode *np)
+{
+ t_hammernode *gone, *parent, *child;
+ /* gone is the actual node to be deleted
+ -- it has to be the parent of no more than one child: */
+ if (np->n_left && np->n_right)
+ {
+ gone = np->n_next; /* gone always exists */
+ child = gone->n_right; /* there is no left child of gone */
+ /* gone is not a requested node, so we replace fields to be
+ deleted with gone's fields: */
+ np->n_index = gone->n_index;
+ np->n_value = gone->n_value;
+ /* update the auxiliary linked list structure */
+ /* np->n_prev is up-to-date */
+ if (np->n_prev)
+ np->n_prev->n_next = np;
+ else tree->t_first = np;
+ if (np->n_next = gone->n_next)
+ np->n_next->n_prev = np;
+ else tree->t_last = np;
+ }
+ else
+ {
+ gone = np;
+ if (gone->n_left)
+ child = gone->n_left;
+ else
+ child = gone->n_right;
+ /* update the auxiliary linked list structure */
+ if (gone->n_prev)
+ gone->n_prev->n_next = gone->n_next;
+ else
+ tree->t_first = gone->n_next;
+ if (gone->n_next)
+ gone->n_next->n_prev = gone->n_prev;
+ else
+ tree->t_last = gone->n_prev;
+ }
+ /* connect gone's child with gone's parent */
+ if (!(parent = gone->n_parent))
+ {
+ if (tree->t_root = child)
+ {
+ child->n_parent = 0;
+ child->n_black = 1; /* LATER rethink */
+ }
+ goto done;
+ }
+ else
+ {
+ if (child) /* (sentinel not used) */
+ child->n_parent = parent;
+ if (gone == parent->n_left)
+ parent->n_left = child;
+ else
+ parent->n_right = child;
+ }
+
+ if (gone->n_black)
+ {
+ /* balance the tree -- LATER clean this if possible... */
+ /* on entry: tree is not empty, parent always exists, child
+ not necessarily... */
+ while (child != tree->t_root &&
+ (!child || /* (sentinel not used) */
+ child->n_black))
+ {
+ t_hammernode *other; /* another child of the same parent */
+ if (child == parent->n_left)
+ {
+ other = parent->n_right;
+ if (other && /* (sentinel not used) */
+ !other->n_black)
+ {
+ other->n_black = 1;
+ parent->n_black = 0;
+ hammertree_lrotate(tree, parent);
+ other = parent->n_right;
+ }
+ if (!other || /* (sentinel not used) */
+ (!other->n_left || other->n_left->n_black) &&
+ (!other->n_right || other->n_right->n_black))
+ {
+ if (other) /* (sentinel not used) */
+ other->n_black = 0;
+ child = parent;
+ parent = parent->n_parent;
+ }
+ else
+ {
+ if (!other || /* (sentinel not used) */
+ !other->n_right || other->n_right->n_black)
+ {
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_left) other->n_left->n_black = 1;
+ other->n_black = 0;
+ hammertree_rrotate(tree, other);
+ other = parent->n_right;
+ }
+ }
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_right) other->n_right->n_black = 1;
+ other->n_black = parent->n_black;
+ }
+ parent->n_black = 1;
+ hammertree_lrotate(tree, parent);
+ tree->t_root->n_black = 1; /* LATER rethink */
+ goto done;
+ }
+ }
+ else /* right child */
+ {
+ other = parent->n_left;
+ if (other && /* (sentinel not used) */
+ !other->n_black)
+ {
+ other->n_black = 1;
+ parent->n_black = 0;
+ hammertree_rrotate(tree, parent);
+ other = parent->n_left;
+ }
+ if (!other || /* (sentinel not used) */
+ (!other->n_left || other->n_left->n_black) &&
+ (!other->n_right || other->n_right->n_black))
+ {
+ if (other) /* (sentinel not used) */
+ other->n_black = 0;
+ child = parent;
+ parent = parent->n_parent;
+ }
+ else
+ {
+ if (!other || /* (sentinel not used) */
+ !other->n_left || other->n_left->n_black)
+ {
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_right) other->n_right->n_black = 1;
+ other->n_black = 0;
+ hammertree_lrotate(tree, other);
+ other = parent->n_left;
+ }
+ }
+ if (other) /* (sentinel not used) */
+ {
+ if (other->n_left) other->n_left->n_black = 1;
+ other->n_black = parent->n_black;
+ }
+ parent->n_black = 1;
+ hammertree_rrotate(tree, parent);
+ tree->t_root->n_black = 1; /* LATER rethink */
+ goto done;
+ }
+ }
+ }
+ if (child) /* (sentinel not used) */
+ child->n_black = 1;
+ }
+done:
+ freebytes(gone, sizeof(*gone));
+#ifdef HAMMERTREE_DEBUG
+ hammertree_verify(tree);
+#endif
+}
+
+t_hammernode *hammertree_search(t_hammertree *tree, int ndx)
+{
+ t_hammernode *np = tree->t_root;
+ while (np && np->n_index != ndx)
+ np = (ndx < np->n_index ? np->n_left : np->n_right);
+ return (np);
+}
+
+t_hammernode *hammertree_closest(t_hammertree *tree, int ndx, int geqflag)
+{
+ t_hammernode *np, *parent;
+ if (!(np = tree->t_root))
+ return (0);
+ do
+ if (np->n_index == ndx)
+ return (np);
+ else
+ parent = np;
+ while (np = (ndx < np->n_index ? np->n_left : np->n_right));
+ if (geqflag)
+ return (ndx > parent->n_index ? parent->n_next : parent);
+ else
+ return (ndx < parent->n_index ? parent->n_prev : parent);
+}
+
+/* LATER preallocate 'freecount' nodes */
+void hammertree_init(t_hammertree *tree, int freecount)
+{
+ tree->t_root = tree->t_first = tree->t_last = 0;
+}
+
+/* LATER keep and/or preallocate 'freecount' nodes (if negative, keep all) */
+void hammertree_clear(t_hammertree *tree, int freecount)
+{
+ t_hammernode *np, *next = tree->t_first;
+ while (next)
+ {
+ np = next;
+ next = next->n_next;
+ freebytes(np, sizeof(*np));
+ }
+ hammertree_init(tree, 0);
+}
diff --git a/shared/hammer/tree.h b/shared/hammer/tree.h
new file mode 100644
index 0000000..fcbc036
--- /dev/null
+++ b/shared/hammer/tree.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2003 krzYszcz and others.
+ * For information on usage and redistribution, and for a DISCLAIMER OF ALL
+ * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#ifndef __HAMMERTREE_H__
+#define __HAMMERTREE_H__
+
+#define HAMMERTREE_DEBUG
+
+typedef struct _hammernode
+{
+ int n_index;
+ float n_value;
+ int n_black;
+ struct _hammernode *n_left;
+ struct _hammernode *n_right;
+ struct _hammernode *n_parent;
+ struct _hammernode *n_prev;
+ struct _hammernode *n_next;
+} t_hammernode;
+
+typedef struct _hammertree
+{
+ t_hammernode *t_root;
+ t_hammernode *t_first;
+ t_hammernode *t_last;
+} t_hammertree;
+
+t_hammernode *hammertree_insert(t_hammertree *tree, int ndx);
+void hammertree_delete(t_hammertree *tree, t_hammernode *np);
+t_hammernode *hammertree_search(t_hammertree *tree, int ndx);
+t_hammernode *hammertree_closest(t_hammertree *tree, int ndx, int geqflag);
+void hammertree_init(t_hammertree *tree, int freecount);
+void hammertree_clear(t_hammertree *tree, int freecount);
+void hammertree_debug(t_hammertree *tree, int level);
+
+#endif