
/*'Liner Version 1.32.  Written by Dave Schreiber                        */
/*©1988, 1989 Dave Schreiber.  All Rights Reserved.                      */
/*1.32 completed Monday, October 31, 1989                                */
/*1.31 completed Friday, October 6, 1989                                 */
/*1.3 completed Saturday, February 25, 1989                              */
/*1.2 completed Friday, November 11, 1988                                */
/*1.1 completed Friday, October 14, 1988                                 */
/*1.0 completed Friday, September 9, 1988                                */

/*Compiled with Lattice C V5.02                                          */
/*To compile:  1> lc -cw -j30 -L+YAFR:YAFR.o 'Liner                      */
/*You must assign YAFR: to the directory where YAFR.o is stored.         */
/*You must also assign LINER: to the directory where the 'Liner source   */
/*is located.                                                            */

/*'Liner is an outliner, with up to six levels of depth, and up to       */
/*26 items per level (each isolated section).  It has both clipboard and */
/*non-clipboard support of CUT, COPY, and PASTE.  It can output files    */
/*in its own format, or in ASCII (to disk or printer).  It can read files*/
/*in its own format only.  It uses dynamic memory allocation to allow    */
/*an infinite number of items (memory providing).*/

/*Amiga features of note that are used:                                  */
/*console device, disk i/o, printer i/o, clipboard i/o                   */

/*Intuition objects (screen, windows, gadgets, menus, etc.) generated by */
/*PowerWindows Version 2.0*/

/*V1.1 corrects some bugs, doubles the number of characters available    */
/*for filenames, and improves PrintItemList() so that it doesn't always  */
/*print the entire item list*/

/*1.2 allows the toggling between 640x200 & 640x400 screens and the      */
/*choice of the order that levels appear in, has a completely reworked   */
/*About... window, lets the user change the level of all the items       */
/*in a highlighted block, and now displays the current filename          */
/*in the title bar.*/

/*1.3 adds support for user-selected keymaps, and now prints an asterisk */
/*in the title bar if an outline has been edited but not saved.  It also */
/*includes my very own file requester, YAFR (Yet Another File Requester) */
/*based in principal upon Charlie Heath's, but with a lot more buttons.  */

/*1.31 changes the default device where files are located from DF0: to   */
/*the current directory                                                  */

/*1.32 fixed a bug that kept the screen from updating properly after a   */
/*paste.  Also uses YAFR V1.1                                            */

#include <exec/types.h>
#include <exec/exec.h>
#include <intuition/intuition.h>
#include <devices/console.h>
#include <devices/clipboard.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <devices/inputevent.h>
#include <devices/keymap.h>
#include <math.h>

struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
ULONG DosBase;

struct IOStdReq *consoleWriteMsg,*consoleReadMsg;
struct Port *consoleWritePort,*consoleReadPort;
struct IOClipReq ClipboardIO;
struct MsgPort clipboardMsgPort;
struct Process *ThisProc;

struct LineItem               /*'Liner structure definition*/
   {
   struct LineItem *NextItem,*PrevItem;
   UBYTE Level,ItemNumber;
   char Text[80];
   };

extern struct LineItem *InsertItem(),*FindNext(),*FindPrev();


struct Screen *Screen;
struct Window *Window,*AboutWindow;
struct KeyMap keymap;
#define Rp Window->RPort;
#define CSI  0x09b
#define SEMI 0x03b
#define NEWPAGE 0x0c
#define CLOSECHAR 0x05c
#define SCRNHEIGHT RowsInScreen
#define REQUESTERCHARS 57
APTR ConsoleDevice;
struct LineItem *CharItem,*StartIItem,*EndIItem,*ClipStart,*ClipEnd;
int ButtonState,PtrX,PtrY,LastX,LastY,InvsMode,StartChar,EndChar,InvY,EndIY;
int BLastX,BLastY,ClipMode,ErrorInTitle=FALSE;
UBYTE Laced,RowsInScreen,StartingLevel,Modified;
char Indent=0;
char DoubleSpc=FALSE;
char FileName[150+33],SDirName[150],SFileName[33],SExt[18];
char PDName[150+33],PDirName[150],PFileName[33],PExt[18];

char ScreenTitle[] = {" 'Liner V1.32:  "};

#define UP 0
#define DOWN 1
#define NOINV 0
#define LINE_PENDING 1
#define LINE_FWD 2
#define LINE_BACK 3

#define BLOCK_PENDING -1
#define BLOCK_DOWN -2
#define BLOCK_UP -3

UBYTE OLCPos[6][6]=
{
   {9,13,17,21,25,29},
   {6,10,14,18,22,26},
   {6,10,14,18,22,26},
   {6,10,14,18,22,26},
   {6,10,14,18,22,26},
   {6,10,14,18,22,26}
};

/*Some macros*/
#define PERPOS(x) OLCPos[StartingLevel][x]-3
#define MinX(LI)  OLCPos[StartingLevel][(LI->Level)-1]
#define MaxX(LI)  OLCPos[StartingLevel][(LI->Level)-1]+strlen(LI->Text)
#define MaxLen(Level) 79-OLCPos[StartingLevel][Level-1]
#define PosInText(Level) CurX-OLCPos[StartingLevel][Level-1]
#define RealLevel(LI) (LI->Level+StartingLevel > 6) ? (LI->Level+StartingLevel-7) : (LI->Level+StartingLevel)
    /* ^^^ == get level when StartingLevel != 0 */
/*Pointers to various LineItems*/
struct LineItem *FirstItem,*LastItem,*CurrentItem,*FirstScrnItem,*ScrnBtm;
int CurX,CurY,DispRows;

/*Outline characters*/
char *OutLineChars[6][26]=
   {
      {
      {"I."},{"II."},{"III."},{"IV."},{"V."},{"VI."},{"VII."},{"VIII."},
      {"IX."},{"X."},{"XI."},{"XII."},{"XIII."},{"XIV."},{"XV."},{"XVI."},
      {"XVII."},{"XVIII."},{"XIX."},{"XX."},{"XXI."},{"XXII."},{"XXIII."},
      {"XXIV."},{"XXV."},{"XXVI."}
      },

      {
      {"A."},{"B."},{"C."},{"D."},{"E."},{"F."},{"G."},{"H."},{"I."},
      {"J."},{"K."},{"L."},{"M."},{"N."},{"O."},{"P."},{"Q."},{"R."},
      {"S."},{"T."},{"U."},{"V."},{"W."},{"X."},{"Y."},{"Z."}
      },

      {
      {"1."},{"2."},{"3."},{"4."},{"5."},{"6."},{"7."},{"8."},{"9."},
      {"10."},{"11."},{"12."},{"13."},{"14."},{"15."},{"16."},{"17."},
      {"18."},{"19."},{"20."},{"21."},{"22."},{"23."},{"24."},{"25."},
      {"26."}
      },

      {
      {"a."},{"b."},{"c."},{"d."},{"e."},{"f."},{"g."},{"h."},{"i."},
      {"j."},{"k."},{"l."},{"m."},{"n."},{"o."},{"p."},{"q."},{"r."},
      {"s."},{"t."},{"u."},{"v."},{"w."},{"x."},{"y."},{"z."}
      },

      {
      {"1)"},{"2)"},{"3)"},{"4)"},{"5)"},{"6)"},{"7)"},{"8)"},{"9)"},
      {"10)"},{"11)"},{"12)"},{"13)"},{"14)"},{"15)"},{"16)"},{"17)"},
      {"18)"},{"19)"},{"20)"},{"21)"},{"22)"},{"23)"},{"24)"},{"25)"},
      {"26)"}
      },

      {
      {"a)"},{"b)"},{"c)"},{"d)"},{"e)"},{"f)"},{"g)"},{"h)"},{"i)"},
      {"j)"},{"k)"},{"l)"},{"m)"},{"n)"},{"o)"},{"p)"},{"q)"},{"r)"},
      {"s)"},{"t)"},{"u)"},{"v)"},{"w)"},{"x)"},{"y)"},{"z)"}
      },
   };

/*The position of each special character in the array below*/
int SCPos[]=
   {0,1,4,5,9,13,14,16,17,18,19,20,21,23,25,27,29};

/*Special characters (TAB, RETURN, etc.)*/
char SpecChars[]=
   {
   32,
   8,CSI,'P',
   0,
   0x0D,0x0A,CSI,'L',
   0x0D,0x0A,CSI,'L',
   0,
   CSI,'P',
   0,
   0,
   0,
   0,
   0,
   CSI,'A',
   CSI,'B',
   CSI,'C',
   CSI,'D'
   };

/*All the other little components of 'Liner*/
#include <liner:mouse.h>
#include <liner:Windows.h>
#include <liner:menu.h>
#include <liner:disk.h>
#include <liner:sure.h>
#include <liner:clip.h>

_main()
{
   ULONG Class;
   USHORT Code,Qualifier;
   APTR IAddress;
   SHORT MouseX,MouseY;
   int Seconds,Micros;
   struct IntuiMessage *Message;

   char Buffer[200];
   Buffer[0]=NULL;
   FileName[0]=SFileName[0]=SExt[0]=NULL;
   strcpy(SDirName,"");
   strcpy(PDirName,"");
   PDName[0]=PFileName[0]=PExt[0]=NULL;
   Laced=FALSE;   /*Not interlaced*/
   Modified=FALSE;  /*No modifications, yet*/
   StartingLevel=0;

   /*Open all the libraries*/
   IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",1);
   if(IntuitionBase==NULL)
      exit(10);

   GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",1);
   if(GfxBase==NULL)
      exit(20);

   DosBase=OpenLibrary("dos.library",1);
   if(DosBase==NULL)
      exit(30);

   RowsInScreen=23;
   ThisProc=(struct Process *)FindTask(NULL);  /*Get 'Liner's process structure*/
   OpenGraphics();   /*Open screen, window, etc.*/
   SetupClipboard(); /*Open the clipboard*/

   /*Setup various system variable*/
   DispRows=1;
   PtrX=PtrY=LastX=LastY=1;
   ButtonState=UP;
   InvsMode=ClipMode=NOINV;
   FirstItem=LastItem=FirstScrnItem=CurrentItem=(struct LineItem *)
         InsertItem(NULL,NULL);
   FirstItem->Level=1;
   FirstItem->ItemNumber=0;
   PrintItemList(FirstItem,1);
   PlotCursor(MinX(CurrentItem),1);
   FileName[0]=PDName[0]=NULL;

   for(;;)    /*The main loop!*/
      {
      Wait(1<<Window->UserPort->mp_SigBit);  /*Wait for an event*/
      while((Message=(struct IntuiMessage *) /*Get the message*/
            GetMsg(Window->UserPort))!=NULL)
         {        /*Get the message's information*/
         Class=Message->Class;
         Code=Message->Code;
         Qualifier=Message->Qualifier;
         MouseX=Message->MouseX;
         MouseY=Message->MouseY;
         Seconds=Message->Seconds;
         Micros=Message->Micros;

         IAddress=(APTR)Message->IAddress;

         ReplyMsg(Message);   /*Reply to the message*/

            /*Decode the message*/
         if(HandleIDCMP(Class,Code,Qualifier,MouseX,MouseY,IAddress,
               Buffer,Seconds,Micros))
            {     /*If the message was a keypress*/
            InsertChar(CurrentItem->Text,Buffer[2],   /*Print the letter*/
                  PosInText(CurrentItem->Level));     /*onto the screen*/
            CurX++;
            WriteConsole(Buffer,-1);
            }
         }
      }
}

WriteConsole(Buffer,Length)   /*Write a string to the console*/
char *Buffer;
int Length;
{
   consoleWriteMsg->io_Command=CMD_WRITE;
   consoleWriteMsg->io_Data=(char *)Buffer;
   consoleWriteMsg->io_Length=Length;
   DoIO(consoleWriteMsg);
}

/*Decode an IDCMP message*/
HandleIDCMP(Class,Code,Qualifier,MouseX,MouseY,IAddress,Buffer,Secs,Micros)
ULONG Class;
USHORT Code,Qualifier;
APTR IAddress;
SHORT MouseX,MouseY;
int Secs,Micros;
char *Buffer;
{
   struct MenuItem *item;
   struct InputEvent KeyEvent;
   struct LineItem *WorkingItem;
   int TempX,TempY;
   Buffer[0]=CSI;
   Buffer[1]=0x40;
   Buffer[2]=NULL;
   switch(Class)
      {
      case MENUPICK: /*There was a menu selection*/
         if(ErrorInTitle)
            TitleErrorCancel();
         while(Code != MENUNULL)
            {
            item=(struct MenuItem *)ItemAddress(&Menu1,Code);
            HandleEvent(item);
            Code=item->NextSelect;
            }
      case MOUSEBUTTONS:   /*The left mouse button was pushed*/
         if(Code==SELECTDOWN)
            HandleButton(Secs,Micros);
         break;
      case MOUSEMOVE:      /*The mouse was moved*/
         MovedMouse(MouseX,MouseY);
         break;
      /*A key was pressed*/
      case RAWKEY:         /*By using IDCMP RAWKEY as input, and the*/
         if(Code & 0x80)   /*console for output, we can both get input*/
            return(FALSE);  /*and IDCMP messsages without having to try*/
         if(ErrorInTitle)   /*and decode ASCII console messages*/
            TitleErrorCancel(); /*(IDCMP structures are MUCH easier)*/
         if((Code < 0x41) || (0x5a <= Code && Code <= 0x5e) ||
               (Code==0x4a))
            {    /*^^^==All the RAWKEY codes that represent ASCII symbols*/
            CheckModified();
            KeyEvent.ie_NextEvent=NULL;     /*Create a fake input event*/
            KeyEvent.ie_Class=IECLASS_RAWKEY; /*for RawKeyConvert() to */
            KeyEvent.ie_SubClass=NULL; /*munch on.  Using RawKeyConvert()*/
            KeyEvent.ie_Code=Code;   /*insures that the current keymap*/
            KeyEvent.ie_Qualifier=Qualifier; /*is used*/
            RawKeyConvert(&KeyEvent,&Buffer[2],200,&keymap),ConsoleDevice;
            Buffer[3]=NULL;   /* ^^^ == RAWKEY to ASCII*/

            if(InvsMode < NOINV) /*If a block of lines was highlighted*/
               {                 /*when a key was pressed, cancel the*/
               EndBlockInvs();   /*highlight*/
               return(FALSE);
               }
                                 /*If a key was pressed when TEXT was*/
            if(InvsMode)         /*highlighted, delete the text & replace*/
               DelTextBlock();   /*it with the letter pressed*/

            /*If the current line isn't full*/
            if(strlen(CurrentItem->Text)<MaxLen(CurrentItem->Level))
               return(TRUE);  /*OK to print the character*/
            else
               return(FALSE); /*Otherwise not*/
            }
         else
            switch(Code)   /*Handle all the "special" characters*/
               {
               case 0x4d:        /*Move cursor down one*/
                  CancelInvs();
                  if(Qualifier & 3) /*Handle SHIFT-DOWN*/
                     WholeScreenDown();
                  else if(Qualifier & 8) /*Handle CTRL-DOWN*/
                     JumpToBottom();
                  else
                     CursorDown();
                  break;
               case 0x4c:        /*Move cursor up one*/
                  CancelInvs();
                  if(Qualifier & 3)     /*Check for shift*/
                     WholeScreenUp();
                  else if(Qualifier & 8) /*Check for control*/
                     JumpToTop();
                  else
                     CursorUp();
                  break;
               case 0x4e:        /*Move cursor one to the right*/
                  CancelInvs();
                  if(CurX >= MaxX(CurrentItem))
                     {
                     TempX=CurX;
                     CurX=1;
                     if(!CursorDown())
                        CurX=TempX;
                     }
                  else
                     if(Qualifier & 3) /*Handle SHIFT-cursor-right*/
                        PlotCursor(MaxX(CurrentItem),CurY);
                     else
                        PlotCursor(CurX+1,CurY);
                  break;
               case 0x4f:        /*Move cursor one to the left*/
                  CancelInvs();
                  if(CurX <= MinX(CurrentItem))
                     {
                     TempX=CurX;
                     CurX=100;
                     if(!CursorUp())
                        CurX=TempX;
                     }
                  else
                     if(Qualifier & 3) /*Handle SHIFT-cursor-left*/
                        PlotCursor(MinX(CurrentItem),CurY);
                     else
                        PlotCursor(CurX-1,CurY);
                  break;
               case 0x41:        /*Backspace over a character*/
                  CheckModified();
                  if(DelInvs())  /*If something is highlighted*/
                     break;      /*delete it*/

         /*Delete the character, if not at the beginning of the line...*/
                  if(CurX>MinX(CurrentItem))
                     {
                     strcpy(Buffer,&SpecChars[SCPos[1]]);
                     Buffer[SCPos[2]-SCPos[1]]=NULL;
                     DeleteChar(CurrentItem->Text,
                           PosInText(CurrentItem->Level)-1);
                     WriteConsole(Buffer,-1);
                     CurX--;
                     }
                  else  /*otherwise, delete the line*/
                     if(CurrentItem->PrevItem != NULL &&
                           strlen(CurrentItem->Text)==0)
                        {
                        DelBlock(CurrentItem,CurrentItem,CurY,CurY);
                        if(CurrentItem != LastItem)
                           CursorUp();
                        PlotCursor(MaxX(CurrentItem),CurY);
                        }
                  break;
               case 0x46:        /*Delete a character*/
                  CheckModified();
                  if(DelInvs())
                     break;

                  if(CurX<MaxX(CurrentItem))
                     {
                     strcpy(Buffer,&SpecChars[SCPos[6]]);
                     Buffer[SCPos[7]-SCPos[6]]=NULL;
                     DeleteChar(CurrentItem->Text,
                           PosInText(CurrentItem->Level));
                     WriteConsole(Buffer,-1);
                     }
                  break;
               case 0x42:           /*TAB (in or out)*/
                  CheckModified();
                  if(InvsMode < NOINV)  /*Block highlighted*/
                     if(Qualifier & 3)
                        if(InvsMode==BLOCK_DOWN)
                           PullOutBlock(StartIItem,EndIItem,InvY);
                        else
                           PullOutBlock(EndIItem,StartIItem,EndIY);
                     else
                        if(InvsMode==BLOCK_DOWN)
                           PushInBlock(StartIItem,EndIItem,InvY);
                        else
                           PushInBlock(EndIItem,StartIItem,EndIY);
                  else
                     {
                     if(CancelInvs())
                        return(FALSE);
                     TempY=CurY;
                     PlotCursor(1,TempY);
                     if(Qualifier & 3)    /*TAB out (shift pressed)*/
                        PullOut(CurrentItem);
                     else
                        PushIn(CurrentItem); /*TAB in*/
                     PlotCursor(MinX(CurrentItem),TempY);
                     }
                  break;
               case 0x43:  /*Return & Enter */
               case 0x44:  /*Insert a line*/
                  CheckModified();
                  if(CancelInvs())
                     return(FALSE);
                  if(CurX < MaxX(CurrentItem))
                     if(!CursorUp())
                        {
                        InsertLineAtTop();
                        return(FALSE);
                        }
                  strcpy(Buffer,&SpecChars[SCPos[4]]);
                  WriteConsole(Buffer,4);
                  WorkingItem=(struct LineItem *)
                        InsertItem(CurrentItem->NextItem,CurrentItem);
                  WorkingItem->Level=CurrentItem->Level;
                  if(CurrentItem->ItemNumber==25)
                     WorkingItem->ItemNumber=0;
                  else
                     WorkingItem->ItemNumber=CurrentItem->ItemNumber+1;
                  if(CurY==SCRNHEIGHT)
                     {
                     ScrnBtm=(struct LineItem *)WorkingItem;
                     FirstScrnItem=(struct LineItem *)
                           FirstScrnItem->NextItem;
                     }
                  else
                     {
                     CurY++;
                     if(DispRows==SCRNHEIGHT)
                        ScrnBtm=(struct LineItem *)ScrnBtm->PrevItem;
                     else
                        ++DispRows;
                     }
                  if(WorkingItem->NextItem==NULL)
                     ScrnBtm=LastItem=(struct LineItem *)
                           WorkingItem;
                  AddItem(WorkingItem);
                  TempY=CurY;
                  RedrawOutlineChars(WorkingItem,WorkingItem->Level,0);
                  CurrentItem=(struct LineItem *)WorkingItem;
                  PlotCursor(MinX(CurrentItem),TempY);
                  break;
               }
            return(FALSE);
      }
   return(FALSE);

}

/*Close everything*/
CloseStuff()
{
   FreeListMem(FirstItem,LastItem);
   if(ClipMode < NOINV)
      FreeListMem(ClipStart,ClipEnd);

   ShutDownClipboard();
   CloseGraphics();

   CloseLibrary(DosBase);
   CloseLibrary(GfxBase);
   CloseLibrary(IntuitionBase);
   exit(0);
}

Leave(err,why) /*If an error occured, tell user, close window & screen*/
int err;          /*& exit*/
char *why;
{
   TitleError(why);
   Delay(500);          /*Give user 5 seconds to read error message*/
   exit(err);
}

PushIn(Item)   /*Increase and redraw the level of an item*/
struct LineItem *Item;
{
   if(Push(Item))
      {
      RedrawOutlineChars(Item,Item->Level-1,Item->Level);
      return(TRUE);
      }
   else
      return(FALSE);
}

Push(Item)   /*Increase the level of an item (I. to A., etc.)*/
struct LineItem *Item;
{
   if(Item->Level==6)
      return(FALSE);

   RemItem(Item);
   Item->Level++; /*Increase the level and chop off excess text if it*/
   Item->Text[MaxLen(Item->Level)]=NULL; /*would go off the line*/
   AddItem(Item);
   PrintItem(Item);

   return(TRUE);
}

PullOut(Item)  /*Decrease the level of an item and redraw*/
struct LineItem *Item;
{
   if(Pull(Item))
      {
      RedrawOutlineChars(Item,Item->Level+1,Item->Level);
      return(TRUE);
      }
   else
      return(FALSE);
}

Pull(Item)  /*Decrease the level of an item (a. to 1., etc.)*/
struct LineItem *Item;
{
   if(Item->Level==1)
      return(FALSE);

   RemItem(Item);
   Item->Level--;
   AddItem(Item);
   AddItem(Item->NextItem);
   PrintItem(Item);

   return(TRUE);
}

PushInBlock(Start,End,YStart) /*Indent a reversed block*/
struct LineItem *Start,*End;
UBYTE YStart;
{
   struct LineItem *Item;
   UBYTE OldX=CurX;
   UBYTE OldY=CurY;
   UBYTE Y=YStart;

   Item=(struct LineItem *)Start; /*Check to see if any items are as*/
   while(Item != End)             /*pushed in as possible*/
      {
      if(Item->Level == 6)
         return(FALSE);
      Item=(struct LineItem *)Item->NextItem;
      }

   PlotCursor(1,Y);
   Item=(struct LineItem *)Start; /*Do first one*/
   Push(Item);

   while(Item != End)  /*Loop until all pushed in*/
      {
      Item=(struct LineItem *)Item->NextItem;
      PlotCursor(1,++Y);
      Push(Item);
      }

   PlotCursor(1,++Y);      /*Redraw outline characters*/
   RedrawOutlineChars(End->NextItem,6,5);
   PlotCursor(1,Y);
   RedrawOutlineChars(End->NextItem,4,3);
   PlotCursor(1,Y);
   RedrawOutlineChars(End->NextItem,2,1);

   PlotCursor(1,YStart);
   RvsBlock(Start,End);
   if(OldX < MinX(CurrentItem)) /*OldX might no longer be possible if*/
      PlotCursor(MinX(CurrentItem),OldY); /*CurrentItem was one pushed in*/
   else
      PlotCursor(OldX,OldY);
}

PullOutBlock(Start,End,YStart) /*Indent a reversed block*/
struct LineItem *Start,*End;
UBYTE YStart;
{
   struct LineItem *Item;
   UBYTE OldX=CurX;
   UBYTE OldY=CurY;
   UBYTE Y=YStart;

   Item=(struct LineItem *)Start; /*Check to see if any items are as*/
   while(Item != End)             /*pushed in as possible*/
      {
      if(Item->Level == 1)
         return(FALSE);
      Item=(struct LineItem *)Item->NextItem;
      }

   PlotCursor(1,Y);
   Item=(struct LineItem *)Start; /*Do first one*/
   Pull(Item);

   while(Item != End)  /*Loop until all pushed in*/
      {
      Item=(struct LineItem *)Item->NextItem;
      PlotCursor(1,++Y);
      Pull(Item);
      }
   PlotCursor(1,++Y);      /*Redraw outline characters*/
   RedrawOutlineChars(End->NextItem,6,5);
   PlotCursor(1,Y);
   RedrawOutlineChars(End->NextItem,4,3);
   PlotCursor(1,Y);
   RedrawOutlineChars(End->NextItem,2,1);

   PlotCursor(1,YStart);
   RvsBlock(Start,End);
   if(OldX > MaxX(CurrentItem)) /*OldX might no longer be possible if*/
      PlotCursor(MaxX(CurrentItem),OldY); /*CurrentItem was one pushed in*/
   else
      PlotCursor(OldX,OldY);
}

JumpToTop() /*Jump to top of outline*/
{
   FirstScrnItem=CurrentItem=(struct LineItem *)FirstItem;
   PrintItemList(CurrentItem,1);
   PlotCursor(MinX(CurrentItem),1);
}

JumpToBottom() /*Jump to bottom of outline*/
{
   struct LineItem *Item;
   int c;

   CurrentItem=(struct LineItem *)LastItem;
   for(c=0,Item=(struct LineItem *)LastItem;Item!=FirstItem && 
         c < SCRNHEIGHT-1;Item=(struct LineItem *)Item->PrevItem,c++);
   FirstScrnItem=(struct LineItem *)Item;
   PrintItemList(FirstScrnItem,1);
   PlotCursor(MinX(CurrentItem),DispRows);
}

PrintItemList(FrstItem,StartY) /*Prints a specified number of items*/
struct LineItem *FrstItem;
int StartY;
{
   struct LineItem *CrntItem;
   int y;
   char Buffer[3];

   PlotCursor(1,StartY);
   CrntItem=(struct LineItem *)FrstItem;
   Buffer[0]=0x9b;
   Buffer[1]=0x4a;
   WriteConsole(Buffer,2);

   for(y=StartY;y<=SCRNHEIGHT && CrntItem!=NULL;y++)
      {
      PlotCursor(1,y);
      PrintItem(CrntItem);
      ScrnBtm=(struct LineItem *)CrntItem;
      CrntItem=(struct LineItem *)CrntItem->NextItem;
      }
   DispRows=y-1;
}

PrintItem(Item)   /*Print an item at the current Y location*/
struct LineItem *Item;
{
   char ConsoleBuffer[100];

   ConsoleBuffer[0]=CSI;
   ConsoleBuffer[1]=0x4b;
   ConsoleBuffer[2]=NULL;

   GetOutlineChars(Item,ConsoleBuffer);
   strcat(ConsoleBuffer,"  ");
   strcat(ConsoleBuffer,Item->Text);
   WriteConsole(ConsoleBuffer,-1);
}

GetOutlineChars(Item,ConsoleBuffer) /*Get the outline chars (IX.,B.,etc.)*/
struct LineItem *Item;     /*for an item*/
char *ConsoleBuffer;
{
   int Level,StrLen,InsLen,c;

   Level=RealLevel(Item)-1;
   StrLen=strlen(OutLineChars[Level][(Item->ItemNumber)]);
   InsLen=PERPOS((Item->Level)-1)-StrLen;
   for(c=0;c<InsLen;c++)
      strcat(ConsoleBuffer," ");

   strcat(ConsoleBuffer,OutLineChars[Level][(Item->ItemNumber)]);
}

RedrawOutlineChars(Item,Hi,Lo)   /*Redraw the outline characters */
struct LineItem *Item;  /*(Lo <= Changed level <= Hi)*/
int Hi,Lo;
{
   char Buffer[60];
   int x=1;
   int y,Level;

   Level=Item->Level;

   for(y=CurY;y<=SCRNHEIGHT & Item != NULL ;y++)
      {
      if(Lo == Item->Level || Hi == Item->Level)
         {
         PlotCursor(x,y);
         strcpy(Buffer,"");
         GetOutlineChars(Item,Buffer);
         WriteConsole(Buffer,-1);
         }
      Item=(struct LineItem *)Item->NextItem;
      }
}

PlotCursor(x,y)      /*Plot the console cursor on the screen*/
int x,y;
{
   char Buffer[10];
   int len=1;

   Buffer[0]=CSI;
   len+=stci_d(&Buffer[1],y,9);
   Buffer[len]=0x3b;
   len+=stci_d(&Buffer[len+1],x,6);
   Buffer[len+1]=0x48;
   Buffer[len+2]=NULL;
   WriteConsole(Buffer,-1);
   CurX=x;
   CurY=y;
}

CursorUp()  /*Move the cursor up (scroll screen, etc.)*/
{
   char Buffer[20];
   int TempX;

   if(CurrentItem->PrevItem !=NULL)
      {
      if(CurrentItem==FirstScrnItem && CurrentItem != FirstItem)
         {
         strcpy(Buffer,&SpecChars[SCPos[0x0c]]);
         WriteConsole(Buffer,2);
         TempX=CurX;
         PlotCursor(1,1);
         PrintItem(CurrentItem->PrevItem);
         ++CurY;
         if(ScrnBtm==LastItem && DispRows < SCRNHEIGHT)
            ++DispRows;
         else
            ScrnBtm=(struct LineItem *)ScrnBtm->PrevItem;
         FirstScrnItem=(struct LineItem *)FirstScrnItem->PrevItem;
         CurX=TempX;
         InvY++;
         }

      if(CurX < MinX(CurrentItem->PrevItem))
         CurX=MinX(CurrentItem->PrevItem);
      else
         if(CurX > MaxX(CurrentItem->PrevItem))
            CurX=MaxX(CurrentItem->PrevItem);
      PlotCursor(CurX,CurY-1);
      CurrentItem=(struct LineItem *) CurrentItem->PrevItem;
      return(TRUE);
      }
   return(FALSE);
}

CursorDown()      /*Move the cursor down (scroll screen, etc.)*/
{
   char Buffer[20];
   int TempX;

   if(CurrentItem->NextItem != NULL)
      {
      if(CurrentItem==ScrnBtm && (CurrentItem->NextItem != NULL))
         {
         strcpy(Buffer,&SpecChars[SCPos[0x0d]]);
         WriteConsole(Buffer,2);
         TempX=CurX;
         PlotCursor(1,CurY);
         PrintItem(CurrentItem->NextItem);
         PlotCursor(TempX,CurY-1);
         ScrnBtm=(struct LineItem *)ScrnBtm->NextItem;
         FirstScrnItem=(struct LineItem *)FirstScrnItem->NextItem;
         InvY--;
         }

      if(CurX < MinX(CurrentItem->NextItem))
         CurX=MinX(CurrentItem->NextItem);
      else
         if(CurX > MaxX(CurrentItem->NextItem))
            CurX=MaxX(CurrentItem->NextItem);
      PlotCursor(CurX,CurY+1);
      CurrentItem=(struct LineItem *)CurrentItem->NextItem;
      return(TRUE);
      }
   return(FALSE);
}

InsertChar(Text,Character,Pos)   /*Insert a char into an item*/
char *Text,Character;
int Pos;
{
   int c;

   for(c=strlen(Text);c>=Pos;c--)
      Text[c+1]=Text[c];
   Text[Pos]=Character;
}

DeleteChar(Text,Pos)    /*Delete a char in an item*/
char *Text;
int Pos;
{
   int lim;

   for(lim=strlen(Text);Pos<lim;Pos++)
      Text[Pos]=Text[Pos+1];
}

/*Insert an item into the item list*/
extern struct LineItem *InsertItem(NextItem,PrevItem)
struct LineItem *NextItem,*PrevItem;
{
   struct LineItem *Item;

   Item=(struct LineItem *)AllocMem(sizeof(struct LineItem),MEMF_CLEAR);
   if(Item==NULL)
      return(NULL);

   if(NextItem != NULL)
      {
      NextItem->PrevItem=(struct LineItem *)Item;
      Item->NextItem=(struct LineItem *)NextItem;
      }

   if(PrevItem != NULL)
      {
      PrevItem->NextItem=(struct LineItem *)Item;
      Item->PrevItem=(struct LineItem *)PrevItem;
      }

   return(Item);
}

RemItem(Item)  /*Adjust the ItemNumbers for items after one is removed*/
struct LineItem *Item;
{
   int ItemNumber;
   struct LineItem *WorkingItem;

   if(Item==NULL)
      return(FALSE);
   if((WorkingItem=(struct LineItem *)FindPrev(Item))==NULL)
      ItemNumber=0;
   else
      ItemNumber=Item->ItemNumber;

   while((Item=(struct LineItem *)FindNext(Item))!=NULL)
      {
      if(ItemNumber==25)
         ItemNumber=0;
      Item->ItemNumber=ItemNumber++;
      }
}

AddItem(Item)  /*Adjust the ItemNumbers after adding an item*/
struct LineItem *Item;
{
   int ItemNumber;
   struct LineItem *CrntItem;

   if((CrntItem=(struct LineItem *)FindPrev(Item))==NULL)
      ItemNumber=0;
   else
      ItemNumber=CrntItem->ItemNumber+1;

   do
      {
      if(ItemNumber==25)
         ItemNumber=0;
      Item->ItemNumber=ItemNumber++;
      Item=(struct LineItem *)FindNext(Item);
      }
   while(Item != NULL && Item->PrevItem->Level >= Item->Level);
}

extern struct LineItem *FindPrev(Item) /*Find the previous item of*/
struct LineItem *Item;  /*the same Level as Item*/
{
   int Level;

   Level=Item->Level;
   Item=(struct LineItem *)Item->PrevItem;

   while(Item->Level > Level && Item !=NULL)
      Item=(struct LineItem *)Item->PrevItem;

   if(Item->Level < Level || Item == NULL)
      return(NULL);
   else
      return(Item);
}

extern struct LineItem *FindNext(Item) /*Find the next item of the same*/
struct LineItem *Item;     /*Level as Item*/
{
   int Level;

   Level=Item->Level;
   Item=(struct LineItem *)Item->NextItem;

   while(Item->Level > Level && Item != NULL)
      Item=(struct LineItem *)Item->NextItem;

   if(Item->Level < Level)
      return(NULL);
   else
      return(Item);
}

InsertLineAtTop() /*Insert a line at the top of the list*/
{
   struct LineItem *WorkingItem;

   WorkingItem=(struct LineItem *)
      InsertItem(FirstItem,NULL);
   WorkingItem->Level=1;

   FirstItem=(struct LineItem *)WorkingItem;
   AddItem(FirstItem);
   PlotCursor(1,1);
   RedrawOutlineChars(WorkingItem->NextItem,1,1);
   PlotCursor(MinX(CurrentItem),1);
   CursorUp();
}

WholeScreenDown() /*Scrolls down by an entire screen*/
{
   struct LineItem *item;
   UBYTE c,X,Y;

   item=(struct LineItem *)ScrnBtm;
   if(item->NextItem == NULL)   /*No lines after screen bottom?*/
      {  /*Move to last line*/
      CurrentItem=(struct LineItem *)LastItem;
      PlotCursor(MinX(CurrentItem),DispRows);
      return(FALSE);
      }
   /*Get the first screen item*/
   item=FirstScrnItem=(struct LineItem *)item->NextItem;
   for(c=1;(c < CurY) && (item->NextItem != NULL);c++)
      item=(struct LineItem *)item->NextItem; /*Get item at old Y*/

   CurrentItem=(struct LineItem *)item;  /*It is now the CurrentItem*/
   Y=c;     /*In case CurY is no longer possible (23 lines down)*/
   /*If CurX's old position is no longer possible*/
   /*move the cursor to the nearest possible position*/
   if(CurX < MinX(CurrentItem))
      X=MinX(CurrentItem);
   else
      if(CurX > MaxX(CurrentItem))
         X=MaxX(CurrentItem);
      else
         X=CurX;

   for(c=1;(c < SCRNHEIGHT-CurY) && (item->NextItem != NULL);c++)
      item=(struct LineItem *)item->NextItem; /*Find screen bottom*/
   ScrnBtm=(struct LineItem *)item;  /*Got it!*/
   DispRows=c; /*Get the number of lines*/
   PrintItemList(FirstScrnItem,1); /*Refresh the display*/
   PlotCursor(X,Y);
   return(TRUE);     /*Done!*/
}


WholeScreenUp() /*Move up one screenfull of lines*/
{
   struct LineItem *item;
   UBYTE c,X,Y;

   Y=CurY;
   item=(struct LineItem *)FirstScrnItem;
   if(item->PrevItem == NULL) /*At the top?*/
      {  /*Move cursor to first line*/
      CurrentItem=(struct LineItem *)FirstItem;
      PlotCursor(MinX(CurrentItem),1);
      return(FALSE);
      }
   for(c=0;(c<SCRNHEIGHT) & (item->PrevItem != NULL);c++)
      item=(struct LineItem *)item->PrevItem;
   FirstScrnItem=(struct LineItem *)item;
   for(c=1;(c<CurY) && (item->NextItem != NULL);c++) /*Get new CurrentItem*/
      item=(struct LineItem *)item->NextItem;
   CurrentItem=(struct LineItem *)item;

   if(CurX < MinX(CurrentItem))  /*If CurX is no longer possible*/
      X=MinX(CurrentItem);    /*get closest possible X*/
   else
      if(CurX > MaxX(CurrentItem))
         X=MaxX(CurrentItem);
      else
         X=CurX;
   for(;(c<SCRNHEIGHT) & (item->NextItem != NULL);c++)
      item=(struct LineItem *)item->NextItem; /*Get ScrnBtm*/
   ScrnBtm=(struct LineItem *)item;
   DispRows=c;    /*Number of lines*/
   PrintItemList(FirstScrnItem,1); /*Refresh display*/
   PlotCursor(X,Y);
   return(TRUE); /*Done!*/
}

OpenGraphics() /*Open screen, window, etc.*/
{
   /*Open screen (640x200x2)*/
   NewWindow.Screen=Screen=(struct Screen *)OpenScreen(&NewScreen);
   if(Screen==NULL)
      exit(40);

   /*Open backdrop window & make requestors open onto it*/
   if((ThisProc->pr_WindowPtr=Window=
         (struct Window *)OpenWindow(&NewWindow))==NULL)
      {
      CloseScreen(Screen);
      exit(50);
      }
   /*Install menus*/
   SetMenuStrip(Window,&Menu1);

   /*Setup the console.device*/
   consoleWritePort=(struct Port *)CreatePort("mycon.write",0);
   if(consoleWritePort==NULL)
      Leave(60,"Couldn't open console.device!");

   consoleWriteMsg=(struct IOStdReq *)CreateStdIO(consoleWritePort);
   if(consoleWriteMsg==NULL)
      Leave(70,"Couldn't open console.device!");

   consoleReadPort=(struct Port *)CreatePort("mycon.read",0);
   if(consoleReadPort==NULL)
      exit(60);

   consoleReadMsg=(struct IOStdReq *)CreateStdIO(consoleReadPort);
   if(consoleReadMsg==NULL)
      exit(70);

   consoleWriteMsg->io_Data=(struct Window *)Window;  /*Attach console*/
   consoleWriteMsg->io_Length=sizeof(struct Window);  /*device to window...*/
   OpenDevice("console.device",0,consoleWriteMsg,0); /*& open it*/

   ConsoleDevice=consoleReadMsg->io_Device=(APTR)consoleWriteMsg->io_Device;
   consoleReadMsg->io_Unit=consoleWriteMsg->io_Unit;
   consoleReadMsg->io_Command=CD_ASKKEYMAP;
   consoleReadMsg->io_Length=sizeof(struct KeyMap);
   consoleReadMsg->io_Data=&keymap;
   DoIO(consoleReadMsg);
   TitleErrorCancel();
}

CloseGraphics()   /*Close the window, screen, etc.*/
{
   CloseDevice(consoleWriteMsg);
   DeleteStdIO(consoleWriteMsg);
   DeleteStdIO(consoleReadMsg);
   DeletePort(consoleWritePort);
   DeletePort(consoleReadPort);

   ClearMenuStrip(Window);
   CloseWindow(Window);
   CloseScreen(Screen);
}

