diff options
Diffstat (limited to 'shared/hammer')
-rw-r--r-- | shared/hammer/Makefile | 4 | ||||
-rw-r--r-- | shared/hammer/Makefile.objects | 0 | ||||
-rw-r--r-- | shared/hammer/Makefile.sources | 4 | ||||
-rw-r--r-- | shared/hammer/file.c | 402 | ||||
-rw-r--r-- | shared/hammer/file.h | 43 | ||||
-rw-r--r-- | shared/hammer/gui.c | 438 | ||||
-rw-r--r-- | shared/hammer/gui.h | 30 | ||||
-rw-r--r-- | shared/hammer/tree.c | 482 | ||||
-rw-r--r-- | shared/hammer/tree.h | 37 |
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 |