#include "Globals.h"
char InvBuf[]=
   {CSI,'7',0x6d,NULL,CSI,'0',0x6d};
char Inv[]=
   {CSI,'7',0x6d,NULL};
char Norm[]=
   {CSI,'0',0x6d,NULL};

struct LineItem *InvItem;

void MovedMouse(x,y)   /*The mouse was moved! Record its new position*/
int x,y;
{
   LastX=PtrX;
   LastY=PtrY;

   PtrY=(prefs.DS) ? (y/16)+1 : (y/8)+1;
   PtrX=(x/8)+1;
}

HandleButton(Seconds,Micros)  /*Handle a button press*/
int Seconds,Micros;
{
   int status;
   static int LastSeconds=1,LastMicros=1,DoubClicked=FALSE;
   static UBYTE ButtonX,ButtonY;

   /*If the user double-clicked (the mouse button), start or modify*/
   /*a highlighted block*/
   if(DoubleClick(LastSeconds,LastMicros,Seconds,Micros) &&
         PtrY==ButtonY && !DoubClicked)
   {
      PtrX=ButtonX;
      PtrY=ButtonY;
      DoubClicked=TRUE;
      HandleInvs();
      BLastX=PtrX;
   }
   else  /*Otherwise, just move the cursor accordingly*/
      if(PtrY > 0 && PtrY <= SCRNHEIGHT)
         {
         ButtonX=PtrX;
         ButtonY=PtrY;
         DoubClicked=FALSE;
         if(PtrY != InvY && InvsMode > NOINV) /*End text highlighting*/
            EndLineInvs(); /*if the cursor is moved to a new line*/

         if(PtrY-CurY < 0)    /*move the cursor to a new line*/
            status=MoveBack(-(PtrY-CurY));
         else
            status=MoveForward(PtrY-CurY);
         if(status==FALSE)
            return(FALSE);

         if(PtrX > MaxX(CurrentItem)) /*Move the cursor to a new*/
            CurX=MaxX(CurrentItem);    /*X position*/
         else
            if(PtrX < MinX(CurrentItem))
               CurX=MinX(CurrentItem);
            else
               CurX=PtrX;
         PlotCursor(CurX,PtrY);
         }
   LastSeconds=Seconds;
   LastMicros=Micros;
}

void HandleInvs() /*Handle a modification or creation of a highlighted block*/
{
   if(InvsMode==NOINV)
      if(PtrX >= MinX(CurrentItem) && PtrX <= MaxX(CurrentItem))
         HandleLineInvs();
      else
         HandleBlockInvs();
   else
      if(InvsMode > NOINV)
         HandleLineInvs(); /*Highlighting on one line*/
      else
         HandleBlockInvs();   /*Block highlighting*/
}

HandleBlockInvs() /*Handle the inverse of a block of lines*/
{
   int X,Y;

   if(InvsMode==NOINV) /*If nothing is highlighted*/
      {
      InvY=EndIY=CurY;
      WriteConsole(Inv,-1);
      PlotCursor(1,CurY);
      PrintItem(CurrentItem);
      WriteConsole(Norm,-1);
      EndIItem=StartIItem=(struct LineItem *)CurrentItem;
      PlotCursor(MinX(CurrentItem),CurY);
      InvsMode=BLOCK_PENDING;
      return(TRUE);
      }

   if(InvsMode==BLOCK_PENDING) /*If one line is highlighted*/
      {
      if(StartIItem == CurrentItem) /*Same line?  End highlighting*/
         {
         EndBlockInvs();
         return(TRUE);
         }
      else /*Else, highlight a block (up or down, depending)*/
         if(IsAfter(CurrentItem,StartIItem)) /*down*/
         {
            EndIItem=(struct LineItem *)CurrentItem;
            if((Y=IsOnScreen(StartIItem->NextItem)))
            {
               PlotCursor(1,Y);
               RvsBlock(StartIItem->NextItem,EndIItem);
            }
            else
            {
               PlotCursor(1,1);
               RvsBlock(FirstScrnItem,EndIItem);
            }
            EndIY=CurY=PtrY;
            InvsMode=BLOCK_DOWN;
            PlotCursor(MinX(EndIItem),CurY);
         }
         else /*up*/
            if(IsBefore(CurrentItem,StartIItem))
               {
               EndIItem=(struct LineItem *)CurrentItem;
               EndIY=CurY;
               if( (Y=IsOnScreen(StartIItem->PrevItem)) )
                  RvsBlock(EndIItem,StartIItem->PrevItem);
               else
                  RvsBlock(EndIItem,ScrnBtm);
               PlotCursor(MinX(EndIItem),EndIY);
               InvsMode=BLOCK_UP;
               }
         return(TRUE);
      }

   if(InvsMode == BLOCK_DOWN) /*If highlighting is down*/
   {
      if((IsAfter(CurrentItem,EndIItem)) && CurrentItem != EndIItem)
      {  /*Highlight more*/
         if( (Y=IsOnScreen(EndIItem->NextItem)))
         {
            PlotCursor(1,Y);
            RvsBlock(EndIItem->NextItem,CurrentItem);
         }
         else
         {
            PlotCursor(1,1);
            RvsBlock(FirstScrnItem,CurrentItem);
         }
         EndIY=CurY=PtrY;
         EndIItem=(struct LineItem *)CurrentItem;
         PlotCursor(MinX(EndIItem),IsOnScreen(CurrentItem));
      }
      if((IsBefore(CurrentItem,EndIItem)) && CurrentItem != EndIItem)
      {  /*Unhighlight some*/
         if(IsBefore(CurrentItem,StartIItem))
         {
            EndBlockInvs(FALSE);
            return(TRUE);
         }
         EndIY=Y=CurY;
         X=CurX;
         PlotCursor(CurX,CurY+1);
         NormBlock(CurrentItem->NextItem,EndIItem);
         PlotCursor(X,Y);
         EndIItem=(struct LineItem *)CurrentItem;
      }
   }
   else  /*The higlighting goes up*/
   {
      if((IsBefore(CurrentItem,EndIItem)) && CurrentItem != EndIItem)
      {     /*Highlight*/
         EndIY=Y=CurY;
         if( (Y=IsOnScreen(EndIItem->PrevItem)) )
            RvsBlock(CurrentItem,EndIItem->PrevItem);
         else
            RvsBlock(CurrentItem,ScrnBtm);

         PlotCursor(MinX(CurrentItem),IsOnScreen(CurrentItem));
         EndIItem=(struct LineItem *)CurrentItem;
      }
            /*Normalize*/
      if((IsAfter(CurrentItem,EndIItem)) && CurrentItem != EndIItem)
      {
         if((CurrentItem==StartIItem)||(IsAfter(CurrentItem,StartIItem)))
         {
            EndBlockInvs();
            return(TRUE);
         }
         Y=CurY;
         PlotCursor(CurX,EndIY);
         NormBlock(EndIItem,CurrentItem->PrevItem);
         EndIItem=(struct LineItem *)CurrentItem;
         PlotCursor(MinX(CurrentItem),IsOnScreen(CurrentItem));
         EndIY=CurY;
      }
   }
}

void NormIt(Start,End,Start_y) /*Un-highlight an area on screen*/
struct LineItem *Start,*End;
int Start_y;
{
   struct LineItem *CurItem;

   CurItem=(struct LineItem *)Start;
   WriteConsole(Norm,-1);
   
   PlotCursor(1,Start_y);
   CurY--;
   while(CurItem != End->NextItem) /*Go down the screen*/
      {
      PlotCursor(1,CurY+1);
      PrintItem(CurItem);
      CurItem=(struct LineItem *)CurItem->NextItem;
      }
}

void NormBlock(Start,End)  /*Unhighlight a block of text, keeping it to*/
struct LineItem *Start,*End;         /*what is on screen currently*/
{
   int beg_y,end_y,old_y,old_x;
   
   old_y=CurY;
   old_x=CurX;
   
   if((beg_y=IsOnScreen(Start))) /*If 'Start' is on the screen*/
      if((end_y=IsOnScreen(End)))   /*If 'End' is on screen*/
         NormIt(Start,End,beg_y);   /*Normalize a block in the normal way*/
      else
         NormIt(Start,ScrnBtm,beg_y);/*Normalize from 'Start' to end of screen*/
   else
      if(IsBefore(Start,FirstScrnItem)) /*If 'Start' is above the screen*/
         if(IsAfter(End,ScrnBtm))      /*and 'End' is below it*/
            NormIt(FirstScrnItem,ScrnBtm,1); /*Unhighlight the whole screen*/
         else
            if((IsOnScreen(End)))  /*If the end is on the screen*/
               NormIt(FirstScrnItem,End,1);  /*Unhighlight from top to End*/
   /*That's it (any other possibilities cause nothing to happen)*/

   PlotCursor(old_x,old_y);
}


void RvsBlock(Start,End)  /*Highlight a block*/
struct LineItem *Start,*End;
{
   struct LineItem *CurItem;
   int y;

   CurItem=(struct LineItem *)Start;   /*Start at the start*/
   WriteConsole(Inv,-1);   /*Activate inversed text*/

   CurY--;
   while(CurItem != End->NextItem)  /*Go through list*/
   {
      if(y=IsOnScreen(CurItem)) /*Only do lines on screen*/
      {
         PlotCursor(1,y);
         PrintItem(CurItem);
      }
      CurItem=(struct LineItem *)CurItem->NextItem;
   }
   WriteConsole(Norm,-1);
}

void EndBlockInvs() /*End the highlight of a block of lines*/
{
   struct WhichOne which;
   
   FindStartEnd(StartIItem,EndIItem,&which);
   NormBlock(which.Start,which.End);
   PlotCursor(MinX(CurrentItem),CurY);
   InvsMode=NOINV;
}

HandleLineInvs()  /*Handle the inverse of individual characters*/
{
   int NewEnd;

   if(PtrX < MinX(CurrentItem))
      PtrX=MinX(CurrentItem);
   else
      if(PtrX > MaxX(CurrentItem))
         PtrX=MaxX(CurrentItem);

   if(InvsMode==NOINV)  /*If nothing is highlighted, start highlighting*/
      {
      InvBuf[3]=CurrentItem->Text[PosInText(CurrentItem->Level)];
      WriteConsole(InvBuf,7);
      StartChar=EndChar=PosInText(CurrentItem->Level);
      PlotCursor(CurX,CurY);
      InvsMode=LINE_PENDING;
      InvY=CurY;
      InvItem=(struct LineItem *)CurrentItem;
      return(TRUE);
      }

   if(InvsMode==LINE_PENDING) /*If one character only*/
      {
      if(PtrX == BLastX)   /*Same character?  Cancel highlighting*/
         {
         EndLineInvs();
         return(TRUE);
         }

      if(PtrX > BLastX) /*After the character*/
      {
         EndChar=PosInText(InvItem->Level);
         PlotCursor(BLastX,CurY);
         RvsText(StartChar,EndChar);
         CurX=CurX+EndChar-StartChar;
         InvsMode=LINE_FWD;
         PlotCursor(CurX,CurY);
         return(TRUE);
      }

      if(PtrX < BLastX) /*Before the character*/
      {
         EndChar=PosInText(InvItem->Level);
         RvsText(EndChar,StartChar-1);
         PlotCursor(PtrX,CurY);
         InvsMode=LINE_BACK;
      }
      return(TRUE);
      }
   else
      if(InvsMode==LINE_FWD)  /*Left to right highlighting*/
         {
         if(PtrX > BLastX) /*Forward (highlight more)*/
            {
            NewEnd=PtrX-MinX(InvItem);
            PlotCursor(EndChar+MinX(InvItem),CurY);
            RvsText(EndChar,NewEnd);
            CurX=NewEnd+MinX(InvItem);
            PlotCursor(CurX,CurY);
            EndChar=NewEnd;
            }

         if(PtrX < BLastX) /*Backward (un-highlight)*/
            if(PtrX-MinX(InvItem)<=StartChar)
               EndLineInvs();
            else
               {
               NewEnd=PtrX-MinX(InvItem);
               NormText(NewEnd+1,EndChar);
               PlotCursor(CurX,CurY);
               EndChar=NewEnd;
               }
         }
      else  /*Right to left highlighting*/
         {
         if(PtrX < BLastX) /*Backward (highlight more)*/
            {
            NewEnd=PtrX-MinX(InvItem);
            RvsText(NewEnd+1,EndChar);
            PlotCursor(CurX,CurY);
            EndChar=NewEnd;
            }

         if(PtrX > BLastX) /*Forward (un-highlight some)*/
            if(PtrX-MinX(InvItem)>=StartChar)
               EndLineInvs();
            else
               {
               NewEnd=PtrX-MinX(InvItem)-1;
               PlotCursor(EndChar+MinX(InvItem),CurY);
               NormText(EndChar,NewEnd);
               CurX=NewEnd+MinX(InvItem);
               EndChar=NewEnd;
               PlotCursor(CurX+1,CurY);
               }
         }
}

void RvsText(Start,End)   /*Highlight a block of text*/
int Start,End;
{
   char Buffer[80];
   int c,l;

   strcpy(Buffer,Inv);
   l=strlen(Buffer);

   for(c=l; c-l < End-Start+1 ; c++)
      {
      Buffer[c]=InvItem->Text[c+Start-l];
      }
   Buffer[c]=0;

   strcat(Buffer,Norm);

   WriteConsole(Inv,-1);
   WriteConsole(Buffer,c);
   WriteConsole(Norm,-1);
}


MoveBack(Number)  /*Move back in the item list a specified number of times*/
int Number;
{
   int c;

   for(c=0;c<Number;c++)
      {
      if(CurrentItem->PrevItem==NULL)
         return(FALSE);
      else
         CurrentItem=(struct LineItem *)CurrentItem->PrevItem;
      }
   return(TRUE);
}

MoveForward(Number)  /*Move forward in the item list a specifiecd*/
int Number;       /*number of times*/
{
   int c;
   struct LineItem *OrigItem;

   OrigItem=(struct LineItem *)CurrentItem;
   for(c=0;c<Number;c++)
      {
      if(CurrentItem->NextItem==NULL)
         {
         CurrentItem=(struct LineItem *)OrigItem;
         return(FALSE);
         }
      else
         CurrentItem=(struct LineItem *)CurrentItem->NextItem;
      }
   return(TRUE);
}

void EndLineInvs()  /*End text inverse*/
{
   if(StartChar < EndChar)
      NormText(StartChar,EndChar);
   else
      NormText(EndChar,StartChar);
   InvsMode=NOINV;
}

void NormText(Start,End)     /*Un-reverse text*/
int Start,End;
{
   int TempX,c;
   char Buffer[80];

   for(c=Start;c<=End;c++)
      Buffer[c-Start]=InvItem->Text[c];

   Buffer[c-Start]=0;

   TempX=CurX;
   PlotCursor(MinX(InvItem)+Start,InvY);
   WriteConsole(Buffer,-1);
   PlotCursor(TempX,InvY);
}

HandleDelBlock()  /*Delete a block of lines*/
{
   struct WhichOne which;
   struct LineItem *temp;
   BOOL stat;

         /*If you're deleting everything, just call New*/
   if((StartIItem->PrevItem==NULL && EndIItem->NextItem==NULL) ||
         (StartIItem->NextItem==NULL && EndIItem->PrevItem==NULL))
      {
      NewAll();
      return(TRUE);
      }

      /*Otherwise, find the true start and end and delete the block*/
   FindStartEnd(StartIItem,EndIItem,&which);
   
   if(which.Start->NextItem != NULL && which.Start->NextItem->cont &&
         !which.Start->cont && which.Start==which.End) /*TRUE==parent*/
      return(FALSE);
      
   InvsMode=NOINV;
   
      /*Find the end (last child)*/
   if(which.End->NextItem->cont && (which.Start!=which.End))
   {
      temp=(struct LineItem *)which.End->PrevItem;
      stat=FALSE;
      
      while(temp!=which.Start)
      {
         if(!temp->cont)
            stat=TRUE;
         temp=(struct LineItem *)temp->PrevItem;
      }
      if(stat)
         which.End=(struct LineItem *)FindNextNonCont(which.End);
   }
   
   DelBlock(which.Start,which.End,IsOnScreen(which.Start),
         IsOnScreen(which.End));
   return(TRUE);
}

/*When dealing with highlighted blocks, it is not guaranteed that*/
/*StartIItem comes before EndIItem in the linked list.  This function*/
/*sorts out which is which and puts each pointer in an appropriate*/
/*space in a WhichOne structure (created especially for this function)*/
/*A kludge, I know.  But 'Liner's grown up around this technique.  It*/
/*would take more work than it is worth to change it*/

void FindStartEnd(Start,End,which)
struct LineItem *Start,*End;
struct WhichOne *which;
{  
   if(IsBefore(Start,End))
   {
      which->Start=(struct LineItem *)Start;
      which->End=(struct LineItem *)End;
   }
   else
   {
      which->Start=(struct LineItem *)End;
      which->End=(struct LineItem *)Start;
   }
}

void DelBlock(Start,End,StartY,EndY)  /*Delete a block of lines*/
struct LineItem *Start,*End;
int StartY,EndY;
{
   int TOS;
   
   if(!Start->cont)
   {
      Start=(struct LineItem *)FindPrevNonCont(Start);
      End=(struct LineItem *)FindNextNonCont(End);
   }
   
   StartY=IsOnScreen(Start);
   EndY=IsOnScreen(End);
   
   if(!StartY)        /*If the start of the inversed block isn't on*/
   {                       /*the screen, put it on the screen and then delete*/
      FirstScrnItem=CurrentItem=(struct LineItem *)Start;
      PrintItemList(Start,(StartY=1));
   }

   if(!EndY)      /*Handle when the end isn't on screen*/
      EndY=SCRNHEIGHT;

   if(Start==FirstScrnItem)
      TOS=TRUE;
   else
      TOS=FALSE;
   
   if(Start->PrevItem == NULL) /*If there is nothing before 'Start'*/
      {  /*Make 'End' the start of everything*/
      CurrentItem=FirstItem=FirstScrnItem=
            (struct LineItem *)End->NextItem;
      CurrentItem->PrevItem=NULL;
      RemItem(CurrentItem);
      AddItem(CurrentItem);
      TOS=FALSE;
      }
   else
      if(End->NextItem==NULL) /*If 'End' is the end of everything, make*/
         {     /*'Start'->PrevItem the start of everything*/
         CurrentItem=LastItem=
               (struct LineItem *)Start->PrevItem;
         LastItem->NextItem=NULL;
         if(Start==FirstScrnItem)
            {
            FirstScrnItem=ScrnBtm=(struct LineItem *)CurrentItem;
            StartY=1;
            }
         else
            StartY--;
         TOS=FALSE;
         }
      else
         {
         CurrentItem=Start->PrevItem->NextItem=(struct LineItem *)
               End->NextItem;
         End->NextItem->PrevItem=
               (struct LineItem *)Start->PrevItem;
         }
         
   if(TOS)  /*If the Start was at the top of the screen...*/
      CurrentItem=FirstScrnItem=(struct LineItem *)End->NextItem;

   /*Free the deleted block's memory*/
   FreeListMem(Start,End);
   /*Update some ItemNumbers*/
   RemItem(CurrentItem);
   AddItem(CurrentItem);
   
   BackSearchRefresh(CurrentItem);
   PrintItemList(CurrentItem,StartY);
   PlotCursor(MinX(CurrentItem),StartY);
}

void FreeListMem(Start,End)  /*Free several Items at once*/
struct LineItem *Start,*End;
{
   struct LineItem *Item,*Next;
   Next=(struct LineItem *)Start;
   do
      {
      Item=(struct LineItem *)Next;
      Next=(struct LineItem *)Item->NextItem;
      RemItem(Item);
     
      if(Item->PrevItem !=NULL)
         Item->PrevItem->NextItem=(struct LineItem *)Item->NextItem;
      if(Item->NextItem != NULL)
         Item->NextItem->PrevItem=(struct LineItem *)Item->PrevItem;
         
      FreeMem(Item,sizeof(struct LineItem));
      }
   while(Item != End && Next != NULL);
}

void DelTextBlock() /*Delete the highlighted text*/
{
   int c,Start,End,len;
   char Buffer[2];

   if(StartChar < EndChar) /*Find the start of the highlighted block*/
      {
      Start=StartChar;
      End=EndChar;
      }
   else
      {
      Start=EndChar;
      End=StartChar;
      }
   len=strlen(InvItem->Text);

   for(c=0;c <= len-End; c++)
      InvItem->Text[c+Start]=InvItem->Text[c+End+1];

   PlotCursor(1,InvY);
   Buffer[0]=CSI;
   Buffer[1]=0x4b;
   WriteConsole(Buffer,2);
   PrintItem(InvItem);
   PlotCursor(MinX(InvItem)+Start,InvY);
   InvsMode=NOINV;
}

CancelLineInvs()  /*Cancel an inversed text section*/
{
   if(NOINV == InvsMode)
      return(FALSE);
   else
      if(InvsMode > NOINV)
         EndLineInvs();
}

CancelInvs()  /*Cancel inversed text or lines*/
{
   if(NOINV == InvsMode)
      return(FALSE);
   else
      if(InvsMode > NOINV)
         EndLineInvs();
      else
         EndBlockInvs();

   return(TRUE);
}

DelInvs()   /*Delete the highlighted block or text*/
{
   if(InvsMode==NULL)
      return(FALSE);

   if(InvsMode > NOINV)
      DelTextBlock();
   else
      HandleDelBlock();

   return(TRUE);
}

void TitleError(string) /*Display 'string' in the title bar*/
char *string;
{
   DisplayBeep(Screen);
   ErrorInTitle = TRUE;
   SetWindowTitles(Window,-1,string);
}

void TitleErrorCancel() /*Set the title bar text to normal (with the */
{                          /*current filename in the title bar*/
   static char Title[85];

   strcpy(Title,ScreenTitle);
   if(FileName[0])
      strncat(Title,FileName,65);
   else
      strcat(Title,"Untitled");
   if(Modified==TRUE)   /*If the outline has been modified*/
      Title[0]='*';        /*Put a '*' before "'Liner"*/
   ErrorInTitle = FALSE;
   SetWindowTitles(Window,-1,Title);
}

void BackSearchRefresh(Base)    /*Do a refresh of ItemNumbers by searching*/
struct LineItem *Base;    /*back through the item list*/
{
   struct LineItem *Work,Dummy;

   Dummy.PrevItem=(struct LineItem *)Base->PrevItem;
   Dummy.NextItem=(struct LineItem *)Base->NextItem;

   for(Dummy.Level=Base->Level;Dummy.Level > 0;Dummy.Level--)
      if((Work=(struct LineItem *)FindPrev(&Dummy))!=NULL)
         {
         RemItem(Work);
         AddItem(Work);
         }
}

/*End of mouse.c*/
