diff --git a/src/x_preset.c b/src/x_preset.c
new file mode 100644
index 0000000000000000000000000000000000000000..2054d7d5d1179a2d702c326918d3193308db9a8d
--- /dev/null
+++ b/src/x_preset.c
@@ -0,0 +1,1044 @@
+/*	preset_node and preset_hub implementation by Ivica Ico Bukvic <ico@vt.edu> (c) 2012
+	distributed under GPL v3 license or newer
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "m_pd.h"
+#include "g_canvas.h"
+#include "x_preset.h"
+#include "s_stuff.h"
+
+#define DEBUG(x)
+
+//TODO: changes in order happen when cutting, undo cut, undo paste,
+//		delete, undo delete, to front, and to back
+//		this should trigger cascading check for all preset_nodes 
+//		is there a way to limit it to ones that are affected?
+//		potentially... by taking all the ordinal values above the one moved
+//		e.g. if object 1,5 was moved, everything above it
+//			 (1,5,1 or 1,6 etc.) needs to be reevaluated BEFORE
+//			 reevaluating moved object (if applicable)
+//			 this does not affect objects like 2 or 2,7
+//			 since it is a different branch altogether
+
+/*	gl_loc int array explanation:
+
+	Each number corresponds to object or parent's location starting with root window.
+	This ensures that each object has a unique ID within the preset even when using
+	multiple instances of the same abstraction within the same patch.
+	This also means that if the object has changed its location within the gl_list
+	(e.g. via to_front/to_back), all corresponding IDs have to be updated in the parent.
+
+	EXAMPLE:
+	1,4,7 means the preset_node is 8th object in the subpatch or abstraction that is a
+	child of 5th object in a parent patch, that is a child of 2nd object of the root
+	patch.
+*/
+
+//==================== forward declarations ========================//
+
+void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x);
+void preset_hub_recall(t_preset_hub *h, t_float f);
+void preset_hub_store(t_preset_hub *h, t_float f);
+void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x);
+void preset_hub_delete_a_node(t_preset_hub *h, t_preset_node *x);
+void preset_node_seek_hub(t_preset_node *x);
+
+//======================== global vars =============================//
+
+/*	following contains a linear list of all hubs and nodes which ensures easy access,
+	pairing, deletion, etc. of all nodes and hubs regardless whether they are a part of
+	subpatches, root canvases, or abstractions.
+*/
+
+t_glob_preset_node_list *gpnl;
+t_glob_preset_hub_list *gphl;
+
+int glob_preset_node_list_add(t_preset_node *x)
+{
+	fprintf(stderr,"glob_preset_node_list_add\n");
+	t_glob_preset_node_list *n1, *n2;
+
+	if (!gpnl) {
+		gpnl = (t_glob_preset_node_list *)t_getbytes(sizeof(*gpnl));
+		n2 = gpnl;
+	} else {
+		n1 = gpnl;
+		while(n1->gpnl_next)
+			n1 = n1->gpnl_next;
+		n2 = (t_glob_preset_node_list *)t_getbytes(sizeof(*n2));
+		n1->gpnl_next = n2;
+	}
+	n2->gpnl_paired = 0;
+	n2->gpnl_node = x;
+	return(0);
+}
+
+int glob_preset_node_list_delete(t_preset_node *x)
+{
+	fprintf(stderr,"glob_preset_node_list_delete\n");
+	t_glob_preset_node_list *n1, *n2;
+	int found;
+
+	if (!gpnl)
+		return(1);
+
+	n1 = gpnl;
+	n2 = gpnl;
+
+	// first check if we are the first gpnl
+	if (n2->gpnl_node == x) {
+		gpnl = n2->gpnl_next;
+		freebytes(n2, sizeof(*n2));
+	} 
+	else {
+		while (n2->gpnl_node != x && n2->gpnl_next) {
+			n1 = n2;
+			n2 = n2->gpnl_next;
+		}
+		if (n2->gpnl_node == x) {
+			n1->gpnl_next = n2->gpnl_next;
+			freebytes(n2, sizeof(*n2));
+		} else {
+			// we should never get here
+			fprintf(stderr,"error, could not find appropriate gpnl to delete\n");
+			return(1);
+		}
+	}
+	
+	return(0);
+}
+
+int glob_preset_node_list_update_paired(t_preset_node *x, int paired)
+{
+	fprintf(stderr,"glob_preset_node_list_update_paired %d\n", paired);
+	t_glob_preset_node_list *n;
+
+	if (!gpnl)
+		return(1);
+
+	n = gpnl;
+
+	while (n->gpnl_node != x && n->gpnl_next) {
+		n = n->gpnl_next;
+	}
+	if (n->gpnl_node == x) {
+		n->gpnl_paired = 1;
+	}
+	else {
+		// we should never get here
+		fprintf(stderr,"error, could not find appropriate gpnl to pair\n");
+		return(1);
+	}
+	
+	return(0);
+}
+
+void glob_preset_node_list_seek_hub(void)
+{
+	fprintf(stderr,"glob_preset_node_list_seek_hub\n");
+	t_glob_preset_node_list *nl;
+
+	if (gpnl) {
+		nl = gpnl;
+		while(nl) {
+			fprintf(stderr,"	seeking\n");
+			if (!nl->gpnl_paired) {
+				preset_node_seek_hub(nl->gpnl_node);
+			}
+			nl = nl->gpnl_next;
+		}
+	}
+}
+
+int glob_preset_hub_list_add(t_preset_hub *x)
+{
+	fprintf(stderr,"glob_preset_hub_list_add\n");
+	t_glob_preset_hub_list *h1, *h2;
+
+	if (!gphl) {
+		gphl = (t_glob_preset_hub_list *)t_getbytes(sizeof(*gphl));
+		h2 = gphl;
+	} else {
+		h1 = gphl;
+		while(h1->gphl_next)
+			h1 = h1->gphl_next;
+		h2 = (t_glob_preset_hub_list *)t_getbytes(sizeof(*h2));
+		h1->gphl_next = h2;
+	}
+	h2->gphl_hub = x;
+	return(0);
+}
+
+int glob_preset_hub_list_delete(t_preset_hub *x)
+{
+	fprintf(stderr,"glob_preset_hub_list_delete\n");
+	t_glob_preset_hub_list *h1, *h2;
+	int found;
+
+	if (!gphl)
+		return(1);
+
+	h1 = gphl;
+	h2 = gphl;
+
+	// first check if we are the first gpnl
+	if (h2->gphl_hub == x) {
+		gphl = h2->gphl_next;
+		freebytes(h2, sizeof(*h2));
+	} 
+	else {
+		while (h2->gphl_hub != x && h2->gphl_next) {
+			h1 = h2;
+			h2 = h2->gphl_next;
+		}
+		if (h2->gphl_hub == x) {
+			h1->gphl_next = h2->gphl_next;
+			freebytes(h2, sizeof(*h2));
+		}
+		else {
+			// we should never get here
+			fprintf(stderr,"error, could not find appropriate gphl to delete\n");
+			return(1);
+		}
+	}	
+	
+	return(0);
+}
+
+//====================== end global vars ===========================//
+
+//======================== preset_node =============================//
+
+// we declare this class as part of the g_canvas.h (to expose it to the rest of the code)
+
+static void preset_node_update_my_glist_location(t_preset_node *x)
+{
+	fprintf(stderr,"node_update_glist_location\n");
+	// location is calculated in respect to the hub (if any), otherwise it is 0
+	t_preset_hub *h;
+	t_canvas *c = x->pn_canvas;
+	int found = 0;
+
+	// do all this only if we are already paired with a hub
+	if (x->pn_hub) {
+
+		if (x->pn_old_gl_loc) free(x->pn_old_gl_loc);
+		x->pn_old_gl_loc = x->pn_gl_loc;
+		x->pn_old_gl_loc_length = x->pn_gl_loc_length;
+	
+		x->pn_gl_loc = NULL;
+		x->pn_gl_loc_length = 0;
+
+		// let's try to find our hub (if any)
+		int depth = 0;
+		while (!found && c) {
+			if (c->gl_phub) {
+				h = c->gl_phub;
+				while (h) {
+					if (!strcmp(c->gl_phub->ph_name->s_name, x->pn_hub_name->s_name)) {
+						found = 1;
+						break;
+					}
+					h = h->ph_next;
+				}
+			}
+			c = c->gl_owner;
+			if (c)
+				depth++;
+		}
+
+		if (found) {
+
+			fprintf(stderr,"	depth = %d\n", depth);
+	
+			// allocate depth array
+			x->pn_gl_loc = (int*)calloc(depth+1, sizeof(int));
+
+			// now let's figure out where each of the child gobj's location is
+			// starting from the back
+			int i = 0; 					// iteration within each gl_list
+			int j = 0; 					// number of gl_list to traverse
+			c = x->pn_canvas;
+			t_gobj *g = c->gl_list;
+			t_gobj *target = (t_gobj *)x;
+
+			for (j = depth; j >= 0; j--) {
+
+				while (g && g != target) // have to do sanity check for g in the event this is run at patch creation time
+				{
+					g = g->g_next;
+					i++;
+					fprintf(stderr,"	searching... %d\n", i);
+				}
+				// even if the g fails sanity check due to creation time, it will still land on the last created element whose
+				// pointer at this point is still null since this means this is being called at the end of the preset_node_new call
+				fprintf(stderr,"	location = %d %lx %lx\n", i, (t_int)g, (t_int)target);
+				x->pn_gl_loc[j] = i;
+
+				// now readjust the target, c, g, and i variables for the next level up
+				if (c->gl_owner) {
+					target = (t_gobj *)c;
+					c = c->gl_owner;
+					g = c->gl_list;
+					i = 0;
+				}
+			}
+			x->pn_gl_loc_length = depth+1;
+
+			fprintf(stderr,"	final structure:\n");
+			for (j = 0; j < x->pn_gl_loc_length; j++) {
+				fprintf(stderr,"	%d: %d\n", j, x->pn_gl_loc[j]);
+			}
+		} else {
+			fprintf(stderr,"	preset_node: no matching hub %s found\n", x->pn_hub_name->s_name);
+		}
+
+		// finally if this is the first time we are creating the object, old_location should be the same as the current location
+		if (x->pn_old_gl_loc_length == 0) {
+			x->pn_old_gl_loc = x->pn_gl_loc;
+			x->pn_old_gl_loc_length = x->pn_gl_loc_length;
+		}
+	}
+}
+
+// this is called during the canvas creation (e.g. at load time) as part of pre-loadbang event,
+// during object creation (in case it is manually created), as well as when hubs try to query
+// nodes that have not been paired yet. 
+void preset_node_seek_hub(t_preset_node *x)
+{
+	fprintf(stderr,"preset_node_seek_hub\n");
+	t_canvas *y = x->pn_canvas;
+	t_preset_hub *h;
+
+	if (!x->pn_hub) {
+		fprintf(stderr,"	have to seek\n");
+		while (!x->pn_hub && y) {
+			h = y->gl_phub;
+			while (h) {
+				if (h->ph_name->s_name == x->pn_hub_name->s_name) {
+					x->pn_hub = h;
+					fprintf(stderr,"	node found hub\n");
+					// update our location in respect to the newfound hub
+					preset_node_update_my_glist_location(x);
+					// add a node on the hub's list of nodes and copy location to its struct
+					preset_hub_add_a_node(x->pn_hub, x);
+					// reflect that this node is paired in the global list of all existing nodes
+					glob_preset_node_list_update_paired(x, 1);		
+					break;
+				}
+				h = h->ph_next;			
+			}
+			y = y->gl_owner;
+		}
+	}
+}
+
+static int preset_node_location_changed(t_preset_node *x)
+{
+	int i;
+	if (x->pn_old_gl_loc_length != x->pn_gl_loc_length)
+		return(1);
+	for (i = 0; i < x->pn_gl_loc_length; i++) {
+		if (x->pn_gl_loc[i] != x->pn_old_gl_loc[i])
+			return(1);
+	}
+	return(0);
+}
+
+static void preset_node_bang(t_preset_node *x)
+{
+	SETSYMBOL(&x->pn_val, gensym("bang"));
+}
+
+static void preset_node_float(t_preset_node *x, t_float f)
+{
+	SETFLOAT(&x->pn_val, f);
+}
+
+static void preset_node_symbol(t_preset_node *x, t_symbol *s)
+{
+	SETSYMBOL(&x->pn_val, s);
+}
+
+	//=============== following functions are for interaction with the hub/pd =================//
+
+int preset_node_check_location(t_preset_node *x)
+{
+	int result;
+	if (x->pn_hub) {
+		preset_node_update_my_glist_location(x);
+		return(preset_node_location_changed(x));
+	}
+	return(0);
+}
+
+void preset_node_request_hub_recall(t_preset_node *x, t_float f)
+{
+	if (x->pn_hub)
+		preset_hub_recall(x->pn_hub, f);
+}
+
+void preset_node_request_hub_store(t_preset_node *x, t_float f)
+{
+	if (x->pn_hub)
+		preset_hub_store(x->pn_hub, f);
+}
+
+t_atom preset_node_request_value(t_preset_node *x)
+{
+    return(x->pn_val);
+}
+
+void preset_node_set_and_output_value(t_preset_node *x, t_atom *val)
+{
+	if (val->a_type == A_FLOAT) {
+		preset_node_float(x, val->a_w.w_float);
+		outlet_float(x->pn_outlet, x->pn_val.a_w.w_float);
+	} else if (val->a_type == A_SYMBOL) {
+		preset_node_symbol(x, val->a_w.w_symbol);
+		outlet_symbol(x->pn_outlet, x->pn_val.a_w.w_symbol);
+	} else
+		pd_error(x, "error: preset_node currently supports only floats and symbols");
+}
+
+	//==================== end functions are for interaction with the hub =====================//
+
+static void *preset_node_new(t_symbol *s, int argc, t_atom *argv)
+{
+	fprintf(stderr,"===preset_node_new===\n");
+	t_glist *glist=(t_glist *)canvas_getcurrent();
+	t_canvas *canvas = (t_canvas *)glist_getcanvas(glist);
+
+	// first check if the mandatory argument is sane, otherwise bail out
+    if (!(argc > 0 && argv[0].a_type == A_SYMBOL)) {
+		pd_error(canvas, "preset_node requires alphanumeric name as its argument");
+		return(NULL);
+	}
+
+    t_preset_node *x = (t_preset_node *)pd_new(preset_node_class);
+
+	// read basic creation arguments
+	x->pn_hub_name = (t_symbol *)atom_getsymbol(&argv[0]);
+	//fprintf(stderr,"hub name %s\n", x->pn_hub_name->s_name);
+
+	x->pn_canvas = canvas;
+	t_canvas *y = x->pn_canvas;
+	t_preset_hub *h;
+
+	x->pn_hub = NULL;
+	x->pn_gl_loc_length = 0;
+	x->pn_old_gl_loc_length = 0;  
+ 	x->pn_outlet = outlet_new(&x->pn_obj, 0);
+
+	glob_preset_node_list_add(x);
+
+	// now we seek our potential hub (if it exists), this is also called as part of 
+	// pre-loadbang if the patch is being loaded (this one is for manually created objects)
+	// do this only if we are not undoing, otherwise, we'll have the undo do it for us
+	// once it has repositioned objects to their original locations
+	if (!we_are_undoing)
+		preset_node_seek_hub(x);
+
+    return(x);
+}
+
+static void preset_node_free(t_preset_node* x)
+{
+	// deactivate a node on the hub's list of nodes
+	if (x->pn_hub)
+		preset_hub_delete_a_node(x->pn_hub, x);
+	glob_preset_node_list_delete(x);
+
+	// the two arrays can point to same locations so here we prevent double free
+	// (this is only possible initially when the object is first instantiated)
+	if (x->pn_gl_loc != x->pn_old_gl_loc) {
+		free(x->pn_gl_loc);
+		free(x->pn_old_gl_loc);
+	} else {
+		free(x->pn_gl_loc);
+	}	
+}
+
+void preset_node_setup(void)
+{
+	preset_node_class = class_new(gensym("preset_node"), 
+	    (t_newmethod)preset_node_new, (t_method)preset_node_free, 
+	    sizeof(t_preset_node), 0, A_GIMME, 0);
+
+	// have to call this after everything has loaded, otherwise, the hub may not yet exist
+	// we do this using pre-loadbang call that is issued before other loadbangs, otherwise
+	// out-of-order loadbangs can issue preset recall before the preset has been properly
+	// configured
+	class_addmethod(preset_node_class, (t_method)preset_node_seek_hub,
+        gensym("pre-loadbang"), 0);
+
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_recall,
+        gensym("recall"), A_DEFFLOAT, 0);
+    class_addmethod(preset_node_class, (t_method)preset_node_request_hub_store,
+        gensym("store"), A_DEFFLOAT, 0);
+
+    class_addbang(preset_node_class, preset_node_bang);
+    class_addfloat(preset_node_class, preset_node_float);
+    class_addsymbol(preset_node_class, preset_node_symbol);
+
+	// TODO: we may add this later if necessary
+    //class_addlist(preset_node_class, preset_node_list);
+}
+
+//====================== end preset_node ===========================//
+
+//======================== preset_hub ==============================//
+
+// we declare this class as part of the g_canvas.h (to expose it to the rest of the code)
+
+typedef enum
+{
+	H_NONE,
+    H_NODE,
+    H_PRESET,
+    H_PRESET_DATA
+}  t_hub_parser;
+
+/*	syntax for saving a preset hub (all in a single line, here it is
+	separated for legibility sakes):
+	#X obj X Y preset_hub NAME %hidden%
+	%node% LOCATION_ARRAY_(INT) 1 2 3 etc.
+	%preset% 1 %float%/%symbol% (e.g. number or symbol, later also possibly a list)
+	%preset% 2 4
+	etc
+	%node% 2 0
+	%preset% 1 5.561
+	%preset% 3 7.00001
+	%preset% 5 some_text
+	;
+
+	NB: %hidden% is used to hide optional arguments following that argument
+		it can be used by any other object as well
+*/
+
+void preset_hub_save(t_gobj *z, t_binbuf *b)
+{
+	fprintf(stderr,"preset_hub_save\n");
+	int i;
+	t_preset_hub_data *phd;
+	t_node_preset *np;
+
+	t_preset_hub *x = (t_preset_hub *)z;
+
+	binbuf_addv(b, "ssiiss", gensym("#X"), gensym("obj"), (int)x->ph_obj.te_xpix,
+				(int)x->ph_obj.te_ypix, gensym("preset_hub"), x->ph_name);
+
+	if (x->ph_invis)
+		binbuf_addv(b, "i", (int)x->ph_invis);
+
+	binbuf_addv(b, "s", gensym("%hidden%"));
+
+	phd = x->ph_data;
+	while (phd) {
+		fprintf(stderr,"	saving phd\n");
+		// designate a node and state whether it is active or disabled
+		// (disabled nodes are ones that have presets saved but have been deleted since--
+		// we keep these in the case of undo actions during the session that may go beyond
+		// saving something into a file)
+		binbuf_addv(b, "s", gensym("%node%"));
+
+		// gather info about the length of the node's location and store it
+		for (i = 0; i < phd->phd_pn_gl_loc_length; i++) {
+			binbuf_addv(b,"i", (int)phd->phd_pn_gl_loc[i]);
+		}
+
+		// save preset data
+		np = phd->phd_npreset;
+		while (np) {
+			binbuf_addv(b, "si", gensym("%preset%"), (int)np->np_preset);
+			if (np->np_val.a_type == A_FLOAT)
+				binbuf_addv(b, "f", np->np_val.a_w.w_float);
+			else if (np->np_val.a_type == A_SYMBOL)
+				binbuf_addv(b, "s", np->np_val.a_w.w_symbol);
+			np = np->np_next;
+		}
+
+		phd = phd->phd_next;
+	}
+	fprintf(stderr,"	done\n");
+	binbuf_addv(b, ";");
+}
+
+void preset_hub_bang(t_preset_hub *x)
+{
+	t_atom ap[1];
+	SETFLOAT(ap+0, (t_float)x->ph_preset);
+    outlet_anything(x->ph_outlet, gensym("current"), 1, ap);
+}
+
+void preset_hub_recall(t_preset_hub *x, t_float f)
+{
+	fprintf(stderr,"hub_recall\n");
+	t_atom ap[2];
+	t_preset_hub_data *hd;
+	t_node_preset *np;
+	t_float valid = 0;
+
+	if (f>=0) {
+		// check if preset exists and apply it
+		if (x->ph_data) {
+			hd = x->ph_data;
+			while (hd) {
+				fprintf(stderr,"   searching\n");
+				// now check if the object is active (node pointer is not NULL)
+				if (hd->phd_node) {
+					fprintf(stderr,"	object active\n");
+					if (hd->phd_npreset) {
+						fprintf(stderr,"	object has presets\n");
+						np = hd->phd_npreset;
+						while (np) {
+							fprintf(stderr,"	searching presets\n");
+							if (np->np_preset == (int)f) {
+								valid = 1;
+								fprintf(stderr,"	valid %d %g\n", (hd->phd_node ? 1:0), np->np_val.a_w.w_float);
+								preset_node_set_and_output_value(hd->phd_node, &np->np_val);
+								break;
+							}
+							np = np->np_next;
+						}
+					}
+				}
+				hd = hd->phd_next;
+			}
+		}
+		if (valid)
+			x->ph_preset = f;
+		SETFLOAT(ap+0, f);
+		SETFLOAT(ap+1, (t_float)valid);
+		outlet_anything(x->ph_outlet, gensym("recall"), 2, ap);
+	}
+}
+
+void preset_hub_store(t_preset_hub *x, t_float f)
+{
+	fprintf(stderr,"preset_hub_store\n");
+	t_atom ap[1];
+	t_preset_hub_data *hd1;
+	t_node_preset *np1, *np2;
+	int overwrite;
+	t_atom val;
+
+	np1 = NULL;
+	np2 = NULL;
+
+	if (f>=0) {
+		//check if there are any existing nodes
+		if (x->ph_data) {
+			hd1 = x->ph_data;
+			while (hd1) {
+				fprintf(stderr,"	analyzing phd\n");
+				if (hd1->phd_node) {
+					fprintf(stderr,"	node is active\n");
+					// only if the node is active (not NULL/disabled)
+					overwrite = 0;
+					if (hd1->phd_npreset) {
+						// if this node has already pre-existing presets
+						// first check if there is already one we need to overwrite
+						np2 = hd1->phd_npreset;
+						while (np2) {
+							if (np2->np_preset == (int)f) {
+								overwrite = 1;
+								break;
+							}
+							np1 = np2;
+							np2 = np2->np_next;
+						}
+					}
+
+					if (!overwrite) {
+						// we need to create a new preset (this is also true if hd1->phd_npreset is NULL)
+						np2 = (t_node_preset *)t_getbytes(sizeof(*np2));
+						if (np1)
+							np1->np_next = np2;
+						else hd1->phd_npreset = np2;
+						np2->np_preset = (int)f;
+					}
+
+					val = preset_node_request_value(hd1->phd_node);
+					if (val.a_type == A_FLOAT)
+						SETFLOAT(&np2->np_val, atom_getfloat(&val));
+					else if (val.a_type == A_SYMBOL)
+						SETSYMBOL(&np2->np_val, atom_getsymbol(&val));
+
+					// finally if this is the first preset, hd1->phd_npreset will be NULL so,
+					// let's have it point to the newly created n2
+					if (!hd1->phd_npreset)
+						hd1->phd_npreset = np2;
+				}
+				hd1 = hd1->phd_next;
+			}
+		}
+		SETFLOAT(ap+0, f);
+		outlet_anything(x->ph_outlet, gensym("store"), 1, ap);
+	}
+}
+
+int preset_hub_compare_loc(int *h_loc, int h_loc_length, int *n_loc, int n_loc_length)
+{
+	int i;
+	if (h_loc_length != n_loc_length)
+		return(1);
+	for (i = 0; i < h_loc_length; i++) {
+		if (h_loc[i] != n_loc[i])
+			return(1);
+	}
+	return(0);
+}
+
+void preset_hub_add_a_node(t_preset_hub *h, t_preset_node *x)
+{
+	t_preset_hub_data *hd1, *hd2;
+	int found = 0;
+	int i;
+
+	hd1 = NULL;
+	hd2 = NULL;
+
+	fprintf(stderr,"preset_hub_add_a_node\n");
+	if (h->ph_data) {
+		// first check for disabled nodes and reenable them if they match location
+		hd1 = h->ph_data;
+		while (hd1) {
+			if (!hd1->phd_node) {
+				// only if the node is disabled (NULL)
+				if (!preset_hub_compare_loc(hd1->phd_pn_gl_loc, hd1->phd_pn_gl_loc_length, x->pn_gl_loc, x->pn_gl_loc_length))
+				{
+					// if this hub node data's location matches that of the node
+					fprintf(stderr,"	found disabled -> enabling\n");
+					found = 1;
+					hd1->phd_node = x;
+					break;
+				}
+			}
+			hd1 = hd1->phd_next;
+		}
+	}
+
+	if (!found) {
+		// we have no stored node data (or none that match node's location) so let's create a new one
+		fprintf(stderr,"	creating a new\n");
+		// create a new data struct
+		hd2 = (t_preset_hub_data *)t_getbytes(sizeof(*hd2));
+		// reconstruct the dynamic location array
+		hd2->phd_pn_gl_loc_length = x->pn_gl_loc_length;
+		hd2->phd_pn_gl_loc = (int*)calloc(hd2->phd_pn_gl_loc_length, sizeof(int));
+		for (i=0; i < hd2->phd_pn_gl_loc_length; i++) {
+			fprintf(stderr,"	loc %d\n", x->pn_gl_loc[i]);
+			hd2->phd_pn_gl_loc[i] = x->pn_gl_loc[i];
+		}
+		// assign node value
+		hd2->phd_node = x;
+
+		// adjust pointers
+		if (!h->ph_data) {
+			h->ph_data = hd2;
+		} else {
+			hd1 = h->ph_data;
+			while (hd1->phd_next) {
+				hd1 = hd1->phd_next;	
+			}
+			hd1->phd_next = hd2;
+		}
+	}
+}
+
+void preset_hub_delete_a_node(t_preset_hub *h, t_preset_node *x)
+{
+	t_preset_hub_data *hd1;
+	hd1 = NULL;
+
+	fprintf(stderr,"preset_hub_delete_a_node\n");
+	if (h->ph_data) {
+		// check for enabled nodes only
+		hd1 = h->ph_data;
+		while (hd1) {
+			if (hd1->phd_node && hd1->phd_node == x) {
+				// only if the node is enabled and matches ours
+				fprintf(stderr,"	found enabled -> disabling\n");
+				hd1->phd_node = NULL;
+				break;
+			}
+			hd1 = hd1->phd_next;
+		}
+	}
+}
+
+static void *preset_hub_new(t_symbol *s, int argc, t_atom *argv)
+{
+	fprintf(stderr,"===preset_hub_new===\n");
+	t_glob_preset_node_list *nl;
+	t_glob_preset_hub_list *hl;
+	t_preset_hub_data *hd1, *hd2;
+	t_node_preset *np1, *np2;
+	t_hub_parser h_cur = H_NONE;
+
+	t_preset_hub *x;
+	t_symbol *name;
+	int i, pos;
+	t_glist *glist=(t_glist *)canvas_getcurrent();
+	t_canvas *canvas = (t_canvas *)glist_getcanvas(glist);
+
+	pos = 0; // position within argc
+
+	// first check if the mandatory argument is sane, otherwise bail out
+	if (!(argc > 0 && argv[0].a_type == A_SYMBOL)) {
+		pd_error(canvas, "preset_hub requires alphanumeric name as its argument");
+		return(NULL);
+	}
+
+	/*
+	   NO! WE CAN HAVE MULTIPLE HUBS WITH SAME NAMES, BUT IT IS USER'S RESPONSIBILITY TO KEEP THEM SANE
+	   THIS IS BECAUSE MULTIPLE INSTANCES OF ABSTRACTIONS WILL HAVE IDENTICAL NAMES!!!
+	// now let's check if there is already a pre-existing hub with the same name
+	// if so, fail at creating the object and report an error to the user
+	if (gphl) {
+		hl = gphl;
+		while(hl) {
+			if (!strcmp(hl->gphl_hub->ph_name->s_name, atom_getsymbol(&argv[0])->s_name)) {
+				pd_error(canvas, "preset_hub with the name %s already exists... hub creation failed", atom_getsymbol(&argv[0])->s_name);
+				return(NULL);
+			}
+			hl = hl->gphl_next;
+		}
+	}
+	*/
+
+/*#ifdef PDL2Ork
+	if (sys_k12_mode && !strcmp(atom_getsymbol(&argv[0])->s_name,"k12")) {
+		// exception, we are the root preset for k12 that is pre-made for us
+	}
+#endif*/
+	x = (t_preset_hub *)pd_new(preset_hub_class);
+	x->ph_invis = 0;
+
+	// read basic creation arguments
+	x->ph_name = (t_symbol *)atom_getsymbol(&argv[0]);
+	pos++;
+	if (argc > 1 && argv[1].a_type == A_FLOAT) {
+		x->ph_invis = (int)atom_getfloat(&argv[1]);
+		if (x->ph_invis < 0) x->ph_invis = 0;
+		/*level = x->ph_level;
+		while (level > 0 && canvas->gl_owner) {
+			if (canvas->gl_owner) canvas = canvas->gl_owner;
+			level--;
+		}*/
+		pos++;
+	}
+	fprintf(stderr,"hub name %s invis %d\n", x->ph_name->s_name, (int)x->ph_invis);
+
+	pos++; // one more time to move ahead of the %hidden% tag
+
+	// assign default x and y position
+	x->ph_obj.te_xpix = 0;
+	x->ph_obj.te_ypix = 0;
+
+	x->ph_preset = -1;
+
+	x->ph_canvas = canvas;
+
+	// add us to the global hub list
+	glob_preset_hub_list_add(x);
+
+	// add hub to the canvas pointer
+	if (!canvas->gl_phub)
+		canvas->gl_phub = x;
+	else {
+		t_preset_hub *gl_phub = canvas->gl_phub;
+		while (gl_phub->ph_next)
+			gl_phub = gl_phub->ph_next;
+		gl_phub->ph_next = x;
+	}
+	x->ph_next = NULL;
+
+	hd1 = NULL;
+	hd2 = NULL;
+	np1 = NULL;
+	np2 = NULL;
+
+	// load the data from the buffer and build the preset db
+	if (pos < argc) {
+		for (i=pos; i < argc; i++) {
+			fprintf(stderr,"	position: %d\n", i);
+			// SYMBOL ANALYSIS
+			if (argv[i].a_type == A_SYMBOL) {
+				fprintf(stderr,"	data = %s\n", atom_getsymbol(&argv[i])->s_name);
+				if (!strcmp(atom_getsymbol(&argv[i])->s_name, "%node%")) {
+					// beginning of a new node
+					fprintf(stderr,"	new node\n");
+					hd2 = (t_preset_hub_data *)t_getbytes(sizeof(*hd2));
+					hd2->phd_pn_gl_loc_length = 0;
+					if (hd1) {
+						hd1->phd_next = hd2;
+						fprintf(stderr,"	new node\n");
+					}
+					else {
+						x->ph_data = hd2;
+						fprintf(stderr,"	first node\n");
+					}
+					hd1 = hd2;
+					np1 = NULL; // have to reset it so that new presets are not erroneously appended to previous node
+					h_cur = H_NODE;
+				}
+				else if (!strcmp(atom_getsymbol(&argv[i])->s_name, "%preset%")) {
+					// beginning of a new preset
+					fprintf(stderr,"	new preset\n");
+					np2 = (t_node_preset *)t_getbytes(sizeof(*np2));
+					if (np1) {
+						np1->np_next = np2;
+						fprintf(stderr,"	new preset\n");
+					}
+					else {
+						hd2->phd_npreset = np2;
+						fprintf(stderr,"	first preset\n");
+					}
+					np1 = np2;
+					h_cur = H_PRESET;
+				}
+				// SYMBOL DATA
+				else if (h_cur == H_PRESET_DATA) {
+					fprintf(stderr,"	sym data %s\n", atom_getsymbol(&argv[i])->s_name);
+					SETSYMBOL(&np2->np_val, atom_getsymbol(&argv[i]));
+				}
+			}
+			// FLOAT ANALYSIS
+			else if (argv[i].a_type == A_FLOAT) {
+				fprintf(stderr,"	data = %g\n", atom_getfloat(&argv[i]));
+				if (h_cur == H_NODE) {
+					// node location
+					hd2->phd_pn_gl_loc_length++;
+					// reconstruct the dynamic location array
+					if (!hd2->phd_pn_gl_loc)
+						hd2->phd_pn_gl_loc = (int*)calloc(1, sizeof(int));
+					else hd2->phd_pn_gl_loc = (int*)realloc(hd2->phd_pn_gl_loc, hd2->phd_pn_gl_loc_length*sizeof(int));
+					hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1] = (int)atom_getfloat(&argv[i]);
+					fprintf(stderr,"	loc = %d\n", hd2->phd_pn_gl_loc[hd2->phd_pn_gl_loc_length-1]);
+				}
+				else if (h_cur == H_PRESET) {
+					// preset number
+					fprintf(stderr,"	preset %g\n", atom_getfloat(&argv[i]));
+					np2->np_preset = (int)atom_getfloat(&argv[i]);
+					h_cur = H_PRESET_DATA;
+				}
+				else if (h_cur == H_PRESET_DATA) {
+					fprintf(stderr,"	preset data %g\n", atom_getfloat(&argv[i]));
+					SETFLOAT(&np2->np_val, atom_getfloat(&argv[i]));
+				}
+			}
+		}
+	}
+	x->ph_outlet = outlet_new(&x->ph_obj, 0);
+
+	// let us pair with our nodes (if any)
+	// this is done node-side (node has to traverse canvases upwards to make sure
+	// multiple abstractions with same hubs/nodes do not trip over each other)
+	// do this only if we are not undoing, otherwise, we'll have the undo do it for us
+	// once it has repositioned objects to their original locations
+	if (!we_are_undoing)
+		glob_preset_node_list_seek_hub();
+
+    return(x);
+}
+
+static void preset_hub_free(t_preset_hub* x)
+{
+	fprintf(stderr,"preset_hub_free\n");
+	t_glob_preset_node_list *nl;
+	t_preset_hub_data *hd1, *hd2;
+	t_node_preset *np1, *np2;
+	t_preset_hub *h1, *h2;
+
+	// inform all nodes that the hub is going bye-bye
+	if (gpnl) {
+		fprintf(stderr,"	we got gpnl\n");
+		nl = gpnl;
+		while(nl) {
+			fprintf(stderr,"	analyzing gpnl entry %d\n", nl->gpnl_paired);
+			if (nl->gpnl_paired && nl->gpnl_node->pn_hub == x) {
+				// we only make the hub pointer null and leave location for undo/redo/save purposes
+				nl->gpnl_paired = 0;
+				nl->gpnl_node->pn_hub = NULL;
+				fprintf(stderr,"	removed gpnl reference\n");
+			}
+			nl = nl->gpnl_next;
+		}
+	}
+	glob_preset_hub_list_delete(x);
+
+	// remove the hub from the canvas pointer list
+	h2 = x->ph_canvas->gl_phub;
+	h1 = h2;
+	if (!h2->ph_next) {
+		// if there is only one hub on this canvas
+		x->ph_canvas->gl_phub = NULL;
+	} else {
+		while (h2 && h2 != x) {
+			h1 = h2;
+			h2 = h2->ph_next;
+		}
+		if (h2 != x) {
+			// this should never happen
+			pd_error(x, "preset_hub %s destructor was unable to find its canvas pointer", x->ph_name->s_name);
+		} else {
+			h1->ph_next = h2->ph_next;
+		}
+	}
+
+	// deallocate all the dynamically-allocated memory
+	if (x->ph_data) {
+		hd1 = x->ph_data;
+		while (hd1) {
+			if (hd1->phd_npreset) {
+				np1 = hd1->phd_npreset;
+				while (np1) {
+					np2 = np1->np_next;
+					freebytes(np1, sizeof(*np1));
+					np1 = np2;
+				}
+			}
+			hd2 = hd1->phd_next;
+			freebytes(hd1, sizeof(*hd1));
+			hd1 = hd2;
+		}
+	}
+}
+
+void preset_hub_setup(void)
+{
+	preset_hub_class = class_new(gensym("preset_hub"), 
+	    (t_newmethod)preset_hub_new, (t_method)preset_hub_free, 
+	    sizeof(t_preset_hub), 0, A_GIMME, 0);
+
+	// have to call this after everything has loaded, otherwise, the hub may not yet exist
+	// we do this using pre-loadbang call that is issued before other loadbangs, otherwise
+	// out-of-order loadbangs can issue preset recall before the preset has been properly
+	// configured
+	class_addmethod(preset_hub_class, (t_method)glob_preset_node_list_seek_hub,
+        gensym("pre-loadbang"), 0);
+
+    class_addmethod(preset_hub_class, (t_method)preset_hub_recall,
+        gensym("recall"), A_DEFFLOAT, 0);
+    class_addmethod(preset_hub_class, (t_method)preset_hub_store,
+        gensym("store"), A_DEFFLOAT, 0);
+
+    class_addbang(preset_hub_class, preset_hub_bang);		// we'll use this to output current preset
+	class_setsavefn(preset_hub_class, preset_hub_save);
+}
+
+//====================== end preset_hub ============================//
+
+void x_preset_setup(void)
+{
+    preset_node_setup();
+	preset_hub_setup();
+}
+
+//TODO: autocreate k12 hub for the root canvas (we will do this in pd.tk's pdtk_canvas_new)
+
diff --git a/src/x_preset.h b/src/x_preset.h
new file mode 100644
index 0000000000000000000000000000000000000000..fbade30f26d24a0ece2652283339cc8cdf345258
--- /dev/null
+++ b/src/x_preset.h
@@ -0,0 +1,84 @@
+
+#ifndef __x_preset_h_
+#define __x_preset_h_
+
+/*
+Global preset system by Ivica Ico Bukvic <ico@vt.edu> July, 2012
+*/
+
+// each node in the patch
+typedef struct _preset_node
+{
+    t_object pn_obj;
+    t_atom pn_val;			// last known value (null if not initialized)
+
+	t_symbol *pn_hub_name;	// hub name this node is associated with
+	t_preset_hub *pn_hub;	// pointer to the hub (null if none, equiv. to active/disabled)
+
+	t_canvas *pn_canvas;
+	int *pn_gl_loc;
+	int  pn_gl_loc_length;
+
+	// we use this to compare with the old position,
+	// so that hub can be notified of node's new position 
+	int *pn_old_gl_loc;
+	int  pn_old_gl_loc_length;
+
+	t_outlet *pn_outlet;
+} t_preset_node;
+
+// stores each of the presets per each preset_node
+typedef struct _node_preset
+{
+	int np_preset;
+	t_atom np_val;
+	struct _node_preset *np_next;
+} t_node_preset;
+
+// stores each of the nodes within a hub
+typedef struct _preset_hub_data
+{
+	t_preset_node *phd_node;		// if the node is erased, this will be NULL (inactive)
+	int *phd_pn_gl_loc;				// last known location of the node (according to the hub)
+	int  phd_pn_gl_loc_length;		// location array's length (see x_preset.c for explanation)
+	struct _preset_hub_data *phd_next;
+	t_node_preset *phd_npreset;
+} t_preset_hub_data;
+
+// preset hub (nexus for data saving/recalling)
+struct _preset_hub
+{
+	t_object ph_obj;
+	t_symbol *ph_name;
+	int ph_invis;					// make it invisible (only for the k12 mode)
+	int ph_preset;					// last enabled preset (-1 at init time)
+
+	t_canvas *ph_canvas;
+
+	struct _preset_hub *ph_next;	// next hub on the same canvas
+	t_preset_hub_data *ph_data;		// pointer to data (single-linked list)
+
+	t_outlet *ph_outlet;
+};
+
+typedef struct _glob_preset_hub_list
+{
+	t_preset_hub *gphl_hub;
+	struct _glob_preset_hub_list *gphl_next;
+} t_glob_preset_hub_list;
+
+typedef struct _glob_preset_node_list
+{
+	t_preset_node *gpnl_node;
+	int gpnl_paired;				// whether the node is paired with a hub (otherwise don't bother updating its location)
+	struct _glob_preset_node_list *gpnl_next;
+} t_glob_preset_node_list;
+
+#ifndef t_preset_hub
+#define t_preset_hub struct _preset_hub
+#endif
+
+EXTERN int we_are_undoing;
+
+#endif /* __x_preset_h_ */
+