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

extern char Inv[4];
extern char Norm[4];


struct LineItem *InvItem;

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

   PtrY=(y/(Screen->Font->ta_YSize))+1;
   PtrX=(x/FontWidth)+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();
	    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,NULL,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,NULL,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*/
