/* ==========================================================================
**
**                         ListBrowser.c
**
** ©1991 WILLISoft
**
** ==========================================================================
*/

#include <string.h>
#include "minmax.h"
#include "ListBrowser.h"
#include "ListBrowserClass.h"
#include "EmbossedGadgetClass.h"
#include "precognition.h"
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include "amigamem.h"

#define CHAR_WIDTH  8   /* Width  of Topaz 80 font.*/
#define CHAR_HEIGHT 9   /* Height of Topaz 80 font.*/
#define BROWSER_ENTRY_HEIGHT (CHAR_HEIGHT + 1)
#define BORDER_WIDTH 3
#define BASELINE     8

#define isSelected(q) (q & ENTRY_SELECTED)

const StringList *ListBrowser_StringList_of( ListBrowser *self )
{
   return &self->List;
}

void ListBrowser_Init( ListBrowser   *self,
                       PIXELS         LeftEdge,
                       PIXELS         TopEdge,
                       PIXELS         Width,
                       PIXELS         Height,
                       pcg_3DPens     Pens,
                       BOOL           SelectMany )
{

   EmbossedGadget_Init( &self->eg, LeftEdge, TopEdge, 0, 0,
      GADGHNONE, RELVERIFY, BOOLGADGET, Pens, NULL );

   self->eg.isa       = ListBrowserClass();
   self->SelectMany   = SelectMany;
   self->YOffset      = 0;
   SetLocation( self, LeftEdge, TopEdge );
   SetSize( self, Width, Height );

   /* Initialize List structure... */
   StringList_Init( &self->List, TRUE );

}

void ListBrowser_CleanUp( ListBrowser *self )
{
   StringList *sl;

   StringList_CleanUp( StringList_of(self) );
   EmbossedGadget_CleanUp( self );
}

Point ListBrowser_AskSize( ListBrowser *self,
                           PIXELS       Width,
                           PIXELS       Height )
{
   Point size;
   short nCols, nRows;

   nCols = ( Width-BORDER_WIDTH*2  ) / CHAR_WIDTH;
   nRows = ( Height-BORDER_WIDTH*2 ) / BROWSER_ENTRY_HEIGHT;

   nCols = MAX( nCols, 5 );
   nRows = MAX( nRows, 5 );

   size.x = nCols*CHAR_WIDTH           + BORDER_WIDTH*2;
   size.y = nRows*BROWSER_ENTRY_HEIGHT + BORDER_WIDTH*2;

   return size;
}


Point ListBrowser_SetSize( ListBrowser *self,
                            PIXELS       Width,
                            PIXELS       Height )
{
   Point size;

   size = AskSize( self, Width, Height );

   Forbid(); /* Don't want Intuition looking at these while we modify em. */
   self->eg.Size     = size;
   self->eg.g.Width  = size.x - BORDER_WIDTH*2;
   self->eg.g.Height = size.y - BORDER_WIDTH*2;

   pcg_Init3DBox(  self->eg.BoxBorder,
                   -BORDER_WIDTH, -BORDER_WIDTH,
                   size.x, size.y,
                   self->eg.Pens.BrightPen,  self->eg.Pens.DarkPen,
                   self->eg.BoxBorder->BottomRight.NextBorder );
   Permit();


   self->nColumns = ( size.x-BORDER_WIDTH*2 ) / CHAR_WIDTH;
   self->nRows    = ( size.y-BORDER_WIDTH*2 ) / BROWSER_ENTRY_HEIGHT;

   return size;
}

Point ListBrowser_SetLocation( ListBrowser *self,
                               PIXELS       LeftEdge,
                               PIXELS       TopEdge )
{
   Point location;

   Forbid();
   self->eg.Location.x = LeftEdge;
   self->eg.Location.y = TopEdge;
   self->eg.g.LeftEdge = LeftEdge + BORDER_WIDTH;
   self->eg.g.TopEdge  = TopEdge  + BORDER_WIDTH;
   Permit();


   return location;
}

USHORT ListBrowser_SetYOffset( ListBrowser *self,
                               USHORT       YOffset )
{
   SHORT MaxY;

   MaxY          = MAX( self->List.nEntries-self->nRows, 0 );
   self->YOffset = MIN( YOffset, MaxY );

   return self->YOffset;
}



#define DESELECT_ALL(b) ListBrowser_SelectAll(b, FALSE)

void ListBrowser_SelectAll( ListBrowser *self, BOOL Select )
/* Selects or deselects everything in the list. */
{
   USHORT i;

   for (i=0; i<self->List.nEntries; i++)
   {
      if (Select)
         self->List.Qualifiers[i] |= ENTRY_SELECTED;
      else
         self->List.Qualifiers[i] &= (UBYTE) ~ENTRY_SELECTED;
   }

}

void ListBrowser_SelectString( ListBrowser  *self,
                               USHORT        i,
                               BOOL          Select   )
{
   if ((! self->SelectMany) && (Select))
      DESELECT_ALL( self );

   if (Select)
      self->List.Qualifiers[i] |= ENTRY_SELECTED;
   else
      self->List.Qualifiers[i] &= ~ENTRY_SELECTED;
}

void ListBrowser_DrawEntry( ListBrowser    *self,
                            RastPort       *RPort,
                            USHORT          index )
{
   USHORT count, X, Y, Xend, Yend;
   UBYTE qual, succ, pred;
   char **Entries;
   UBYTE *Qualifiers;
   pcg_3DPens pens;

   Entries    = self->List.Entries;
   Qualifiers = self->List.Qualifiers;

   X      = self->eg.Location.x + BORDER_WIDTH;
   Y      = self->eg.Location.y + BORDER_WIDTH
            + (index-self->YOffset)*BROWSER_ENTRY_HEIGHT;
   Xend   = X + self->eg.g.Width-1;
   Yend   = Y + BROWSER_ENTRY_HEIGHT;
   pens   = self->eg.Pens;

   SetDrMd( RPort, JAM1 );
   /* Clear the entry. */
   SetAPen( RPort, pens.BackPen );
   RectFill( RPort, X, Y, Xend, Yend );


   qual = Qualifiers[index];
   if (index)
      pred = Qualifiers[index-1];
   else
      pred = 0;

   if (index+1 < self->List.nEntries)
      succ = Qualifiers[index+1];
   else
      succ = 0;


   if (qual & ENTRY_SPECIAL)
      SetAPen( RPort, pens.FrontPen+1 );
   else
      SetAPen( RPort, pens.FrontPen );

   Move( RPort, X+1, Y+BASELINE );
   count = strlen( Entries[index] );
   count = MIN( self->nColumns, count ); 
   Text( RPort, Entries[index], count );


   if (isSelected(qual))
   {
      SetDrMd( RPort, COMPLEMENT );

      /* Draw 3D lines. */
      RectFill( RPort, X, Y, Xend, Yend );
      SetDrMd(  RPort, JAM1 );
      SetAPen( RPort, pens.DarkPen );
      Move( RPort, X, Yend );
      Draw( RPort, X, Y );

      SetAPen( RPort, pens.BrightPen ); /* Right side bar. */
      Move( RPort, Xend, Y );
      Draw( RPort, Xend, Yend );
   }

   /* Draw top bar. */
   if (( (!isSelected(pred)) && isSelected(qual)))
         /* previous is not selected, entry is  */
   {
      SetAPen( RPort, pens.DarkPen );
      Move( RPort, X,    Y );
      Draw( RPort, Xend, Y );
   }
   else if (isSelected(pred) && (!isSelected(qual)))
      /* previous is selected, entry is not */
   {
      SetAPen( RPort, pens.BrightPen );
      Move( RPort, X,    Y );
      Draw( RPort, Xend, Y );
   }

   /* Draw bottom bar */
   if ((!isSelected(succ)) && isSelected(qual))
      /* successor is not selected, entry is  */
   {
      SetAPen( RPort, pens.BrightPen );
      Move( RPort, X,    Yend );
      Draw( RPort, Xend, Yend );
   }
   else if (isSelected(succ) && (!isSelected(qual)))
      /* successor is selected, entry is not */
   {
      SetAPen( RPort, pens.DarkPen );
      Move( RPort, X,    Yend );
      Draw( RPort, Xend, Yend );
   }
}


void ListBrowser_Render( ListBrowser *self,
                         RastPort    *RPort )
{
   USHORT        X, Y, Xend, Yend;
   USHORT        i, j;

   X      = self->eg.g.LeftEdge;/* + BORDER_WIDTH;*/
   Y      = self->eg.g.TopEdge; /*  + BORDER_WIDTH;*/
   Xend   = X + self->eg.g.Width-1;
   Yend   = Y + self->eg.g.Height-1;


   DrawBorder( RPort, self->eg.BoxBorder, self->eg.g.LeftEdge, self->eg.g.TopEdge );

   /* Clear the display area. */
   SetDrMd( RPort, JAM1 );
   SetAPen( RPort, self->eg.Pens.BackPen );
   RectFill( RPort, X, Y, Xend, Yend );


   /* Display the list */
   for( j=0, i=self->YOffset; j<self->nRows; j++, i++)
   {
      if (i>=self->List.nEntries) break;
      ListBrowser_DrawEntry( self, RPort, i );
   }
}


void ListBrowser_ChangeHighlight( ListBrowser *self,
                                  USHORT       new_highlighted_entry, /* index */
                                  RastPort    *RPort )
{
   USHORT        i, j;
   UBYTE  *Qualifiers;


   /* Display the list */
   Qualifiers = self->List.Qualifiers;

   /* Deselect the old one. */
   i = self->YOffset;
   for (j=0; j<self->nRows; j++, i++)
   {
      if (i >= self->List.nEntries) break;

      if (isSelected(Qualifiers[i]) )
      {
         /* If this is the old highlight */
         Qualifiers[i] &= ~ENTRY_SELECTED;
         ListBrowser_DrawEntry( self, RPort, i );
      }
   }

   Qualifiers[new_highlighted_entry] |= ENTRY_SELECTED;

   if ((new_highlighted_entry - self->YOffset >= 0) &&
       (new_highlighted_entry - self->YOffset < self->nRows))
      ListBrowser_DrawEntry( self, RPort, new_highlighted_entry );

}


void ListBrowser_Refresh( ListBrowser *self )
{
   struct pcgWindow *window;

   window = InteractorWindow( self );
   Render( self, window->Window->RPort );
}



void ListBrowser_AddString( ListBrowser    *self,
                            char           *entry,
                            UBYTE           qualifier )
{
   if ((! self->SelectMany) && isSelected(qualifier))
      DESELECT_ALL( self );
      /* This new entry is selected, so all others must be deselected. */

   StringList_AddString( &self->List, entry, qualifier );
}


BOOL ListBrowser_DeleteString( ListBrowser *self, USHORT i )
{
   BOOL result;
   StringList *sl;
   
/*DUMPWAIT("ListBrowser_DeleteString\n");*/

   sl = StringList_of(self);
   
   result = StringList_DeleteString( sl, i );

   ListBrowser_SetYOffset( self, self->YOffset );

/*DUMPWAIT("end ListBrowser_DeleteString\n");*/

   return result;

}

BOOL ListBrowser_DeleteAllStrings( ListBrowser *self )
{
   BOOL result;

   result = StringList_DeleteAllStrings( &self->List );
   ListBrowser_SetYOffset( self, self->YOffset );

   return result;
}



#define CLAIM_EVENT(b,e) (&b->eg.g == (Gadget *) e->IAddress)

USHORT ListBrowser_Respond( ListBrowser  *self,
                            IntuiMessage *Event )
{
   Point        Mouse;
   USHORT       entry_no;
   USHORT       response = 0;
   UBYTE        qual;

   switch (Event->Class)
   {
      case REFRESHWINDOW:
         ListBrowser_Render( self, Event->IDCMPWindow->RPort );
         response = RESPONDED;
         break;

      case GADGETUP:
         if (CLAIM_EVENT(self, Event))
         {
            /* Translate into Gadget-Relative Coordinates. */
            GadgetRelativeCoords( &self->eg.g, Event, &Mouse );
            entry_no = (Mouse.y - BORDER_WIDTH) / BROWSER_ENTRY_HEIGHT
                        + self->YOffset;
            entry_no = MAX( entry_no, 0 );

            qual = self->List.Qualifiers[entry_no];

            if ( entry_no < self->List.nEntries )
            {
               if (self->SelectMany)
               {
                  SelectString( self, entry_no, !isSelected(qual) );
                  ListBrowser_DrawEntry( self,
                     Event->IDCMPWindow->RPort, entry_no );
               }
               else
               {
                  if (!isSelected(qual)) /* Only select if not already.  */
                  {                      /* (Avoids ugly screen update.) */
                     ListBrowser_ChangeHighlight( self, entry_no,
                           Event->IDCMPWindow->RPort );
                  }
               }
            }
            response = RESPONDED | CHANGED_STATE | CONSUMED_EVENT;
         }
         break;

   }
   return response;
}



BOOL ListBrowser_elaborated = FALSE;

struct StringListerClass ListBrowser_Class;

void ListBrowserClass_Init( struct StringListerClass *class )
{

   StringListerClass_Init( class );
   EmbossedGadgetClass_Init( class );
   class->isa         = StringListerClass();
   class->ClassName   = "ListBrowser";
   class->CleanUp     = ListBrowser_CleanUp;
   class->AskSize     = ListBrowser_AskSize;
   class->SetSize     = ListBrowser_SetSize;
   class->SizeFlags   = GraphicObject_SizeFlagsAll;
   class->SetLocation = ListBrowser_SetLocation;
   class->Respond     = ListBrowser_Respond;
   class->Render      = ListBrowser_Render;
   class->Refresh     = ListBrowser_Refresh;

   class->AddString     = ListBrowser_AddString;
   class->DeleteString  = ListBrowser_DeleteString;
   class->DeleteAllStrings  = ListBrowser_DeleteAllStrings;
   class->StringList_of = ListBrowser_StringList_of;
   class->SelectString  = ListBrowser_SelectString;
}


struct StringListerClass *ListBrowserClass( void )
{
   if (! ListBrowser_elaborated)
   {
      ListBrowserClass_Init( &ListBrowser_Class );
      ListBrowser_elaborated = TRUE;
   }

   return &ListBrowser_Class;
}
