diff --git a/exec/load/layout.js b/exec/load/layout.js
index 6aea04e9868b4f6a183abd22963f6d39ce0b53f4..33a82df5c86cbc54fd45d8c34c2bc8e7d43b88ac 100644
--- a/exec/load/layout.js
+++ b/exec/load/layout.js
@@ -19,32 +19,48 @@
 		lightbar menus
 		ANSI graphics (static or animated)
 		etc...
+		
+	usage example:
+	
+		var layout=new Layout();
+		
 	
 */
 
 load("funclib.js");
 load("sbbsdefs.js");
 
-var layout_settings=new Layout_Settings(
-	LIGHTGRAY, /* view foreground */
-	BG_BLACK,	/* view background */
-	LIGHTRED,	/* menu highlight foreground */
-	RED,	/* menu highlight background (use foreground value) */
-	YELLOW,	/* view highlight foreground */
-	BG_BLUE,	/* view highlight background */
-	LIGHTGRAY,	/* border foreground color */
-	BG_BLACK,	/* border background color */
-	BLACK,		/* tab foreground (normal) */
-	BG_LIGHTGRAY, /* tab background */
-	LIGHTCYAN,	/* tab highlight foreground */	
-	BG_CYAN,		/* tab highlight background */
-	RED,		/* tab alert foregruond */
-	MAGENTA,	/* tab update foreground */
-	BLUE,		/* tab user msg foreground */
-	YELLOW,		/* clock foreground */
-	BG_BLUE		/* clock background */
-);
-var tab_settings=new Tab_Settings();
+/* layout color settings */
+var layout_settings={
+	vfg:	LIGHTGRAY, 	/* view foreground */
+	vbg:	BG_BLACK,	/* view background */
+	lfg:	LIGHTCYAN,	/* menu highlight foreground */
+	lbg:	BG_BLUE,	/* menu highlight background */
+	vtfg:	YELLOW,		/* view highlight foreground */
+	vtbg:	BG_BLUE,	/* view highlight background */
+	bfg:	LIGHTGRAY,	/* border foreground color */
+	bbg:	BG_BLACK,	/* border background color */
+	tfg:	BLACK,		/* tab foreground (normal) */
+	tbg:	BG_LIGHTGRAY, /* tab background */
+	hfg:	LIGHTCYAN,	/* tab highlight foreground */	
+	hbg:	BG_CYAN,	/* tab highlight background */
+	afg:	RED,		/* tab alert foregruond */
+	nfg:	MAGENTA,	/* tab update foreground */
+	pfg:	BLUE,		/* tab user msg foreground */
+	cfg:	LIGHTGRAY,	/* clock foreground */
+	cbg:	BG_BLUE,	/* clock background */
+	ifg:	DARKGRAY,	/* inactive view foreground */
+	ibg:	BG_BLACK	/* inactive view background */
+};
+
+/* tab status settings */
+var tab_settings={
+	INACTIVE:-1,
+	DEFAULT:0,
+	NOTICE:1,
+	ALERT:2,
+	PRIVATE:3
+};
 
 /* main layout object */
 function Layout() 
@@ -66,15 +82,53 @@ function Layout()
 	}
 	
 	/* initialize layout */
-	this.init=function(theme) {
-		/* load theme, if specified */
-		if(theme) {
-			//ToDo: create layout_themes.ini
+	this.init=function() {
+		this.load();
+	}
+
+	/* load layout settings */
+	this.load=function() {
+		var settings_file=new File(
+			system.data_dir + "user/" +
+			printPadded(user.number,4,"0","right") + 
+			".layout.ini"
+		);
+		if(file_exists(settings_file.name)) {
+			settings_file.open('r',true);
+			
+			if(!settings_file.is_open) {
+				log(LOG_WARNING,"error loading user layout settings");
+			}
+			else {
+				var set=settings_file.iniGetObject("settings");
+				settings_file.close();
+				for(var s in set) 
+					layout_settings[s]=set[s];
+				log(LOG_DEBUG,"loaded user layout settings");
+			}
 		}
-		
-		/* otherwise, load default color scheme */
-		else {
+	}
+	
+	/* save layout settings */
+	this.save=function() {
+		for(var s in layout_settings) 
+			this.save_settings(s,layout_settings[s]);
+	}
+	
+	/* save individual layout setting */
+	this.save_setting=function(setting,value) {
+		var settings_file=new File(
+			system.data_dir + "user/" +
+			printPadded(user.number,4,"0","right") + 
+			".layout.ini"
+		);
+		settings_file.open((file_exists(settings_file.name)?'r+':'w+'),true);
+		if(!settings_file.is_open) {
+			log("error opening user layout settings file",LOG_WARNING);
+			return;
 		}
+		settings_file.iniSetValue("settings",setting,value);
+		settings_file.close();
 	}
 
 	/* add a new view to layout */
@@ -113,6 +167,22 @@ function Layout()
 		return true;
 	}
 	
+	/* delete a named view from this layout */
+	this.del_view=function(name) {
+		var view_index=views_map[name.toUpperCase()];
+		views.splice(view_index,1);
+		delete views_map[name.toUpperCase()];
+		for(var v=0;v<views.length;v++) {
+			views_map[views[v].name.toUpperCase()]=v;
+			views[v].view_position=v;
+		}
+		if(!views[this.index])
+			this.index=0;
+		log(LOG_DEBUG, "view deleted: " + name);
+		this.draw();
+		return true;
+	}
+	
 	/* layout update loop */
 	this.cycle=function() {
 		for(var w=0;w<views.length;w++) 
@@ -152,9 +222,6 @@ function Layout()
 	this.handle_command=function(str) {
 		cmd=str.split(" ");
 		switch(cmd[0].toUpperCase()) {
-		case "/CLOSE":
-			//ToDo: add window deletion
-			break;
 		case '\x09':	/* CTRL-I TAB */
 			var old_index=this.index;
 			for(count=0;count<views.length;count++) {
@@ -164,68 +231,30 @@ function Layout()
 					this.index++;
 				if(this.current_view.interactive) break;
 			}
-			if(views[old_index].show_title)
-				views[old_index].drawTitle();
-			if(views[this.index].show_title)
-				this.current_view.drawTitle();
-			return false;
+			if(old_index != this.index) {
+				if(views[old_index].expire) 
+					this.del_view(views[old_index].name);
+				else {
+					if(views[old_index].show_title)
+						views[old_index].drawTitle();
+				}
+				if(views[this.index].show_title)
+					this.current_view.drawTitle();
+			}
+			return cmd[0];
 		}
 		return this.current_view.handle_command(str);
 	}
 	this.init();
 }
 
-/* layout settings object */
-function Layout_Settings(vfg,vbg,mhfg,mhbg,vtfg,vtbg,bfg,bbg,tfg,tbg,hfg,hbg,afg,nfg,pfg,cfg,cbg)
-{
-	/* main view colors */
-	this.vfg=vfg;
-	this.vbg=vbg;
-	
-	/* menu highlights */
-	this.mhfg=mhfg;
-	this.mhbg=mhbg;
-	
-	/* title hightlight colors */
-	this.vtfg=vtfg;
-	this.vtbg=vtbg;
-
-	/* view border color */
-	this.bfg=bfg;
-	this.bbg=bbg;
-	
-	/* tab colors */
-	this.tfg=tfg;
-	this.tbg=tbg;
-	
-	/* tab highlight colors */
-	this.hfg=hfg;
-	this.hbg=hbg;
-	
-	/* tab alert color */
-	this.afg=afg;
-	
-	/* tab notice color */
-	this.nfg=nfg;
-	
-	/* tab user message color */
-	this.pfg=pfg;
-	
-	/* clock colors */
-	this.cfg=cfg;
-	this.cbg=cbg;
-	
-	/* inactive view colors */
-	this.ibg=BG_BLACK;
-	this.ifg=DARKGRAY;
-}
-
 /* tabbed view object */
 function Layout_View(name,x,y,w,h)
 {
 	this.name=name;
 	this.update_tabs=true;
 	this.update_title=true;
+	this.expire=false;
 
 	/* private array containing view tabs 
 		tabs can be anything that will fit in the view
@@ -248,6 +277,9 @@ function Layout_View(name,x,y,w,h)
 	this.width=w;
 	this.height=h;
 	
+	this.view_position=undefined;
+	this.layout_position=undefined;
+		
 	/* public tab index */
 	this.index getter=function() {
 		return index.value;
@@ -434,7 +466,6 @@ function Layout_View(name,x,y,w,h)
 			this.index=0;
 		this.current_tab.update=true;
 		log(LOG_DEBUG, "tab deleted: " + name);
-		this.update_tabs=true;
 		return true;
 	}
 	
@@ -468,7 +499,7 @@ function Layout_View(name,x,y,w,h)
 				tabs[this.index].status=tab_settings.DEFAULT;
 				this.drawTabs();
 				this.draw();
-				return true;
+				return KEY_LEFT;
 			case KEY_RIGHT:
 				if(this.index+1>=tabs.length)
 					this.index=0;
@@ -477,7 +508,7 @@ function Layout_View(name,x,y,w,h)
 				tabs[this.index].status=tab_settings.DEFAULT;
 				this.drawTabs();
 				this.draw();
-				return true;
+				return KEY_RIGHT;
 			}
 		} 
 		if(tabs[this.index] &&
@@ -599,7 +630,7 @@ function Layout_View(name,x,y,w,h)
 	/* draw the title bar of the current view */
 	this.drawTitle=function() {
 		var color_str="";
-		if(this.view_position == this.layout_position.value) {
+		if(this.layout_position == undefined || (this.view_position == this.layout_position.value)) {
 			color_str+=getColor(layout_settings.vtbg) 
 				+ getColor(layout_settings.vtfg); 
 		} else {
@@ -622,7 +653,7 @@ function Layout_View(name,x,y,w,h)
 	
 	/* draw the border of the current view */
 	this.drawBorder=function() {
-		var border_color=getColor(layout_settings.bbg) + getColor(layout_settings.bfg);
+		var border_color="\1n" + getColor(layout_settings.bbg) + getColor(layout_settings.bfg);
 		setPosition(this.x,this.y);
 		pushMessage(splitPadded(border_color + "\xDA","\xBF",this.width,"\xC4"));
 		for(var y=0;y<this.height-2;y++) {
@@ -646,23 +677,13 @@ function Layout_View(name,x,y,w,h)
 		,this.x,this.y,this.width,this.height));
 }
 
-/* tab status values */
-function Tab_Settings()
-{
-	this.INACTIVE=-1;
-	this.DEFAULT=0;
-	this.NOTICE=1;
-	this.ALERT=2;
-	this.PRIVATE=3;
-}
- 
 /* view tab prototype object */
 function View_Tab()
 {
 	this.status=0;
 	this.fixed=false;
 	this.hotkeys=false;
-	this.update=false;
+	this.update=true;
 
 	/* dynamic content and command processor placeholders */
 	// this.handle_command;
@@ -716,16 +737,17 @@ Tab_Templates["CHAT"]=function()
 		if(!this.channel)
 			return;
 		/* move any waiting messages to window */
-		while(this.channel.message_list.length > 0) 
-			this.window.post(this.channel.message_list.shift());
-		/* 	if this tab is not the current view tab,
-			mark this tab for a user list update */
-		if(!this.channel.update_users && this.parent_view.index != this.view_index) {
-			this.channel.update_users=true;
+		while(this.channel.messages.length > 0) {
+			new_messages = true;
+			var msg = this.channel.messages.shift();
+			var str = 
+				getColor(chat_settings.NICK_COLOR) + msg.nick.name + 
+				getColor(chat_settings.TEXT_COLOR) + ": " + msg.str;
+			this.window.post(str);
 		}
 	}
 	this.handle_command=function(cmd) {
-		switch(cmd) {
+		switch(cmd[0]) {
 		case KEY_UP:
 		case KEY_DOWN:
 		case KEY_LEFT:
@@ -734,57 +756,14 @@ Tab_Templates["CHAT"]=function()
 		case KEY_END:
 			/* pass all window-navigation commands to the window object command handler */
 			return this.window.handle_command(cmd);
-			break;
+		/* process a user command */
+		case '/':
+			return this.chat.handle_command(this.name,cmd);
+		/* process all other input */
 		default:
-			/* pass any other commands to the chat command handler */
-			return this.chat.handle_command(cmd,this.name);
-			break;
-		}
-	}
-	this.draw=function() {
-		this.window.draw();
-	}
-}
-
-Tab_Templates["USERLIST"]=function() 
-{
-	this.type="userlist";
-	if(!js.global.Window) {
-		load(js.global,"msgwndw.js");
-	}
-	
-	this.init=function(chat_view) {
-		this.chat_view=chat_view;
-		this.window=new Window();
-		this.window.init(this.x,this.y,this.width,this.height);
-	}
-	this.cycle=function() {
-		if(!this.chat_view.current_tab) {
-			this.clear();
-			this.update=false;
-			return;
-		}
-		
-		var chat=this.chat_view.current_tab.chat;
-		var channel=chat.channels[this.chat_view.current_tab.name.toUpperCase()];
-		
-		if(channel && channel.update_users) {
-			channel.update_users=false;
-			
-			var num_users=countMembers(channel.users);
-			this.window.clear();
-			if(num_users > 0) {
-				this.window.init(this.x,this.y,this.width,this.height);
-				for each(var u in channel.users) {
-					var str="";
-					for(var m=0;m<u.modes.length;m++) 
-						str+=u.modes[m];
-					str+=u.nick;
-					this.window.post(str);
-				}
-			}
+			/* send a message to the current channel */
+			return this.chat.submit(this.name,cmd);
 		}
-		this.update=this.window.update;
 	}
 	this.draw=function() {
 		this.window.draw();
@@ -794,6 +773,7 @@ Tab_Templates["USERLIST"]=function()
 Tab_Templates["GRAPHIC"]=function() 
 {
 	this.type="graphic";
+	this.hotkeys=true;
 	if(!js.global.Graphic) {
 		load(js.global,"graphic.js");
 	}
@@ -855,19 +835,67 @@ Tab_Templates["LIGHTBAR"]=function()
 	}
 	this.draw=function() {
 		this.lightbar.fg=layout_settings.vfg;
-		this.lightbar.bg=layout_settings.vbg;
-		this.lightbar.hfg=layout_settings.mhfg;
-		this.lightbar.hbg=layout_settings.mhbg;
+		this.lightbar.bg=layout_settings.vbg>>4;
+		this.lightbar.lfg=layout_settings.lfg;
+		this.lightbar.lbg=layout_settings.lbg>>4;
 		this.lightbar.draw();
+		if(this.lightbar.node_action)
+			bbs.node_action=this.lightbar.node_action;
 	}
 	this.add=function(txt, retval, width, lpadding, rpadding, disabled, nodraw)	{
 		this.lightbar.add(txt, retval, width, lpadding, rpadding, disabled, nodraw);
 	}
 }
 
+Tab_Templates["TREE"]=function() 
+{
+	this.type="tree";
+	this.hotkeys=true;
+	
+	if(!js.global.Tree) {
+		load(js.global,"tree.js");
+	}
+	
+	this.init=function(tree) {
+		/* if we are supplied a tree, make it fit */
+		if(tree) { 
+			this.tree=tree;
+			this.tree.x=this.x;
+			this.tree.y=this.y;
+			this.tree.width=this.width;
+			this.tree.height=this.height;
+			this.tree.open=true;
+			this.tree.index=0;
+		}
+		/* otherwise create a blank lightbar */
+		else
+			this.tree=new Tree(this.x,this.y,this.width,this.height);
+		this.update=true;
+	}
+	this.addItem = function() {
+		return this.tree.addItem.apply(this.tree,arguments);
+	}
+	this.addTree = function() {
+		return this.tree.addTree.apply(this.tree,arguments);
+	}
+	this.handle_command=function(key) {
+		return this.tree.handle_command(key);
+	}
+	this.draw=function() {
+		tree_settings.fg=layout_settings.vfg;
+		tree_settings.bg=layout_settings.vbg;
+		tree_settings.lfg=layout_settings.lfg;
+		tree_settings.lbg=layout_settings.lbg;
+		this.tree.draw();
+		if(this.tree.node_action)
+			bbs.node_action=this.tree.node_action;
+	}
+}
+
 Tab_Templates["CLOCK"]=function() 
 {
 	this.type="clock";
+	this.hotkeys=true;
 	if(!js.global.DigitalClock) {
 		load(js.global,"clock.js");
 	}
@@ -894,6 +922,7 @@ Tab_Templates["CLOCK"]=function()
 Tab_Templates["CALENDAR"]=function() 
 {
 	this.type="calendar";
+	this.hotkeys=true;
 	if(!js.global.Calendar) {
 		load(js.global,"calendar.js");
 	}
@@ -920,64 +949,25 @@ Tab_Templates["CALENDAR"]=function()
 for(var tt in Tab_Templates) 
 	Tab_Templates[tt].prototype=new View_Tab;
 
-/* handy functions */
+/* use this function to add/remove tabs
+from a view automatically  */
 function updateChatView(chat,view) 
 {
-	/* 	use this function to add/remove tabs
-		from a view automatically when
-		the appropriate data comes through 
-		called by updateChatView() */
-	var data=chat.cycle();
-	if(!data || !data.func) return;
-	
-	switch(data.func) {
-	case "001":
-		log("checking channel tabs");
-		for each(var t in view.tabs) {
-			log("tab: " + t);
-			if(!chat.channels[t.toUpperCase()]) {
-				log("joining channel: " + t.toUpperCase());
-				chat.handle_command("/j " + t,"IRC");
-			}
-		}
-		break;
-	case "JOIN":
-		if(data.origin == chat.nick.nickname) {
-			if(!view.tab(data.chan)) {
-				view.add_tab("chat",data.chan,chat);
-				view.current_tab=data.chan;
-			}
-		}
-		else if(data.chan != view.current_tab.name) {
-			if(view.tab(data.chan)) 
-				view.tab(data.chan).status=tab_settings.NOTICE;
-		}
-		break;
-	case "PART":
-		if(data.origin == chat.nick.nickname) {
-			if(view.tab(data.chan))
-				view.del_tab(data.chan);
-		}
-		else if(data.chan != view.current_tab.name) {
-			if(view.tab(data.chan)) 
-				view.tab(data.chan).status=tab_settings.NOTICE;
-		}
-		break;
-	case "PRIVMSG":
-	case "NOTICE":
-		/* ignore server messages */
-		if(data.origin.search(/[.]/) >= 0) 
-			break;
-		if(data.dest.toUpperCase() == chat.nick.nickname.toUpperCase()) {
-			if(!view.tab(data.origin))
-				view.add_tab("chat",data.origin,chat);
-			if(data.origin != view.current_tab.name)
-				view.tab(data.origin).status=tab_settings.PRIVATE;
-		}
-		else if(data.dest != view.current_tab.name) {
-			if(view.tab(data.dest)) 
-				view.tab(data.dest).status=tab_settings.ALERT;
-		}
-		break;
+	for(var c in chat.channels) {
+		var chan = chat.channels[c];
+		
+		/* if we do not have a tab matching a joined channel, 
+		add a new tab for that channel */
+		if(!view.tab(chan.name)) 
+			view.add_tab("chat",chan.name,chat);
+	}
+	
+	/* verify local channel cache */
+	for(var v=0;v<view.tabs.length;v++) {
+		var chan = view.tabs[v];
+		if(!chat.channels[chan.toUpperCase()]) {
+			view.del_tab(chan);
+			v--;
+		}	
 	}
 }