/* CLASS: BKPTDIAL.CLS
   Provide dialog box to receive user's input for defining a new breakpoint or
   modifying the existing one.  See BkptPresenter Class for more information
   on usage.
   After a succesfully runModal(), send bkpt() message to retrieve the BkptInfo
   object.
   REQUIRES: PBKPT.H BKPTINFO.CLS BKPTPRES.CLS
*/!!

inherit(Dialog, #BkptDialog, #(bkptInfo /* BkptInfo object */
bkptId /* breakpoint ID */
bkptState /* Enable/Disable */
bkptLife /* Permanent/Temporary */
modulesDict /* modules dictionary */
funcsDict /* functions  of selected module */
), 2, nil)!!

now(class(BkptDialog))!!

/* 6/12/1992 16:51 - PUBLIC
  Create a new BkptDialog object.
*/
Def new(self, mDict| theDlg)
{
  theDlg := new(self:Behavior);
  ^init(theDlg, mDict);
}
!!

now(BkptDialog)!!

/* 4/13/1994 15:08 - PRIVATE
  Select the edit box string
*/
Def selectEditText(self | p)
{ 
  /* select the entire string in edit box */
  if (p := point(0,32767)) then
    ^sendDlgItemMessage(self, BKPT_DLG_EDIT, 0x401 /* EM_SETSEL */, 0, p);
  endif;  
}
!!

/* 11/10/1992 13:46 - PRIVATE
  Revise form Actor's Base class.
  Return the selected text for a ComboBox - Upto 125 characters long.
*/
Def getCBText(self, item | iv, ans)
{ 
  ^((iv := asInt(sendDlgItemMessage(self, item, CB_GETCURSEL,
     0, 0))) ~= CB_ERR) cand
   (asInt(sendDlgItemMessage(self, item, CB_GETLBTEXT,
     iv, ans := new(String, 125))) ~= CB_ERR) cand
   removeNulls(ans);
}
!!

/* 6/16/1992 10:33 - PUBLIC
  Return the breakpoint object defined in self.
*/
Def bkpt(self)
{ 
  ^bkptInfo;
}
!!

/* 6/14/1992 19:49 - PRIVATE
  Extract function symbol descriptors to build a dictionary of Functions in module.
  return nil or a sorted collection of function symbols in module.  
  NOTES: For language other than C (PASCAL, for eaxmple), the functionColl 
  needs to be built for all nested functions. EX:
    level 1:   [module head] -> [module 1] -> [module 2]...
                                 |
    level 2:                     [func 1] -> [func 2]...
                                  |
    level 3:  {nested functions}  [func 1'] -> [func 2']...                              
  Using indentation in the list is might be adequate for showing the nested level. 
*/
Def buildFuncsDict(self, parentModuleID | symColl, symDesc, nextDesc, symName, symAddr)
{
  symColl := nil; symAddr := nil;
  if not(parentModuleID) cor not(symDesc := getSymbolChildFunc(SymbolLibClass$Inst, parentModuleID)) then
    ^nil
  endif ;
  /* Build dictionary of functions symbol */
  if (symColl := new(SortedCollection, 10)) then
    initWithCompareBlock(symColl, 
      { using(e1,e2) 
        e1[0] < e2[0] /* ascending order */
      });    
  else   
    ^nil;
  endif;
  /* extract all function descriptor for the module */  
  loop while (symDesc <> 0) /* (NULL_SYMBOL == 0L) */
  begin
    /* Get the function name if not found then get its address instead */
    if not(symName := getSymbolName(SymbolLibClass$Inst, symDesc)) then
      /* Get the address descriptor of symbol to replace the name */
      if not(symAddr := getSymbolAddress(SymbolLibClass$Inst, symDesc)) then
        ^nil;
      endif;
      /* Convert the address to text */
      if not(symName := getAddressTextParam(AddressLibClass$Inst, symAddr, 1, 1)) then
        destroyAddress(AddressLibClass$Inst, symAddr); 
        ^nil; /* Something awfully wrong */
      endif;       
      /* get rid of the unused address descriptor */
      destroyAddress(AddressLibClass$Inst, symAddr); 
    endif;  
    /* DEBUG - printLine(symName); */
    /* symColl = ([symFuncName, symFuncDesc], ...) */
    add(symColl, tuple(symName, symDesc));
    nextDesc := getSymbolSibling( SymbolLibClass$Inst, symDesc ) ;
    if nextDesc then
      symDesc := nextDesc[0] ;
    endif ;  
  endLoop;
  ^symColl;
}
!!

/* 6/14/1992 21:05 - Actor  
  Dialog box command processing. 
*/
Def command(self, wp, lp | val, bkptInput, errMsg )
{ 
  select
    case wp = IDCANCEL
      ^end(self, IDCANCEL);
    endCase
     
    case wp = IDOK
      errMsg := getErrorText(ErrorTextLibClass$Inst, ER_INVALID_BKPT_INFO);
      /* Set breakpoint and create a new BkptInfo object before return control */
      if (size(bkptInput := getItemText(self, BKPT_DLG_EDIT)) > 0) then
        errMsg := setBkptWithInput(self, bkptInput);
      endif;  
      if errMsg then
        /* Since there is no breakpoint defined - Error occurs */
        beep();
        if (errMessageBox(ErrorTextLibClass$Inst, "Error", errMsg + "  Retry?", 
          MB_RETRYCANCEL bitOr MB_ICONQUESTION, HE_DLGM_BKPTDIAL_1) <> IDCANCEL) then
          setFocus(self);
          ^GOOD;
        endif;
        ^end(self, IDCANCEL);
      endif;  
    endCase

    /* Display Help for the BkptDialog */  
    case wp = IDHELP
      is contextNumber(TheHelpObject, hWnd, HE_DLGR_SET_BREAKPOINT);
    endCase           
    
    /* User select module - display module's internal information */
    case wp = BKPT_DLG_MODULES
      select
        case high(lp) = LBN_SELCHANGE /* Suppose to be CBN_SELCHANGE - Actor not defined */
          setSelEdit(self, wp, nil);
          showWaitCurs();
          if fillFuncsList(self, getCBText(self, wp)) then
            enableItem(self, BKPT_DLG_FUNCS);
            setSelCombo(self, BKPT_DLG_FUNCS, nil);
            setItemFocus(self, BKPT_DLG_FUNCS); /* Set focus to function list */
            setSelEdit(self, BKPT_DLG_FUNCS, #true); 
          endif;
          showOldCurs();
        endCase
        case high(lp) = LBN_SETFOCUS /* Suppose to be CBN_SETFOCUS */
          setSelEdit(self, wp, nil);
        endCase
      endSelect;     
    endCase
    
    case wp = BKPT_DLG_FUNCS
      select
        case high(lp) = LBN_SELCHANGE 
          /* Check if 1st char is a # or error - User might type in sonething */
          bkptInput := getItemText(self, BKPT_DLG_EDIT);
          if bkptInput cand getKeyAtValue(self, modulesDict, findStrings(bkptInput, "#")[0]) then      
            setSelEdit(self, wp, #true);
          else
            beep();
            displayFormattedError(ErrorTextLibClass$Inst, 
              ER_INVALID_MODULE_SYMBOL, FORCE_POPUP, nil, nil, nil);
          endif;        
        endCase
        case high(lp) = LBN_SETFOCUS /* Suppose to be CBN_SETFOCUS */
          setSelEdit(self, wp, #true);
        endCase
      endSelect;                   
    endCase
    
    case (wp = BKPT_DLG_ENABLE) cor (wp = BKPT_DLG_DISABLE)
      bkptState := wp - BKPT_DLG_ENABLE; /* 0  = ENABLE */
    endCase   
    
    case (wp = BKPT_DLG_TEMP) cor (wp = BKPT_DLG_PERM) 
      bkptLife := wp - BKPT_DLG_PERM; /*  0  = PERMANENT, 1  = TEMPORARY */
    endCase   
    
    default 
      ^command(self:ancestor, wp, lp);
  endSelect;
  ^GOOD;
}
!!

/* 6/14/1992 19:39 - PRIVATE
  Given a Modules descriptor, extract all its child function symbol.
  return the funcsDict or nil.
*/
Def fillFuncsList(self, moduleName | moduleID)
{ 
  clearCBText(self, BKPT_DLG_FUNCS);
  if funcsDict then funcsDict := nil;  endif;
  moduleID := getKeyAtValue(self, modulesDict, moduleName);
  /* Extract its functions child symbol - using module descriptor */
  if not(funcsDict := buildFuncsDict(self, moduleID)) then
    disableItem(self, BKPT_DLG_FUNCS);
    ^nil; /* There is no Function List, so disable it */
  endif;
  /* WHERE: funcsDict is a SortedCollection of ([funcName,funcDesc],...) */
  do (funcsDict,
    {using(element)
      /* add funcName to combo box */
      addCBText(self, BKPT_DLG_FUNCS, element[0]);
    });
  ^funcsDict;
}
!!

/* 6/18/1992 11:43 - PRIVATE */
Def getKeyAtValue(self, collection, symName)
{ 
  do(collection, 
    {using(element) 
      /* return symDesc if symName is matched */
      if (element[0] = symName) then
        ^element[1];
      endif;
    });
  ^nil;    
}
!!

/* 6/12/1992 17:06 - PRIVATE
  Initialize Bkpt Dialog box and its instance variables
*/
Def init(self, mDict)
{ 
  init(self:ancestor);
  bkptState   := 0; /* ENABLE = 0, DISABLE = 1 Notes: See HLBREAK.H */
  bkptLife    := 0;  /* PERM = 0, TEMP = 1 */      
  bkptId      := nil;
  bkptInfo    := nil;
  modulesDict := mDict; /* modules list from ProjectInfoObject */
  funcsDict   := nil;
}
!!

/* 6/14/1992 13:22 - Actor 
  By returning a 1 from the INITDIALOG message, we
  are telling MS-Windows to set the input focus to
  first tabstop item. (See MS-Windows Reference). 
*/
Def initDialog(self, wP, lP | currentModule, currentFunc)
{
  toggle(self, BKPT_DLG_ENABLE);
  toggle(self, BKPT_DLG_PERM);
  registerF1Help(CLIULibraryClass$Inst, HI_ENTRY_BREAKPOINT,
     getHWnd(self), HE_DLGR_SET_BREAKPOINT);

  /* Initialize dialog items */
  if not(modulesDict) then
    disableItem(self, BKPT_DLG_MODULES);
    disableItem(self, BKPT_DLG_FUNCS);
    ^1; /* No symbol information - set edit box focus first */
  else
    /* Get the module and function descriptor from the current debug context */
    requireWithPath( StackLib, "stkservr.dll");
    currentModule := getCurrentModuleFromContext(StackLibClass$Inst);  
    currentFunc   := getCurrentFuncFromContext(StackLibClass$Inst);
    clearCBText(self, BKPT_DLG_MODULES);
    /* Fill Modules combo box with module name */
    do (modulesDict,
      {using(element) 
        addCBText(self, BKPT_DLG_MODULES, element[0]);
      });
    
    /* select the current module if any, or the first one in list */ 
    setSelCombo(self, BKPT_DLG_MODULES, currentModule);
    setSelEdit(self, BKPT_DLG_MODULES, nil);
    if fillFuncsList(self, getCBText(self, BKPT_DLG_MODULES)) then
      setSelCombo(self, BKPT_DLG_FUNCS, currentFunc);
      setSelEdit(self, BKPT_DLG_FUNCS, #true);
    endif;  
    selectEditText(self);
    setItemFocus(self, BKPT_DLG_EDIT); /* Set focus to edit box */
  endif;    
  ^0; 
} 
!!

/* 6/16/1992 15:08 - PRIVATE
  Set the specified string to be the current selection
*/
Def selectCBText(self, Cbox, str, index | selStr)
{ 
  ^sendDlgItemMessage(self, Cbox, CB_SELECTSTRING, index, asciiz(str));
}
!!

/* 6/17/1992 8:45 - PRIVATE
  Set a breakpoint according to user input. 
  Return nil if successful or error message.
*/
Def setBkptWithInput(self, bkptInput | addrDesc)
{ 
  /* Check breakpoint input */
  if not(addrDesc := convertTextToAddressNoError(AddressLibClass$Inst, bkptInput)) then 
    ^getErrorText(ErrorTextLibClass$Inst, lastError(AddressLibClass$Inst));
  endif;
  /* Set breakpoint using address descriptor - Server consumes descriptor */
  if not(prime_setAsmBkpt(HLBreakLibClass$Inst, bkptState, bkptLife, addrDesc)) then
    ^getErrorText(ErrorTextLibClass$Inst, lastError(HLBreakLibClass$Inst));       
  endif;  
  ^nil; /* Successful */
}
!!

/* 6/16/1992 14:53 - PRIVATE 
  Get the Name of the symbolDesc and set it to be the 
  current selection. Return the index of the current selection. 
*/
Def setSelCombo(self, comboID, symbolDesc | itemStr, tmp)
{ 
  /* Select the first item to be the current selection */
  if not(symbolDesc) cor not(itemStr := getSymbolName(SymbolLibClass$Inst, symbolDesc)) then 
    tmp := sendDlgItemMessage(self, comboID, CB_SETCURSEL, 0, 0L);
  else
    /* Set item as current selection */ 
    tmp := selectCBText(self, comboID, itemStr, 0); 
  endif;

  ^tmp  
}
!!

/* 6/17/1992 12:42 - PRIVATE
  Set the Edit item with current module and functions. 
*/
Def setSelEdit(self, comboID, concatenate | selStr, cbStr, tmp )
{ 
  selStr := new(String, 256);
  selStr := "";
  if concatenate then
    tmp := findStrings(getItemText(self, BKPT_DLG_EDIT), "#");
    selStr := tmp[0];
    selStr := "#"+selStr;
  endif;  
  
  if (cbStr := getCBText(self, comboID)) then
    selStr := asString(selStr + "#" + cbStr);
    setItemText(self, BKPT_DLG_EDIT, selStr);
  endif; 
  ^selStr;   
}
!!
