From c0ef26ed867763ea52b211475c8fc2709e405f29 Mon Sep 17 00:00:00 2001
From: mcmlxxix <>
Date: Thu, 23 Aug 2012 20:34:10 +0000
Subject: [PATCH] clean up leftover text when moving tree, clear remaining
 lines when tree doesnt fill frame. draw +/- characters for subtree expansion.
 add pageUp(), pageDown() methods, make home() and end() work relative to the
 top-level. various other shite.

---
 exec/load/tree.js | 325 +++++++++++++++++++++++++++++++++-------------
 1 file changed, 232 insertions(+), 93 deletions(-)

diff --git a/exec/load/tree.js b/exec/load/tree.js
index 7b1432f11a..562048aa85 100644
--- a/exec/load/tree.js
+++ b/exec/load/tree.js
@@ -72,14 +72,28 @@ function Tree(frame,text,tree) {
 	var properties = {
 		frame:undefined,
 		parent:undefined,
-		status:flags.OPEN,
+		status:0,
 		index:0,
 		text:"",
 		line:1,
 		list:[],
 		items:[]
 	};
-	
+	var values = {
+		NOT_HANDLED:false,
+		HANDLED:true
+	}
+	var commands = {
+		DOWN:KEY_DOWN,
+		UP:KEY_UP,
+		HOME:KEY_HOME,
+		END:KEY_END,
+		PGDN:"]",
+		PGUP:"[",
+		SELECT:"\r",
+		
+	}
+		
 	/* protected properties */
 	this.__defineGetter__("frame",function() {
 		if(properties.parent)
@@ -169,6 +183,10 @@ function Tree(frame,text,tree) {
 		lfg:LIGHTRED,
 		// current item background
 		lbg:BG_RED,
+		// current tree heading foreground
+		cfg:WHITE,
+		// current tree heading background
+		cbg:BG_BLACK,
 		// disabled item foreground
 		dfg:DARKGRAY,
 		// hotkey foreground
@@ -176,7 +194,7 @@ function Tree(frame,text,tree) {
 		// tree branch foreground
 		tfg:BROWN,
 		// tree heading foreground
-		hfg:WHITE,
+		hfg:LIGHTGRAY,
 		// tree heading background
 		hbg:BG_BLACK,
 		// tree expansion foreground
@@ -189,7 +207,7 @@ function Tree(frame,text,tree) {
 	}
 	this.getcmd = function(cmd) {
 		/* initialize return value */
-		var retval=false;
+		var retval=values.NOT_HANDLED;
 		
 		if(!(properties.status&flags.CLOSED)) {
 			/* if the current tree item is a subtree, pass control to the next subtree */
@@ -197,21 +215,27 @@ function Tree(frame,text,tree) {
 				retval=this.current.getcmd(cmd);
 			
 			/* if the submenu did not handle it, let this menu handle the command */
-			if(retval === false) {
+			if(retval === values.NOT_HANDLED) {
 				switch(cmd) {
-				case KEY_DOWN:
-					retval = moveDown(this.depth==0);
+				case commands.DOWN:
+					retval = this.down();
+					break;
+				case commands.UP:
+					retval = this.up();
+					break;
+				case commands.HOME:
+					retval = this.home();
 					break;
-				case KEY_UP:
-					retval = moveUp(this.depth==0);
+				case commands.END:
+					retval = this.end();
 					break;
-				case KEY_HOME:
-					retval = home();
+				case commands.PGUP:
+					retval = this.pageUp(0,this.frame.height);
 					break;
-				case KEY_END:
-					retval = end();
+				case commands.PGDN:
+					retval = this.pageDown(0,this.frame.height);
 					break;
-				case "\r":
+				case commands.SELECT:
 					if(properties.index >= 0) 
 						retval = this.current.action();						
 					else 
@@ -221,9 +245,33 @@ function Tree(frame,text,tree) {
 					retval=matchHotkey(cmd);
 					break;
 				}
-				if(retval === true) {
-					if(this.current instanceof Tree)
-						this.current.updateIndex(cmd);
+				
+				/* update the tree on an item being handled */
+				if(retval !== values.NOT_HANDLED) {
+					this.refresh();
+				}
+			}
+			
+			/* handle any residual movement from pageup or pagedown
+			on a subtree */
+			else {
+				switch(cmd) {
+				case commands.PGUP:
+					retval = this.pageUp(retval,this.frame.height-1);
+					break;
+				case commands.PGDN:
+					retval = this.pageDown(retval,this.frame.height-1);
+					break;
+				case commands.HOME:
+					retval = this.home();
+					break;
+				case commands.END:
+					retval = this.end();
+					break;
+				}
+				
+				/* update the tree on an item being handled */
+				if(retval !== values.NOT_HANDLED) {
 					this.refresh();
 				}
 			}
@@ -326,16 +374,147 @@ function Tree(frame,text,tree) {
 			if(this.line > this.frame.height)
 				offset = this.line-this.frame.height;
 			var output = properties.list.splice(offset,this.frame.height);
-			for(var y=0;y<output.length;y++) {
-				for(var x=0;x<output[y].length;x++) {
-					var d = output[y][x];
-					this.frame.setData(x,y,d.ch,d.attr,false);
+			
+			var y;
+			var x;
+			var ch;
+			var attr;
+			
+			/* push tree items into frame */
+			for(y=0;y<output.length;y++) {
+				for(x=0;x<output[y].length;x++) {
+					ch = output[y][x].ch;
+					attr = output[y][x].attr;
+					this.frame.setData(x,y,ch,attr,false);
+				}
+				/* clear remaining spaces at end of line */
+				for(;x<this.frame.width;x++) {
+					this.frame.setData(x,y," ",attr,false);
 				}
 			}
+			/* clear remaining lines */
+			for(y++;y<=this.frame.height;y++) {
+				this.frame.gotoxy(0,y);
+				this.frame.clearline(this.colors.bg);
+			}
 			properties.list=[];
 		}
 	}
 
+	/* tree navigation */
+	this.up=function() {
+		var start = properties.index;
+		while(this.current) {
+			properties.index--;
+			if(properties.index < 0) {
+				if(this.depth == 0) {
+					properties.index = 0;
+					return values.HANDLED;
+				}
+				else {
+					return values.HANDLED;
+				}
+			}
+			if(properties.index == start)
+				break;
+			if(!(this.current.status&flags.DISABLED)) {
+				if(this.current instanceof Tree) 
+					this.current.updateIndex(commands.UP);
+				return values.HANDLED;
+			}
+		}
+		return values.NOT_HANDLED;
+	}
+	this.down=function() {
+		var start = properties.index;
+		while(properties.index == -1 || this.current) {
+			properties.index++;
+			if(properties.index >= properties.items.length) {
+				properties.index = properties.items.length-1;
+				return values.NOT_HANDLED;
+			}
+			if(properties.index == start)
+				break;
+			if(!(this.current.status&flags.DISABLED)) {
+				if(this.current instanceof Tree) 
+					this.current.updateIndex(commands.DOWN);
+				return values.HANDLED;
+			}
+		}
+		return values.NOT_HANDLED;
+	}
+	this.home=function() {
+		/*ToDo: track starting position and compare to ending position 
+		(prevent infinite loop and avoid empty menus) */
+		if(this.depth == 0) {
+			properties.index = 0;
+			while(this.current.status&flags.disabled)
+				this.down();
+			if(this.current instanceof Tree) 
+				this.current.index = -1;
+			return values.HANDLED;
+		}
+		return values.NOT_HANDLED;
+	}
+	this.end=function() {
+		/*ToDo: track starting position and compare to ending position 
+		(prevent infinite loop and avoid empty menus) */
+		properties.index = properties.items.length-1;
+		while(this.current.status&flags.disabled)
+			this.up();
+		if(this.current instanceof Tree && 
+			!(this.current.status&flags.CLOSED) &&
+			!(this.current.status&flags.DISABLED) && 
+			!(this.current.status&flags.HIDDEN)) 
+			this.current.end();
+		if(this.depth == 0)
+			return values.HANDLED;
+		return values.NOT_HANDLED;
+	}
+	this.pageUp=function(count,distance) {
+		/* move up in this menu until the given distance
+		or the top of the menu have been reached */
+		for(var i=count;i<distance;i++) {
+			var r;
+			if(this.current instanceof Tree && 
+				!(this.current.status&flags.CLOSED) &&
+				!(this.current.status&flags.DISABLED) && 
+				!(this.current.status&flags.HIDDEN)) {
+				var r=this.current.pageUp(i,distance);
+				if(r === values.HANDLED) 
+					break;
+				else if(r > i && r < distance)
+					i = r;
+			}
+			if(i < distance && this.up() == values.NOT_HANDLED) {
+				return i;
+			}
+		}
+		return values.HANDLED;
+
+	}
+	this.pageDown=function(count,distance) {
+		/* move up in this menu until the given distance
+		or the top of the menu have been reached */
+		for(var i=count;i<distance;i++) {
+			var r;
+			if(this.current instanceof Tree && 
+				!(this.current.status&flags.CLOSED) &&
+				!(this.current.status&flags.DISABLED) && 
+				!(this.current.status&flags.HIDDEN)) {
+				var r=this.current.pageDown(i,distance);
+				if(r === values.HANDLED) 
+					break;
+				else if(r > i && r < distance)
+					i = r;
+			}
+			if(i < distance && this.down() == values.NOT_HANDLED) {
+				return i;
+			}
+		}
+		return values.HANDLED;
+	}
+	
 	/* DO NOT USE */
 	this.generate=function(last,current,line) {
 		if(properties.status&flags.HIDDEN)
@@ -356,20 +535,25 @@ function Tree(frame,text,tree) {
 		
 			/* if this is the current item, set lightbar bg color */
 			if(current) {
-				bg=this.colors.lbg;
-				this.line = line;
+				if(properties.index == -1) {
+					bg=this.colors.lbg;
+					this.line = line;
+				}
+				else {
+					bg=this.colors.cbg;
+				}
 			}
 			/* otherwise use regular menu bg color */
 			else {
-				bg=this.colors.bg;
+				bg=this.colors.hbg;
 			}
 			
 			/* add indentation on subtrees */
 			for(var i=0;i<this.depth-1;i++) 
-				list.push({ch:undefined,attr:bg+fg});
+				list.push({ch:" ",attr:bg+fg});
 			/* do not draw tree branches on top level tree */
 			if(this.depth == 1) 
-				list.push({ch:undefined,attr:bg+fg});
+				list.push({ch:" ",attr:bg+fg});
 			/* if this is the bottom of a subtree */
 			else if(last) {
 				fg=this.colors.tfg;
@@ -381,24 +565,31 @@ function Tree(frame,text,tree) {
 				list.push({ch:"\xC3",attr:bg+fg});
 			}
 			
+			fg = this.colors.xfg;
+			if(properties.status&flags.CLOSED) 
+				list.push({ch:"+",attr:bg+fg});
+			else 
+				list.push({ch:"-",attr:bg+fg});
+			
 			/* if this tree is disabled, use disabled fg */
 			if(properties.status&flags.DISABLED)
 				fg=this.colors.dfg;
-			/* otherwise, if current? lightbar fg */
-			else if(current)
-				fg=this.colors.lfg;
+				
+			else if(current) {
+				/* if current, lightbar fg */
+				if(properties.index == -1)
+					fg=this.colors.lfg;
+				else
+					fg=this.colors.cfg;
+			}
 			/* normal menu fg */
 			else
-				fg=this.colors.fg;
+				fg=this.colors.hfg;
 			
 			/* push text string into list */
 			for(var i=0;i<properties.text.length;i++) 
 				list.push({ch:properties.text[i],attr:bg+fg});
 
-			/* populate remaining characters as undefined */
-			for(var i=0;i<this.frame.width-list.length;i++) 
-				list.push({ch:undefined,attr:bg+fg});
-				
 			line++;
 			this.list(list);
 		}
@@ -416,9 +607,9 @@ function Tree(frame,text,tree) {
 	}
 	this.updateIndex=function(cmd) {
 		if(!(this.status&flags.CLOSED)) {
-			if(cmd == KEY_UP)
+			if(cmd == commands.UP)
 				properties.index = properties.items.length-1;
-			else if(cmd == KEY_DOWN)
+			else if(cmd == commands.DOWN)
 				properties.index = -1;
 			if(properties.items[properties.index] instanceof Tree)
 				properties.items[properties.index].updateIndex(cmd);
@@ -428,57 +619,9 @@ function Tree(frame,text,tree) {
 	}
 	
 	/* private functions */
-	function moveDown(loop) {
-		var start = properties.index;
-		while(properties.index == -1 || properties.items[properties.index]) {
-			properties.index++;
-			if(properties.index >= properties.items.length) {
-				if(loop) 
-					properties.index = 0;
-				else
-					break;
-			}
-			if(properties.index == start)
-				break;
-			if(!(properties.items[properties.index].status&flags.disabled))
-				return true;
-		}
-		return false;
-	}
-	function moveUp(loop) {
-		var start = properties.index;
-		while(properties.items[properties.index]) {
-			properties.index--;
-			if(properties.index < 0) {
-				if(loop) 
-					properties.index = properties.items.length-1;
-				else
-					return true;
-			}
-			if(properties.index == start)
-				break;
-			if(!(properties.items[properties.index].status&flags.disabled))
-				return true;
-		}
-		return false;
-	}
-	function home() {
-		if(properties.index == 0)
-			return false;
-		properties.index = 0;
-		return true;
-	}
-	function end() {
-		if(properties.index == properties.items.length-1)
-			return false;
-			if(!(properties.items[properties.index].status&flags.disabled))
-				return true;
-		properties.index = properties.items.length-1;
-		return true;
-	}
 	function matchHotkey(cmd) {
 		if(!cmd.match(/\w/))
-			return false;
+			return values.NOT_HANDLED;
 		var pattern=new RegExp(cmd,"i");
 		var stop=properties.items.length-1;
 		if(this.depth == 0)
@@ -492,12 +635,12 @@ function Tree(frame,text,tree) {
 				properties.index=i;
 				if(properties.items[i] instanceof Tree)
 					properties.items[i].index = -1;
-				return true;
+				return values.HANDLED;
 			}
 			if(i == stop)
 				break;
 		}	
-		return false;
+		return values.NOT_HANDLED;
 	}
 	function init(frame,text,tree) {
 		if(tree instanceof Tree) {
@@ -640,10 +783,10 @@ function TreeItem(text,parent,func,args) {
 		
 		/* add indentation on subtrees */
 		for(var i=0;i<this.depth-1;i++) 
-			list.push({ch:undefined,attr:bg+fg});
+			list.push({ch:" ",attr:bg+fg});
 		/* do not draw tree branches on top level tree */
 		if(this.depth == 1) 
-			list.push({ch:undefined,attr:bg+fg});
+			list.push({ch:" ",attr:bg+fg});
 		/* if this is the bottom of a subtree */
 		else if(last) {
 			fg=this.colors.tfg;
@@ -669,10 +812,6 @@ function TreeItem(text,parent,func,args) {
 		for(var i=0;i<properties.text.length;i++) 
 			list.push({ch:properties.text[i],attr:bg+fg});
 
-		/* populate remaining characters as undefined */
-		for(var i=0;i<this.frame.width-list.length;i++) 
-			list.push({ch:undefined,attr:bg+fg});
-			
 		this.list(list);
 		return ++line;
 	}
-- 
GitLab