/* This implements the Variable List Delete Window.
    Vars are sorted for display by var name.
    When a selected var is deleted, it is pushed into undoLifo.
    Undelete undeletes the last varInfo.
    Var session instances are not closed until this window is closed,
they are, however, removed from the Variable Browser.
 */!!

inherit(TextWindow, #VarDelWindow, #(selectedLine /* nil or index into workText */
startLine  /* start line # (from 0) */
startCol  /* start col offset */
undoLifo  /* deleted Vars */
varColl  /* sorted collection of varInfo */
varSession
), 2, nil)!!

now(class(VarDelWindow))!!

/* PUBLIC */
Def open(self, parent, varSession, name, rect | newInst)
{
  newInst := newStyle(self, parent, nil, name, rect, nil, 
        WS_OVERLAPPED bitOr WS_CAPTION bitOr WS_SYSMENU bitOr WS_THICKFRAME
        bitOr WS_HSCROLL bitOr WS_VSCROLL);
   
  setVarSession( newInst, varSession ) ;

  ^init( newInst ) ;  /* does not do SHOW of self */
}
!!

now(VarDelWindow)!!

/* PUBLIC */
Def add(self, aVarInfo)
{
  addVar( self, aVarInfo ) ;
}
!!

/* PRIVATE  -- Add aVarInfo to varCollection; add it's display text 
   to the text collection in the proper place.
 */
Def addVar(self, aVarInfo | index, viewText)
{
  unSelectLine( self ) ;
  add( varColl, aVarInfo ) ;
  viewText := expandedName( aVarInfo ) ;
  index := find( varColl, aVarInfo ) ;  /* N.B.: always found */
  insert( workText, viewText, index ) ; /* put wherever.. */
  invalidate( self ) ;  /* cause repaint */
}
!!

/* PRIVATE  -- aVarInfo has already been added to varCollection;
   add it's display text to the text collection in the proper place.
 */
Def addVarText(self, aVarInfo | index, viewText)
{
  unSelectLine( self ) ;
  viewText := expandedName( aVarInfo ) ;
  index := find( varColl, aVarInfo ) ;  /* N.B.: always found */
  insert( workText, viewText, index ) ; /* put wherever.. */
  invalidate( self ) ;  /* cause repaint */
}
!!

/* PRIVATE -- show last page
*/
Def bottomPage(self | numLines)
{
  numLines := size(workText) ;

  if (numLines = 0) cor (visLines(self) >= numLines)
  then /* all text displayed */
    ^nil
  endif ;

  startLine := (numLines - visLines(self)) ;

  invalidate( self ) ; /* causes repaint, which does setVScrollPos */
}
!!

/* PRIVATE */
Def bs(self)
{
  /* Override parent's method to ignore backspace */
}
!!

/* PUBLIC */
Def close(self)
{
  /* flush lifo -- kill deleted vars */
  undoLifo cand (size(undoLifo) > 0) cand
    do(undoLifo,
    {using(aVarInfo)
      closeVar( varSession, varID(aVarInfo) ) ;
    });

  clearVarListWin( parent ) ;
  close( self:ancestor ) ;
}
!!

/* PRIVATE -- scroll left ~1 charwidth */
Def columnLeft(self)
{
  startCol   := max( (startCol-1), 0 ) ;

  invalidate( self ) ; /* causes repaint, which does setHScrollPos */
}
!!

/* PRIVATE -- scroll right ~1 charwidth */
Def columnRight(self)
{
  startCol   := min( (startCol+1), maxWidth(self) ) ;

  invalidate( self ) ; /* causes repaint, which does setHScrollPos */
}
!!

/* Dispatch menu choices, accelerators. */
Def command(self, item, lP | msg)
{
  select
    case ( msg := action(menu, item) )
    is
      ^perform( self, item, msg )
    endCase
    case (item == VK_DELETE) cor (item == 301)  /* "EDIT_DELETE" */
    is ^deleteKey( self ) ; 
    endCase
    default
  endSelect;
}!!

/* PRIVATE */
Def deleteKey(self)
{
  selectedLine cand menuDelete( self, #ignored ) ;
}
!!

/* PRIVATE */
Def displaySelectedLine(self, hDC)
{ 
  if selectedLine 
     cand (selectedLine >= startLine) 
     cand ((selectedLine - startLine) <= visLines(self))
  then  /* old is unhilighted if displayed */
    invertLine( self, hDC, (selectedLine - startLine) ) ;
  endif ;
}
!!

/* WINDOWS */
Def gotFocus(self, ignored)
{ /* do nothing; overrides TextWindow cursor creation */ }
!!

/* PRIVATE -- scroll to proximal col */
Def hThumbScroll(self, hPos)
{
   startCol := asInt( (hPos * maxWidth(self)) / 100 ) ;
   
   invalidate( self ) ;
}
!!

/* PRIVATE */
Def init(self)
{
  init( self:ancestor ) ;
  startLine := startCol := 0 ;
/*selectedLine := nil ; */
  varColl  := newWithCompareBlock(
                      SortedCollection,
                       8,  
                       {using(a,b) /* sort block */
                         if (name(a) = name(b))
                         then  /* break all ties, else binary search breaks */
                           (varID(a) < varID(b))
                         else
                           (name(a) < name(b))
                         endif ;
                       }
                      );
  undoLifo := new( OrderedCollection, 4 ) ;  /* LIFO Stack */

  menu := create( new(Menu), self );
  addItem(menu, new(MenuItem, "&Delete",   550, #menuDelete));
  addItem(menu, new(MenuItem, "&Restore",  551, #menuRestore));
  addItem(menu, new(MenuItem, "E&xit",     552, #menuExit));
  grayMenuItem( menu, 550 ) ; /* DELETE item */
  grayMenuItem( menu, 551 ) ; /* UNDO   item */
  drawMenu( self ) ;

}
!!

/* PRIVATE
  Initialize text metrics data for this browser.  Load the font data 
  into textMetrics, set the text width and height instance variables.
  
  Nota Bene: This sets up for Windows' defaults.  The containing window
  should use setTmWidth and setTmHeight methods to size for its chosen
  font characteristics.
*/
Def initTextMetrics(self | hdc, tm)
{ tm := new(Struct, 32);
  Call SelectObject(hdc := getContext(self),
                    Call GetStockObject(SYSTEM_FIXED_FONT));
  Call GetTextMetrics(hdc, tm);
  tmWidth  := asInt(wordAt(tm, 10));
  tmHeight := asInt(wordAt(tm, 8)) + asInt(wordAt(tm, 0));
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FONT));
  releaseContext(self, hdc);
}!!

/* PRIVATE */
Def invertLine(self, hDC, displayIndex)
{
  Call PatBlt( hDC, 
               0,                               /* x0 */
               (displayIndex * tmHeight)+2,     /* y0 */
               width(clientRect(self))*tmWidth, /* width */
               tmHeight+2,                      /* height */
               DSTINVERT )
}
!!

/* PRIVATE -- scroll up 1 line (down arrow key) */
Def lineDown(self | fixRect)
{
  if ( (startLine + visLines(self)) < size(workText) ) 
  then
    startLine := (startLine + 1) ;
/*@@{
    fixRect := clientRect( self ) ;
    setTop( fixRect, (bottom(fixRect) - (2*tmHeight)) ) ;
    Call ScrollWindow(hWnd, 0, negate(tmHeight), 0, 0) ;
    Call InvalidateRect( hWnd, fixRect, 1 ) ;
}@@*/
    invalidate( self ) ;
    setVScrollPos( self ) ;
  endif ;
}
!!

/* PRIVATE -- scroll down 1 line (up arrow key) */
Def lineUp(self | fixRect)
{
  if (startLine > 0) 
  then
    startLine := (startLine - 1) ;
    /* do the Windows hacks */
/*@@{
    hWnd := hWnd( self ) ;
    fixRect := clientRect( self ) ;
    setBottom( fixRect, tmHeight + 6 ) ;
    Call ScrollWindow( hWnd, 0, tmHeight, 0, 0 ) ;
    Call InvalidateRect( hWnd, fixRect, 1 ) ;
}@@*/
    invalidate( self ) ;
    setVScrollPos( self ) ;
  endif ;
}
!!

/* WINDOWS */
Def losingFocus(self, hWndNew)
{
  selectedLine cand unSelectLine( self ) ;
}
!!

/* PRIVATE  -- return the number of chars in the longest line or 1 */
Def maxWidth(self | maxCharWidth)
{
  maxCharWidth := 1 ; /* default max */
  
  do(over(0,size(workText)),
  {using(index)
    maxCharWidth := max( maxCharWidth, size(workText[index]) ) ;
  });

  ^maxCharWidth
}
!!

/* MENU */
Def menuDelete(self, ignored)
{
  selectedLine cand removeSelected(self, selectedLine) ;

  grayMenuItem(menu, 550) ; /* delete item */
  drawMenu( self ) ;
}
!!

/* MENU */
Def menuExit(self, ignored)
{
  close( self ) ;
}
!!

/* MENU */
Def menuRestore(self, ignored)
{
  restoreLastVar(self) ;

  if (size(undoLifo) = 0)
  then 
    grayMenuItem(menu, 551) ; /* undo item */
    drawMenu( self ) ;
  endif ;
}
!!

/* PRIVATE -- scroll up ~1 screen */
Def pageDown(self | displayLines delta)
{
  displayLines := visLines( self ) ;
  delta := (size(workText) - startLine) ;

  if (delta <= displayLines) 
  then ^0 ;  /* done; last text is showing */
  else
    startLine := ( startLine + displayLines ) ;
  endif ;

  invalidate( self ) ; /* causes repaint, which does setVScrollPos */
}
!!

/* PRIVATE -- scroll left ~1 screenwidth */
Def pageLeft(self | screenWidth)
{  
/*@@ printLine( "pageLeft(varP)" ); @@*/
  screenWidth := max((width(clientRect(self)) / tmWidth)-1, 1) ;
  startCol    := max( (startCol-screenWidth), 0 ) ; 

  invalidate( self ) ; /* causes repaint, which does setHScrollPos */
}
!!

/* PRIVATE -- scroll right ~1 screenwidth */
Def pageRight(self | screenWidth)
{  
  screenWidth := max((width(clientRect(self)) / tmWidth)-1, 1) ;
  startCol    := min( (startCol+screenWidth), maxWidth(self) ) ; 

  invalidate( self ) ; /* causes repaint, which does setHScrollPos */
}
!!

/* PRIVATE -- scroll down ~1 screen */
Def pageUp(self)
{
  startLine := max( 0, (startLine - visLines(self)) ) ;
  invalidate( self ) ; /* causes repaint, which does setVScrollPos */
}
!!

/* WINDOWS (Public)
   Draw the text on the screen -- just iterate over viewColl
*/
Def paint(self, hDC | viewLines hScrollPix)
{
  viewLines := visLines( self ) ; /* # lines on display */

  setVScrollPos( self ) ;  setHScrollPos( self ) ;

  hScrollPix  := negate(startCol * tmWidth) ;

  do(over(startLine, min(size(workText), (viewLines + startLine)) ),
    {using(index | anoText)
      anoText := new( ATParser, workText[index] ) ;
      doDisplay( anoText, hDC, 
                 hScrollPix, ((index-startLine) * tmHeight + 2), 
                 tmWidth, tmHeight ) ;
    });

  selectedLine cand displaySelectedLine(self, hDC)
}
!!

/* PRIVATE  -- Remove aVarInfo from varCollection; delete it's display text 
   from the text collection.  Inform parent.
 */
Def removeSelected(self, displayLine | index)
{
  index := (displayLine + startLine) ;
  unSelectLine( self ) ;
  removeVarByVarInfo( parent, varColl[index] ) ;
  push( undoLifo, varColl[index] ) ;
  enableMenuItem( menu, 551 ) ;  /* undo menu item */
  drawMenu( self ) ;
  remove( varColl,  varColl[index] ) ;
  remove( workText, index ) ;
  invalidate( self ) ;  /* cause repaint */
}
!!

/* PUBLIC (to parent) -- Remove aVarInfo from varCollection; 
   delete it's display text from the text collection.
 */
Def removeVar(self, aVarInfo | index)
{
  unSelectLine( self ) ;
  push( undoLifo, aVarInfo ) ;
  enableMenuItem( menu, 551 ) ;  /* undo menu item */
  drawMenu( self ) ;
  index := find( varColl, aVarInfo ) ;  /* N.B.: always found */
  remove( varColl, varColl[index] ) ;
  remove( workText, index ) ;
  invalidate( self ) ;  /* cause repaint */
}
!!

/* WINDOWS */
Def reSize(self, wP, lP)
{
  invalidate( self ) ;
}
!!

/* PRIVATE  -- restore a variable to parent & self from undoLifo */
Def restoreLastVar(self | aVarInfo)
{
  unSelectLine( self ) ;
  if (size(undoLifo) = 0)
  then
    grayMenuItem( menu, 550 ) ;  /* delete menu item */
    drawMenu( self ) ;
    displayFormattedError(ErrorTextLibClass$Inst, 
       ER_VAR_NO_VIEWS, FORCE_POPUP, nil, nil, nil);
  else
    aVarInfo := pop( undoLifo ) ;
    addVarByDeadVarInfo( parent, aVarInfo ) ;
    addVar( self, aVarInfo ) ;
  endif ;
}
!!

/* PRIVATE
   Set scroll bar position based on startLine.
*/
Def setHScrollPos(self | columnPos)
{
  columnPos := asInt( (startCol * 100) / maxWidth(self) ) ;

  Call SetScrollPos(hWnd(self),
                    SB_HORZ,  /* what bar to redraw (only 1) */
                    columnPos, /* where to put it -- % */
                    1) ;       /* do the redraw (why else would we call?) */
}!!

/* PUBLIC */
Def setVarSession(self, session)
{
  varSession := session ;
}
!!

/* PRIVATE
   Set scroll bar position based on startLine.
*/
Def setVScrollPos(self 
                 | currLineNum totalLines varIndex viewLines perCent adjust)
{
  totalLines  := size( workText ) ;
  currLineNum := startLine ;
/*@@{
  printLine( "setVScrollPos: (totalLines,currLineNum) = ("
                  +asString(totalLines) + "," + asString(currLineNum) + ")" ) ;
}@@*/
  viewLines := visLines( self ) ;

  select
    case (totalLines = 0) cor (currLineNum = 0)
    is /* empty */
      varIndex := 0 ;
    endCase
    case (currLineNum + viewLines) >= totalLines
    is /* ~max */
      varIndex := 100 ;
    endCase
    default
      perCent  := ( (100 * currLineNum) / (totalLines - viewLines) ) ;
      adjust   := asInt((perCent * viewLines) / 100) ;
      varIndex := asInt( (100 * (currLineNum + adjust)) 
                         / totalLines ) ;
  endSelect;
/*@@{
  printNewLine( "setVScrollPos: (%,adj,vis) = (" 
                   + asString(perCent)+","+asString(adjust)
                   +","+asString(viewLines)+")" ) ;
  printNewLine( "setVScrollPos: scrollIndex = " + asString(varIndex) ) ;
}@@*/
  Call SetScrollPos(hWnd(self),
                    SB_VERT,  /* what bar to redraw (only 1) */
                    varIndex, /* where to put it -- % */
                    1) ;      /* do the redraw (why else would we call?) */
}!!

/* PRIVATE -- scroll to proximal var */
Def thumbScroll(self, vPos 
               | totalLines lineWanted varAndLine viewLines adjust)
{
/*@@{
  printLine("thumbScroll(varP, " + asString(vPos) + ")" ); 
}@@*/

  select
    case (size(varColl) = 0)
    is ^nil;
    endCase
    case (vPos < 1)
    is ^topPage(self);
    endCase
    case (vPos > 98)
    is ^bottomPage(self);
    endCase
  endSelect;
  
  totalLines := size( workText ) ;
  viewLines  := visLines( self ) ;
/*@@{
  printLine("thumbScroll: (viewCollSize,totalLines) = (" 
     + asString(numLinesIn(varColl)) + "," + asString(totalLines) + ")" );
}@@*/
  if (viewLines >= totalLines)
  then /* all text displayed */
    ^nil
  else  /* adjust for % displayed */
    adjust := asInt((vPos * viewLines) / 100) ; 
    lineWanted := asInt( (totalLines * vPos) / 100 ) - adjust ;

/*@@{
  printLine("thumbScroll: lineWanted = "
            + asStringRadix(lineWanted, 10) );
}@@*/
    startLine := lineWanted ;
    invalidate( self ) ;
  endif ;
}
!!

/* PRIVATE */
Def topPage(self)
{
  startLine := 0 ;
  invalidate( self ) ; /* causes repaint, which does setVScrollPos */
}
!!

/* WINDOWS */
Def unSelectLine(self | hDC)
{
  if selectedLine
  then
    hDC := getContext( self ) ;
    displaySelectedLine( self, hDC ) ;
    releaseContext( self, hDC ) ;
    selectedLine := nil ;
  endif ;
  grayMenuItem(menu, 550) ; /* delete item */
  drawMenu( self ) ;
}
!!

/* PRIVATE -- update selected line based on display line.
   Unhighlight old selection, hilight new. 
 */
Def updateSelectedLine(self, displayLineNum | newSelected hDC)
{
  newSelected := (displayLineNum + startLine) ;

  hDC := getContext( self ) ;

  if selectedLine 
     cand (selectedLine >= startLine) 
     cand ((selectedLine - startLine) < visLines(self))
  then  /* old is unhilighted if displayed */
    invertLine( self, hDC, (selectedLine - startLine) ) ;
  endif ;

  if (newSelected >= (size(workText)-1)) 
  then /* out of range */
    selectedLine := nil ;
    grayMenuItem( menu, 550 ) ;  /* delete menu item */
  else /* hilight new selection */
    selectedLine := newSelected ;
    invertLine( self, hDC, displayLineNum ) ;
    enableMenuItem( menu, 550 ) ;  /* delete menu item */
  endif ;

  releaseContext( self, hDC ) ;
  drawMenu( self ) ;
}
!!

/* WINDOWS */
Def WM_COMMAND(self, wP, lP)
{ ^command(self, wP, lP);
}!!

/* WINDOWS
  Respond to MS-Window's vertical scrolling message. wP tells what kind of
  scrolling request has been made.
*/
Def WM_HSCROLL(self, wP, lP)
{
  select

    case wP == SB_LINEDOWN
    is columnRight( self );
    endCase

    case wP == SB_PAGEDOWN
    is pageRight( self ) ;
    endCase

    case wP == SB_BOTTOM
    is maxRight( self ) ;
    endCase

    case (wP == SB_LINEUP)
    is columnLeft( self ) ;
    endCase

    case wP == SB_PAGEUP
    is pageLeft( self ) ;
    endCase

    case wP == SB_TOP
    is maxLeft( self ) ;
    endCase

    case  wP == SB_THUMBPOSITION
    is hThumbScroll( self, low(lP) ) ;
    endCase

    /* IGNORE is the default */

  endSelect;

  ^0 
}
!!

/* WINDOWS (PUBLIC)
   Select line nearest to mouse point.
*/
Def WM_LBUTTONDBLCLK(self, wp, lp | mousePt displayLineNum)
{
  mousePt := asPoint( lp ) ; /* N.B.: window relative */
  displayLineNum := asInt( max( 0, y(mousePt)-2 ) / tmHeight ) ;
  updateSelectedLine( self, displayLineNum ) ;
  removeSelected( self, displayLineNum ) ;
}
!!

/* WINDOWS (PUBLIC)
   Select line nearest to mouse point.
*/
Def WM_LBUTTONDOWN(self, wp, lp | mousePt displayLineNum)
{
  mousePt := asPoint( lp ) ; /* N.B.: window relative */
  displayLineNum := asInt( max( 0, y(mousePt)-2 ) / tmHeight ) ;
  updateSelectedLine( self, displayLineNum ) ;
}

!!

/* WINDOWS
  MS-Window's message to paint self -- sends a
  paint(self) message.  This overrides Window:WM_PAINT
  so that TextWindow and its descendants use the
  System Fixed Font instead of the System Font. */
Def WM_PAINT(self, wP, lP | hdc)
{ hdc := Call BeginPaint(getHWnd(self), paintStruct);
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FIXED_FONT));
  initTextColors( self, hdc ) ;
  paint(self, hdc);
  Call SelectObject(hdc, Call GetStockObject(SYSTEM_FONT));
  Call EndPaint(getHWnd(self), paintStruct);
  ^0;
}!!

/* WINDOWS
  Respond to MS-Window's vertical scrolling message. wP tells what kind of
  scrolling request has been made.
*/
Def WM_VSCROLL(self, wP, lP)
{
  select

    case wP == SB_LINEDOWN
    is lineDown( self );
    endCase

    case wP == SB_PAGEDOWN
    is pageDown( self ) ;
    endCase

    case wP == SB_BOTTOM
    is bottomPage( self ) ;
    endCase

    case (wP == SB_LINEUP)
    is lineUp( self ) ;
    endCase

    case wP == SB_PAGEUP
    is pageUp( self ) ;
    endCase

    case wP == SB_TOP
    is topPage( self ) ;
    endCase

    case  wP == SB_THUMBPOSITION
    is thumbScroll( self, low(lP) ) ;
    endCase

    /* IGNORE is the default */

  endSelect;

  ^0 
}
!!
