		/*********************************
		 *                               *
		 *   Visual Shell v1.17  10/92   *
		 *                               *
		 *     by Torsten Jürgeleit      *
		 *                               *
		 *           tree part           *
		 *                               *
		 *********************************/

	/* Includes */

#include "includes.h"
#include "imports.h"
#include "protos.h"

	/* Show directory tree */

   VOID
action_tree(VOID)
{
   struct FileRequest  *freq = &file_req[active_freq];
   struct TreeRequest  *treq = &tree_req;
   struct TreeNode     *old_cursor_tnode = NULL;
   BYTE  *name = &file2_buffer[0];
   SHORT error;

   if ((error = build_device_name(name, freq->fr_DirLock)) ==
							VSH_STATUS_NORMAL) {
      hcomp_freq_cursor(freq);
      print_info_line(INFO_LINE_MODE_EMPTY);
      print_fkey_text(FKEY_MODE_NONE);
      if ((error = build_dir_tree(treq, name)) == VSH_STATUS_NORMAL) {

	 /* Get our position in dir tree */
	 name = BaseName(&freq->fr_DirName[0]);
	 do {
	    old_cursor_tnode = get_tree_node_by_name(treq, old_cursor_tnode,
								      name);
	 } while (Strcmp(&freq->fr_DirName[0], build_tnode_path_name(treq,
							old_cursor_tnode)));
	 treq->tr_DirNode    = old_cursor_tnode;
	 treq->tr_CursorNode = &treq->tr_RootNode;
	 treq->tr_XPos       = 0;
	 treq->tr_YPos       = 0;
	 print_tree_page(treq);
	 do {
	    ULONG signals = Wait(SIGF_INTUITION | SIGF_ACTION);

	    if (signals & SIGF_INTUITION) {   /* timing events from Intuition */
	       struct IntuiMessage  *msg;

	       if (msg = (struct IntuiMessage *)
					     GetMsg(con_window->UserPort)) {
		  ULONG class = msg->Class;

		  ReplyMsg((struct Message *)msg);
		  switch (class) {
		     case REFRESHWINDOW :
			print_tree_page(treq);
			break;
		     case INTUITICKS :
			if (auto_repeat) {
			   scroll_tree_req();
			}
			break;
		  }
	       }
	    }
	    if (signals & SIGF_ACTION) {
	       switch (action) {
		  case VSH_ACTION_NUM_ENTER :
		     error = read_new_dir(freq, build_tnode_path_name(treq,
			     treq->tr_CursorNode), READ_DIR_MODE_NO_OUTPUT);
		     break;
		  case VSH_ACTION_SPEED_SEARCH :
		     old_cursor_tnode = treq->tr_CursorNode;
		     if (get_input(INPUT_MODE_TREQ_ENTRY, &gadget_buffer[0])
								 == FALSE) {
			move_treq_cursor(treq, old_cursor_tnode,
							  MOVE_MODE_NORMAL);
		     }
		     print_fkey_text(FKEY_MODE_QUIT_ONLY);
		     print_treq_status(treq);
		     break;
		  case VSH_ACTION_SCROLL_LEFT :
		  case VSH_ACTION_SCROLL_RIGHT :
		  case VSH_ACTION_SCROLL_UP :
		  case VSH_ACTION_SCROLL_DOWN :
		     scroll_tree_req();
		     break;
	       }
	    }
	 } while (action != VSH_ACTION_QUIT && action != VSH_ACTION_ESC &&
					    action != VSH_ACTION_NUM_ENTER);
      }
      print_status(VSH_STATUS_FREE_TREE);
      free_dir_tree(treq);
      status = error;
      draw_requesters(DRAW_MODE_CLEAR);
   }
}
	/* Displaye tree page */

   VOID
print_tree_page(struct TreeRequest  *treq)
{
   SetAPen(con_rport, (LONG)COLOR0);
   RectFill(con_rport, (LONG)(BORDER_LEFT - 2), (LONG)
		    (BORDER_TOP_HIDDEN - 1), (LONG)(vsh_width - BORDER_RIGHT
						+ 1), (LONG)(cli_vpos - 3));
   draw_line(COLOR1, (USHORT)2, (USHORT)(BORDER_TOP_HIDDEN - 2),
		  (USHORT)(vsh_width - 3), (USHORT)(BORDER_TOP_HIDDEN - 2));
   move_treq_cursor(treq, treq->tr_DirNode, MOVE_MODE_NO_CURSOR);
   print_fkey_text(FKEY_MODE_QUIT_ONLY);
   print_treq_status(treq);
}
	/* Build directory tree */

   SHORT
build_dir_tree(struct TreeRequest  *treq, BYTE *dev_name)
{
   struct TreeNode  *tnode = &tree_req.tr_RootNode;
   SHORT error;

   strcpy(&treq->tr_DeviceName[0], dev_name);
   strcpy(&treq->tr_DirName[0], dev_name);
   NewList((struct List *)&tnode->tn_List);   /* init root node */
   tnode->tn_Node.mln_Succ = NULL;
   tnode->tn_Node.mln_Pred = NULL;
   tnode->tn_ParentNode    = NULL;   /* indicates root node */
   tnode->tn_XPos          = 0;
   tnode->tn_YPos          = 0;
   tnode->tn_MaxDirs       = 1;
   strcpy(&tnode->tn_Name[0], dev_name);
   enable_abort = 1;
   do {
      if ((error = read_tree_dirs(treq, tnode)) == VSH_STATUS_NORMAL) {
	 tnode = get_next_unscanned_tree_node(treq, tnode);
      }
   } while (tnode && error == VSH_STATUS_NORMAL);
   enable_abort = 0;
   return(error);
}
	/* Get all subdirectories in given directory */

   SHORT
read_tree_dirs(struct TreeRequest  *treq, struct TreeNode  *parent_tnode)
{
   struct MinList   *list = &parent_tnode->tn_List;
   struct TreeNode  *tnode;
   BPTR  lock;
   BYTE  *path = &path1_buffer[0];
   BOOL  keepon = TRUE;
   SHORT error = VSH_STATUS_NORMAL;

   if (!(lock = quiet_lock(&treq->tr_DirName[0], (LONG)SHARED_LOCK))) {
      error = VSH_ERROR_LOCK_FAILED;
   } else {
      if (Examine(lock, fib) == DOSFALSE) {   /* enter dir */
	 error = VSH_ERROR_EXAMINE_FAILED;
      } else {

	 /* Scan loop */
	 do {

	    /* ESC pressed ? */
	    if (CheckAbort(NULL)) {
	       error = VSH_ERROR_ABORTED;
	    } else {

	       /* Get next dir entry */
	       if (ExNext(lock, fib) == DOSFALSE) {
		  if (IoErr() != ERROR_NO_MORE_ENTRIES) {
		     error = VSH_ERROR_EXNEXT_FAILED;
		  }
		  keepon = FALSE;
	       } else {

		  /* New dir found */
		  if (fib->fib_DirEntryType > 0) {
		     if (!(tnode = (struct TreeNode *)AllocMem((LONG)
				      sizeof(struct TreeNode), MEMF_PUBLIC |
							     MEMF_CLEAR))) {
			error = VSH_ERROR_OUT_OF_MEM;
		     } else {

			/* Build new directory tree node */
			NewList((struct List *)&tnode->tn_List);
			tnode->tn_XPos       = parent_tnode->tn_XPos + 1;
			tnode->tn_YPos       = 0;
			tnode->tn_MaxDirs    = 1;
			tnode->tn_MaxDepth   = tnode->tn_XPos + 1;
			tnode->tn_ParentNode = parent_tnode;
			strcpy(&tnode->tn_Name[0], &fib->fib_FileName[0]);
			AddTail((struct List *)list, (struct Node *)tnode);

			/* Print full dir name in status line */
			if ((error = build_path_name_from_lock(path, lock))
						     == VSH_STATUS_NORMAL) {
			   print_dos_status(DOS_MODE_TREE, path, NULL);
			}
		     }
		  }
	       }
	    }
	 } while (error == VSH_STATUS_NORMAL && keepon == TRUE);
      }
      UnLock(lock);
   }
   return(error);
}
	/* Get node of next unscanned directory in tree */

   struct TreeNode *
get_next_unscanned_tree_node(struct TreeRequest  *treq,
					     struct TreeNode  *parent_tnode)
{
   struct TreeNode  *tnode;
   ULONG max_dirs, max_depth;
   BYTE  *ptr, *path = &treq->tr_DirName[0];
   BOOL  keepon = TRUE;

   tnode = (struct TreeNode *)parent_tnode->tn_List.mlh_Head;   /* try to go deeper */
   if (tnode->tn_Node.mln_Succ) {   /* list not empty ? */
      tnode->tn_YPos = parent_tnode->tn_YPos;
      TackOn(path, &tnode->tn_Name[0]);
   } else {
      do {   /* else go back */
	 ptr = BaseName(path);   /* go one dir back */
	 if (*(ptr - 1) == '/') {
	    ptr--;
	 }
	 *ptr  = '\0';
	 tnode = (struct TreeNode *)parent_tnode->tn_Node.mln_Succ;   /* next dir in list */
	 if (tnode->tn_Node.mln_Succ) {   /* not end of list ? */
	    tnode->tn_YPos = parent_tnode->tn_YPos + parent_tnode->tn_MaxDirs;
	    TackOn(path, &tnode->tn_Name[0]);
	    keepon = FALSE;
	 } else {
	    if (!(parent_tnode = parent_tnode->tn_ParentNode)) {   /* root reached ? */
	       tnode  = NULL;
	       keepon = FALSE;
	    } else {
	       max_dirs  = 0;   /* count dirs and get max depth in branch of tree */
	       max_depth = 0;
	       for (tnode = (struct TreeNode *)parent_tnode->tn_List.mlh_Head;
			tnode->tn_Node.mln_Succ; tnode = (struct TreeNode *)
						  tnode->tn_Node.mln_Succ) {
		  max_dirs += tnode->tn_MaxDirs;
		  if (tnode->tn_MaxDepth > max_depth) {
		     max_depth = tnode->tn_MaxDepth;
		  }
	       }
	       parent_tnode->tn_MaxDirs += max_dirs - 1;
	       parent_tnode->tn_MaxDepth = max_depth;
	    }
	 }
      } while (keepon == TRUE);
   }
   return(tnode);
}
	/* Free directory tree */

   VOID
free_dir_tree(struct TreeRequest  *treq)
{
   struct TreeNode  *tnode = &treq->tr_RootNode;
   struct MinList   *list;

   do {
      list = &tnode->tn_List;
      if ((struct MinList *)list->mlh_TailPred != list) {   /* list not empty ? */
	 tnode = (struct TreeNode *)list->mlh_Head;
      } else {
	 if (tnode = tnode->tn_ParentNode) {
	    FreeMem((APTR)RemHead((struct List *)&tnode->tn_List), (LONG)
						   sizeof(struct TreeNode));
	 }
      }
   } while (tnode);
}
	/* Scroll directory tree */

   VOID
scroll_tree_req(VOID)
{
   struct TreeRequest  *treq = &tree_req;
   struct TreeNode     *tnode2, *tnode = treq->tr_CursorNode;
   USHORT columns = treq->tr_Columns, rows = treq->tr_Rows;
   ULONG  cxpos, cypos, xpos = treq->tr_XPos, ypos = treq->tr_YPos,
		   max_dirs  = treq->tr_RootNode.tn_MaxDepth,
		   max_depth = treq->tr_RootNode.tn_MaxDirs;

   do {
      switch (scroll_flag) {
	 case 1 :   /* scroll up */
	    tnode = get_prev_column_tree_node(tnode);
	    break;
	 case 2 :   /* scroll down */
	    tnode = get_next_column_tree_node(tnode);
	    break;
	 case 4 :   /* scroll left */
	    if (tnode2 = tnode->tn_ParentNode) {
	       tnode = tnode2;
	    }
	    break;
	 case 8 :   /* scroll right */
	    tnode2 = (struct TreeNode *)tnode->tn_List.mlh_Head;
	    if (tnode2->tn_Node.mln_Succ) {
	       tnode = tnode2;
	    } else {
	       tnode = get_next_column_tree_node(tnode);
	    }
	    break;
      }
      if (tnode != treq->tr_CursorNode) {
	 cxpos = tnode->tn_XPos;
	 cypos = tnode->tn_YPos;
	 if (cxpos <= xpos) {
	    xpos = cxpos;
	    if (xpos) {
	       xpos--;
	    }
	 } else {
	    if (cxpos >= (xpos + columns - 1)) {
	       xpos = cxpos - columns + 1;
	       if ((xpos + columns) < max_dirs) {
		  xpos++;
	       }
	    }
	 }
	 if (cypos <= ypos) {
	    ypos = cypos;
	    if (ypos) {
	       ypos--;
	    }
	 } else {
	    if (cypos >= (ypos + rows - 1)) {
	       ypos = cypos - rows + 1;
	       if ((ypos + rows) < max_depth) {
		  ypos++;
	       }
	    }
	 }
	 hcomp_treq_cursor(treq);
	 treq->tr_CursorNode = tnode;
	 if (xpos != treq->tr_XPos || ypos != treq->tr_YPos) {
	    treq->tr_XPos = xpos;
	    treq->tr_YPos = ypos;
	    print_dir_tree(treq);
	 }
	 hcomp_treq_cursor(treq);
	 print_treq_status(treq);
      }
   } while (vsh_scroll_speed == SCROLL_SPEED_FAST && auto_repeat);
}
	/* Build full path name for given node in dir tree */

   BYTE *
build_tnode_path_name(struct TreeRequest  *treq,
					     struct TreeNode  *search_tnode)
{
   struct TreeNode  *tnode2, *tnode = &treq->tr_RootNode;
   ULONG xpos = search_tnode->tn_XPos, ypos = search_tnode->tn_YPos;
   BYTE  *path = &path1_buffer[0];

   strcpy(path, &tnode->tn_Name[0]);
   while (tnode != search_tnode) {
      tnode2 = (struct TreeNode *)tnode->tn_Node.mln_Succ;
      if (tnode2 && tnode2->tn_Node.mln_Succ && tnode2->tn_YPos <= ypos) {
	 tnode  = tnode2;   /* next entry in list */
	 tnode2 = (struct TreeNode *)tnode->tn_Node.mln_Succ;
	 if (!tnode2->tn_Node.mln_Succ || tnode2->tn_YPos > ypos) {
	    TackOn(path, &tnode->tn_Name[0]);
	 }
      } else {
	 tnode2 = (struct TreeNode *)tnode->tn_List.mlh_Head;
	 if (tnode2->tn_Node.mln_Succ && tnode2->tn_XPos <= xpos) {
	    tnode  = tnode2;   /* go deeper */
	    tnode2 = (struct TreeNode *)tnode->tn_Node.mln_Succ;
	    if (!tnode2->tn_Node.mln_Succ || tnode2->tn_YPos > ypos) {
	       TackOn(path, &tnode->tn_Name[0]);
	    }
	 }
      }
   }
   return(path);
}
	/* Jump to an specified entry in dir tree */

   VOID
jump_to_treq_entry(BYTE *name, USHORT mode)
{
   struct TreeRequest  *treq = &tree_req;
   struct TreeNode     *tnode;

   if (mode == JUMP_MODE_FIRST) {
      tnode = NULL;
   } else {
      tnode = treq->tr_CursorNode;
   }
   if ((tnode = get_tree_node_by_name(treq, tnode, name)) || (tnode =
	      get_tree_node_by_name(treq, (struct TreeNode *)NULL, name))) {
      move_treq_cursor(treq, tnode, MOVE_MODE_NORMAL);
   }      
}
	/* Move cursor to specified entry in dir tree */

   VOID
move_treq_cursor(struct TreeRequest *treq, struct TreeNode *tnode,
								USHORT mode)
{
   ULONG  xpos  = treq->tr_XPos, ypos = treq->tr_YPos,
		   cxpos = tnode->tn_XPos, cypos = tnode->tn_YPos;
   USHORT columns   = treq->tr_Columns, rows = treq->tr_Rows,
		   max_depth = treq->tr_RootNode.tn_MaxDepth,
		   max_dirs  = treq->tr_RootNode.tn_MaxDirs;

   if (tnode != treq->tr_CursorNode || mode == MOVE_MODE_NO_CURSOR) {
      if (mode != MOVE_MODE_NO_CURSOR) {
	 hcomp_treq_cursor(treq);
      }
      if (cxpos <= xpos || cxpos >= (xpos + columns - 1) || cypos <= ypos ||
					       cypos >= (ypos + rows - 1)) {
	 if (max_depth > columns) {
	    if (cxpos <= (columns / 2)) {
	       xpos = 0;
	    } else {
	       xpos = cxpos - columns / 2;
	       if ((xpos + columns) > max_depth) {
		  xpos -= (xpos + columns) - max_depth;
	       }
	    }
	 }
	 if (max_dirs > rows) {
	    if (cypos <= (rows / 2)) {
	       ypos = 0;
	    } else {
	       ypos = cypos - rows / 2;
	       if ((ypos + rows) > max_dirs) {
		  ypos -= (ypos + rows) - max_dirs;
	       }
	    }
	 }
	 treq->tr_XPos = xpos;
	 treq->tr_YPos = ypos;
	 print_dir_tree(treq);
      } else {
	 if (mode == MOVE_MODE_NO_CURSOR) {
	    print_dir_tree(treq);
	 }
      }
      treq->tr_CursorNode = tnode;
      hcomp_treq_cursor(treq);
   }
}
	/* Get tree node by given name */

   struct TreeNode *
get_tree_node_by_name(struct TreeRequest *treq, struct TreeNode *tnode,
								 BYTE *name)
{
   struct TreeNode  *tnode2;
   ULONG len = strlen(name);
   BYTE  *ptr, *path = &treq->tr_DirName[0];
   BOOL  keepon, searching = TRUE;

   if (! len) {
      tnode = &treq->tr_RootNode;
      strcpy(path, &tnode->tn_Name[0]);
   } else {
      if (! tnode) {
 	 tnode = &treq->tr_RootNode;
	 strcpy(path, &tnode->tn_Name[0]);
	 if (! Strncmp(&tnode->tn_Name[0], name, len)) {
	    searching = FALSE;   /* found */
	 }
      }
      while (searching == TRUE) {
	 tnode2 = (struct TreeNode *)tnode->tn_List.mlh_Head;   /* try to go deeper */
	 if (tnode2->tn_Node.mln_Succ) {   /* list not empty ? */
	    tnode = tnode2;
	    TackOn(path, &tnode->tn_Name[0]);
	 } else {
	    keepon = TRUE;
	    do {   /* else go back */
	       ptr = BaseName(path);   /* go one dir back */
	       if (*(ptr - 1) == (BYTE)'/') {
		  ptr--;
	       }
	       *ptr  = '\0';
	       tnode2 = (struct TreeNode *)tnode->tn_Node.mln_Succ;   /* next dir in list */
	       if (tnode2->tn_Node.mln_Succ) {   /* not end of list ? */
		  tnode = tnode2;
		  TackOn(path, &tnode->tn_Name[0]);
		  keepon = FALSE;
	       } else {
		  if (!(tnode = tnode->tn_ParentNode)) {   /* root reached ? */
		     tnode     = NULL;
		     keepon    = FALSE;
		     searching = FALSE;   /* not found */
		  }
	       }
	    } while (keepon == TRUE);
	 }
	 if (searching == TRUE) {
	    if (! Strncmp(&tnode->tn_Name[0], name, len)) {
	       searching = FALSE;
	    }
	 }
      }
   }
   return(tnode);
}
