/* A video window is the application's main window.  It shows the main line item view of the current data.  A video window's menu includes choices for adding new items to the inventory list, for changing the views, and for bringing up the help system. */!!

inherit(Window, #VideoWindow, #(actions            /*control actions dictionary */
controlDict      /* collection of child controls */
videoLB          /* a video list box */
currentVideo   /* the currently selected video */
fileName         /* name of currently saved file */
isDirty         /* true if data needs to be saved */
reorderWin /* reorder view as popup window */
chartWin     /* chart view as popup window */), 2, nil)!!

now(class(VideoWindow))!!

/* Return the default window style. */
Def style(self)
{ ^WS_OVERLAPPEDWINDOW
}!!

now(VideoWindow)!!

/* Responds to a View Chart menu selection by 
  displaying a chart window. */
Def chartCommand(self)
{ if size(videoLB) > 0 then
    if not(chartWin) cor (handle(chartWin) == nil)
    then chartWin := new(VidChWindow, self, nil, 
      "Chart View: Quantities on hand by part number", 
      &(50, 50, 625, 350)); 
    endIf;
    newChart(chartWin, getLabels(videoLB), 
      getData(videoLB));
    setFocus(chartWin);
    show(chartWin, SW_SHOWNORMAL); 
  else new(ErrorBox, self, "No videos to view", 
    "VideoMATIC", MB_OK bitOr MB_ICONASTERISK);
  endIf;
}
!!

/* Responds to a the View Reorder menu selection by
  producing a reorder view in a window. */
Def reorderCommand(self)
{ if reorderTotal(videoLB) <> 0. then
    if not(reorderWin) cor (handle(reorderWin) == nil)
    then reorderWin := newStyle(ReorderWindow, self, nil, 
      "Reorder View", &(50, 50, 625, 350), nil, 
      WS_POPUPWIND); 
    endIf;
    setFocus(reorderWin);
    cls(reorderWin);
    printString(reorderWin, 
      "Part# Title                Qty   Price " +
      "Manufacturer             Cost   ");
    printString(reorderWin, 
      "---------------------------------------" +
      "-----------------------------");
    do(reorderStrings(videoLB),
    {using(str) printString(reorderWin, str);
    });
    printString(reorderWin, 
      "----------------------------------------" +
      "----------------------------");
    printString(reorderWin,
      "                                        " +
      "       Total       $" + 
      format("%8.2lf", reorderTotal(videoLB)));

    show(reorderWin, SW_SHOWNORMAL); 
  else new(ErrorBox, self, "No orders today", 
    "VideoMATIC", MB_OK bitOr MB_ICONASTERISK);
  endIf;
}
!!

/* Asks the user how many videos to ship and ships
  that many. */
Def shipVideo(self, wP, lP | inputDlg, qtyToShip)
{ if currentVideo
  then inputDlg := new(InputDialog, "VideoMATIC", 
      "Quantity to ship:", "");
    if runModal(inputDlg, INPUT_BOX, self) <> 0 then
      if not(qtyToShip := asInt(getText(inputDlg), 10))
      then new(ErrorBox, self, 
          "You must enter an integer.", "VideoMATIC", 
          MB_OK bitOr MB_ICONEXCLAMATION);
      else
        if qtyToShip > quantityOnHand(currentVideo)
        then new(ErrorBox, self, 
          "Transaction not possible." + CR_LF + 
          "Choose lower quantity.", "VideoMATIC", MB_OK bitOr
          MB_ICONEXCLAMATION);
        else 
          shipVideo(videoLB, qtyToShip);
          isDirty := true;
          displayCurrentVideo(self);
        endIf;
      endIf;
    endIf;
  endIf;
}
!!

/* Asks the user how many videos to receive and 
  receives that many. */
Def receiveVideo(self, wP, lP | inputDlg, qtyToRec)
{ if currentVideo
  then inputDlg := new(InputDialog, "VideoMATIC", 
      "Quantity to receive:", "");
    if runModal(inputDlg, INPUT_BOX, self) <> 0 
    then
      isDirty := true;
      if not(qtyToRec := asInt(getText(inputDlg), 10))
      then new(ErrorBox, self, 
        "You must enter an integer.", "VideoMATIC", 
        MB_OK bitOr MB_ICONEXCLAMATION);
      else
        shipVideo(videoLB, negate(qtyToRec));
        displayCurrentVideo(self);
      endIf;
    endIf;
  endIf;
}
!!

/* Responds the the user selecting a new video by 
  setting the current video and filling the 
  information box. */
Def videoSelected(self, wP, lP)
{ if high(lP) = LBN_SELCHANGE
  then
    currentVideo := getSelVideo(videoLB);
    displayCurrentVideo(self);
  endIf;   
}
!!

/* Uses a dynamic dialog to prompt the user for data. 
  After checking that data, adds a new video to the 
  system. */
Def newVideo(self, wP, lP | answer, theVideo)
{ /* prompt the user */
  answer := prompter(DialogDesign, tuple(
    tuple("Part #:  ", 201, 3),
    tuple("Title:  ", 202, 19),
    tuple("Quantity on Hand:  ", 203, 3),
    tuple("Unit price:  ", 204, 5),
    tuple("Minimum quantity:  ", 205, 3),
    tuple("Reorder quantity:  ", 206, 3),
    tuple("Manufacturer:  ", 207, 19),
    tuple("Phone:  ", 208, 14)
  ));
  /* check the data's validity */
  if answer 
  then 
    if asInt(answer[201], 10) cand
       asInt(answer[203], 10) cand
       asInt(answer[205], 10) cand
       asInt(answer[206], 10)
    then  
      /* create and add the video */
      theVideo := init(new(Video),
        answer[202],
        asInt(answer[203], 10),
        asInt(answer[205], 10),
        asInt(answer[206], 10),
        asReal(answer[204]),
        answer[207],
        answer[208]);
      addVideo(videoLB, asInt(answer[201], 10), 
        theVideo);
      selectString(videoLB, 
        right(asString(asInt(answer[201], 10)), 3, " "));
      currentVideo := getSelVideo(videoLB);
      displayCurrentVideo(self);
      isDirty := true;
    else new(ErrorBox, self, 
      "You have entered data incorrectly.", "VideoMATIC", 
      MB_OK bitOr MB_ICONEXCLAMATION);
    endIf;
  endif;
}
!!

/* Responds to the user's request to close the 
  window by sending a canClose message to check for 
  lost work. */
Def close(self)
{ if canClose(self)
  then close(self:ancestor);
  endIf;
}
!!

/* Returns true if the window's data does not need to
  be saved or if it does, but the user does not want
  to save it. */
Def canClose(self | response)
{ if isDirty
  then 
    response := new(ErrorBox, self, 
      "Do you wish to save changes", "VideoMATIC",
      MB_YESNO bitOr MB_ICONQUESTION);
    select
      case response == IDYES
      is ^false;
      endCase
      case response == IDNO
      is ^true;
      endCase
    endSelect;
  else ^true;
  endIf;
}
!!

/* Saves to the current file name the data in text
  form corresponding to the collection of videos 
  stored in the video list box. */
Def saveFile(self | theFile)
{ theFile := new(TextFile);
  setDelimiter(theFile, asString(Tab));
  setName(theFile, fileName);
  create(theFile);
  checkError(theFile);
  write(theFile, asExportString(videoLB));
  checkError(theFile);
  close(theFile);
  isDirty := false;
}
!!

/* Responds to a File Save menu selection by saving
  a file. */
Def saveAsCommand(self | inputDlg, defText)
{ if fileName
  then defText := fileName;
  else defText := "*.VID";
  endIf;
  inputDlg := new(InputDialog, "Save File", 
    "Save video inventory as:", defText);
  if runModal(inputDlg, INPUT_BOX, self) == IDOK
  then 
    fileName := getText(inputDlg);
    saveFile(self);
    setText(self, "VideoMATIC - " + 
      asUpperCase(fileName));
  endIf;
} !!

/* Responds to a File Export to Excel menu selection 
  by seeing if there are any videos in the system.  
  If so, it copies to the Clipboard a string 
  representing the collection of video.  Finally,
  it starts Microsoft Excel with a macro that pastes
  the data from the Clipboard and formats it. */
Def exportCommand(self | hText, errorCode, errorStr)
{ if size(videoLB) == 0 
  then new(ErrorBox, self, "No videos to export", 
    "VideoMATIC", MB_OK bitOr MB_ICONASTERISK);
  else
    showWaitCurs();
    /* Copy text to Clipboard. */
    if Call OpenClipboard(hWnd) <> 0
    then
      hText := asHandle(asExportString(videoLB));
      Call SetClipboardData(CF_TEXT, hText);
      Call CloseClipboard();
    endIf;
    /* Run Excel and check for errors. */
    if (errorCode := exec("EXCEL EXPORT.XLM")) <= 32
    then 
      select
        case errorCode == 0
        is errorStr := "Not enough memory to run Excel";
        endCase
        case (errorCode == 2) or (errorCode == 3)
        is errorStr := "Excel not in path";
        endCase
        default errorStr := "Not able to run Excel";
      endSelect;    
      new(ErrorBox, self, errorStr, "VideoMATIC",
        MB_OK bitOr MB_ICONASTERISK);
    endIf;
    showOldCurs();
  endIf;
}!!

/* Responds to a File Save menu selection by saving
  a file. */
Def saveCommand(self | inputDlg, theFile)
{ if fileName 
  then saveFile(self);
  else saveAsCommand(self);
  endIf;
} !!

/* Responds to a File Open menu selection by opening
  a file. */
Def openCommand(self | fileDlg, theFile)
{ fileDlg := new(FileDialog, "*.VID");
  if canClose(self) cand (runModal(fileDlg, 
    FILE_BOX, self) == IDOK)
  then 
    clear(videoLB);
    fileName := loadFile(fileDlg);
    isDirty := false;
    theFile := new(TextFile);
    setDelimiter(theFile, asString(Tab));
    setName(theFile, fileName);
    open(theFile, 0);
    checkError(theFile);
    load(videoLB, theFile);
    close(theFile);
    checkError(theFile);
    selectIndex(videoLB, 0);
    currentVideo := getSelVideo(videoLB);
    displayCurrentVideo(self);
    setText(self, "VideoMATIC - " + 
      asUpperCase(fileName));
  endIf;
} !!

/* Responds to a File New menu selection by clearing
  the inventory list of all items. */
Def newCommand(self | aVideo, tempDC)
{ if canClose(self)
  then 
    clear(videoLB);
    currentVideo := nil;
    displayCurrentVideo(self);
    isDirty := false;
    fileName := nil;
    setText(self, "VideoMATIC - (untitled)");
  endIf;
} !!

/* Displays in the information box additional fields
  of the currently selected video. */
Def displayCurrentVideo(self | theText, theDC)
{ theDC := getContext(self);
  Call Rectangle(theDC, 20, 140, 370, 260);
  if currentVideo then
    theText := "Cost of Inventory:   " + 
      costOfInventoryStr(currentVideo);
    Call TextOut(theDC, 30, 150, theText, size(theText));
    theText := "Minimum Quantity:   " + 
      asString(minimumQuantity(currentVideo));
    Call TextOut(theDC, 30, 170, theText, size(theText));
    theText := "Reorder Quantity:   " +
      asString(reorderQuantity(currentVideo));
    Call TextOut(theDC, 30, 190, theText, size(theText));  
    theText := "Manufacturer:   " +
      manufacturer(currentVideo);
    Call TextOut(theDC, 30, 210, theText, size(theText));  
    theText := "Phone:   " +
      phone(currentVideo);
    Call TextOut(theDC, 30, 230, theText, size(theText));  
  endIf;
  releaseContext(self, theDC);
}
!!

/* Displays, in a special font, the company's logo
  on the specified display context, hdc.  */
Def drawLogo(self, hdc | theText, newFont, oldFont, 
  oldTextColor, oldBkColor)
{ theText := "  Chartbuster Video Distribution, Inc.  ";
  newFont := Call CreateFont(24, 0, 0, 0, FW_BOLD, 1,
    0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, 
    CLIP_DEFAULT_PRECIS, PROOF_QUALITY, 
    VARIABLE_PITCH bitOr FF_SWISS, "Helv");
  oldFont := Call SelectObject(hdc, newFont);
  /* Change the text and background colors here.
    Use the hex format 0xBBGGRR for blue, red and 
    green. */  
  oldTextColor := Call SetTextColor(hdc, 0x00FFFFL);
  oldBkColor := Call SetBkColor(hdc, 0x800000L);
  Call TextOut(hdc, 10, 270, theText, size(theText));
  Call SetBkColor(hdc, oldBkColor);
  Call SetTextColor(hdc, oldTextColor);
  Call SelectObject(hdc, oldFont);
  Call DeleteObject(newFont);
}
!!

/* Updates the window by drawing the text lables, the
  logo and the information box. */
Def paint(self, hdc | theText)
{ theText := "Part #";
  Call TextOut(hdc, 22, 2, theText, size(theText));
  theText := "Title";
  Call TextOut(hdc, 70, 2, theText, size(theText));
  theText := "Qty.";
  Call TextOut(hdc, 250, 2, theText, size(theText));
  theText := "Price";
  Call TextOut(hdc, 300, 2, theText, size(theText));
  displayCurrentVideo(self);
  drawLogo(self, hdc);
}!!

/* Adds to the actions dictionary the associated pairs
  of control IDs and Actor message names to send as a
  result of an event in the corresponding control. */
Def initActions(self)
{ actions := new(Dictionary, 10);
  add(actions, 601, #videoSelected);
  add(actions, 602, #newVideo);
  add(actions, 603, #shipVideo);
  add(actions, 604, #receiveVideo);
}
!!

/* Initializes the newly created video window. */
Def initControls(self | theControl)
{ controlDict := new(Dictionary, 4);
  /* build the child controls */
  videoLB := new(VideoList, 601, self);
  setCRect(videoLB, &(20, 20, 370, 125));
  add(controlDict, 601, videoLB);
  
  theControl := newPush(Button, 602, self, "&New Video");
  setCRect(theControl, &(390, 22, 470, 47));
  add(controlDict, 602, theControl);
  
  theControl := newPush(Button, 603, self, "&Ship");
  setCRect(theControl, &(390, 56, 470, 81));
  add(controlDict, 603, theControl);
  
  theControl := newPush(Button, 604, self, "&Receive");
  setCRect(theControl, &(390, 90, 470, 115));
  add(controlDict, 604, theControl);

  do(controlDict,
  {using(ctrl) moveWindow(ctrl);
  });
}!!

/* Displays the help window on the screen by invoking
  its inherited show method and then showing its 
  child windows. */
Def show(self, scrnMode)
{ show(self:ancestor, scrnMode);
  do(controlDict,
  {using(ctrl) show(ctrl, SW_SHOWNORMAL);
  });
}!!

/* Builds a dynamic menu and stores it in the menu
instance variable. */
Def buildMenu(self | pop1, act1, act2, act3, act4, 
  act5)
{ menu := create(new(Menu), self);
  pop1 := newPopup(MenuItem, "&File");
    act1 := new(MenuItem, "&New", 1001, #newCommand);
    act2 := new(MenuItem, "&Open", 1002, #openCommand);
    act3 := new(MenuItem, "&Save", 1003, #saveCommand);
    act4 := new(MenuItem, "Save&As", 1004, #saveAsCommand);
    act5 := new(MenuItem, "&Export to Excel", 1005, #exportCommand);
    addItem(pop1, act1);
    addItem(pop1, act2);
    addItem(pop1, act3);
    addItem(pop1, act4);
    addItem(pop1, act5);
    addItem(menu, pop1);
  pop1 := newPopup(MenuItem, "&View");
    act1 := new(MenuItem, "&Chart", 2001, #chartCommand);
    act2 := new(MenuItem, "&Reorder", 2002, #reorderCommand);
    addItem(pop1, act1);
    addItem(pop1, act2);
    addItem(menu, pop1);
}
!!

/* Processes the menu selections by automatically 
  sending the corresponding message. */
Def command(self, wP, lP | msg)
{ /* process menu selections */
  if msg := action(menu, wP)
  then ^perform(self, msg);
  endif; 
  /* process child control actions */
  if msg := at(actions, wP)
  then ^perform(self, wP, lP, msg);
  endif; 
}!!

/* Initializes the newly created video window. */
Def init(self)
{ init(self:ancestor);
  buildMenu(self);
  drawMenu(self);
  initControls(self);
  initActions(self);
  currentVideo := nil;
  fileName := nil;
  isDirty := false;
}!!
