Compile function REACT_RUN_EVENTS(CtrlEntId, Event, @PARAMS)
/***********************************************************************************************************************

    Name        :   React_Run_Events

    Description :   Commuter module for the REACT_RUN form.

    Notes       :   Application errors should be logged using the Error Services module. There are a few methodological
                    assumptions built into way errors are managed which are important to understand in order to properly
                    work with Error Services:

                    - The term 'top' refers to the originating procedure of a call stack and the term 'bottom' refers to
                      the last routine (or the current routine) within a call stack. Within the OpenInsight Debugger
                      this will appear backwards since the originating procedure always appears at the bottom of the
                      list and the current routine appears at the top of the list. We are using this orientation because
                      it is common to refer to the process of calling other procedures as 'drilling down'.

                    - The reason for defining the orientation of the call stack is because Error_Services allows for
                      multiple error conditions to be appended to an original error. In most cases this will happen when
                      a procedure at the bottom of the stack generates an error condition and then returns to its
                      calling procedure. This higher level procedure can optionally add more information relevant to
                      itself. This continues as the call stack 'bubbles' its way back to the top to where the
                      originating procedure is waiting.

                    - Native OpenInsight commands that handle errors (e.g., Set_Status, Set_FSError, Set_EventStatus)
                      preserve their error state until explicitly cleared. This can hinder the normal execution of code
                      since subsequent procedures (usually SSPs) will fail if a pre-existing error condition exists.
                      Our philosophy is that error conditions should automatically be cleared before a new procedure
                      is executed to avoid this problem. However, the nature of Basic+ does not make this easy to
                      automate for any given stored procedure. Therefore, if a stored procedure wants to conform to our
                      philosophy then it should include a call into the 'Clear' service request at the top of the
                      program. Alternatively this can be done through a common insert (see SERVICE_SETUP for example.)

                    - Service modules will use the SERVICE_SETUP insert and therefore automatically clear out any
                      error conditions that were set before.

    Parameters  :
        Service         [in] -- Name of the service being requested
        Param1-10   [in/out] -- Additional request parameter holders
        Response       [out] -- Response to be sent back to the Controller (MCP) or requesting procedure

    Metadata    :

    History     :   (Date, Initials, Notes)
        01/11/19    djs     Original programmer.
        07/12/19    fr      Updated FillDispEditTable subroutine to not allow "Internal" wafers to be NCR'd.
        07/23/19    djs     Updated the UpdateDispData routine to only look for
        06/06/20    djs     Cutover to CR-004 and CR-005

***********************************************************************************************************************/
#pragma precomp SRP_PreCompiler
#window REACT_RUN

DECLARE SUBROUTINE Set_Property, End_Dialog, Send_Event, Set_Status, Center_Window, Post_Event, SRP_EditTable_Manager
DECLARE SUBROUTINE ErrMsg, Send_Message, Set_Property, Send_Event, Btree.Extract, obj_AppWindow, SRP_Show_Window
DECLARE SUBROUTINE obj_Notes, Security_Err_Msg, End_Window, Forward_Event, Start_Window, Create_Note, Excel_Services
DECLARE SUBROUTINE obj_RDS_Test, Print_Control_Plan, Print_RDS_Instruction, Send_Info, Post_Event, Database_Services
DECLARE SUBROUTINE obj_Run_Stage_Wfr, obj_WO_Wfr, obj_Run_Stage, GaN_Services, Utility, SRP_Stopwatch, COMM_NCR
DECLARE SUBROUTINE Dialog_Box, Get_Status, Logging_Services, Messaging_Services, SRP_Array, Error_Services
DECLARE SUBROUTINE Disposition_Services, Engineering_Services, Reactor_Services

DECLARE FUNCTION Get_Property, Get_Status, Dialog_Box, Utility, Center_Window, Popup, Collect.Ixvals, Security_Services
DECLARE FUNCTION Send_Message, Msg, Security_Check, RowExists,  NextKey, obj_React_Esc, obj_React_Run, Form_Services
DECLARE FUNCTION obj_RDS_Test, MemberOf, Create_Dialog, obj_Popup, obj_WO_Wfr, SRP_EditTable_Manager, Database_Services
DECLARE FUNCTION Obj_Run_Stage, Obj_Run_Stage_Wfr, Gan_Services, SRP_Array, DateTime, Error_Services, Obj_NCR
DECLARE FUNCTION Memory_Services, SRP_Get_Window_Rect, Excel_Services, Environment_Services, Logging_Services
DECLARE FUNCTION SRP_Array

$INSERT APPCOLORS
$INSERT RTI_STYLE_EQUATES
$INSERT EVENT_SETUP
$INSERT MSG_EQUATES
$INSERT LSL_USERS_EQU
$INSERT SECURITY_RIGHTS_EQU
$INSERT REACT_EVENT_EQUATES
$INSERT REACT_RUN_EQUATES
$INSERT WO_STEP_EQU
$INSERT WO_MAT_EQUATES
$INSERT WO_WFR_WIP_EQUATES
$INSERT EPI_SUSCEPTOR_EQUATES
$INSERT REACTOR_EQUATES
$INSERT POPUP_EQUATES
$INSERT TOOL_CLASS_EQUATES
$INSERT RUN_STAGE_EQUATES
$INSERT LOCATION_EQUATES
$INSERT RUN_STAGE_WFR_EQUATES
$INSERT WO_WFR_EQUATES
$INSERT SRP_POPUP_EQUATES
$INSERT LOGICAL
$INSERT WO_LOG_EQUATES

// Cassette / Wafer Action Combo Box Selections
EQU COMBO_X$            TO 1
EQU COMBO_START$        TO 2
EQU COMBO_STOP$         TO 3
EQU COMBO_COMP$         TO 4
EQU COMBO_PART$         TO 5

EQU COL$LSID			TO 1
EQU COL$RECIPE			TO 2
EQU COL$RECIPE_NAME		TO 3
EQU COL$PARAMS			TO 4
EQU COL$PARMS_OUT		TO 5
EQU COL$BLANK_COL		TO 6
EQU COL$MET_NO			TO 7
EQU COL$THICK_AVG		TO 8
EQU COL$RRHO_AVG		TO 9
EQU COL$MET_NO_Z1		TO 10
EQU COL$THICK_AVG_Z1	TO 11
EQU COL$RRHO_AVG_Z1		TO 12
EQU COL$MET_NO_Z2		TO 13
EQU COL$THICK_AVG_Z2	TO 14
EQU COL$RRHO_AVG_Z2		TO 15
EQU COL$MET_OUT			TO 16
EQU COL$MET_OUT_Z1		TO 17
EQU COL$MET_OUT_Z2		TO 18
EQU COL$MET_TWSIG		TO 19
EQU COL$MET_TWSIG_Z1	TO 20
EQU COL$MET_TWSIG_Z2	TO 21

EQU COL$CHAR_WFR_ID		TO 1
EQU COL$SCRIBE			TO 2
EQU COL$SPLIT_BY		TO 3
EQU COL$SPLIT_DTM		TO 4
EQU COL$CURR_STAGE		TO 5
EQU COL$CHAR_DEST		TO 6
EQU COL$CHAR_TW_NO		TO 7
EQU COL$CHAR_WFR_STATUS	TO 8

EQU COL$CARR_SLOG_NO	TO 1
EQU COL$CARR_WFR_ID		TO 2
EQU COL$CARR_SCRIBE		TO 3
EQU COL$CARR_CHAR_FLAG	TO 4
EQU COL$CARR_NCR		TO 5
EQU COL$CARR_GRADE		TO 6

EQU COL$SIG_PROF		TO 1
EQU COL$SIGNATURE		TO 2
EQU COL$SIG_DTM			TO 3
EQU COL$STAGE_COMMENT	TO 4
EQU COL$STAGE_STATUS	TO 5

EQU COL$MET_STAGE       TO 1
EQU COL$MET_TOOL_ID     TO 2

// Disposition OLE Edit Table
EQU COL$DISP_SLOT        TO 1
EQU COL$DISP_WFR_ID      TO 2
EQU COL$DISP_SCRIBE      TO 3
EQU COL$DISP_GRADE       TO 4
EQU COL$DISP_REASON      TO 5
EQU COL$DISP_RDS_NCR     TO 6
EQU COL$DISP_WMO_NCR     TO 7
EQU COL$DISP_SHIP        TO 8
EQU COL$DISP_SHIP_ID     TO 9
EQU COL$DISP_INTERNAL    TO 10
EQU COL$DISP_EXTERNAL    TO 11
EQU COL$DISP_RETAIN      TO 12
EQU COL$DISP_RETAIN_BOX  TO 13
EQU COL$DISP_RETAIN_SLOT TO 14

// NCR OLE Edit Table
EQU COL$NCR_NO			 TO 1
EQU COL$NCR_STATUS		 TO 2
EQU COL$NCR_REJ_QTY		 TO 3
EQU COL$NCR_RESP		 TO 4
EQU COL$NCR_STAGE		 TO 5
EQU COL$NCR_LOSS_DESC	 TO 6
EQU COL$NCR_LOSS_COMM	 TO 7
EQU COL$NCR_FIN_SIG		 TO 8
EQU COL$NCR_FIN_SIG_DTM	 TO 9

EQU COL$SUSC_LOAD_WFR_ID        TO 6    ;* SUSC_LOAD control
EQU COL$SUSC_LOAD_WFR_SCRIBE	TO 7

EQU EDITABLE$			TO 4
EQU PROTECTED$			TO 8			;* Protected  - Edittable COLSTYLE constants
EQU MULTILINE_STYLE$	TO 512 			;* MultiLine Select
EQU LOCKED$				TO 8192
EQU DROPDOWN_STYLE$		TO 131072

LogPath     = Environment_Services('GetApplicationRootPath') : '\LogFiles\GaNWIP'
LogDate     = Oconv(Date(), 'D4/')
LogTime     = Oconv(Time(), 'MTS')
LogFileName = LogDate[7, 4] : '-' : LogDate[1, 2] : '-' : LogDate[4, 2] : ' GaN WIP Log.csv'
Headers     = 'Logging DTM' : @FM : 'RDS Key ID' : @FM : 'Username' : @FM : 'Notes'
objLog      = Logging_Services('NewLog', LogPath, LogFileName, CRLF$, Comma$, Headers, '', False$, False$)
LoggingDTM  = LogDate : ' ' : LogTime   ; // Logging DTM

SubclassInfo    = Form_Services('FindSubclassControl')
Subclass        = SubclassInfo<1>

// Update the arguments so that the OpenInsight OLE event will treate the ActiveX event as a native event handler.
If Event EQ 'OLE' then
    Transfer Event to OIEvent
    Transfer Param1 to Event
    Transfer Param2 to Param1
    Transfer Param3 to Param2
    Transfer Param4 to Param3
    Transfer Param5 to Param4
    Transfer Param6 to Param5
    Transfer Param7 to Param6
    Transfer Param8 to Param7
end

GoToEvent Event for CtrlEntId else
    // Event not implemented
end

Return EventFlow or 1

//-----------------------------------------------------------------------------
// EVENT HANDLERS
//-----------------------------------------------------------------------------

Event WINDOW.CREATE(CreateParam)
    
            
    IOOptions    = Get_Property(@Window, 'IOOPTIONS')
    IOOptions<2> = 2 ;// Do not lock the record
    Set_Property(@Window, 'IOOPTIONS', IOOptions)
    
    FormVisible = Get_Property(@Window, 'VISIBLE')
    If CreateParam NE '' then
        RDSNo = CreateParam
        WONo  = Xlate('REACT_RUN', RDSNo, 'WO_NO', 'X')
        PSNo  = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X')
        TechType = Xlate('PROD_SPEC', PSNo, 'TECH_TYPE', 'X')
        
        If TechType EQ 'P' then            
            ParentWindow = Get_Property(@Window, "PARENT")
            Start_Window('DBW_GAN_RDS_PROD', ParentWindow, RDSNo)
            GoTo Abort  
        end
        
        Set_Property(@Window, '@RDSNO', RDSNo)
        Def = ""
        Def<MCAPTION$>   = "Loading..."
        Def<MTYPE$>      = "G"
        Def<MEXTENT$>    = 100 ; // 100%
        Def<MTEXTWIDTH$> = 400
        Def<MCOL$>       = -2        ;* message h-pos in pixels, or -2 (center screen, the default), -1 (center parent)
        Def<MROW$>       = -2        ;* message v-pos in pixels
        MsgUp  = Msg(@Window, Def)   
        If FormVisible then
            Set_Property(@Window, 'REDRAW', False$)
            Set_Property(@Window:'.OLE_REACTOR_EDT'     , "OLE.Redraw", False$)
            Set_Property(@Window:'.OLE_METROLOGY_EDT'   , "OLE.Redraw", False$)
            Set_Property(@Window:'.OLE_DISP_EDT'        , "OLE.Redraw", False$)
            Set_Property(@Window:'.OLE_DISP_STAGE_EDT'  , "OLE.Redraw", False$)
            Set_Property(@Window:'.OLE_NCR_EDT'         , "OLE.Redraw", False$)
        end

        RDSNo = CreateParam
        Set_Property(@Window : '.RDS_NO', 'TEXT', RDSNo)
        RunPriorities = '':@FM:'P1':@FM:'P2':@FM:'P3'
        Set_Property(@Window:'.COMBO_PRIORITY', 'LIST', RunPriorities)
        
        If RDSNo Not(Num(RDSNo)) then
            * update the gauge
            Msg(@Window, MsgUp, 10, MSGINSTUPDATE$)
            // Check if run info scribes have been updated
            ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
            InWfrIDs    = ReactRunRec<REACT_RUN_IN_WFR_ID$>
            RunPriority = ReactRunRec<REACT_RUN_RUN_PRI$>
            Set_Property(@Window:'.COMBO_PRIORITY', 'TEXT', RunPriority)
            GaNRunID    = ReactRunRec<REACT_RUN_GAN_RUN_ID$>
            GrowthKey   = RDSNo:'*GROWTH'
            GrowthRec   = Database_Services('ReadDataRow', 'RUN_STAGE', GrowthKey)
            InvActions  = GrowthRec<RUN_STAGE_SPEC_INV_ACTION$>
            Locate 'LOAD' in InvActions using @VM setting vPos then
                LoadSig = GrowthRec<RUN_STAGE_SIA_COMP_BY$, vPos>
                If LoadSig NE '' then
                    RRScribes  = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
                    GaNRunInfo = Gan_Services('GetRunInfo', GaNRunID)
                    If Error_Services('NoError') then
                        RunScribes = GaNRunInfo<4>
                        If ( (RRScribes NE RunScribes) and (RunScribes NE '') ) then                           
                            // Scribes have been updated in wafer track database
                            For each WfrID in InWfrIDs using @VM setting vPos
                                WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
                                WOWfrRec<WO_WFR_SCRIBE$> = RunScribes<0, vPos>
                                Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec)
                            Next WfrID
                        end
                    end
                end
            end
            
            Send_Event(@Window, 'READ')
            FormVisible = Get_Property(@Window, 'VISIBLE')
            * update the gauge
            Msg(@Window, MsgUp, 20, MSGINSTUPDATE$)
            
            WONo              = Get_Property(@Window:'.WO_NO', 'TEXT')
            If WONo EQ '' then WONo = Xlate('RDS', RDSNo, 'WO', 'X')
            WOLogRec          = Database_Services('ReadDataRow', 'WO_LOG', WONo)
            SPCWaferTrackPart = WOLogRec<WO_LOG_WAFER_TRACK_PART$>
            Set_Property(@Window:'.EDL_WAFER_TRACK_PART', 'TEXT', SPCWaferTrackPart)
            Reactor = ReactRunRec<REACT_RUN_REACTOR$>
            If Reactor EQ '' then Reactor = GaN_Services('GetReactor', WONo)
            Set_Property(@Window, '@REACTOR', Reactor)
            ReactRunRec<REACT_RUN_REACTOR$> = Reactor
            Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
            * update the gauge
            Msg(@Window, MsgUp, 30, MSGINSTUPDATE$)
            If GaNRunID EQ '' then
                AvailGaNRunIDs = Gan_Services('GetAvailableRunIDs', Reactor, WoNo)
                Msg(@Window, MsgUp, 40, MSGINSTUPDATE$)
                EpiPartNo = Get_Property(@Window:'.EPI_PART_NO', 'TEXT')
                NumRunIDs = DCount(AvailGaNRunIDs, @FM)
                FilteredList = ''
                Voltage = ''
                Voltage = Xlate('WO_LOG', WONo, 'GAN_COST_TYPE', 'X')
                Begin Case
                    Case Voltage _EQC 'High Voltage'
                        // Filter on hvfet
                        For RunIndex = 1 to NumRunIDs
                            RunID = AvailGaNRunIDs<RunIndex>
                            If Indexc(RunID, 'HV', 1) then
                                FilteredList<-1> = RunID
                            end
                        Next RunIndex
                    Case Voltage _EQC 'Mid Voltage'
                        // Filter on mvfet - ( e.g. U014 is Medium Voltage (MV) )
                        For RunIndex = 1 to NumRunIDs
                            RunID = AvailGaNRunIDs<RunIndex>
                            If Indexc(RunID, 'MV', 1) then
                                FilteredList<-1> = RunID
                            end
                        Next RunIndex
                    Case Otherwise$
                        ErrorMsg = 'Unrecognized GaN Cost Type (Voltage): "':Voltage:'" read from work order.':CRLF$ |
                        : 'Unabled to filter GaN Run ID list.':CRLF$:' Verify GaN Cost Type in work order ' |
                        : 'or contact FI team for assistance.'
                        ErrMsg(ErrorMsg)
                        FilteredList = AvailGaNRunIDs
                End Case
                
                Set_Property(@Window : '.CMB_GAN_RUN_ID', 'LIST', FilteredList)
            end else
                Convert @Upper.Case to @Lower.Case in GaNRunID
                Set_Property(@Window : '.CMB_GAN_RUN_ID', 'LIST', GaNRunID)
                Set_Property(@Window : '.CMB_GAN_RUN_ID', 'SELPOS', 1)
            end
            * update the gauge
            Msg(@Window, MsgUp, 50, MSGINSTUPDATE$)
            GaNEtchIDs = Xlate('REACT_RUN', RDSNo, 'GAN_ETCH_ID', 'X')
            Convert @VM to @FM in GaNEtchIDs
            If GaNEtchIDs NE '' then
                NumEtchIDs = DCount(GaNEtchIDs, @FM)
                Set_Property(@Window : '.CMB_GAN_ETCH_ID', 'LIST', GaNEtchIDs)
                // Select the last external etch ID to display by default
                Set_Property(@Window : '.CMB_GAN_ETCH_ID', 'SELPOS', NumEtchIDs)
                GaNEtchRecipes = Xlate('REACT_RUN', RDSNo, 'GAN_ETCH_RECIPE', 'X')
                LastEtchRecipe = GaNEtchRecipes<0, NumEtchIDs>
                Set_Property(@Window : '.ETCH_RECIPE', 'TEXT', LastEtchRecipe)
                EtchKey    = RDSNo:'*ETCH'
                EtchStatus = Xlate('RUN_STAGE', EtchKey, 'STATUS', 'X')
                If EtchStatus EQ 'COMP' then
                    // Verify the etch recipe hasn't changed.
                    ExternalEtchID       = Get_Property(@Window:'.CMB_GAN_ETCH_ID', 'TEXT')
                    GaNEtchInfo          = Gan_Services('GetRunInfo', ExternalEtchID)
                    WaferTrackEtchRecipe = GaNEtchInfo<1,1>                    
                    If WaferTrackEtchRecipe NE LastEtchRecipe then
                        ReactRunRec<REACT_RUN_GAN_ETCH_RECIPE$, NumEtchIDs> = WaferTrackEtchRecipe
                        Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
                        Set_Property(@Window:'.ETCH_RECIPE', 'TEXT', WaferTrackEtchRecipe)
                    end
                end                
            end else
                Set_Property(@Window : '.CMB_GAN_RUN_ID', 'ENABLED', False$)
            end
            GoSub Setup_OLE_Controls
            // Need to check if any wafers have made it to disposition. If so, then check the Disposition Report
            // for any updates.
            * update the gauge
            Msg(@Window, MsgUp, 60, MSGINSTUPDATE$)
            WfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>
            If WfrIDs NE '' then
                For each WfrID in WfrIDs using @VM setting vPos
                    MetDone = GaN_Services('GetMetrologyStatus', WfrID)
                Until MetDone EQ True$
                Next WfrID
                RDSClosed = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
                Begin Case
                    Case ( (MetDone EQ True$) and (RDSClosed NE True$) )
                        Set_Property(@Window:'.PUB_UPDATE_DISP', 'ENABLED', True$)
                        GoSub UpdateDispData
                        Disposition_Services('AutoDispositionRun', RDSNo)
                    Case ( (MetDone EQ True$) and (RDSClosed EQ True$) )
                        Set_Property(@Window:'.PUB_UPDATE_DISP', 'ENABLED', True$)
                    Case Otherwise$
                        Null
                End Case
            end
            GoSub VerifyRecipe
            * update the gauge
            Msg(@Window, MsgUp, 70, MSGINSTUPDATE$)
            GoSub FillDispEditTable
            * update the gauge
            Msg(@Window, MsgUp, 80, MSGINSTUPDATE$)
            GoSub FillNCREditTable
            * update the gauge
            Msg(@Window, MsgUp, 90, MSGINSTUPDATE$)
            GoSub FillCommentsTable
        end
*        GoSub VerifyScribes
        GoSub EnableRunIDComboBox
        GoSub EnableDispButton
        GoSub UpdateRDSStatus
        GoSub EnableLotAbortButton
        * update the gauge
        Msg(@Window, MsgUp, 100, MSGINSTUPDATE$)
        Msg(@Window, MsgUp)         ;* take down the gauge
    end
    If Not(FormVisible) then SRP_Show_Window(@Window, '', 'C', 'C', 1, '', False$, False$, FormSize)
    Set_Property(@Window, 'REDRAW', True$)
    Set_Property(@Window:'.OLE_REACTOR_EDT'     , "OLE.Redraw", True$)
    Set_Property(@Window:'.OLE_METROLOGY_EDT'   , "OLE.Redraw", True$)
    Set_Property(@Window:'.OLE_DISP_EDT'        , "OLE.Redraw", True$)
    Set_Property(@Window:'.OLE_DISP_STAGE_EDT'  , "OLE.Redraw", True$)
    Set_Property(@wINDOW:'.OLE_NCR_EDT'         , "OLE.Redraw", True$)
    
End Event


Event WINDOW.CLEAR(bSaveKey, bSuppressWarning, bMaintainFocus, CtrlIDFocus)
    
    IF Get_Property(@WINDOW,'@READONLY') THEN
        obj_AppWindow('ReadOnly',@RM:1)			;* Reenables data bound controls
        Set_Property(@WINDOW,'@READONLY',0)		;* Clear flag on window
    END
    
end event



Event WINDOW.CLOSE(CancelFlag)        
    
    obj_Notes('Inbox',@USER4)	;* Checks for any new messages
    
    Parent = Get_Property(@WINDOW,'PARENT')
    
    IF Parent[1,7] = 'WO_PROD' THEN
        Post_Event(Parent, 'READ')
    END
    
end event


Event WINDOW.WRITE()
    
    prevControl = Get_Property("SYSTEM" , "PREVFOCUS")
    EventFlow = 1
    
end event


Event WINDOW.PAGE(PageAction)
    
    Page = Get_Property(@WINDOW:'.TABCONTROL', 'VALUE')
    Set_Property(@WINDOW,'VPOSITION', Page)
    
end event


Event WINDOW.VSCROLL(Value)
    
    Set_Property(@WINDOW:'.TABCONTROL','VALUE', Value)
    
end event


Event WINDOW.OMNIEVENT(Message, Param1, Param2, Param3, Param4)
    
    Begin Case
        Case Message _EQC 'FillDispEditTable'
            GoSub FillDispEditTable
        Case Message _EQC 'FillNCREditTable'
            GoSub FillNCREditTable
    End Case
    
end event


Event CMB_GAN_RUN_ID.CHANGED(NewData)
    
    If NewData NE '' then
        GaNRunID          = NewData
        ReactorNo         = Get_Property(@Window, '@REACTOR')
        GaNRunInfo        = Gan_Services('GetRunInfo', GaNRunID)
        Recipe            = GaNRunInfo<1,1>
        SuscSerialNo      = GaNRunInfo<2,1>
        SPCWaferTrackPart = GaNRunInfo<6,1>
        Set_Property(@Window:'.EDL_WAFER_TRACK_PART', 'TEXT', SPCWaferTrackPart)
        Set_Property(@Window:'.OLE_REACTOR_EDT', "OLE.HeaderText[1; 2]", 'Reactor ':ReactorNo)
        WOLogOpenInsightPart = Get_Property(@Window:'.EPI_PART_NO', 'TEXT')
        Convert @Lower.Case to @Upper.Case in GaNRunID
        SchedWaferTrackPart  = Xlate('GAN_SCHEDULE', GaNRunID, 'WAFER_TRACK_PART', 'X')
        SchedOpenInsightPart = Xlate('GAN_SCHEDULE', GaNRunID, 'OPENINSIGHT_PART', 'X')
        // OpenInsightPart Verification
        If SchedOpenInsightPart NE WOLogOpenInsightPart then
            Set_Property(@Window:'.EPI_PART_NO', 'BACKCOLOR', RED$)
        end else
            Set_Property(@Window:'.EPI_PART_NO', 'BACKCOLOR', GREEN$)
        end
        // WaferTrackPart Verification
        If SchedWaferTrackPart NE SPCWaferTrackPart then
            Set_Property(@Window:'.EDL_WAFER_TRACK_PART', 'BACKCOLOR', RED$)
        end else
            Set_Property(@Window:'.EDL_WAFER_TRACK_PART', 'BACKCOLOR', GREEN$)
        end
        Set_Property(@Window, '@GANRUNINFO', GaNRunInfo)
        Set_Property(@Window : '.GAN_RECIPE', 'TEXT', Recipe)
        Set_Property(@Window : '.GAN_SUSC_SERIAL', 'TEXT', SuscSerialNo)
        GoSub VerifyRecipe   
    end
    
end event


Event CMB_GAN_ETCH_ID.CHANGED(NewData)
    
    ExternalEtchID = NewData
    Begin Case
        Case (ExternalEtchID _EQC 'No Etch Required')
            Set_Property(@Window : '.ETCH_RECIPE', 'TEXT', 'Not Applicable')
        Case (ExternalEtchID NE '') and (ExternalEtchID _NEC 'No Etch Required')
            GaNEtchInfo  = Gan_Services('GetRunInfo', ExternalEtchID)
            Recipe       = GaNEtchInfo<1,1>
            Set_Property(@Window : '.ETCH_RECIPE', 'TEXT', Recipe)
    End Case
    
end event


Event CTRL_PLAN.CLICK()
    
    PSNo = Get_Property(@WINDOW:'.PS_NO','DEFPROP')
    
    IF PSNo NE '' THEN
        Print_Control_Plan(PSNo)
    END
    
end event


Event WONO_BUTTON.CLICK()
    
    WONo = Get_Property(@WINDOW:'.WO_NO','DEFPROP')
    IF WONo NE '' THEN
        NewForm = Xlate('APP_INFO', 'NEW_WO_FORM', '', 'X')
        If NewForm then
            Start_Window('NDW_WO_LOG', @Window, WONo)
        end else
            Start_Window('WO_LOG2',@WINDOW, WONo:'*CENTER', '', '' )	;* Added (copied from ViewPSN) -dkk 10/23/14
        end
    END
    
end event


Event RDS_NO.LOSTFOCUS(Flag, FocusID)

    ReactRunKey = Get_Property(@Window:'.RDS_NO', 'TEXT')    
    If ReactRunKey NE '' then
        RDSNo = Get_Property(@Window, '@RDSNO')
        If ReactRunKey NE RDSNo then
            Reactor = Xlate('REACT_RUN', ReactRunKey, 'REACTOR', 'X')            
            If Reactor EQ '' then
                WONo    = Get_Property(@Window:'.WO_NO', 'DEFPROP')
                Reactor = GaN_Services('GetReactor', WONo)
                If ( (Reactor EQ 69) or (Reactor EQ 71) ) then
                    ReactRunRec                     = Database_Services('ReadDataRow', 'REACT_RUN', ReactRunKey)
                    ReactRunRec<REACT_RUN_REACTOR$> = Reactor
                    Database_Services('WriteDataRow', 'REACT_RUN', ReactRunKey, ReactRunRec, True$, False$, True$)
                end
            end
            ReactorType = Xlate('REACT_RUN', ReactRunKey, 'REACTOR_TYPE', 'X')
            If ReactorType _EQC 'GAN' then
                If RowExists('REACT_RUN', ReactRunKey) then            
                    Send_Event(@Window, 'CREATE', ReactRunKey)
                end else
                    ErrMsg('REACT_RUN record ':ReactRunKey:' does not exist! Contact FI admin for assistance.')
                end
            end else
                ErrMsg('Incompatible reactor type ':ReactorType:'. Reactor type must be GAN.')
            end           
        end
    end
    
end event


Event SEND_MSG.CLICK()
    CurrWindow	= @WINDOW
    CurrKey		= Get_Property(CurrWindow,'ID')
    
    IF CurrKey = '' THEN 
        MsgInfo = ''
        MsgInfo<MTEXT$> = 'You must have a record  present...' 
        MsgInfo<MICON$> = 'H'
        Msg( '', MsgInfo )
        RETURN
    END
    
    IF RowExists('REACT_RUN',CurrKey) THEN
        
        NoteID = NextKey('NOTES')
        obj_AppWindow('ViewRelated','NOTE_MESSAGE':@RM:NoteID:@RM:@WINDOW:@FM:CurrKey)
        
    END ELSE
        
        MsgInfo = ''
        MsgInfo<mtext$> = 'You must save this run data sheet first...' 
        MsgInfo<micon$> = 'H'
        Msg( '', MsgInfo )
    END
    
end event


Event PSNO_BUTTON.CLICK()
    
    PSNo = Get_Property(@WINDOW:'.PS_NO','DEFPROP')
    
    IF PSNo NE '' THEN
        Start_Window('PROD_SPEC',@WINDOW, PSNo:'*CENTER', '', '' )	;* Old style call to old style window
    END
    
end event


Event EDB_CASS_COMMENTS.CHANGED(NewData)
    
    NewComments = NewData
    OrigComments = Get_Property(@Window, '@COMMENTS')
    
    If NewComments NE OrigComments then
        Set_Property(@Window:'.PUB_SAVE_COMMENTS', 'ENABLED', True$)
    end else
        Set_Property(@Window:'.PUB_SAVE_COMMENTS', 'ENABLED', True$)
    end
    
end event


Event PUB_SAVE_COMMENTS.CLICK()
    
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    CassComments = Get_Property(@Window:'.EDB_CASS_COMMENTS', 'TEXT')
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    ReactRunRec<REACT_RUN_CASS_COMMENTS$> = CassComments
    Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
    Set_Property(CtrlEntID, 'ENABLED', False$)
    Set_Property(@Window, '@COMMENTS', CassComments)
    
end event


Event PUB_UPDATE_DISP.CLICK()
    
    PrevCursor  = Set_Property('SYSTEM', 'CURSOR', 'H')
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    GoSub UpdateDispData
    Disposition_Services('AutoDispositionRun', RDSNo)
    GoSub FillDispEditTable
    GoSub FillMetEditTable
    PrevCursor  = Set_Property('SYSTEM', 'CURSOR', 'A')
    
end event


Event PUB_UPDATE_EA_DATA.CLICK()
    
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    Engineering_Services('UpdateAll', RDSNo)
    
end event


Event PUB_DISP.CLICK()
    
    RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    DispComp    = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
    WfrIDs      = ReactRunRec<REACT_RUN_IN_WFR_ID$>
    
    If DispComp EQ True$ then
        // User is attempting to open this RDS. Check for GAN_WIP_SUPERVISOR group membership.
        Response    = Dialog_Box('NDW_VERIFY_USER', @Window, @USER4 : @FM : 'GAN_WIP_SUPERVISOR')
        Valid       = Response<1>
        If Valid EQ True$ then
            CurrDTM  = Datetime()
            DispComp = False$
            // Move wafers back into the DISP location queue.
            For each WfrID in WfrIDs using @VM setting vPos
                // Mark DISP, G_PACK, and RETAIN as INIT
                Stages = 'DISP,G_PACK,RETAIN'
                For each Stage in Stages using ','
                    FormattedWfrID = WfrID
                    Convert '*' to '.' in FormattedWfrID
                    RunStageWfrKey = RDSNo:'*':Stage:'*':FormattedWfrID
                    RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
                    RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$>  = ''
                    RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$> = ''
                    RunStageWfrRec<RUN_STAGE_WFR_STATUS$>   = 'INIT'
                    Database_Services('WriteDataRow','RUN_STAGE_WFR',RunStageWfrKey,RunStageWfrRec,True$,False$,True$)
                Next Stage
                DispQ = 'GGR*Q_DISP'
                CurrQ = Gan_Services('GetWfrQueue', WfrID)
                Gan_Services('RemoveWfrFromWIP', WfrID)
                Gan_Services('MoveWfrToQueue', WfrID, DispQ)
                WOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
                LocDTM   = WOWfrRec<WO_WFR_LOC_DTM$>
                NextPos  = DCount(LocDTM, @VM) + 1
                WOWfrRec<WO_WFR_LOC_DTM$, NextPos>   = CurrDTM
                WOWfrRec<WO_WFR_LOC_BY$, NextPos>    = @USER4
                WOWfrRec<WO_WFR_LOC_EVENT$, NextPos> = 'OPEN'
                WOWfrRec<WO_WFR_TOOL_ID$, NextPos>   = ''
                WOWfrRec<WO_WFR_INV_LOC$, NextPos>   = DispQ
                Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
            Next WfrID
            ReactRunRec<REACT_RUN_DISP_COMPLETE$> = DispComp
            Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
            GoSub UpdateDispData
        end
        
    end else

        AllWfrsComp = GaN_Services('ReadyToClose', RDSNo)
        If AllWfrsComp EQ True$ then       
            // User is marking disposition complete for the cassette
            DispComp = True$
            ReactRunRec<REACT_RUN_DISP_COMPLETE$> = DispComp
            Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
            Engineering_Services('ClearEARequests', RDSNo) 
            // Refresh the WO_PROD_GAN form if it is open to reflect possible updates to outbound cassettes.
            GaNProdVis = Get_Property('WO_PROD_GAN', 'VISIBLE')
            If GaNProdVis EQ True$ then Send_Event('WO_PROD_GAN', 'READ')                 
        end else
            Dialog_Box('NDW_WAFER_STATUS', @Window, RDSNo)
        end
  
    end
    
    Disposition_Services('AutoDispositionRun', RDSNo)
    GoSub EnableDispButton
    GoSub UpdateRDSStatus
    
end event


Event PUB_CREATE_NCR.CLICK()
    
    DispEDTCtrl  = @Window:".OLE_DISP_EDT"
    CarrSlotList = Get_Property(DispEDTCtrl, 'OLE.List')
    SelectedRows = Get_Property(DispEDTCtrl, "OLE.ColumnData[":COL$DISP_RDS_NCR:"]")
    CONVERT @VM TO @FM in SelectedRows
    NumWfrs		= DCount(SelectedRows, @FM)
    
    WONo		= Get_Property(@Window:'.WO_NO', 'TEXT')
    RDSNo		= Get_Property(@Window:'.RDS_NO', 'TEXT')
    Reactor		= Get_Property(@Window, '@REACTOR')
    RejWfrIDs	= ''
    RejSlotIDs	= ''
    
    GoodLines = 0
    FOR WfrIndex = 1 TO NumWfrs
        RowSelected = SelectedRows<WfrIndex>
        If RowSelected EQ True$ then
            SlotNo = CarrSlotList<WfrIndex, COL$DISP_SLOT>
            IF CarrSlotList<WfrIndex,COL$CARR_WFR_ID> NE '' Then
                GoodLines += 1
                RejWfrIDs<1,GoodLines> 	= CarrSlotList<WfrIndex,COL$DISP_WFR_ID>
                RejSlotIDs<1,GoodLines>	= RDSNo:'*':SlotNo
            END
        end        
    NEXT I
    
    ncrParms  = WONo:@RM
    ncrParms := '1':@RM
    ncrParms := '':@RM			;* Place holder for WO_MAT_CASS_NO
    ncrParms := RDSNo:@RM		;* Single RDS field 
    ncrParms := Reactor:@RM		;* Reactor No
    ncrParms := '':@RM			;* Loss Stage
    ncrParms := RejWfrIDs:@RM	;* WfrIDs			;* CarrWafer IDs
    ncrParms := RejSlotIDs:@RM	;* CarrSlotIDs		;* GaN PostEpi Carrier Slot ID	-> GaN non-characterization wafers
    ncrParms := '':@RM			;* CassSlotIDs		;* WOMat Cassette Slot ID		-> Rej from cassette
    ncrParms := ''				;* RunPocketZone	;* RPZ							-> Rej from susceptor
    
    Set_Status(0)
    NCRNo = obj_NCR('CreateWfr',ncrParms)		;* Create new NCR for this wafer/group of wafers
    errCode = ''
    IF Get_Status(errCode) THEN
        ErrMsg(errCode)
    end else
        
        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
        Grades      = ReactRunRec<REACT_RUN_CARR_WFR_GRADE$>
        WfrIDs      = ReactRunRec<REACT_RUN_IN_WFR_ID$>
        For each RejWfrID in RejWfrIDs using @VM setting vPos
            Locate RejWfrID in WfrIDs using @VM setting wPos then
                Grades<0, wPos>     = 'Scrap'
            end
        Next RejWfrID
        ReactRunRec<REACT_RUN_CARR_WFR_GRADE$>  = Grades        
        Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
        
        // Check if these wafers are in an outbound cassette. If so,
        // then remove them. Wafers cannot be shipped and scrapped at the same time.
        
        OutSlotIDs  = ReactRunRec<REACT_RUN_OUT_SLOT_ID$>
        WfrIDs      = ReactRunRec<REACT_RUN_IN_WFR_ID$>
        For each RejWfrID in RejWfrIDs using @VM setting vPos
            Locate RejWfrID in WfrIDs using @VM setting wPos then
                Database_Services('ActivateRecord', 'WO_WFR', RejWfrID)
                {RDS_NCR_NO} = NCRNo
                Database_Services('WriteDataRow', 'WO_WFR', RejWfrID, @Record, True$, False$, True$)
                OutSlotID = OutSlotIDs<0, wPos>
                If OutSlotID NE '' then
                    // Remove wafer from outbound cassette
                    OutCass = Field(OutSlotID, '*', 1, 2)
                    GaN_Services('RemoveWfrsFromOutCass', OutCass, RejWfrID)
                end
            end
        Next RejWfrID
        Dialog_Box('NCR', @Window, NCRNo)
        PrevCursor  = Set_Property('SYSTEM', 'CURSOR', 'H')
        Gan_Services('UpdateDisposition')
        GoSub FillMetEditTable
        GoSub FillDispEditTable
        GoSub FillNCREditTable
        PrevCursor  = Set_Property('SYSTEM', 'CURSOR', 'A')
    end
    
end event


Event PUB_LOT_ABORT.CLICK()
    
    RDSNo  = Get_Property(@Window : '.RDS_NO', 'TEXT')
    WfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
    For each WfrID in WfrIDs using @VM setting vPos
        FormattedWfrID = WfrID
        Convert '*' to '.' in FormattedWfrID
        CurrStage = Gan_Services('GetCurrStage', WfrID)
        Begin Case
            Case ( (CurrStage _EQC 'UV') or (CurrStage _EQC 'UV_PRE') or (CurrStage _EQC 'UV_PST') or (CurrStage _EQC 'WARP') or (CurrStage _EQC 'CAN') or (CurrStage _EQC 'CAN_PRE') or (CurrStage _EQC 'CAN_PST') )
                // Pre-SPLIT wafer
                // a) Skip any pre-SPLIT stages
                // b) Mark all wafers as "Disposition" at SPLIT stage
                Loop                
                    StageKey = RDSNo:'*':CurrStage:'*':FormattedWfrID
                    StageStatus = Xlate('RUN_STAGE_WFR', StageKey, 'STATUS', 'X')
                    Begin Case
                        Case StageStatus _EQC 'INIT'
                            // Skip stage - Moves wafer to next stage
                            Gan_Services('SkipWaferStage', RDSNo, WfrID, CurrStage)
                        Case StageStatus _EQC 'START'
                            // Stop wafer - Moves wafer to next stage
                            Gan_Services('StopWaferStage', RDSNo, WfrID, CurrStage)
                        Case StageStatus _EQC 'COMP'
                            Null                       
                        Case Otherwise$
                            Null
                    End Case                   
                    CurrStage = Gan_Services('GetCurrStage', WfrID)
                Until CurrStage _EQC 'SPLIT'
                Repeat
                If CurrStage _EQC 'SPLIT' then Gan_Services('DispositionWfr', RDSNo, WfrID)         
                
            Case CurrStage _EQC 'SPLIT'
                // SPLIT wafer - Simply disposition the wafer
                Gan_Services('DispositionWfr', RDSNo, WfrID)               
                
            Case (CurrStage _EQC 'XRD') or (CurrStage _EQC 'AFM')                                      | 
                or (CurrStage _EQC 'RPM')  or (CurrStage _EQC 'PR')    or (CurrStage _EQC 'SCOPE')     |
                or (CurrStage _EQC 'HALL') or (CurrStage _EQC 'EBEAM') or (CurrStage _EQC 'RTA_HALL')  |
                or (CurrStage _EQC 'RTA')   or (CurrStage _EQC 'HALL_PGAN')                            |
                or (CurrStage _EQC 'BV') or (CurrStage _EQC 'JV_XRD') or (CurrStage _EQC 'BV100%')
                // Post-SPLIT wafer
                // a) Currently running wafers will need to be stopped
                // b) All subsequent stages will need to be auto-skipped including unprescribed stages
                Loop
                    CurrQ = Gan_Services('GetWfrQueue', WfrID)
                    QType = Gan_Services('GetQueueType', CurrQ)
                    Begin Case
                        Case QType _EQC 'Location'
                            // Wafer not running on a tool. Skip the stage.
                            Gan_Services('SkipWaferStage', RDSNo, WfrID, CurrStage)
                        Case QType _EQC 'Tool'
                            // Wafer running on tool. Stop the stage.
                            Gan_Services('StopWaferStage', RDSNo, WfrID, CurrStage)                            
                    End Case                    
                    CurrStage = Gan_Services('GetCurrStage', WfrID)
                Until CurrStage _EQC 'DISP'
                Repeat
                
            Case CurrStage _EQC 'DISP'
                // Wafer is where it needs to be. Nothing needed to do.
                Null            
                
            Case Otherwise$
                ErrorMsg = 'Error aborting wafer ':WfrID:'. Contact FI team for assistance.'
                ErrMsg(ErrorMsg)
                
        End Case
        
    Next WfrID
    
    GoSub UpdateDispData
    Disposition_Services('AutoDispositionRun', RDSNo)
    GoSub FillMetEditTable
    GoSub FillDispEditTable
    
end event


Event PUB_SEND_MSG.CLICK()
    
    RDSNo		= Get_Property(@Window:'.RDS_NO', 'TEXT')
    
    IF RDSNo = '' THEN 
        MsgInfo = ''
        MsgInfo<MTEXT$> = 'You must have a record  present...' 
        MsgInfo<MICON$> = 'H'
        Msg( '', MsgInfo )
        RETURN
    END
    
    IF RowExists('REACT_RUN',RDSNo) THEN
        
        NoteID = NextKey('NOTES')
        obj_AppWindow('ViewRelated','NOTE_MESSAGE':@RM:NoteID:@RM:@WINDOW:@FM:RDSNo)
        
    END ELSE
        
        MsgInfo = ''
        MsgInfo<mtext$> = 'You must save this run data sheet first...' 
        MsgInfo<micon$> = 'H'
        Msg( '', MsgInfo )
    END
    
end event


Event TABCONTROL.CLICK()
    
    Page = Get_Property(@WINDOW:'.TABCONTROL', 'VALUE')
    
    Set_Property(@WINDOW,'VPOSITION', Page)
    
end event


Event OLE_METROLOGY_EDT.OnButtonClick(Cell, Point, Button, Shift, Ctrl)

    Row       = Field(Cell, ';', 2)
    Col       = Field(Cell, ';', 1)
    SelStage     = Get_Property(CtrlEntID, "OLE.CellText[":Col:";":Row:"]")
    SelStageID   = GaN_Services('GetStageID', SelStage)
    
    If ( (SelStageID EQ 'UV') or (SelStageID EQ 'UV_PRE') or (SelStageID EQ 'UV_PST') or (SelStageID EQ 'WARP') or (SelStageID EQ 'WET_CLEAN') or (SelStageID EQ 'CAN_PRE') or (SelStageID EQ 'CAN_PST') ) then        
        RDSNo            = Get_Property(@Window : '.RDS_NO', 'TEXT')
        ToolID           = Get_Property(@Window : '.OLE_METROLOGY_EDT', 'OLE.CellText[2; ':Row:']')
        IsSplit          = Gan_Services('IsCassSplit', RDSNo)
        Menu             = ""
        // Initialize menu options
        StartStatus      = False$
        StopStatus       = False$
        CompStatus       = False$
        SkipStatus       = False$
        UnskipStatus     = False$
        WfrIDs           = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
        WfrStages        = Xlate('REACT_RUN', RDSNo, 'WFR_STAGE', 'X')
        CanPreStartCnt   = 0
        CanPstStartCnt   = 0
        WetCleanStartCnt = 0
        NumWfrs          = Get_Property(@Window, '@NUMWFRS')
        // Check each wafer in the selected stage to determine which menu options should be enabled.
        For each WfrID in WfrIDs using @VM setting wPos
            Convert '*' to '.' in WfrID
            Begin Case                   
                Case SelStageID EQ 'UV_PRE'
                    // Check if UV stage exists
                    UVStageWfrKey = RDSNo:'*UV*':WfrID
                    If RowExists('RUN_STAGE_WFR', UVStageWfrKey) then
                        // Display this on the UV_PRE line
                        RunStageWfrKey = UVStageWfrKey
                    end else
                        RunStageWfrKey = RDSNo:'*':SelStageID:'*':WfrID
                    end
                Case SelStageID EQ 'CAN_PRE'
                    // Check if CAN stage exists
                    CanStageWfrKey = RDSNo:'*CAN*':WfrID
                    If RowExists('RUN_STAGE_WFR', CanStageWfrKey) then
                        // Display this on the CAN_PRE line
                        RunStageWfrKey = CanStageWfrKey
                    end else
                        RunStageWfrKey = RDSNo:'*':SelStageID:'*':WfrID
                    end    
                Case Otherwise$
                    RunStageWfrKey = RDSNo:'*':SelStageID:'*':WfrID                           
            End Case
            WfrStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
            Begin Case
                Case WfrStatus EQ 'INIT' and ToolID NE 'Select'
                    StartStatus = True$
                    SkipStatus  = True$
                Case WfrStatus EQ 'INIT'
                    SkipStatus  = True$ 
                Case WfrStatus EQ 'START'
                    StopStatus  = True$
                Case WfrStatus EQ 'STOP'
                    CompStatus  = True$                        
                Case WfrStatus EQ 'SKIP'  
                    If IsSplit EQ False$ then UnskipStatus = True$
            End Case
        Next WfrID
        Menu<-1> = "START"   :@VM:"Start"                :@VM:StartStatus
        Menu<-1> = "STOP"    :@VM:"Stop"                 :@VM:StopStatus
        Menu<-1> = "COMP"    :@VM:"Complete"             :@VM:CompStatus
        Menu<-1> = "SKIP"    :@VM:"Skip"                 :@VM:SkipStatus
        Menu<-1> = "UNSKIP"  :@VM:"Unskip"               :@VM:UnskipStatus
        
        Send_Message(CtrlEntID, "OLE.ShowContextMenu", Point, Menu, Cell)
    end
    
end event


Event OLE_REACTOR_EDT.OnButtonClick(Cell, Point, Button, Shift, Ctrl)
       
    RecipeMismatch  = Get_Property(@Window, '@RECIPEMISMATCH')
    RDSNo           = Get_Property(@Window:'.RDS_NO', 'TEXT')
    Row             = Field(Cell, ';', 2)
    Col             = Field(Cell, ';', 1)
    Stage           = Get_Property(CtrlEntID, "OLE.CellText[":Col:";":Row:"]")
    StageID         = Gan_Services('GetStageID', Stage)
    If StageID NE 'ETCH' then
        Menu            = ""
        // Initialize all menu options to false until we deem they need to be turned on
        StartStatus     = False$
        StopStatus      = False$
        LoadStatus      = False$
        UnloadStatus    = False$
        CompStatus      = False$
        
        // Get inventory actions associated with this particular stage
        RDSNo       = Get_Property(@Window : '.RDS_NO', 'TEXT')
        RunStageKey = RDSNo:'*':StageID
        InvActions  = Xlate('RUN_STAGE', RunStageKey, 'SPEC_INV_ACTION', 'X')
        CurrStatus  = Xlate('RUN_STAGE', RunStageKey, 'STATUS', 'X')
        Begin Case
            Case StageID EQ 'G_RDS'
                // Need to check if GaN Run ID has been selected and if the GaN Recipe is populated.
                GaNRunID      = Get_Property(@Window:'.CMB_GAN_RUN_ID', 'TEXT')
                GaNRecipe     = Get_Property(@Window:'.GAN_RECIPE', 'TEXT')
                EtchKey       = RDSNo:'*ETCH'
                EtchStageComp = Xlate('RUN_STAGE', EtchKey, 'STATUS', 'X')
                If (GaNRunID NE '') and (GaNRecipe NE '') and (EtchStageComp _EQC 'COMP') and (CurrStatus NE 'COMP') then
                    CompStatus = True$
                end
            Case StageID EQ 'GROWTH'
                PrevStageKey = RDSNo:'*':'ETCH'
                PrevStageStatus  = Xlate('RUN_STAGE', PrevStageKey, 'STATUS', 'X')
                If PrevStageStatus EQ 'COMP' then
                    Begin Case
                        Case CurrStatus EQ 'START'
                            UnloadStatus = True$
                        Case CurrStatus EQ 'COMP'
                            Null
                        Case Otherwise$
                            LoadStatus = True$
                    End Case
                end            
            Case StageID EQ 'G_RATE'
                PrevStageKey = RDSNo:'*':'GROWTH'
                PrevStageStatus  = Xlate('RUN_STAGE', PrevStageKey, 'STATUS', 'X')
                If PrevStageStatus EQ 'COMP' and CurrStatus NE 'COMP' then CompStatus = True$
        End Case
        
        For each Action in InvActions using @VM
            Begin Case
                Case Action EQ "START"
                    Menu<-1> = "START"  :@VM:"Start"        :@VM:StartStatus
                Case Action EQ "STOP"
                    Menu<-1> = "STOP"   :@VM:"Stop"         :@VM:StopStatus
                Case Action EQ "LOAD"
                    Menu<-1> = "LOAD"   :@VM:"Load"         :@VM:LoadStatus
                Case Action EQ "UNLOAD"
                    Menu<-1> = "UNLOAD" :@VM:"Unload"       :@VM:UnloadStatus
            End Case
            
        Next Action
        
        If RecipeMismatch EQ True$ then CompStatus = False$
        If StageID NE 'GROWTH' then Menu<-1> = "COMP"   :@VM:"Complete" :@VM:CompStatus
        
        Send_Message(CtrlEntID, "OLE.ShowContextMenu", Point, Menu, Cell)
    end
    
end event


Event OLE_REACTOR_EDT.OnContextMenuClick(Item, UserData)
    
    CurrDTM          = DateTime()
    SelCell          = UserData
    Row              = Field(SelCell, ';', 2)
    RowData          = Get_Property(CtrlEntID, "OLE.RecordData[":Row:"]")
    ReactorStageCtrl = @Window:'.OLE_REACTOR_EDT'
    RDSNo            = Get_Property(@Window : '.RDS_NO', 'TEXT')   
    PSNo             = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X')
    NumWfrs          = DCount(InWfrIDs, @VM)
    StartCol         = 3
    EndCol           = StartCol + (NumWfrs - 1)
    WfrActions       = ''
    WfrStage         = RowData<1, 1>
    For Col = StartCol to EndCol
        WfrActions<1, -1> = RowData<1, Col>
    Next Col
    StageName        = Get_Property(CtrlEntID, 'OLE.CellText[1; ':Row:']')
    StageID          = Gan_Services('GetStageID', StageName)
    PRSStageKey      = PSNo :'*': StageID
    CurrAction       = Item
    RunStageKey      = RDSNo:'*':StageID
    RunStageRec      = Database_Services('ReadDataRow', 'RUN_STAGE', RunStageKey)
    WONo             = Get_Property(@Window:'.WO_NO', 'TEXT')
    CassNo           = Get_Property(@Window:'.CASS_NO', 'TEXT')
    WOMatKey         = WONo:'*':CassNo
    WOMatRec         = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
    ReactRunRec      = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    
    Begin Case
        
        Case CurrAction EQ 'UNLOAD'
            
            Begin Case
                
                Case StageID EQ 'GROWTH'
                    
                    GaN_Services('UnloadSusceptor', RDSNo)
                    If Error_Services('NoError') then
                        Set_Property(ReactorStageCtrl, "OLE.CellText[2; ":Row:"]", 'Complete')
                        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
                        CarrSlotIDs = ReactRunRec<REACT_RUN_CARR_WFR_ID$>
                        Scribes     = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
                        Set_Property(@Window, '@CARRSLOTIDS', CarrSlotIDs)
                        Set_Property(@Window, '@SCRIBES', Scribes)
                        Engineering_Services('PostEARequest', RDSNo, 'GROWTH')
                        Reactor     = Xlate('REACT_RUN', RDSNo, 'REACTOR', 'X')
                        ToolID      = 'R':Reactor                       
                        Reactor_Services('IncrementCycleCount', ToolID)
                    end else
                        ErrorMessages = Error_Services('GetMessages')
                        Convert @FM to CRLF$ in ErrorMessages
                        ErrMsg(ErrorMessages)
                    end
                    
            End Case
            
        Case CurrAction EQ 'LOAD'
            
            Begin Case
                
                Case StageID EQ 'GROWTH'
                    
                    RunStageKey = RDSNo:'*':StageID
                    Reactor = Get_Property(@Window, '@REACTOR')
                    // Need to sign 'LOAD' inventory action                   
                    LoadComplete = Dialog_Box('DIALOG_LOAD_SUSCEPTOR', @WINDOW, RDSNo:@FM:StageName:@FM:Reactor)
                    If LoadComplete EQ True$ then
                        // Update the UI
                        Set_Property(ReactorStageCtrl, "OLE.CellText[2; ":Row:"]", 'Load')
                        Scribes = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
                        WfrIDs  = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
                        Set_Property(@Window, '@WFRIDS', WfrIDs)
                        HeaderTitles = "Stage":@VM:"Tool ID":@VM:Scribes
                        Set_Property(MetStageCtrl, "OLE.TitleList", HeaderTitles)
                        GaNProdVis = Get_Property('WO_PROD_GAN', 'VISIBLE')
                        If GaNProdVis EQ True$ then Send_Event('WO_PROD_GAN', 'READ')
                        GoSub FillDispEditTable
                        GoSub FillNCREditTable
                    end
                    
            End Case
            
            
        Case CurrAction EQ 'COMP'
            // 1. Save REACT_RUN record (Bind REACT_RUN (RDS) key id to GaN Run ID and to GaN Etch ID). Remove GaN Run ID and GaN Etch ID from their respective lists.
            // 2. Remove all wafers from previous Location/Tool ID queue if applicable
            // 3. Insert all wafers into Location/Tool ID queue (e.g. LOCATION*GCH*Q_ETCH/TOOL_WFR*R69)
            // 4. Add COMP_BY and COMP_DTM to RUN_STAGE record
            // 5. Update UI
            // 6. Refresh GaN Work Order status form
            Begin Case
                
                Case StageID EQ 'G_RDS'
                    // 1
                    GaNRunID            = Get_Property(@Window:'.CMB_GAN_RUN_ID', 'TEXT')
                    GanSuscSerial       = Get_Property(@Window:'.GAN_SUSC_SERIAL', 'TEXT')
                    GaNRunRecipe        = Get_Property(@Window:'.GAN_RECIPE', 'TEXT')
                    SPCWaferTrackPart   = Get_Property(@Window:'.EDL_WAFER_TRACK_PART', 'TEXT')
                    ReactRunRec         = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
                    RunInfo             = Gan_Services('GetRunInfo', GaNRunID)
                    PocketNos           = RunInfo<3>
                    Scribes             = RunInfo<4>
                    SatSerialNos        = RunInfo<5>
                    // Save the Wafer Track Part from SPC into the WO_LOG record.
                    WOLogRec            = Database_Services('ReadDataRow', 'WO_LOG', WONo)
                    WOLogRec<WO_LOG_WAFER_TRACK_PART$> = SPCWaferTrackPart
                    Database_Services('WriteDataRow', 'WO_LOG', WONo, WOLogRec, True$, False$, True$)
                    // Just get the first reator in the list               
                    Reactor       = Get_Property(@Window, '@REACTOR')
                    ReactRunRec<REACT_RUN_GAN_RUN_ID$>      = GaNRunID
                    ReactRunRec<REACT_RUN_GAN_RECIPE$>      = GaNRunRecipe
                    ReactRunRec<REACT_RUN_GAN_SUSC_SERIAL$> = GanSuscSerial
*                    ReactRunRec<REACT_RUN_POCKET$>          = PocketNos
                    ReactRunRec<REACT_RUN_PKT_SAT_ID$>      = SatSerialNos
                    ReactRunRec<REACT_RUN_REACTOR$>         = Reactor
                    GaN_Services('RemoveRunID', GaNRunID)
                    Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)                    
                    // 2 - N/A       
                    // 3 - Move cassette to GROWTH queue.
                    GrowthQ = 'GCH*Q_GROWTH'
                    GaN_Services('MoveCassToQueue', WOMatKey, GrowthQ)
                    WOMatRec = Database_Services('ReadDataRow', 'WO_MAT', WOMatKey)
                    WOMatRec<WO_MAT_LOC_DTM$, -1> = CurrDTM
                    WOMatRec<WO_MAT_LOC_BY$, -1>  = @User4
                    WOMatRec<WO_MAT_INV_LOC$, -1> = GrowthQ
                    Database_Services('WriteDataRow', 'WO_MAT', WOMatKey, WOMatRec, True$, False$, True$)
                    // 4
                    RunStageRec<RUN_STAGE_COMP_BY$>  = @User4
                    RunStageRec<RUN_STAGE_COMP_DTM$> = CurrDTM
                    RunStageRec<RUN_STAGE_STATUS$>   = 'COMP'
                    Database_Services('WriteDataRow', 'RUN_STAGE', RunStageKey, RunStageRec, True$, False$, True$)
                    // 5
                    Set_Property(ReactorStageCtrl, "OLE.CellText[2; ":Row:"]", 'Complete')
                    // 6
                    GaNProdVis = Get_Property('WO_PROD_GAN', 'VISIBLE')
                    If GaNProdVis EQ True$ then Send_Event('WO_PROD_GAN', 'READ')                   
                    
                Case StageID EQ 'G_RATE'
                    
                    // 1. Remove all wafers from current Location queue
                    // 2. Lookup next Location queue
                    // 3. Insert all wafers into next Location queue                    
                    CurrLocQID = 'GCH*Q_RATE'
                    NextLocQID = 'GCH*UV_PRE_Q'
                    InWfrIDs   = ReactRunRec<REACT_RUN_IN_WFR_ID$>
                    For each WfrID in InWfrIDs using @VM setting vPos
                        If WfrID NE '' then
                            Gan_Services('RemoveWfrFromWIP', WfrID)
                            WOWfrRec  = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
                            LocDTMs   = WOWfrRec<WO_WFR_LOC_DTM$>
                            NextIndex = DCount(LocDTMs, @VM) + 1
                            WOWfrRec<WO_WFR_LOC_DTM$, NextIndex>   = CurrDTM
                            WOWfrRec<WO_WFR_LOC_BY$, NextIndex>    = @User4
                            WOWfrRec<WO_WFR_INV_LOC$, NextIndex>   = NextLocQID
                            WOWfrRec<WO_WFR_LOC_EVENT$, NextIndex> = 'COMP'
                            Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
                            Gan_Services('MoveWfrToQueue', WfrID, NextLocQID)
                        end
                    Next WfrID
                    // 3. Add COMP_BY and COMP_DTM to RUN_STAGE record
                    RunStageRec<RUN_STAGE_COMP_BY$>  = @User4
                    RunStageRec<RUN_STAGE_COMP_DTM$> = CurrDTM
                    RunStageRec<RUN_STAGE_STATUS$>   = 'COMP'
                    Database_Services('WriteDataRow', 'RUN_STAGE', RunStageKey, RunStageRec, True$, False$, True$)
                    // 4. Create prescribed RUN_STAGE_WFR records
                    Gan_Services('CreateAllRunStageWfr', RDSNo, 'SPLIT')
                    // 5. Update REACT_RUN form
                    Set_Property(ReactorStageCtrl, "OLE.CellText[2; ":Row:"]", 'Complete')
                    GoSub EnableRows
                    GoSub FillMetEditTable
                    GoSub FillMetHeader
                    // 6. Refresh GaN Work Order status form if it is open
                    GaNProdVis = Get_Property('WO_PROD_GAN', 'VISIBLE')
                    If GaNProdVis EQ True$ then Send_Event('WO_PROD_GAN', 'READ')                   
                    
            End Case
    End Case
    
end event


Event OLE_METROLOGY_EDT.OnContextMenuClick(Item, UserData)

    PrevCursor  = Set_Property('SYSTEM', 'CURSOR', 'H')
    SelCell     = UserData
    Row         = Field(SelCell, ';', 2)
    RowData     = Get_Property(CtrlEntID, "OLE.RecordData[":Row:"]")
    RDSNo       = Get_Property(@Window : '.RDS_NO', 'TEXT')
    InWfrIDs    = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
    PSNo        = Xlate('REACT_RUN', RDSNo, 'PS_NO', 'X')
    NumWfrs     = DCount(InWfrIDs, @VM)
    StartCol    = 3
    EndCol      = StartCol + (NumWfrs - 1)
    WfrActions  = ''
    WfrStage    = RowData<1, 1>
    For Col = StartCol to EndCol
        WfrActions<1, -1> = RowData<1, Col>
    Next Col
    StageName       = Get_Property(@Window : '.OLE_METROLOGY_EDT', 'OLE.CellText[1; ':Row:']')
    StageID         = GaN_Services('GetStageID', StageName)
    ToolID          = Get_Property(@Window : '.OLE_METROLOGY_EDT', 'OLE.CellText[2; ':Row:']')
    If ToolID EQ 'Select' then ToolID = ''
    PRSStageKey     = PSNo :'*': StageName
    Action          = Item
    SpecToolClass   = Xlate('PRS_STAGE', PRSStageKey, 'PROC_TOOL_CLASS', 'X')
    ToolClassRec    = Database_Services('ReadDataRow', 'TOOL_CLASS', SpecToolClass)
    CurrDTM         = DateTime()
    
    Begin Case
        Case Action _EQC 'START'
            
            For each WfrID in InWfrIDs using @VM setting WaferIndex
                FormattedWfrID = WfrID
                Swap '*' with '.' in FormattedWfrID
                RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                ReadyToStart   = GaN_Services('ReadyToStart', RDSNo, WfrID)
                If ( (StageStatus EQ 'INIT') and (ReadyToStart EQ True$) ) then GaN_Services('StartWaferStage', RDSNo, WfrID, StageID, ToolID)         
            Next WfrID
            
        Case Action _EQC 'STOP'
            
            For each WfrID in InWfrIDs using @VM setting WaferIndex
                FormattedWfrID = WfrID
                Swap '*' with '.' in FormattedWfrID
                RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                If StageStatus EQ 'START' then GaN_Services('StopWaferStage', RDSNo, WfrID, StageID)
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                If StageStatus EQ 'STOP'  then GaN_Services('CompleteWaferStage', RDSNo, WfrID, StageID)
            Next WfrID
            Engineering_Services('PostEARequest', RDSNo, StageID)
            If StageID EQ 'WARP' then GaN_Services('CheckForCriticalFailures', RDSNo, ToolID)
*            If ( (StageID EQ 'CAN_PRE') or (StageID EQ 'CAN_PST') or (StageID EQ 'JV_XRD') ) then
*                GaN_Services('VerifyWaferScribes', RDSNo, ToolID)
*                GoSub VerifyScribes
*            end
            
        Case Action _EQC 'SKIP'
            
            For each WfrID in InWfrIDs using @VM setting WaferIndex
                FormattedWfrID = WfrID
                Swap '*' with '.' in FormattedWfrID
                RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                If StageStatus EQ 'INIT' then GaN_Services('SkipWaferStage', RDSNo, WfrID, StageID)                                                
            Next WfrID
            
        Case Action _EQC 'UNSKIP'
            
            For each WfrID in InWfrIDs using @VM setting WaferIndex
                FormattedWfrID = WfrID
                Swap '*' with '.' in FormattedWfrID
                RunStageWfrKey = RDSNo:'*':StageID:'*':FormattedWfrID
                StageStatus    = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                If StageStatus EQ 'SKIP' then GaN_Services('UnskipWaferStage', RDSNo, WfrID, StageID)                             
            Next WfrID
            
    End Case
    Disposition_Services('AutoDispositionRun', RDSNo)
    GoSub FillMetEditTable
    PrevCursor = Set_Property('SYSTEM', 'CURSOR', PrevCursor)
    
end event


Event OLE_METROLOGY_EDT.OnClick(Cell, Point, Button, Shift, Ctrl)

    If Button _EQC 'Right' then
        Column          = Field(Cell, ';', 1, 1)
        Row             = Field(Cell, ';', 2, 1)
        PopupCtrl       = @Window : '.OLE_POPUP'
        AllowPopup      = False$
        PopupVisible    = Get_Property(PopupCtrl, 'OLE.Visible')
        If PopupVisible EQ True$ then
            GoSub DismissPopup
        end
        GoSub DisplayPopup
    end
    
end event


Event OLE_METROLOGY_EDT.OnHeaderDblClick(Cell, Point, Button, Shift, Ctrl)
    
    Col = Field(Cell, ';', 1, 1)
    If Col GT 3 then
        RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
        WfrIDs      = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
        WfrIndex    = Col - 3 
        WfrID       = WfrIDs<0, WfrIndex>
        WfrComments = ''
        WfrComments = Xlate('WO_WFR', WfrID, 'COMMENTS', 'X')
        // Display wafer comments and allow user to edit/erase comments. 
        Response    = Dialog_Box('NDW_WFR_COMMENTS', @WINDOW, WfrComments)
        Save        = Response<1>
        NewComments = Response<2>
        
        If Save EQ True$ then
            // Insert comments into WO_WFR record
            WOWfrRec    = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
            WOWfrRec<WO_WFR_COMMENTS$> = NewComments
            Database_Services('WriteDataRow', 'WO_WFR', WfrID, WOWfrRec, True$, False$, True$)
            ColNo = WfrIndex + 3
            Set_Property(CtrlEntID, "OLE.HeaderToolTip[":ColNo:";1]", NewComments)
            HeaderColors = Get_Property(CtrlEntID, "OLE.HeaderColors[":ColNo:";1]")
            If NewComments NE '' then
                // Set header to cyan so the user knows there's a comment                
                HeaderColors<2> = 'LightCyan'
                HeaderColors<4> = 'LightCyan'
            end else
                // Comment has been removed, so reset header colors back to default colors
                HeaderColors<2> = 'None'
                HeaderColors<4> = OI_HOT_BLUE$
            end
            Set_Property(CtrlEntID, "OLE.HeaderColors[":ColNo:";1]", HeaderColors)
        end
    end
    
end event


Event OLE_REACTOR_EDT.OnClick(Cell, Point, Button, Shift, Ctrl)
    
    If Button _EQC 'Right' then
        Column          = Field(Cell, ';', 1, 1)
        Row             = Field(Cell, ';', 2, 1)
        PopupCtrl       = @Window : '.OLE_POPUP'
        AllowPopup      = False$
        PopupVisible    = Get_Property(PopupCtrl, 'OLE.Visible')
        If PopupVisible EQ True$ then
            GoSub DismissPopup
        end
        GoSub DisplayPopup
    end
    
end event


Event OLE_METROLOGY_EDT.OnComboClicked(Cell, SelPos, Value)

    PrevCursor      = Set_Property('SYSTEM', 'CURSOR', 'H')
    Column          = Field(Cell, ';', 1, 1)
    Row             = Field(Cell, ';', 2, 1)
    MetStageCtrl    = @Window:'.OLE_METROLOGY_EDT'
    MetStageList    = Get_Property(MetStageCtrl, "OLE.ColumnData[1]")
    StageDesc       = MetStageList<0, Row>
    StageID         = GaN_Services('GetStageID', StageDesc)
    RDSNo           = Get_Property(@Window:'.RDS_NO', 'TEXT')
    WfrIDs          = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
    MetRowsEnabled  = Get_Property(@Window, '@METROWSENABLED')
    ColorArray      = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
    ReactRunRec     = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    WfrStages       = ReactRunRec<REACT_RUN_WFR_STAGE$>
    UnRxStages      = ReactRunRec<REACT_RUN_UNPRESCRIBED_WFR_STAGE$>
    ToolID          = Get_Property(@Window : '.OLE_METROLOGY_EDT', 'OLE.CellText[2; ':Row:']')
    CurrDTM         = DateTime()
    WfrIndex        = Column - 2
    
    If Column EQ COL$MET_TOOL_ID then
        ToolID = Value
        // A tool ID has been selected.  Need to check all RUN_STAGE_WFR statuses. Tool IDs can only be updated if the
        // status is 'INIT' or 'X'.
        For each WfrID in WfrIDs using @VM setting vPos
            If WfrID NE '' then
                WfrIDFormatted = WfrID
                Swap '*' with '.' in WfrIDFormatted
                RunStageWfrKey = RDSNo:'*':StageID:'*':WfrIDFormatted
                If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then
                    WfrStatus      = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                    If WfrStatus _EQC 'INIT' then
                        Set_Property(MetStageCtrl, "OLE.CellProtection[":Column:";":Row:"]", "None")
                        RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
                        RunStageWfrRec<RUN_STAGE_WFR_TOOL_ID$> = ToolID
                        Database_Services('WriteDataRow','RUN_STAGE_WFR',RunStageWfrKey,RunStageWfrRec,True$,False$,True$)
                    end
                end
            end
        Next WfrID
        MetRowsEnabled<Row> = True$
        Set_Property(@Window, '@METROWSENABLED', MetRowsEnabled)       
        
    end else
        
        Action = Value
        WfrID  = WfrIDs<0, WfrIndex>
        Set_Property(@Window, '@SINGLE_WFR_ID', WfrID)  
        Begin Case
            Case Action _EQC 'START'
                
                Gan_Services('StartWaferStage', RDSNo, WfrID, StageID, ToolID)      
                
            Case Action _EQC 'STOP'
                
                Gan_Services('StopWaferStage', RDSNo, WfrID, StageID)
                Gan_Services('CompleteWaferStage', RDSNo, WfrID, StageID)
                Engineering_Services('PostEARequest', RDSNo, StageID)
                
            Case Action _EQC 'COMPLETE'
                
                Gan_Services('CompleteWaferStage', RDSNo, WfrID, StageID)
                Engineering_Services('PostEARequest', RDSNo, StageID)   
                
            Case Action _EQC 'SKIP'
                
                Gan_Services('SkipWaferStage', RDSNo, WfrID, StageID)
                
            Case Action _EQC 'UNSKIP'
                
                Gan_Services('UnskipWaferStage', RDSNo, WFrID, StageID)  
                
            Case Action _EQC 'ADD'

                If ( (ToolID EQ '') or (ToolID EQ 'Select') ) then
                    Error_Services('Add', 'A tool ID must be selected before adding a stage')
                    Set_Property(@Window : '.OLE_METROLOGY_EDT', 'OLE.CellText[':Cell:']', '')
                end else
                    Gan_Services('AddWaferStage', RDSNo, WfrID, StageID, ToolID)
                end
                
            Case Action _EQC 'REMOVE'
                
                Gan_Services('RemoveWaferStage', RDSNo, WfrID, StageID)
                
            Case Action _EQC 'CHARACTERIZE'
                
                Gan_Services('CharacterizeWafer', RDSNo, WfrID)                
                
            Case Action _EQC 'DISPOSITION'
                
                Gan_Services('DispositionWfr', RDSNo, WfrID)                   
                
            Case Action _EQC 'WITHDRAW'
                
                Gan_Services('WithdrawWfr', RDSNo, WfrID)                 
                
            Case Otherwise$             
        End Case
        
    end
    
    If Error_Services('HasError') then
        PrevCursor = Set_Property('SYSTEM', 'CURSOR', 'A')
        Errmsg(Error_Services('GetMessage'))
    end else
        CurrStage = Gan_Services('GetCurrStage', WfrID)
        If CurrStage _EQC 'DISP' then
            // Update the Disposition DTM for all wafers that have been through or are at disposition
            ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
            InWfrIDs = ReactRunRec<REACT_RUN_IN_WFR_ID$>                    
            For each WOWfrID in InWfrIDs using @VM setting vPos
                ThisWOWfrRec = Database_Services('ReadDataRow', 'WO_WFR', WOWfrID)
                LocDTMs      = ThisWOWfrRec<WO_WFR_LOC_DTM$>
                LocQs        = ThisWOWfrRec<WO_WFR_INV_LOC$>
                Locate 'GGR*Q_DISP' in LocQs using @VM setting vPos then
                    // Wafer has been dispositioned at some point, so update the disposition DTM
                    LocDTMs<0, vPos> = CurrDTM
                    ThisWOWfrRec<WO_WFR_LOC_DTM$> = LocDTMs
                    Database_Services('WriteDataRow', 'WO_WFR', WOWfrID, ThisWOWfrRec, True$, False$, True$)
                end
            Next WOWfrID
            Set_Property(@Window:'.PUB_UPDATE_DISP', 'ENABLED', True$)
        end    
        GoSub UpdateDispData     
        Disposition_Services('AutoDispositionRun', RDSNo)
        GoSub FillMetEditTable
        Set_Property(@Window, '@SINGLE_WFR_ID', '')   
        GoSub FillDispEditTable    
    end
    PrevCursor = Set_Property('SYSTEM', 'CURSOR', 'A')
    
end event


Event OLE_DISP_EDT.OnCheckChanged(Cell, OldValue, NewValue)
    
    RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    Col         = Field(Cell, ';', 1, 1)
    Row         = Field(Cell, ';', 2, 1)
    NCRButton   = ''
    Begin Case
        
        Case Col EQ COL$DISP_EXTERNAL
            ExternalFlags                    = ReactRunRec<REACT_RUN_EXTERNAL$>
            ExternalFlags<0, Row>            = NewValue
            ReactRunRec<REACT_RUN_EXTERNAL$> = ExternalFlags
            Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
            If Error_Services('NoError') then
                Disposition_Services('AutoDispositionRun', RDSNo)
                Engineering_Services('PostEARequest', RDSNo, 'CHAR')
                GoSub FillDispEditTable 
            end                  
            
        Case Col EQ COL$DISP_RDS_NCR
            // The checkbox is only used for creating NCRs. We don't need to save anything at this time.
            // Check (no pun intended) if any of the checkboxes in this column are True$. If so, then enable
            // the "Create NCR" button, otherwise disable it.
            GoSub EnableNCRButton
            
    End Case
    
end event


Event COMBO_PRIORITY.CHANGED(NewData)
    
    Priority    = NewData
    RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    ReactRunRec<REACT_RUN_RUN_PRI$> = Priority
    Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
    
end event


Event CMB_GAN_ETCH_ID.DROPDOWN(EditLineText)
    
    /*
    If ETCH stage complete, then display dropdown of all external etch IDs bound to this RDS.
    If ETCH stage is not complete, then provide a popup of all available etch IDs and allow the user to select
    multiple external etch IDs to bind to the RDS    
    */
    RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
    
    LogData     = ''
    LogData<1>  = OConv(Datetime(), 'DT')
    LogData<2>  = RDSNo                     
    LogData<3>  = @User4
    LogData<4>  = 'Begin dropdown event.'                  
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
    
    
    EtchStageKey    = RDSNo:'*ETCH'
    EtchStageStatus = Xlate('RUN_STAGE', EtchStageKey, 'STATUS', 'X')
    
    LogData<4>  = 'Etch Stage Status: ':EtchStageStatus                  
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
    
    If EtchStageStatus _NEC 'COMP' then
        // Open the External Etch Manager so that users can select one or more internal and external etch ID(s) to
        // bind to this RDS (REACT_RUN) record.
        Reactor = Get_Property(@Window, '@REACTOR')
        
        LogData<4>  = 'Reactor: ':Reactor                 
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
        
        If (Reactor NE '') then
            SelEtchIDs  = ''
            SelEtchIDs  = Dialog_Box('NDW_EXTERNAL_ETCH_MANAGER', @Window, Reactor)
            
            LogEtchIDs = SelEtchIDs
            Convert @FM to ',' in LogEtchIDs 
            LogData<4>  = 'Selected etch IDs: ':LogEtchIDs       
            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
            // If SelEtchIDs is equal to False$ (0) then the user canceled the external etch selection process.
            If SelEtchIDs NE False$ then                   
                SelIntEtchIDs = SelEtchIDs<1>
                SelExtEtchIDs = SelEtchIDs<2>
                // Update RUN_STAGE record
                RunStageKey      = RDSNo:'*ETCH'
                RunStageRec      = Database_Services('ReadDataRow', 'RUN_STAGE', RunStageKey)
                If Error_Services('NoError') then
                    CurrDTM          = DateTime()
                    RunStageRec<RUN_STAGE_COMP_BY$>  = @User4
                    RunStageRec<RUN_STAGE_COMP_DTM$> = CurrDTM
                    RunStageRec<RUN_STAGE_STATUS$>   = 'COMP'
                    RunStageRec<RUN_STAGE_ETCH_IDS$> = SelIntEtchIDs
                    Database_Services('WriteDataRow', 'RUN_STAGE', RunStageKey, RunStageRec, True$, False$, True$)
                    If Error_Services('NoError') then
                        // Update REACT_RUN record
                        NumExtEtchIDs  = DCount(SelExtEtchIDs, @VM)
                        LastExtEtchID  = SelExtEtchIDs<0, NumExtEtchIDs>
                        ExtEtchRecipes = ''
                        Set_Property(@Window:'.CMB_GAN_ETCH_ID', 'LIST', SelExtEtchIDs)
                        Set_Property(@Window:'.CMB_GAN_ETCH_ID', 'SELPOS', -1)
                        Set_Property(@Window:'.CMB_GAN_RUN_ID', 'ENABLED', True$)              
                        For each ExtEtchID in SelExtEtchIDs using @VM setting vPos
                            If ExtEtchID _NEC 'No Etch Required' then
                                
                                LogData<4>  = 'Calling Gan_Services("GetRunInfo", ':ExtEtchID:').'
                                Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                
                                GaNEtchInfo           = Gan_Services('GetRunInfo', ExtEtchID)
                                
                                If Error_Services('NoError') then
                                    LogData<4>  = 'Call to Gan_Services("GetRunInfo", ':ExtEtchID:') was successful.'
                                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                end else
                                    LogData<4>  = 'Error Calling Gan_Services("GetRunInfo", ':ExtEtchID:').'
                                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                end
                                
                                ExtEtchRecipes<0, -1> = GaNEtchInfo<1,1>
                                
                                LogData<4>  = 'Calling Gan_Services("ConsumeExtEtchID", ':ExtEtchID:').'
                                Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                
                                GaN_Services('ConsumeExtEtchID', ExtEtchID)
                                
                                If Error_Services('NoError') then
                                    LogData<4>  = 'Call to Gan_Services("ConsumeExtEtchID", ':ExtEtchID:') was successful.'
                                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                end else
                                    ErrorMessage = Error_Services('GetMessage')
                                    LogData<4>  = 'Error Calling Gan_Services("ConsumeExtEtchID", ':ExtEtchID:'). Error message: ':ErrorMessage
                                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                end
                                
                            end else
                                ExtEtchRecipes = 'Not Applicable'
                            end                       
                        Next ExtEtchID
                        LastExtEtchRecipe = ExtEtchRecipes<0, NumExtEtchIDs>
                        Set_Property(@Window:'.ETCH_RECIPE', 'TEXT', LastExtEtchRecipe)
                        ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
                        If Error_Services('NoError') then
                            ReactRunRec<REACT_RUN_GAN_ETCH_ID$>     = SelExtEtchIDs
                            ReactRunRec<REACT_RUN_GAN_ETCH_RECIPE$> = ExtEtchRecipes 
                            Database_Services('WriteDataRow', 'REACT_RUN', RDSNo, ReactRunRec, True$, False$, True$)
                            If Error_Services('NoError') then                                            
                                For each IntEtchID in SelIntEtchIDs using @VM setting vPos
                                    
                                    LogData<4>  = 'Calling Gan_Services("ConsumeIntEtchID", ':IntEtchID:').'
                                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                    
                                    GaN_Services('ConsumeIntEtchID', IntEtchID)
                                    
                                    If Error_Services('NoError') then
                                        LogData<4>  = 'Call to Gan_Services("ConsumeIntEtchID", ':IntEtchID:') was successful.'
                                        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                    end else
                                        ErrorMessage = Error_Services('GetMessage')
                                        LogData<4>  = 'Error Calling Gan_Services("ConsumeIntEtchID", ':IntEtchID:'). Error message: ':ErrorMessage
                                        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                                    end
                                    
                                Next IntEtchID                               
                                // Update the UI
                                Row = 1 ; // Etch Configuration Row
                                Set_Property(@Window:'.OLE_REACTOR_EDT', "OLE.CellText[2; ":Row:"]", 'Complete')
                                Set_Property(@Window:'.CMB_GAN_RUN_ID', 'ENABLED', True$)
                            end else
                                ErrorMessage = Error_Services('GetMessage')
                                LogData<4>  = 'Error writing REACT_RUN record: ':RDSNo:'. Error message: ':ErrorMessage:'.'
                                Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                            end
                            
                        end else
                            ErrorMessage = Error_Services('GetMessage')
                            LogData<4>  = 'Error reading REACT_RUN record: ':RDSNo:'. Error message: ':ErrorMessage:'.'
                            Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                        end
                    end else
                        ErrorMessage = Error_Services('GetMessage')
                        LogData<4>  = 'Error writing RUN_STAGE record: ':RunStageKey:'. Error message: ':ErrorMessage:'.'
                        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                    end
                end else
                    ErrorMessage = Error_Services('GetMessage')
                    LogData<4>  = 'Error reading RUN_STAGE record: ':RunStageKey:'. Error message: ':ErrorMessage:'.'
                    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
                end
            end
        end else
            ErrorMsg = 'Reactor number is missing. Unable to complete ETCH stage.'
            ErrMsg(ErrorMsg)
        end
        
    end
    
    If Error_Services('NoError') then
        LogData<4>  = 'Dropdown event successful.'                  
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
    end else
        LogData<4>  = 'Error in dropdown event.'                  
        Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
    end
    
    LogData<4>  = 'End dropdown event.'                  
    Logging_Services('AppendLog', objLog, LogData, @RM, @FM, True$)
    
end event


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Setup_OLE_Controls:
    
    NumReactorCols = 2
    NumReactorRows = 4
    NumMetCols     = 10
    NumMetRows     = 21
    NumDispCols    = 14
    NumDispRows    = 8
    NumNCRCols     = 9
    NumNCRRows     = 8
    
    ReactorCtrlList     = ''
    MetCtrlList         = ''
    DispCtrlList        = ''
    ReactorToolIDArray  = ''
    MetToolIDArray      = ''
    GaNStageArray = Database_Services('ReadDataRow', 'APP_INFO', 'GAN_STAGES')
    
    Set_Property(@Window, '@GAN_STAGES', GaNStageArray)
    ReactorStageIndex = 1
    MetStageIndex     = 1
    For each Row in GaNStageArray using @FM setting fPos
        StageDesc    = Row<0,2>
        ToolClassIDs = Row<0,3>
        RunType      = Row<0,5>

        Begin Case
            Case RunType EQ 'Reactor'                
                ReactorCtrlList<ReactorStageIndex> = StageDesc
                ReactorStageIndex += 1                
            Case RunType EQ 'Metrology'              
                MetCtrlList<MetStageIndex> = StageDesc
                TempArray = ''
                MetCtrlList<MetStageIndex, 2> = 'Select'
                For each ToolClassID in ToolClassIDs using @SVM setting svPos                    
                    ToolIDs   = Xlate('TOOL_CLASS', ToolClassID, 'TOOL', 'X')
                    ProdTools = ''
                    For each ToolID in ToolIDs using @VM setting vPos
                        CurrMode    = Xlate('TOOL', ToolID, 'CURR_MODE', 'X')
                        If CurrMode EQ 'PROD' then ProdTools<0, -1> = ToolID
                    Next ToolID
                    Convert @VM to @STM in ProdTools
                    TempArray = SRP_Array('Join', TempArray, ProdTools, 'OR', @STM)                               
                Next ToolClassID
                If TempArray[-1, 1] EQ @STM then TempArray[-1, 1] = ''                
                MetToolIDArray<MetStageIndex> = TempArray
                MetStageIndex += 1      
        End Case
    Next Row 
    
    RDSNo        = Get_Property(@Window : '.RDS_NO', 'DEFPROP')
    ReactRunRec  = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    WfrIDs       = ReactRunRec<REACT_RUN_IN_WFR_ID$>
    Scribes      = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
    CarrSlotIDs  = ReactRunRec<REACT_RUN_CARR_SLOT_ID$>
    Slots        = ReactRunRec<REACT_RUN_CARR_SLOT_NO$>
    NumWfrs      = DCount(Scribes, @VM)
    Set_Property(@Window, '@SCRIBES', Scribes)
    Set_Property(@Window, '@CARRSLOTIDS', CarrSlotIDs)
    Set_Property(@Window, '@NUMWFRS', NumWfrs)
    Set_Property(@Window, '@WFRIDS', WfrIDs)
    Set_Property(@Window, '@SLOTS', Slots)
    NumDummyWfrs = 0
    DummyPockets = ''
    NumEmptyWfrs = 0
    EmptyPockets = ''
    For each Scribe in Scribes using @VM setting vPos
        DummyID = GanRunID[1,7]:'D'
        EmptyID = GaNRunID[1,7]:'E'
        If Scribe[1,8] EQ DummyID then
            NumDummyWfrs += 1
            DummyPockets<1, vPos> = True$
        end
        If Scribe[1,8] EQ EmptyID then
            NumEmptyWfrs += 1
            EmptyPockets<1, vPos> = True$
        end
    Next Scribe
    Set_Property(@Window, '@DUMMYPOCKETS', DummyPockets)
    Set_Property(@Window, '@EMPTYPOCKETS', EmptyPockets)
    
    SuscUnloaded = False$
    If NumWfrs GT 0 then
        // Wafers have not yet been unloaded from the susceptor. We will use this flag to
        // deactivate various control elements.
        SuscUnloaded = True$
    end
    ReactorStageCtrl = @Window:'.OLE_REACTOR_EDT'
    MetStageCtrl     = @Window:'.OLE_METROLOGY_EDT'
    DispStageCtrl    = @Window:'.OLE_DISP_EDT'
    NCRCtrl          = @Window:'.OLE_NCR_EDT'
    HeaderFontArray  = 'Segoe UI':@SVM:8:@SVM:700
    ReactorDimArray  = NumReactorCols :@FM: NumReactorRows
    MetDimArray      = NumMetCols :@FM: NumMetRows
    DispDimArray     = NumDispCols :@FM: NumDispRows
    NCRDimArray      = NumNCRCols :@FM: NumNCRRows
    Set_Property(@Window, '@METDIMARRAY', MetDimArray)
    Set_Property(@Window, '@DISPDIMARRAY', DispDimArray)
    
    Set_Property(ReactorStageCtrl, "OLE.CellFont[All; All]", 'Segoe UI':@SVM:8)
    Set_Property(ReactorStageCtrl, "OLE.Dimension", ReactorDimArray)
    Set_Property(ReactorStageCtrl, "OLE.CellFont[1;All]", HeaderFontArray)
    Set_Property(ReactorStageCtrl, "OLE.LIST", ReactorCtrlList)
    Set_Property(ReactorStageCtrl, "OLE.HeaderFont[All; 1]", HeaderFontArray)
    Set_Property(ReactorStageCtrl, "OLE.HeaderFont[1; All]", HeaderFontArray)
    
    Set_Property(MetStageCtrl, "OLE.CellFont[All; All]", 'Segoe UI':@SVM:8)
    Set_Property(MetStageCtrl, "OLE.Dimension", MetDimArray)
    Set_Property(MetStageCtrl, "OLE.CellFont[1;All]", HeaderFontArray)
    Set_Property(MetStageCtrl, "OLE.LIST", MetCtrlList)
    Set_Property(MetStageCtrl, "OLE.HeaderFont[All; 1]", HeaderFontArray)
    Set_Property(MetStageCtrl, "OLE.HeaderFont[1; All]", HeaderFontArray)
    
    Set_Property(DispStageCtrl, "OLE.CellFont[All; All]", 'Segoe UI':@SVM:8)
    Set_Property(DispStageCtrl, "OLE.Dimension", DispDimArray)
    Set_Property(DispStageCtrl, "OLE.HeaderFont[All; 1]", HeaderFontArray)
    Set_Property(DispStageCtrl, "OLE.HeaderFont[1; All]", HeaderFontArray)
    
    Set_Property(NCRCtrl, "OLE.CellFont[All; All]", 'Segoe UI':@SVM:8)
    Set_Property(NCRCtrl, "OLE.Dimension", NCRDimArray)
    Set_Property(NCRCtrl, "OLE.HeaderFont[All; 1]", HeaderFontArray)
    Set_Property(NCRCtrl, "OLE.HeaderFont[1; All]", HeaderFontArray)
    
    ScribesCopy = Scribes
    Convert @VM to '' in ScribesCopy
    HeaderTitles = "Stage":@VM:"Tool ID"
    If ScribesCopy NE '' then       
        For each Scribe in Scribes using @VM setting vPos
            SlotID = Field(CarrSlotIDs<0, vPos>, '.', 2, 1)
            HeaderTitles<0, -1> = Scribe:' (':SlotID:')'
        Next Scribe
    end else
        DefWfrTitles = ''
        For WfrIndex = 1 to NumWfrs
            DefWfrTitles<1, -1> = 'Wafer ':WfrIndex
        Next WfrIndex
        HeaderTitles = "Stage":@VM:"Tool ID":@VM:DefWfrTitles
    end
    
    ReactorNo = Get_Property(@Window, '@REACTOR')
    ReactorHeaderTitles = "Stage":@VM:"Status"
    Set_Property(ReactorStageCtrl, "OLE.TitleList", ReactorHeaderTitles)
    Set_Property(ReactorStageCtrl, "OLE.HeaderMerge[1; 2]", 1:@FM:NumReactorRows)
    Set_Property(ReactorStageCtrl, "OLE.HeaderText[1; 2]", 'Reactor ':ReactorNo)
    Set_Property(ReactorStageCtrl, "OLE.HeaderAlignment[All; 1]", 'C':@FM:'C':@FM:'L')
    Set_Property(ReactorStageCtrl, "OLE.CellAlignment[All; All]", 'C':@FM:'C':@FM:'L')
    Set_Property(ReactorStageCtrl, "OLE.CellAlignment[1; All]", 'C':@FM:'L':@FM:'L')
    Set_Property(ReactorStageCtrl, "OLE.AllowDeletions", False$)
    Set_Property(ReactorStageCtrl, "OLE.AllowInserts", False$)
    
    Set_Property(MetStageCtrl, "OLE.TitleList", HeaderTitles)
    Set_Property(MetStageCtrl, "OLE.HeaderMerge[1; 2]", 1:@FM:NumMetRows)
    Set_Property(MetStageCtrl, "OLE.HeaderText[1; 2]", 'Metrology')
    Set_Property(MetStageCtrl, "OLE.HeaderAlignment[All; 1]", 'C':@FM:'C':@FM:'L')
    Set_Property(MetStageCtrl, "OLE.CellAlignment[All; All]", 'C':@FM:'C':@FM:'L')
    Set_Property(MetStageCtrl, "OLE.CellAlignment[1; All]", 'C':@FM:'L':@FM:'L')
    Set_Property(MetStageCtrl, "OLE.AllowDeletions", False$)
    Set_Property(MetStageCtrl, "OLE.AllowInserts", False$)
    
    DispHeaderTitles = 'Slot':@VM:'Wfr ID':@VM:'Scribe':@VM:'Grade':@VM:'Reason':@VM:'RDS NCR':@VM:'WMO NCR':@VM |
    :'Ship':@VM:'Ship ID':@VM:'Internal':@VM:'External':@VM:'Retain':@VM:'Retain Box':@VM:'Retain Slot'
    Set_Property(DispStageCtrl, "OLE.TitleList", DispHeaderTitles)
    Set_Property(DispStageCtrl, "OLE.HeaderMerge[1; 2]", 1:@FM:NumDispRows)
    Set_Property(DispStageCtrl, "OLE.HeaderText[1; 2]", 'Disposition')
    Set_Property(DispStageCtrl, "OLE.HeaderAlignment[All; 1]", 'C':@FM:'C':@FM:'L')
    Set_Property(DispStageCtrl, "OLE.CellAlignment[All; All]", 'C':@FM:'C':@FM:'L')
    Set_Property(DispStageCtrl, "OLE.AllowDeletions", False$)
    Set_Property(DispStageCtrl, "OLE.AllowInserts", False$)           
    
    NCRHeaderTitles = 'NCR':@VM:'Status':@VM:'Qty':@VM:'Responsible':@VM:'Stage':@VM:'Loss Desc':@VM:'Loss Comments' |
    :@VM:'Signature':@VM:'Date & Time'
    Set_Property(NCRCtrl, "OLE.TitleList", NCRHeaderTitles)
    Set_Property(NCRCtrl, "OLE.HeaderMerge[1; 2]", 1:@FM:NumNCRRows)
    Set_Property(NCRCtrl, "OLE.HeaderText[1; 2]", 'Scrap')
    Set_Property(NCRCtrl, "OLE.HeaderAlignment[All; 1]", 'C':@FM:'C':@FM:'L')
    Set_Property(NCRCtrl, "OLE.CellAlignment[All; All]", 'C':@FM:'C':@FM:'L')
    Set_Property(NCRCtrl, "OLE.AllowDeletions", False$)
    Set_Property(NCRCtrl, "OLE.AllowInserts", False$)
    
    // Set default header colors
    HeaderColorArray    = ''
    HeaderColorArray<1> = 'Auto'
    HeaderColorArray<2> = 'None'
    HeaderColorArray<3> = 'None'
    HeaderColorArray<4> = OI_HOT_BLUE$
    HeaderColorARray<5> = False$
    Set_Property(ReactorStageCtrl, "OLE.HeaderColors[All; All]", HeaderColorArray)
    Set_Property(MetStageCtrl, "OLE.HeaderColors[All; All]", HeaderColorArray)
    Set_Property(DispStageCtrl, "OLE.HeaderColors[All; All]", HeaderColorArray)
    Set_Property(NCRCtrl, "OLE.HeaderColors[All; All]", HeaderColorArray)
    
    // Set header tooltips (wafer comments)    
    For each WfrID in WfrIDs using @VM setting WfrIndex
        WfrComments = ''
        WfrComments = Xlate('WO_WFR', WfrID, 'COMMENTS', 'X')
        ColNo = WfrIndex + 3
        If WfrComments NE '' then
            Set_Property(MetStageCtrl, "OLE.HeaderToolTip[":ColNo:";1]", WfrComments)
            // Set header to light cyan so the user knows there's a comment
            HeaderColors = Get_Property(MetStageCtrl, "OLE.HeaderColors[":ColNo:";1]")
            HeaderColors<2> = 'LightCyan'
            HeaderColors<4> = 'LightCyan'
            Set_Property(MetStageCtrl, "OLE.HeaderColors[":ColNo:";1]", HeaderColors)
        end       
    Next WfrID
    
    // Qualify OLE events that we want to intercept
    Qualifier = ''
    Qualifier<1> = 1
    Qualifier<4> = 0 ; * process synchronously (i.e. immediately)
    Send_Message(ReactorStageCtrl,  'QUALIFY_EVENT', 'OLE.OnClick',             Qualifier)
    Send_Message(ReactorStageCtrl,  'QUALIFY_EVENT', 'OLE.OnButtonClick',       Qualifier)
    Send_Message(ReactorStageCtrl,  'QUALIFY_EVENT', 'OLE.OnContextMenuClick',  Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.OnClick',             Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.PosChanged',          Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.OnButtonClick',       Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.OnComboClicked',      Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.OnContextMenuClick',  Qualifier)
    Send_Message(MetStageCtrl,      'QUALIFY_EVENT', 'OLE.OnHeaderDblClick',    Qualifier)
    Send_Message(DispStageCtrl,     'QUALIFY_EVENT', 'OLE.OnCheckChanged',      Qualifier)
    Send_Message(DispStageCtrl,     'QUALIFY_EVENT', 'OLE.AfterUpdate',         Qualifier)
    Send_Message(DispStageCtrl,     'QUALIFY_EVENT', 'OLE.OnClick',             Qualifier)
    Send_Message(DispStageCtrl,     'QUALIFY_EVENT', 'OLE.OnContextMenuClick',  Qualifier)
    Send_Message(NCRCtrl,           'QUALIFY_EVENT', 'OLE.OnDblClick',          Qualifier)
    
    // Setup Tool ID and Wafer status/action combo boxes
    CellTypeArray = ""
    CellTypeArray<1>    = "Combo"
    CellTypeArray<2, 1> = ""
    CellTypeArray<2, 2> = "L"
    CellTypeArray<2, 3> = ''
    CellTypeArray<2, 4> = 1                          ;// Column 1 contains the values we care about
    CellTypeArray<2, 5> = 1                          ;// Auto fill on
    CellTypeArray<2, 6> = 0                          ;// Case sensitive on
    CellTypeArray<2, 7> = 10                         ;// 10 visible rows max
    CellTypeArray<2, 8> = 0                          ;// Don't fire the OnOptionClick
    CellTypeArray<2, 9> = 1                          ;// Reduce the list to partial matches
    CellTypeArray<2, 10> = 0                         ;// Only show the drop down when the user types
    CellTypeArray<2, 11> = 0                         ;// Do not use LIST Format
    CellTypeArray<2, 12> = 1                         ;// Autofill on Tool ID name
    CellTypeArray<2, 13> = 1                         ;// Hide dropdown when user clears cell
    CellTypeArray<2, 14> = 0                         ;// Let navigation keys show the drop down
    CellTypeArray<2, 15> = 0                         ;// Show the drop down regardless of the cell's contents
    CellTypeArray<2, 16> = 0                         ;// Show the drop down during autofill
    CellTypeArray<2, 17> = 1                         ;// Remove selection when user backspaces/deletes
    CellTypeArray<2, 18> = 0                         ;// Show Popup while in read only mode
    CellTypeArray<2, 19> = 1                         ;// Show Popup When Navigating
    CellTypeArray<2, 20> = 1                         ;// Always Tab Out on Enter
    CellTypeArray<2, 21> = -1                        ;// Always show the dropdown above when close to the screen bottom
    CellTypeArray<2, 23> = 1                         ;// Limit possible values to those in dropdown
    // Store this for later use elsewhere   
    Set_Property(@Window, '@COMBOBOX', CellTypeArray)
    
    For RowIndex = 1 to NumMetRows
        ThisComboArray = CellTypeArray
        ThisComboArray<2, 3> = MetToolIDArray<RowIndex>
        Set_Property(MetStageCtrl, "OLE.CellType[2; ":RowIndex:"]", ThisComboArray)
    Next RowIndex
    
    // Cell selection style setup
    Set_Property(ReactorStageCtrl,  "OLE.ResetSelPos", False$)  ;// Prevent cell selection from being reset upon lost focus event
    Set_Property(MetStageCtrl,      "OLE.ResetSelPos", False$)
    Set_Property(DispStageCtrl,     "OLE.ResetSelPos", False$)
    Set_Property(NCRCtrl,           "OLE.ResetSelPos", False$)
    ReactorSelStyleArray    = Get_Property(ReactorStageCtrl, "OLE.SelectionStyle")
    ReactorSelStyleArray<8> = True$
    MetSelStyleArray        = Get_Property(MetStageCtrl, "OLE.SelectionStyle")
    MetSelStyleArray<8>     = True$                         ;// Keep cell highlighted when table loses focus
    DispSelStyleArray       = Get_Property(DispStageCtrl, "OLE.SelectionStyle")
    DispSelStyleArray<8>    = True$                         ;// Keep cell highlighted when table loses focus
    NCRSelStyleArray        = Get_Property(NCRCtrl, "OLE.SelectionStyle")
    NCRSelStyleArray<8>     = True$                         ;// Keep cell highlighted when table loses focus
    Set_Property(ReactorStageCtrl,  'OLE.SelectionStyle', ReactorSelStyleArray)
    Set_Property(MetStageCtrl,      'OLE.SelectionStyle', MetSelStyleArray)
    Set_Property(DispStageCtrl,     'OLE.SelectionStyle', DispSelStyleArray)
    Set_Property(NCRCtrl,           'OLE.SelectionStyle', MCRSelStyleArray)
    
    CellSelStyleArray    = ''
    CellSelStyleArray<1> = 'White'                      ;// Selected cell foreground color
    CellSelStyleArray<2> = OI_BLUE$                     ;// Selected cell background color
    CellSelStyleArray<3> = 'None'                       ;// Selected record (row) foreground color - none = default
    CellSelStyleArray<4> = OI_HOT_BLUE$                 ;// Selected record (row) background color
    CellSelStyleArray<5> = 'None'                       ;// Selected field (column) foreground color - none = default
    CellSelStyleArray<6> = OI_HOT_BLUE$                 ;// Selected field (column) background color
    Set_Property(ReactorStageCtrl,  'OLE.CellSelColors[2; All]', CellSelStyleArray)
    Set_Property(MetStageCtrl,      'OLE.CellSelColors[2-':NumDummyWfrs+2:'; All]', CellSelStyleArray)
    Set_Property(NCRCtrl,           'OLE.CellSelColors[All; All]', CellSelStyleArray)
    
    // Stage cell button style setup
    ButtonTypeArray       = ''
    ButtonTypeArray<1>    = 'Push Button'
    ButtonTypeArray<2>    = ''
    ButtonTypeArray<2, 1> = False$
    ButtonTypeArray<2, 2> = True$
    ButtonTypeArray<2, 3> = True$
    ButtonTypeArray<3>    = 'None'
    ButtonTypeArray<4>    = "Vertical(Gradient(":OI_HOT_BLUE$:", ":OI_HOT_BLUE$:"), Border(":OI_BLUE$:"))"
    ButtonTypeArray<5>    = "Vertical(Gradient(":OI_CLICK_BLUE$:", ":OI_CLICK_BLUE$:"), Border(":OI_BLUE$:"))"
    ButtonTypeArray<6>    = 'OD'
    ButtonTypeArray<7>    = 'None'
    Set_Property(ReactorStageCtrl, "OLE.CellType[1; All]", ButtonTypeArray)
    Set_Property(MetStageCtrl, "OLE.CellType[1; All]", ButtonTypeArray)
    
    MetRowsEnabled = ''
    For RowIndex = 1 to NumMetRows
        MetRowsEnabled<RowIndex> = False$
    Next RowIndex
    
    // Get associated run stage keys and check them for assigned Tool IDs. If Tool ID is
    // set, then set the Tool ID combo box selection accordingly.
    ReactorStageList    = Get_Property(ReactorStageCtrl, "OLE.ColumnData[1]")
    MetStageList        = Get_Property(MetStageCtrl, "OLE.ColumnData[1]")
    ReactorStageIDList  = ''
    MetStageIDList      = ''
    For each StageDesc in ReactorStageList using @VM setting vPos
        ReactorStageIDList<1, vPos> = Gan_Services('GetStageID', StageDesc)
    Next StageDesc
    For each StageDesc in MetStageList using @VM setting vPos
        MetStageIDList<1, vPos> = Gan_Services('GetStageID', StageDesc)
    Next StageDesc
    Set_Property(@Window, '@METSTAGEIDLIST', MetStageIDList)
    RunStageKeys = ReactRunRec<REACT_RUN_RUN_STAGE_KEY$>
    MetStages    = ReactRunRec<REACT_RUN_WFR_STAGE$>
    Set_Property(@Window, '@METTOOLIDARRAY', MetToolIDArray)
    
    // Protect and color dummy and empty wafer columns
    ColorArray = @FM:'OD'
    CellColorArray = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
    For each Pocket in DummyPockets using @VM setting vPos
        If Pocket EQ True$ then
            ColNo = vPos + 2
            Set_Property(MetStageCtrl,"OLE.CellProtection[":ColNo:"; All]", "Full")
            Set_Property(MetStageCtrl,"OLE.CellColors[":ColNo:"; All]", ColorArray)
            Set_Property(MetStageCtrl,"OLE.ColumnData[":ColNo:"]", '')
            Set_Property(MetStageCtrl,"OLE.HeaderColors[":ColNo+1:"; 1]", ColorArray)
        end
    Next Pocket
    
    For each Pocket in EmptyPockets using @VM setting vPos
        If Pocket EQ True$ then
            ColNo = vPos + 2
            Set_Property(MetStageCtrl,"OLE.CellProtection[":ColNo:"; All]", "Full")
            Set_Property(MetStageCtrl,"OLE.CellColors[":ColNo:"; All]", ColorArray)
            Set_Property(MetStageCtrl,"OLE.ColumnData[":ColNo:"]", '')
            Set_Property(MetStageCtrl,"OLE.HeaderColors[":ColNo+1:"; 1]", ColorArray)
        end
    Next Pocket
    
    // Protect and color N/A wafer columns
    If NumWfrs < 8 then         ; // 8 wafers make a full cassette in GaN       
        StartCol = NumWfrs + 3  ; // Must add the three columns before the wafer columns 
        Set_Property(MetStageCtrl,"OLE.CellProtection[":StartCol:"-":NumMetCols:"; All]", "Full")
        Set_Property(MetStageCtrl,"OLE.CellColors[":StartCol:"-":NumMetCols:"; All]", ColorArray)
        Set_Property(MetStageCtrl,"OLE.ColumnData[":StartCol:"-":NumMetCols:"]", '')
        Set_Property(MetStageCtrl,"OLE.HeaderColors[":StartCol+1:"-":NumMetCols+1:"; 1]", ColorArray)       
    end
    
    // Get and set each cassette's current status for each reactor stage
    // Cassette level (RUN_STAGE)
    For each Stage in ReactorStageIDList using @VM setting StageIndex
        RunStageKey = RDSNo:'*':Stage
        If RowExists('RUN_STAGE', RunStageKey) then
            CurrStatus = Xlate('RUN_STAGE', RunStageKey, 'STATUS', 'X')
            DispStatus = ''
            Begin Case
                Case CurrStatus _EQC 'COMP'
                    DispStatus = 'Complete'
                Case CurrStatus _EQC 'STOP'
                    Begin Case
                        Case Stage _EQC 'GROWTH'
                            DispStatus = 'Unload'
                        Case Otherwise$
                            DispStatus = 'Stop'                                
                    End Case
                Case CurrStatus _EQC 'START'
                    If Stage EQ 'GROWTH' then
                        DispStatus = 'Load'
                    end else
                        DispStatus = 'Start'
                    end
                Case CurrStatus _EQC 'SKIP'
                    DispStatus = 'Skip'
                Case Otherwise$
                    DispStatus = 'X'
            End Case
            Set_Property(ReactorStageCtrl, "OLE.CellText[2; ":StageIndex:"]", DispStatus)
            Set_Property(ReactorStageCtrl, "OLE.CellProtection[2; ":StageIndex:"]", "RO")
        end
    Next Stage
    
    // Get and set each wafer's current status for each stage within the Metrology and Disposition edit tables.
    GoSub FillMetEditTable
    
    //////////////////////////////////// Code below here only runs once //////////////////////////////////////////
    
    FirstPassComp = Get_Property(@Window, '@FIRSTPASS')
    
    If FirstPassComp EQ '' or FirstPassComp EQ False$ then
        Set_Property(@Window, '@FIRSTPASS', True$)
        
        // Disable resizing of the header row and header column
        // Resize header column to fit contents
        HeaderRowArray = Get_Property(MetStageCtrl, "OLE.HeaderRow[1]")
        HeaderColArray = Get_Property(MetStageCtrl, "OLE.HeaderColumn[1]")
        HeaderRowArray<3> = False$
        HeaderColArray<1> = 80
        HeaderColArray<3> = False$
        Set_Property(MetStageCtrl, "OLE.HeaderColumn[1]", HeaderColArray)
        Set_Property(MetStageCtrl, "OLE.HeaderRow[1]", HeaderRowArray)
        // Disable resizing of columns as there is no need for this on this form
        StageColSize = Get_Property(MetStageCtrl, "OLE.DataColumn[1]")
        StageColSize<3> = False$
        Set_Property(MetStageCtrl, "OLE.DataColumn[All]", StageColSize)
        // Resize the stage column to fit stage descriptions
        StageColSize<1> = 186
        Set_Property(MetStageCtrl, "OLE.DataColumn[1]", StageColSize)
        WfrColSize = Get_Property(MetStageCtrl, "OLE.DataColumn[3]")
        WfrColSize<1> = 96
        Set_Property(MetStageCtrl, "OLE.DataColumn[3-10]", WfrColSize)
        // Disable resizing of rows as there is no need for this on this form
        RowSizeProps = Get_Property(MetStageCtrl, "OLE.DataRow[1]")
        RowSizeProps<3> = False$
        Set_Property(MetStageCtrl, "OLE.DataRow[All]", RowSizeProps)
        
        // Disable resizing of the header row and header column
        // Resize header column to fit contents
        HeaderRowArray = Get_Property(ReactorStageCtrl, "OLE.HeaderRow[1]")
        HeaderColArray = Get_Property(ReactorStageCtrl, "OLE.HeaderColumn[1]")
        HeaderRowArray<3> = False$
        HeaderColArray<1> = 80
        HeaderColArray<3> = False$
        Set_Property(ReactorStageCtrl, "OLE.HeaderColumn[1]", HeaderColArray)
        Set_Property(ReactorStageCtrl, "OLE.HeaderRow[1]", HeaderRowArray)
        // Disable resizing of columns as there is no need for this on this form
        StageColSize = Get_Property(ReactorStageCtrl, "OLE.DataColumn[1]")
        StageColSize<3> = False$
        Set_Property(ReactorStageCtrl, "OLE.DataColumn[All]", StageColSize)
        // Resize the stage column to fit stage descriptions
        StageColSize<1> = 186
        Set_Property(ReactorStageCtrl, "OLE.DataColumn[1]", StageColSize)
        // Disable resizing of rows as there is no need for this on this form
        RowSizeProps = Get_Property(ReactorStageCtrl, "OLE.DataRow[1]")
        RowSizeProps<3> = False$
        Set_Property(ReactorStageCtrl, "OLE.DataRow[All]", RowSizeProps)
        
        
        //////////////////////////////////// Setup Disposition OLE Edit Table //////////////////////////////////////////        
        
        // Disable resizing of the header row and header column
        // Resize header column to fit contents
        HeaderRowArray = Get_Property(DispStageCtrl, "OLE.HeaderRow[1]")
        HeaderColArray = Get_Property(DispStageCtrl, "OLE.HeaderColumn[1]")
        HeaderRowArray<3> = False$
        HeaderColArray<1> = 80
        HeaderColArray<3> = False$
        Set_Property(DispStageCtrl, "OLE.HeaderColumn[1]", HeaderColArray)
        Set_Property(DispStageCtrl, "OLE.HeaderRow[1]", HeaderRowArray)
        // Disable resizing of columns as there is no need for this on this form
        StageColSize = Get_Property(DispStageCtrl, "OLE.DataColumn[1]")
        StageColSize<3> = False$
        Set_Property(DispStageCtrl, "OLE.DataColumn[All]", StageColSize)
        // 1,034 px wide
        // Disposition OLE Edit Table
        WfrColSize = Get_Property(DispStageCtrl, "OLE.DataColumn[3]")
        WfrColSize<1> = 79
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_SLOT:"-":COL$DISP_RETAIN_BOX:"]", WfrColSize)
        // Slot column        
        WfrColSize<1> = 30
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_SLOT:"]", WfrColSize)
        // Wfr ID column
        WfrColSize<1> = 86
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_WFR_ID:"]", WfrColSize)
        // Reason column
        WfrColSize<1> = 147
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_REASON:"]", WfrColSize)
        // NCR columns
        WfrColSize<1> = 85
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_RDS_NCR:"-":COL$DISP_WMO_NCR:"]", WfrColSize)
        // Ship column
        WfrColSize<1> = 60
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_SHIP:"]", WfrColSize)
        // Ship ID column
        WfrColSize<1> = 80
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_SHIP_ID:"]", WfrColSize)
        // Retain column
        WfrColSize<1> = 60
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_RETAIN:"]", WfrColSize)
        // Retain Slot columns
        WfrColSize<1> = 65
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_RETAIN_SLOT:"]", WfrColSize)
        // Internal column, External column, Retain column
        WfrColSize<1> = 50
        Set_Property(DispStageCtrl, "OLE.DataColumn[":COL$DISP_INTERNAL:"-":COL$DISP_EXTERNAL:"]", WfrColSize)
        // Disable resizing of rows as there is no need for this on this form
        RowSizeProps = Get_Property(DispStageCtrl, "OLE.DataRow[1]")
        RowSizeProps<3> = False$
        Set_Property(DispStageCtrl, "OLE.DataRow[All]", RowSizeProps)
        // Disable scrollbars
        Set_Property(DispStageCtrl, "OLE.ScrollBarsVisible", 'N':@FM:'N')
        
        CheckBoxArray      = 'Check Box'
        CheckBoxArray<2,1> = True$
        Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_SHIP:"; All]", CheckBoxArray)
        Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_RETAIN:"; All]", CheckBoxArray)
        Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_INTERNAL:"; All]", CheckBoxArray)
        Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_EXTERNAL:"; All]", CheckBoxArray)
        Set_Property(DispStageCtrl, "OLE.CellProtection[All; All]", 'RO')
        Set_Property(DispStageCtrl, "OLE.CellCheckEnabled[":COL$DISP_SHIP:"; All]", False$)
        Set_Property(DispStageCtrl, "OLE.CellCheckEnabled[":COL$DISP_RETAIN:"; All]", False$)
        Set_Property(DispStageCtrl, "OLE.CellCheckEnabled[":COL$DISP_INTERNAL:"; All]", False$)
        
        
        //////////////////////////////////// Setup NCR OLE Edit Table //////////////////////////////////////////////
        
        // Disable resizing of the header row and header column
        // Resize header column to fit contents
        HeaderRowArray = Get_Property(NCRCtrl, "OLE.HeaderRow[1]")
        HeaderColArray = Get_Property(NCRCtrl, "OLE.HeaderColumn[1]")
        HeaderRowArray<3> = False$
        HeaderColArray<1> = 80
        HeaderColArray<3> = False$
        Set_Property(NCRCtrl, "OLE.HeaderColumn[1]", HeaderColArray)
        Set_Property(NCRCtrl, "OLE.HeaderRow[1]", HeaderRowArray)
        // Disable resizing of columns as there is no need for this on this form
        ColSize = Get_Property(NCRCtrl, "OLE.DataColumn[1]")
        ColSize<3> = False$
        ColSize<1> = 56
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_NO:"]", ColSize)
        ColSize<1> = 45
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_STATUS:"]", ColSize)
        ColSize<1> = 35
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_REJ_QTY:"]", ColSize)
        ColSize<1> = 90
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_RESP:"]", ColSize)
        ColSize<1> = 58
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_STAGE:"]", ColSize)
        ColSize<1> = 140
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_LOSS_DESC:"]", ColSize)
        ColSize<1> = 173
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_LOSS_COMM:"]", ColSize)
        ColSize<1> = 100
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_FIN_SIG:"]", ColSize)
        ColSize<1> = 120
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_FIN_SIG_DTM:"]", ColSize)
        
        // Disable resizing of rows as there is no need for this on this form
        RowSizeProps = Get_Property(NCRCtrl, "OLE.DataRow[1]")
        RowSizeProps<3> = False$
        Set_Property(NCRCtrl, "OLE.DataRow[All]", RowSizeProps)   
        Set_Property(NCRCtrl, "OLE.CellProtection[":COL$NCR_NO:"-":COL$NCR_FIN_SIG_DTM:"; All]", 'SEL')
        
        // Color headers
        Set_Property(NCRCtrl, "OLE.HeaderColors[All; 1]", @FM:"{200, 200, 200}")
        // Set Loss Comments column to autosized so that the table resizes when scrollbars are necessary
        ColArray = Get_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_LOSS_COMM:"]")
        ColArray<4> = True$
        Set_Property(NCRCtrl, "OLE.DataColumn[":COL$NCR_LOSS_COMM:"]", ColArray)
        
    end
    
return


AutoSelectToolID:

    EnabledColorArray   = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
    MetStageIDList      = Get_Property(@Window, '@METSTAGEIDLIST')
    AllWfrIDs           = Get_Property(@Window, '@WFRIDS')
    SingleWfrID         = Get_Property(@Window, '@SINGLE_WFR_ID')
    
    If SingleWfrID NE '' then
        WfrIDs = SingleWfrID
    end else
        WfrIDs = AllWfrIDs
    end
    
    MetStageCtrl        = @Window:'.OLE_METROLOGY_EDT'
    MetRowsEnabled      = Get_Property(@Window, '@METROWSENABLED')
    MetToolIDArray      = Get_Property(@Window, '@METTOOLIDARRAY')
    For each MetStage in MetStageIDList using @VM setting mPos
        If MetStage _NEC 'SPLIT' then
            RunStageWfrKey = ''
            For each WfrID in WfrIDs using @VM
                If WfrID NE '' then                    
                    Locate WfrID in AllWfrIDs using @VM setting wPos then                        
                        CurrWfrCol = wPos + 2
                        Convert '*' to '.' in WfrID
                        Begin Case                   
                            Case MetStage EQ 'UV_PRE'
                                // Check if UV stage exists
                                UVStageWfrKey = RDSNo:'*UV*':WfrID
                                If RowExists('RUN_STAGE_WFR', UVStageWfrKey) then
                                    // Display this on the UV_PRE line
                                    RunStageWfrKey = UVStageWfrKey
                                end else
                                    RunStageWfrKey = RDSNo:'*':MetStage:'*':WfrID
                                end
                            Case MetStage EQ 'CAN_PRE'
                                // Check if CAN stage exists
                                CanStageWfrKey = RDSNo:'*CAN*':WfrID
                                If RowExists('RUN_STAGE_WFR', CanStageWfrKey) then
                                    // Display this on the CAN_PRE line
                                    RunStageWfrKey = CanStageWfrKey
                                end else
                                    RunStageWfrKey = RDSNo:'*':MetStage:'*':WfrID
                                end    
                            Case Otherwise$
                                RunStageWfrKey = RDSNo:'*':MetStage:'*':WfrID                           
                        End Case
                        KeyFound = RowExists('RUN_STAGE_WFR', RunStageWfrKey)
                        If KeyFound EQ True$ then
                            RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
                            ToolID         = RunStageWfrRec<RUN_STAGE_WFR_TOOL_ID$>
                            If ToolID EQ '' then
                                // Check to see how many tools exist for the current stage. If only one exists, then
                                // auto-select it for convenience.
                                ToolIDs     = Gan_Services('GetToolIDs', MetStage, 'PROD')
                                NumTools    = DCount(ToolIDs, @VM)
                                If NumTools EQ 1 then
                                    ToolID = ToolIDs<0, 1>
                                    RunStageWfrRec<RUN_STAGE_WFR_TOOL_ID$> = ToolID
                                    Database_Services('WriteDataRow', 'RUN_STAGE_WFR', RunStageWfrKey, RunStageWfrRec, True$, False$, True$)
                                end
                            end
                            If ToolID NE '' then
                                Locate MetStage in MetStageIDList using @VM setting RowIndex then                               
                                    ThisComboArray = MetToolIDArray<RowIndex>
                                    Convert @STM to @VM in ThisComboArray                                    
                                    Locate ToolID in ThisComboArray using @VM setting vPos then
                                        Set_Property(MetStageCtrl, "OLE.CellComboSelPos[2; ":RowIndex:"]", vPos)                                    
                                    end else
                                        Set_Property(MetStageCtrl, "OLE.CellText[2;":RowIndex:"]", ToolID)
                                    end
                                    // Tool ID is set, so enable wafer action dropdown menus.                               
                                    If ( (MetStage NE 'UV') and (MetStage NE 'UV_PRE') and (MetStage NE 'UV_PST') and (MetStage NE 'WARP') and (MetStage NE 'CAN') and (MetStage NE 'CAN_PRE') and (MetStage NE 'CAN_PST') and (MetStage NE 'WET_CLEAN') ) then
                                        Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:"; ":RowIndex:"]","None")
                                    end
                                    MetRowsEnabled<RowIndex> = True$
                                end
                            end
                        end
                    end
                end
            Next WfrID
        end
    Next MetStage
    Set_Property(@Window, '@METROWSENABLED', MetRowsEnabled)
    
return


FillMetHeader:
    
    Scribes      = Xlate('REACT_RUN', RDSNo, 'WAFER_SCRIBE', 'X')
    CarrSlotIDs  = Xlate('REACT_RUN', RDSNo, 'CARR_SLOT_NO', 'X')
    Set_Property(@Window, '@SCRIBES', Scribes)
    Set_Property(@Window, '@CARRSLOTIDS', CarrSlotIDs)
    MetStageCtrl = @Window:'.OLE_METROLOGY_EDT'
    ScribesCopy  = Scribes
    Convert @VM to '' in ScribesCopy
    HeaderTitles = "Stage":@VM:"Tool ID"
    If ScribesCopy NE '' then       
        For each Scribe in Scribes using @VM setting vPos
            SlotID = CarrSlotIDs<0, vPos>
            HeaderTitles<0, -1> = Scribe:' (':SlotID:')'
        Next Scribe
    end else
        DefWfrTitles = ''
        For WfrIndex = 1 to NumWfrs
            DefWfrTitles<1, -1> = 'Wafer ':WfrIndex
        Next WfrIndex
        HeaderTitles = "Stage":@VM:"Tool ID":@VM:DefWfrTitles
    end
    Set_Property(MetStageCtrl, "OLE.TitleList", HeaderTitles)
    
return


EnableRows:
    
    // Enable rows and columns now that RUN_STAGE_WFR records have been created
    GaNRunID      = Get_Property(@Window:'.CMB_GAN_RUN_ID', 'TEXT')
    RDSNo         = Get_Property(@Window:'.RDS_NO', 'TEXT')
    MetStageCtrl  = @Window:'.OLE_METROLOGY_EDT'
    Scribes       = Xlate('REACT_RUN', RDSNo, 'PKT_SAT_ID', 'X')
    DummyPockets  = ''
    NumDummyWfrs = 0
    For each Scribe in Scribes using @VM setting vPos
        DummyID = GanRunID[1,7]:'D'
        If Scribe[1,8] EQ DummyID then
            NumDummyWfrs += 1
            DummyPockets<1, vPos> = True$
        end else
            DummyPockets<1, vPos> = False$
        end
    Next Scribe
    NumWfrs       = DCount(Scribes, @VM)
    Set_Property(@Window, '@DUMMYPOCKETS', DummyPockets)
    Set_Property(@Window, '@NUMWFRS', NumWfrs)
    MetDimArray   = Get_Property(@Window, '@METDIMARRAY')
    NumMetCols    = MetDimArray<1>
    DispDimArray  = Get_Property(@Window, '@DISPDIMARRAY')
    NumDispCols   = DispDimArray<1>
    EnabledColorArray = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
    For each Pocket in DummyPockets using @VM setting vPos
        If Pocket EQ True$ then
            ColNo = vPos + 2
            Set_Property(MetStageCtrl,"OLE.CellProtection[":ColNo:"; All]", "None")
            Set_Property(MetStageCtrl,"OLE.CellColors[":ColNo:"; All]", EnabledColorArray)
            Set_Property(MetStageCtrl,"OLE.ColumnData[":ColNo:"]", '')
            Set_Property(MetStageCtrl,"OLE.HeaderColors[":ColNo+1:"; 1]", EnabledColorArray)
        end
    Next Pocket
    // Protect and color empty wafer columns
    If NumWfrs GT 0 then
        ColOffset = 3    
        StartCol = ColOffset
        EndCol   = NumWfrs + ColOffset
        Set_Property(MetStageCtrl,"OLE.CellProtection[":StartCol:"-":EndCol:"; 1-4]", "None")
        Set_Property(MetStageCtrl,"OLE.CellColors[":StartCol:"-":EndCol:"; 1-4]", EnabledColorArray)
        Set_Property(MetStageCtrl,"OLE.ColumnData[":StartCol:"-":EndCol:"]", '')
        Set_Property(MetStageCtrl,"OLE.HeaderColors[":StartCol+1:"-":EndCol+1:"; 1]", EnabledColorArray)
    end
return


FillDispEditTable:
    
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    DispStageCtrl = @Window:'.OLE_DISP_EDT'
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    DispCtrlArray = ''
    InWfrIDs      = ReactRunRec<REACT_RUN_IN_WFR_ID$>
    NumWfrs       = DCount(InWfrIDs, @VM)
    
    For WfrIndex = 1 to NumWfrs
        WfrID            = InWfrIDs<0, WfrIndex>
        WOWfrRec         = Database_Services('ReadDataRow', 'WO_WFR', WfrID)
        RDSNCRNo         = WOWfrRec<WO_WFR_RDS_NCR_NO$>
        WMONCRNo         = WOWfrRec<WO_WFR_WMO_NCR_NO$>              
        ShipFlag         = ReactRunRec<REACT_RUN_CARR_WFR_SHIP$, WfrIndex>
        InternalFlag     = ReactRunRec<REACT_RUN_INTERNAL$, WfrIndex>
        ExternalFlag     = ReactRunRec<REACT_RUN_EXTERNAL$, WfrIndex>
        RetainFlag       = ReactRunRec<REACT_RUN_GAN_RET_WFR$, WfrIndex>
        Grade            = WOWfrRec<WO_WFR_GRADE$>
        Scribe           = WOWfrRec<WO_WFR_SCRIBE$>
        RetainBox        = WOWfrRec<WO_WFR_RETAIN_BOX$>
        RetainSlot       = WOWfrRec<WO_WFR_RETAIN_SLOT$>
        CriticalFailFlag = WOWfrRec<WO_WFR_CRITICAL_FAILURE$>
        DispCtrlArray<COL$DISP_SLOT, WfrIndex>        = ReactRunRec<REACT_RUN_CARR_SLOT_NO$, WfrIndex>
        DispCtrlArray<COL$DISP_WFR_ID, WfrIndex>      = ReactRunRec<REACT_RUN_IN_WFR_ID$, WfrIndex>
        DispCtrlArray<COL$DISP_SCRIBE, WfrIndex>      = Scribe
        DispCtrlArray<COL$DISP_GRADE, WfrIndex>       = Grade    
        DispCtrlArray<COL$DISP_RDS_NCR, WfrIndex>     = RDSNCRNo
        DispCtrlArray<COL$DISP_WMO_NCR, WfrIndex>     = WMONCRNo
        DispCtrlArray<COL$DISP_SHIP, WfrIndex>        = ShipFlag
        DispCtrlArray<COL$DISP_SHIP_ID, WfrIndex>     = ReactRunRec<REACT_RUN_OUT_SLOT_ID$, WfrIndex>       
        DispCtrlArray<COL$DISP_INTERNAL, WfrIndex>    = InternalFlag
        DispCtrlArray<COL$DISP_EXTERNAL, WfrIndex>    = ExternalFlag
        DispCtrlArray<COL$DISP_RETAIN, WfrIndex>      = RetainFlag
        DispCtrlArray<COL$DISP_RETAIN_SLOT, WfrIndex> = RetainSlot
        DispCtrlArray<COL$DISP_RETAIN_BOX, WfrIndex>  = RetainBox
        MetComp  = Gan_Services('GetMetrologyStatus', WfrID)
        DispComp = Gan_Services('GetDispStatus', WfrID)
        
        // The wafer scrap reason may vary between the disposition report and NCR.  The wafer scrap reason obtained
        // from the disposition report is saved in the REACT_RUN record.  First look for the reason documented in
        // the NCR.  If no NCR value is available use the value obtained from the disposition report.
        Reason = ''
        If (RDSNCRNo NE '') then
            // Display the reason from the NCR record
            Reason = Xlate('NCR', RDSNCRNo, 'AC_DESC', 'X')
        end
        if (Reason EQ '') then
            // Display the reason from the Disposition report since none was found in the NCR
            Reason = ReactRunRec<REACT_RUN_DISP_REASON$, WfrIndex>
        end
        
        DispCtrlArray<COL$DISP_REASON, WfrIndex> = Reason
        // Updated code to not allow "Internal Characterization" wafers to be NCR'd       
        If ( ( (Grade _EQC 'Scrap' and (InternalFlag EQ False$ or InternalFlag EQ '') ) or (ExternalFlag EQ True$) or (CriticalFailFlag EQ True$) ) or ( (Reason _EQC 'Lot Aborted') or (Reason _EQC 'Non-rotation') or (Reason _EQC 'Broken') or (Reason _EQC 'HB - Handling Breakage') ) ) |
        and (RDSNCRNo EQ '') |
        and ( (MetComp EQ True$) or (DispComp EQ True$) ) then
            // Convert NCR cell for this row into a check box
            CheckBoxArray      = 'Check Box'
            CheckBoxArray<2,1> = True$
            Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_RDS_NCR:";":WfrIndex:"]", CheckBoxArray)                                  
        end else
            // Reset cell back to a text type cell in case it was previously set to a check box
            Set_Property(DispStageCtrl, "OLE.CellType[":COL$DISP_RDS_NCR:";":WfrIndex:"]", 'Text')
            Set_Property(DispStageCtrl, "OLE.CellText[":COL$DISP_RDS_NCR:";":WfrIndex:"]", RDSNCRNo)
        end
        
        If ( (ShipFlag EQ True$) or (InternalFlag EQ True$) or (RetainFlag EQ True$) or (MetComp EQ False$) or (DispComp EQ False$) ) then
            Set_Property(DispStageCtrl, "OLE.CellCheckEnabled[":COL$DISP_EXTERNAL:";":WfrIndex:"]", False$)
        end else
            Set_Property(DispStageCtrl, "OLE.CellCheckEnabled[":COL$DISP_EXTERNAL:";":WfrIndex:"]", True$)
        end
        
    Next WfrIndex
    
    Set_Property(DispStageCtrl, "OLE.ARRAY", DispCtrlArray)
    GoSub EnableNCRButton
    
return


Event OLE_NCR_EDT.OnDblClick(Cell, Point, Button, Shift, Ctrl)
    
    Row   = Field(Cell, ';', 2, 1)
    NCRNo = Get_Property(CtrlEntID, "OLE.CellText[":COL$NCR_NO:";":Row:"]")
    If RowExists('NCR', NCRNo) then
        Dialog_Box('NCR', @Window, NCRNo)
        GoSub FillNCREditTable
    end
    
end event


Event OLE_DISP_EDT.OnClick(Cell, Point, Button, Shift, Ctrl)
    
    Col = Field(Cell, ';', 1, 1)
    Row = Field(Cell, ';', 2, 1)
    If Col EQ COL$DISP_WFR_ID then
        If Button _EQC 'Right' then
            RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')           
            WfrID       = Get_Property(CtrlEntID, "OLE.CellText[":COL$DISP_WFR_ID:";":Row:"]")
            Menu        = ''
            MenuEnabled = False$
            If RowExists('WO_WFR', WfrID) then MenuEnabled = True$
            Menu<-1> = "CharStages"   :@VM:"View Char Stages"   :@VM:MenuEnabled
            Menu<-1> = "WaferTrace"   :@VM:"View Wafer Trace"   :@VM:MenuEnabled            
            Send_Message(CtrlEntID, "OLE.ShowContextMenu", Point, Menu, WfrID)
        end
    end
    
end event


Event OLE_DISP_EDT.OnContextMenuClick(Item, UserData)
    
    RDSNo  = Get_Property(@Window:'.RDS_NO', 'TEXT')
    Action = Item
    WfrID  = UserData
    Begin Case
        Case Action _EQC 'CharStages'
            
            WfrSigProfile       = obj_React_Run('GetWfrSigKeys', RDSNo:@RM:WfrID)
            TypeOver            = ''
            TypeOver<PDISPLAY$> = WfrSigProfile
            Dummy               = Popup(@Window, TypeOver, 'CHAR_WFR_STAGES')
            
        Case Action _EQC 'WaferTrace'
            
            TraceData           = obj_WO_Wfr('TraceData', WfrID:@RM:'')
            TypeOver            = ''
            TypeOver<PDISPLAY$> = TraceData
            Dummy               = Popup(@Window, TypeOver, 'WAFER_TRACE')
            
    End Case
end event


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Internal GoSubs
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


EnableNCRButton:
    
    Checkmarks = Get_Property(@Window:".OLE_DISP_EDT", "OLE.CellCheck[":COL$DISP_RDS_NCR:"; All]")
    Checksum   = Sum(Checkmarks)
    If ( Checksum GT 0 ) then
        NCRButton = True$ 
    end else
        NCRButton = False$
    end
    Set_Property(@Window:'.PUB_CREATE_NCR', 'ENABLED', NCRButton)
    
return


FillNCREditTable:
    
    RDSNo    = Get_Property(@Window:'.RDS_NO', 'TEXT')
    NCRCtrl  = @Window:'.OLE_NCR_EDT'
    InWfrIDs = Get_Property(@Window, '@WFRIDS')
    
    NCRCtrlArray = ''
    NCRNos       = ''
    RDSNCRs      = Xlate('WO_WFR', InWfrIDs, 'RDS_NCR_NO', 'X')
    WMONCRs      = Xlate('WO_WFR', InWfrIDs, 'WMO_NCR_NO', 'X')
    NCRNos       = SRP_Array('Join', RDSNCRs, WMONCRs, 'OR', @VM)
    NCRNos       = SRP_Array('Clean', NCRNos, 'Trim', @VM)
    If NCRNos NE '' then
        For each NCRNo in NCRNos using @VM setting nPos
            Database_Services('ActivateRecord', 'NCR', NCRNo)
            If Error_Services('NoError') then
                NCRCtrlArray<COL$NCR_NO, nPos>          = NCRNo
                NCRCtrlArray<COL$NCR_STATUS, nPos>      = {STATUS}
                NCRCtrlArray<COL$NCR_REJ_QTY, nPos>     = {REJ_CNT}
                NCRCtrlArray<COL$NCR_RESP, nPos>        = {LOSS_BY}
                NCRCtrlArray<COL$NCR_STAGE, nPos>       = {LOSS_STAGE}
                NCRCtrlArray<COL$NCR_LOSS_DESC, nPos>   = {LOSS_DESC}
                NCRCtrlArray<COL$NCR_LOSS_COMM, nPos>   = {LOSS_COMMENTS}
                NCRCtrlArray<COL$NCR_FIN_SIG, nPos>     = OConv({AUTH_SHIP_SIG}, '[XLATE_CONV,LSL_USERS*FIRST_LAST]')
                NCRCtrlArray<COL$NCR_FIN_SIG_DTM, nPos> = OConv({AUTH_SHIP_SIG_DTM}, 'DT')
            end else
                NCRCtrlArray<COL$NCR_NO, nPos>          = NCRNo
                NCRCtrlArray<COL$NCR_STATUS, nPos>      = 'Error'
                NCRCtrlArray<COL$NCR_REJ_QTY, nPos>     = 'Error'
                NCRCtrlArray<COL$NCR_RESP, nPos>        = 'Error'
                NCRCtrlArray<COL$NCR_STAGE, nPos>       = 'Error'
                NCRCtrlArray<COL$NCR_LOSS_DESC, nPos>   = 'Error'
                NCRCtrlArray<COL$NCR_LOSS_COMM, nPos>   = 'Error'
                NCRCtrlArray<COL$NCR_FIN_SIG, nPos>     = 'Error'
                NCRCtrlArray<COL$NCR_FIN_SIG_DTM, nPos> = 'Error'
            end
        Next NCRNo
    end else        
        // No NCRs associated with this inbound cassette, so clear the NCR table.
        For nPos = 1 to 5
            NCRCtrlArray<COL$NCR_NO, nPos>          = ''
            NCRCtrlArray<COL$NCR_STATUS, nPos>      = ''
            NCRCtrlArray<COL$NCR_REJ_QTY, nPos>     = ''
            NCRCtrlArray<COL$NCR_RESP, nPos>        = ''
            NCRCtrlArray<COL$NCR_STAGE, nPos>       = ''
            NCRCtrlArray<COL$NCR_LOSS_DESC, nPos>   = ''
            NCRCtrlArray<COL$NCR_LOSS_COMM, nPos>   = ''
            NCRCtrlArray<COL$NCR_FIN_SIG, nPos>     = ''
            NCRCtrlArray<COL$NCR_FIN_SIG_DTM, nPos> = ''
        Next nPos
    end
    
    Set_Property(NCRCtrl, "OLE.ARRAY", NCRCtrlArray)
    
return


DismissPopup:
    
    PopupCtrl       = @Window : '.OLE_POPUP'
    PopupVisible    = Get_Property(PopupCtrl, 'OLE.Visible')
    If PopupVisible EQ True$ then
        Send_Message(PopupCtrl, 'OLE.Close')
    end
    
return


DisplayPopup:
    
    Set_Property('SYSTEM', 'CURSOR', 'H')
    BackColor   = Get_Property(@Window, 'BACKCOLOR')
    ForeColor   = Get_Property(@Window, 'FORECOLOR')
    CursorXPos  = Field(Point, ',', 1, 1)
    CursorYPos  = Field(Point, ',', 2, 1)
    FormSize    = SRP_Get_Window_Rect(@Window)
    FormXPos    = FormSize<1>
    FormYPos    = FormSize<2>
    CtrlSize    = Get_Property(CtrlEntID, 'SIZE')
    CtrlXPos    = CtrlSize<1>
    CtrlYPos    = CtrlSize<2>
    PopupWidth  = 160
    PopupHeight = 60
    XPadding    = 10
    YPadding    = 10
    XOffset     = 10
    YOffset     = 55
    PopupXPos   = FormXPos + CtrlXPos + CursorXPos + XOffset
    PopupYPos   = FormYPos + CtrlYPos + CursorYPos + YOffset
    StatusBy    = ''
    StatusDTM   = ''
    RDSNo       = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    StageDesc   = Get_Property(CtrlEntID, 'OLE.CellText[1; ':Row:']')
    StageID     = Gan_Services('GetStageID', StageDesc)
    Begin Case
        Case CtrlEntID _EQC @Window:'.OLE_REACTOR_EDT'
            // Cassette Level
            RunStageKey = RDSNo:'*':StageID
            RunStageRec = Database_Services('ReadDataRow', 'RUN_STAGE', RunStageKey)
            StageStatus = RunStageRec<RUN_STAGE_STATUS$>
            Begin Case
                Case StageStatus _EQC 'COMP' or StageStatus _EQC 'SKIP'            
                    StatusBy  = RunStageRec<RUN_STAGE_COMP_BY$>
                    StatusDTM = RunStageRec<RUN_STAGE_COMP_DTM$>                   
                Case StageStatus _EQC 'START'
                    InvActions = RunStageRec<RUN_STAGE_SPEC_INV_ACTION$>
                    Locate 'START' in InvActions using @VM setting vPos then
                        StatusBy  = RunStageRec<RUN_STAGE_SIA_COMP_BY$, vPos>
                        StatusDTM = RunStageRec<RUN_STAGE_SIA_COMP_DTM$, vPos>
                    end
            End Case
            
            
        Case CtrlEntID _EQC @Window:'.OLE_METROLOGY_EDT'
            // Wafer level
            ScribeHeader = Get_Property(CtrlEntID, 'OLE.HeaderText[':Column+1:'; 1]')
            Scribe       = ScribeHeader[1, 'F ']
            Scribes      = Xlate('REACT_RUN', RDSNo, 'WFR_SCRIBES', 'X')
            WfrIDs       = ReactRunRec<REACT_RUN_IN_WFR_ID$>
            WfrID        = ''
            Locate Scribe in Scribes using @VM setting vPos then
                WfrID = WfrIDs<0, vPos>
            end
            Convert '*' to '.' in WfrID
            RunStageWfrKey = RDSNo:'*':StageID:'*':WfrID
            RunStageWfrRec = Database_Services('ReadDataRow', 'RUN_STAGE_WFR', RunStageWfrKey)
            StageStatus    = RunStageWfrRec<RUN_STAGE_WFR_STATUS$>
            Begin Case
                Case StageStatus _EQC 'COMP' or StageStatus _EQC 'SKIP' or StageStatus _EQC 'CHAR' or StageStatus _EQC 'DISP'                 
                    StatusBy  = RunStageWfrRec<RUN_STAGE_WFR_COMP_BY$>
                    StatusDTM = RunStageWfrRec<RUN_STAGE_WFR_COMP_DTM$>                               
                Case StageStatus _EQC 'START'
                    InvActions = RunStageWfrRec<RUN_STAGE_WFR_SPEC_INV_ACTION$>
                    Locate 'START' in InvActions using @VM setting vPos then
                        StatusBy  = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_BY$, vPos>
                        StatusDTM = RunStageWfrRec<RUN_STAGE_WFR_SIA_COMP_DTM$, vPos>
                    end                    
            End Case
    End Case
    If (StatusBy NE '') and (StatusDTM NE '') then
        HoverData   = ''
        HoverData  := OConv(StatusBy, '[XLATE_CONV,LSL_USERS*FIRST_LAST]') : @FM
        HoverData  := OConv(StatusDTM, 'DT')
        NumLines    = DCount(HoverData, @FM)
        ItemList    = ''
        If HoverData NE '' then
            For Each Item in HoverData using @FM setting ItemCount            
                ItemList<ItemCount,SRP_POPUP.RECTANGLE,1> = XPadding                        ; // X Position of item     
                ItemList<ItemCount,SRP_POPUP.RECTANGLE,2> = (20 * (ItemCount - 1) )         ; // Y Position of item      
                ItemList<ItemCount,SRP_POPUP.RECTANGLE,3> = PopupWidth - (2 * XPadding)     ; // Width           
                ItemList<ItemCount,SRP_POPUP.RECTANGLE,4> = PopupHeight - (2 * YPadding)    ; // Height
                ItemList<ItemCount,SRP_POPUP.CAPTION>     = Item
                ItemList<ItemCount,SRP_POPUP.COLORS>      = Forecolor
                ItemList<ItemCount,SRP_POPUP.FONT>        = '' : @SVM : '' : @SVM : '700' : @SVM : '0' : @SVM : '0'
                ItemList<ItemCount,SRP_POPUP.ALIGNMENT>   = 'Left' : @SVM : 'Center'       
            Next Item
        end
        
        Set_Property(PopupCtrl, 'OLE.Theme', 'Custom')
        Set_Property(PopupCtrl, 'OLE.Opacity', 255)
        Set_Property(PopupCtrl, 'OLE.Animation', 'N')
        Set_Property(PopupCtrl, 'OLE.Background', 'Vertical(Gradient(' : Backcolor : ', ' : Backcolor : '), Border(' : Backcolor : ' L=20))' : @FM : 'None' : @FM : 'None')
        Set_Property(PopupCtrl, 'OLE.ItemList', ItemList)
        Set_Property(PopupCtrl, 'OLE.ShowDelay', 1000)
        Set_Property(PopupCtrl, 'OLE.Size', 0 : @FM : 0 : @FM : PopupWidth : @FM : PopupHeight)   
        Send_Message(PopupCtrl, 'OLE.ShowAt', PopupXPos, PopupYPos)
    end
    Set_Property('SYSTEM', 'CURSOR', 'A')
    
return


UpdateDispData:

    DispCtrl         = @Window:'.OLE_DISP_EDT' 
    RDSNo            = Get_Property(@Window:'.RDS_NO', 'TEXT')   
    Reactor          = Get_Property(@Window, '@REACTOR')
    Scribes          = Get_Property(@Window, '@SCRIBES')
    NumWfrs          = Get_Property(@Window, '@NUMWFRS')
    GanRunID         = Get_Property(@Window:'.CMB_GAN_RUN_ID', 'TEXT')
    EpiPart          = Get_Property(@Window:'.EDL_WAFER_TRACK_PART', 'TEXT')
    GaNRecipe        = Get_Property(@Window:'.GAN_RECIPE', 'TEXT')
    ReactRunRec      = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    OrigReactRunRec  = ReactRunRec
    WfrIDs           = ReactRunRec<REACT_RUN_IN_WFR_ID$>
    CassDispComp     = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
    If (CassDispComp EQ False$) or (CassDispComp EQ '') then
        Response = GaN_Services('PostDispositionRequest', RDSNo)
        If Response EQ True$ then
            Set_Property(@Window:'.STATUS_LINE', 'BACKCOLOR', GREEN$)
            Set_Property(@Window:'.STATUS_LINE', 'TEXT', 'Disposition report processed successfully.')           
        end else
            Set_Property(@Window:'.STATUS_LINE', 'BACKCOLOR', YELLOW$)
            Set_Property(@Window:'.STATUS_LINE', 'TEXT', Response)
        end
    end
    
return


VerifyRecipe:    
    
    GaNRunID    = Get_Property(@Window:'.CMB_GAN_RUN_ID', 'TEXT')
    If GaNRunID NE '' then
        GaNRecipe   = Get_Property(@Window:'.GAN_RECIPE', 'TEXT')
        Convert @Lower_Case to @Upper_Case in GaNRunID
        SchedRecipe = Xlate('GAN_SCHEDULE', GaNRunID, 'RECIPE', 'X')
        If GaNRecipe NE '' then            
            If SchedRecipe _EQC GaNRecipe then
                // If the recipes match, then color the recipe ID bright green, otherwise
                // color it bright red and block the Run Configuration "Complete" event.
                Set_Property(@Window:'.GAN_RECIPE', 'BACKCOLOR', BGREEN$)
                Set_Property(@Window, '@RECIPEMISMATCH', False$)
            end else
                Set_Property(@Window:'.GAN_RECIPE', 'BACKCOLOR', BRED$)
                Set_Property(@Window, '@RECIPEMISMATCH', True$)
            end
        end
    end
    
return


VerifyScribes:    

    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    If RDSNo NE '' then
        ScribeErrors = Xlate('REACT_RUN', RDSNo, 'SCRIBE_ERROR', 'X')
        DispCtrl     = @Window:'.OLE_DISP_EDT'
        InWfrIDs     = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
        NumWfrs      = DCount(InWfrIDs, @VM)
        For WfrIndex = 1 to NumWfrs
            If ScribeErrors<0, WfrIndex> EQ True$ then
                // Color cell red
                ColorArray = 'None':@FM:RED$
            end else
                // Color cell green
                ColorArray = 'None':@FM:BGREEN$          
            end
            Set_Property(DispCtrl, 'OLE.CellColors[':COL$DISP_SCRIBE:';':WfrIndex:']', ColorArray)
        Next WfrIndex
    end
    
return


EnableRunIDComboBox:
    
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    EtchKey = RDSNo:'*ETCH'
    EtchStatus = Xlate('RUN_STAGE', EtchKey, 'STATUS', 'X')
    If EtchStatus _EQC 'COMP' then
        Set_Property(@Window:'.CMB_GAN_RUN_ID', 'ENABLED', True$)
    end else
        Set_Property(@Window:'.CMB_GAN_RUN_ID', 'ENABLED', False$)
    end
    
return


FillCommentsTable:
    
    RDSNo = Get_Property(@Window:'.RDS_NO', 'TEXT')
    CassComments = Xlate('REACT_RUN', RDSNo, 'CASS_COMMENTS', 'X')
    Set_Property(@Window:'.EDB_CASS_COMMENTS', 'TEXT', CassComments)
    Set_Property(@Window, '@COMMENTS', CassComments)
    
return


EnableDispButton:
    
    RDSNo         = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec   = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    DispComp      = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
*    WfrsDispReady = ReactRunRec<REACT_RUN_WFRS_DISP_READY$>
    DispCtrl      = @Window:'.PUB_DISP'
*    ReadyForDisp  = True$
*    For each DispStatus in WfrsDispReady using @VM setting vPos
*        If (DispStatus EQ False$) or (DispStatus EQ '') then
*            ReadyForDisp = False$
*        end
*    Until ReadyForDisp = False$
*    Next DispStatus

    If DispComp EQ True$ then
        Set_Property(DispCtrl, 'TEXT', 'Open RDS')
        Set_Property(DispCtrl, 'ENABLED', True$)
    end else
        Set_Property(DispCtrl, 'TEXT', 'Close RDS')
        Set_Property(DispCtrl, 'ENABLED', True$)
    end
    
*    Begin Case
*        
*        Case (DispComp EQ False$) or (DispComp EQ '') and (ReadyForDisp EQ True$)
*            Set_Property(DispCtrl, 'TEXT', 'Close RDS')
*            Set_Property(DispCtrl, 'ENABLED', True$)
*            
*        Case (DispComp EQ False$) or (DispComp EQ '') and (ReadyForDisp EQ False$)
*            Set_Property(DispCtrl, 'TEXT', 'Close RDS')
*            Set_Property(DispCtrl, 'ENABLED', False$)
*            
*        Case (DispComp EQ True$)
*            Set_Property(DispCtrl, 'TEXT', 'Open RDS')
*            
*    End Case
    
return


UpdateRDSStatus:
    
    RDSNo         = Get_Property(@Window:'.RDS_NO', 'TEXT')
    ReactRunRec   = Database_Services('ReadDataRow', 'REACT_RUN', RDSNo)
    DispComp      = ReactRunRec<REACT_RUN_DISP_COMPLETE$>
    If DispComp EQ True$ then
        Set_Property(@Window:'.LBL_RDS_STATUS', 'TEXT', 'CLOSED')
        Set_Property(@Window:'.LBL_RDS_STATUS', 'FORECOLOR', BRED$)
    end else
        Set_Property(@Window:'.LBL_RDS_STATUS', 'TEXT', 'OPEN')
        Set_Property(@Window:'.LBL_RDS_STATUS', 'FORECOLOR', BGREEN$)
    end
    
return


EnableLotAbortButton:
    
    RDSNo   = Get_Property(@Window:'.RDS_NO', 'TEXT')
    GrowthKey = RDSNo:'*GROWTH'
    GrowthStatus = Xlate('RUN_STAGE', GrowthKey, 'STATUS', 'X')
    // Need to check each wafer's current stage. If all wafers are at disposition, then
    // there is no need to enable the "Lot Abort" button.
    LotAbortAllowed = False$
    WfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
    For each WfrID in WfrIDs using @VM setting vPos
        CurrStage = Gan_Services('GetCurrStage', WfrID)
        If (CurrStage _NEC 'DISP') and (CurrStage _NEC 'G_PACK') and (CurrStage _NEC 'RETAIN') then
            LotAbortAllowed = True$
        end
    Next WfrID
    If (GrowthStatus _EQC 'COMP') and (LotAbortAllowed EQ True$) then
        Set_Property(@Window:'.PUB_LOT_ABORT', 'ENABLED', True$)
    end else
        Set_Property(@Window:'.PUB_LOT_ABORT', 'ENABLED', False$)
    end
    
return


FillMetEditTable:

    GoSub AutoSelectToolID
    
    RDSNo          = Get_Property(@Window:'.RDS_NO', 'TEXT')
    NumWfrs        = Get_Property(@Window, '@NUMWFRS')
    MetStageIDList = Get_Property(@Window, '@METSTAGEIDLIST')
    AllWfrIDs      = Get_Property(@Window, '@WFRIDS')
    SingleWfrID    = Get_Property(@Window, '@SINGLE_WFR_ID')
    If SingleWfrID NE '' then
        // We only want need to update one column (wafer)
        WfrIDs = SingleWfrID
        MetStageIDList = Delete(MetStageIDList, 0, 1, 0)
        MetStageIDList = Delete(MetStageIDList, 0, 1, 0)
        MetStageIDList = Delete(MetStageIDList, 0, 1, 0)
    end else
        WfrIDs         = AllWfrIDs
        If WfrIDs EQ '' then
            WfrIDs = Xlate('REACT_RUN', RDSNo, 'IN_WFR_ID', 'X')
            Set_Property(@Window, '@WFRIDS', WfrIDs)
        end
    end
    If NumWfrs EQ '' then
        NumWfrs = DCount(WfrIDs, @VM)
        Set_Property(@Window, '@NUMWFRS')
    end
    MetStageCtrl      = @Window:'.OLE_METROLOGY_EDT'
    MetRowsEnabled    = Get_Property(@Window, '@METROWSENABLED')
    CellTypeArray     = Get_Property(@Window, '@COMBOBOX')
    ColorArray        = @FM:'OD'
    CellColorArray    = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
    EnabledColorArray = CellColorArray
    // Get and set each wafer's current status for each stage within the Metrology edit table.
    EndCol = NumWfrs + 2
    
    For each WfrID in WfrIDs using @VM
        ReadyToSplit = Gan_Services('ReadyToSplit', WfrID)
        ReadyToStart = Gan_Services('ReadyToStart', RDSNo, WfrID)
        Locate WfrID in AllWfrIDs using @VM setting WfrIndex then
            CurrWfrCol  = WfrIndex + 2    
        end
        For each Stage in MetStageIDList using @VM setting StageIndex           
            Destructive = Gan_Services('IsDestStage', Stage)
            If SingleWfrID NE '' then
                CurrWfrRow = StageIndex + 3
            end else
                CurrWfrRow  = StageIndex
            end
            BackgroundColor = 'None'                                
            If WfrID NE '' then
                Convert '*' to '.' in WfrID               
                Begin Case                    
                    Case Stage EQ 'UV_PRE'
                        // Check if UV stage exists
                        UVStageWfrKey = RDSNo:'*UV*':WfrID
                        If RowExists('RUN_STAGE_WFR', UVStageWfrKey) then
                            // Display this on the UV_PRE line
                            RunStageWfrKey = UVStageWfrKey
                        end else
                            RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID
                        end
                    Case Stage EQ 'CAN_PRE'
                        // Check if CAN stage exists
                        CanStageWfrKey = RDSNo:'*CAN*':WfrID
                        If RowExists('RUN_STAGE_WFR', CanStageWfrKey) then
                            // Display this on the CAN_PRE line
                            RunStageWfrKey = CanStageWfrKey
                        end else
                            RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID
                        end    
                    Case Otherwise$
                        RunStageWfrKey = RDSNo:'*':Stage:'*':WfrID                            
                End Case
                SplitKey = RDSNo:'*SPLIT*':WfrID
                SplitStatus = (Xlate('RUN_STAGE_WFR', SplitKey, 'STATUS', 'X') _EQC 'CHAR')
                If SplitStatus EQ True$ then MetRowsEnabled<StageIndex> = True$
                Convert '.' to '*' in WfrID
                If (Stage _EQC 'SPLIT') and (ReadyToSplit EQ True$) then
                    Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")                            
                    Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", EnabledColorArray)
                    Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", EnabledColorArray)
                    Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")
                end
                ThisComboArray = CellTypeArray    
                If RowExists('RUN_STAGE_WFR', RunStageWfrKey) then
                    UnRxStage  = Gan_Services('GetUnRxStatus', RDSNo, Stage)
                    If UnRxStage EQ True$ then BackgroundColor = YELLOW$
                    CurrStatus = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'STATUS', 'X')
                    ToolID     = Xlate('RUN_STAGE_WFR', RunStageWfrKey, 'TOOL_ID', 'X')
                    // Has another stage has already begun for this wafer?                                                    
                    DispStatus = ''
                    Begin Case
                        Case CurrStatus _EQC 'COMP'                           
                            DispStatus = 'Complete'
                            MetActionArray = 'Complete'
                            If ( ( (Stage _EQC 'CAN') or (Stage _EQC 'WET_CLEAN') or (Stage _EQC 'CAN_PRE') or (Stage _EQC 'CAN_PST') ) and (ReadyToStart EQ True$) and ( (ToolID NE '') or (ToolID NE 'Select') ) ) then MetActionArray<2> = 'Start'
                        Case CurrStatus _EQC 'START'
                            DispStatus = 'Start'
                            MetActionArray = 'Stop'
                        Case CurrStatus _EQC 'STOP'
                            DispStatus = 'Stop'
                            MetActionArray = 'Complete'
                        Case CurrStatus _EQC 'SKIP'
                            DispStatus = 'Skip'
                            MetActionArray = 'Unskip'
                        Case CurrStatus _EQC 'CHAR'
                            DispStatus = 'Characterize'
                            MetActionArray    = ''
                            // If any metrology stages after split have been started and/or completed, then withdraw
                            // will not be an option.                            
                            CanWithdraw = Gan_Services('EligibleToWithdrawWfr', RDSNo, WfrID)
                            If CanWithdraw EQ True$ then MetActionArray = 'Withdraw'
                        Case CurrStatus _EQC 'DISP'
                            DispStatus = 'Disposition'
                            MetActionArray    = ''
                            // If G_PACK or RETAIN have been completed, then withdraw will not be an option.
                            CanWithdraw = Gan_Services('EligibleToWithdrawWfr', RDSNo, WfrID)
                            If CanWithdraw EQ True$ then MetActionArray = 'Withdraw'
                        Case CurrStatus _EQC 'INIT'                          
                            DispStatus = 'X'
                            If Stage _NEC 'SPLIT' then
                                // Need to check if stage is prescribed or not.
                                If UnRxStage EQ True$ then
                                    MetActionArray    = 'Remove'
                                end else                               
                                    MetActionArray    = 'Skip'                                    
                                end
                                If ( (ReadyToStart EQ True$) and (ToolID NE '') and (ToolID NE 'Select') )then
                                    Temp = MetActionArray
                                    MetActionArray    = 'Start'
                                    MetActionArray<2> = Temp                    
                                end
                            end else
                                MetActionArray     = 'Characterize'
                                MetActionArray<2>  = 'Disposition'                   
                                If ReadyToSplit EQ False$ then
                                    ColorArray = 'None':@FM:BackgroundColor:@FM:'White':@FM:OI_BLUE$:@FM:'None'
                                    Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "FUL")                           
                                    Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", ColorArray)
                                    Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_TOOL_ID:";":CurrWfrRow:"]", ColorArray)
                                    Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_TOOL_ID:";":CurrWfrRow:"]", "FUL")     
                                end
                            end
                            
                    End Case
                    
                    Convert @FM to @STM in MetActionArray                                
                    ThisComboArray<2, 3> = MetActionArray
                    Set_Property(MetStageCtrl, "OLE.CellType[":CurrWfrCol:";":CurrWfrRow:"]", ThisComboArray)
                    // Cassette/Wafer status/action combo boxes
                    // Need to check current status and set the dropdown options accordingly
                    // Combo box selection options should be context aware. (e.g. "Skip" should not be an option if a 
                    // wafer has already started on a tool.)
                    
                    If (Stage _EQC 'XRD') or (Stage _EQC 'AFM') or (Stage _EQC 'RPM')        |
                    or (Stage _EQC 'PR')   or (Stage _EQC 'SCOPE') or (Stage _EQC 'RTA')      |
                    or (Stage _EQC 'HALL') or (Stage _EQC 'EBEAM') or (Stage _EQC 'RTA_HALL') |
                    or (Stage _EQC 'HALL_PGAN') or (Stage _EQC 'BV') or (Stage _EQC 'JV_XRD') |
                    or (Stage _EQC 'BV100%') then
                        
                        If ReadyToSplit EQ False$ then
                            Set_Property(MetStageCtrl, "OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "FUL")
                        end else
                            Set_Property(MetStageCtrl, "OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")
                        end
                        
                        RowEnabled  = MetRowsEnabled<StageIndex>
                        If (Destructive EQ True$) then
                            // Set row text color to red
                            If (RowEnabled EQ True$) then
                                DestEditArray = 'Red':@FM:BackgroundColor:@FM:'White':@FM:OI_BLUE$:@FM:'None'
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", DestEditArray)
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", DestEditArray)                           
                            end else
                                DestEditArray = 'Red':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", DestEditArray)
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")                        
                                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", DestEditArray)                            
                            end
                        end else
                            // Set row text color to default (i.e. 'None')
                            If (RowEnabled EQ True$) then
                                ColorArray = 'None':@FM:BackgroundColor:@FM:'White':@FM:OI_BLUE$:@FM:'None'
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", ColorArray)
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", ColorArray)                           
                            end else
                                ColorArray = 'None':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")
                                Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", ColorArray)
                                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")                        
                                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", ColorArray)                            
                            end
                        end
                        
                    end                   
                    Set_Property(MetStageCtrl, "OLE.CellText[":CurrWfrCol:"; ":CurrWfrRow:"]", DispStatus)
                    If ( ( (CurrStatus EQ 'COMP') and (Stage NE 'CAN') and (Stage NE 'WET_CLEAN') and (Stage NE 'CAN_PRE') and (Stage NE 'CAN_PST') ) or (Stage EQ 'UV') or (Stage EQ 'UV_PRE') or (Stage EQ 'UV_PST') or (Stage EQ 'WARP') ) then
                        Set_Property(MetStageCtrl, "OLE.CellText[":CurrWfrCol:"; ":CurrWfrRow:"]", DispStatus)
                        Set_Property(MetStageCtrl, "OLE.CellProtection[":CurrWfrCol:"; ":CurrWfrRow:"]", "FUL")
                    end
                    If ( (Stage _EQC 'CAN') or (Stage _EQC 'CAN_PRE') or (Stage _EQC 'CAN_PST') ) and (CurrStatus _EQC 'START') then
                        Set_Property(MetStageCtrl, "OLE.CellProtection[":CurrWfrCol:"; ":CurrWfrRow:"]", "FUL")
                    end
                    
                end else
                    // RunStageWfr record for this stage is unprescribed. Add option for users to add the stage.
                    MetActionArray = 'Add'
                    Convert @FM to @STM in MetActionArray
                    ThisComboArray       = CellTypeArray
                    ThisComboArray<2, 3> = MetActionArray
                    Set_Property(MetStageCtrl, "OLE.CellType[":CurrWfrCol:";":CurrWfrRow:"]", ThisComboArray)
                    Set_Property(MetStageCtrl, "OLE.CellText[":CurrWfrCol:"; ":CurrWfrRow:"]", '')
                    If (Stage _EQC 'CAN_PRE') or (Stage _EQC 'CAN_PST') or (Stage _EQC 'XRD')                  |
                    or (Stage _EQC 'AFM') or (Stage _EQC 'RPM') or (Stage _EQC 'PR')   or (Stage _EQC 'SCOPE') |
                    or (Stage _EQC 'RTA')or (Stage _EQC 'HALL') or (Stage _EQC 'EBEAM')                        |
                    or (Stage _EQC 'RTA_HALL') or (Stage _EQC 'HALL_PGAN') or (Stage _EQC 'BV')                |
                    or (Stage _EQC 'JV_XRD' ) or (Stage _EQC 'BV100%') then                        
                        RowEnabled  = MetRowsEnabled<StageIndex>
                        If ( (StageIndex LE 7) or (SplitStatus EQ True$) ) then                                      
                            If Destructive EQ True$ then
                                DestEditArray     = 'Red':@FM:'None':@FM:'White':@FM:OI_BLUE$:@FM:'None'
                                EnabledColorArray = DestEditArray
                            end else
                                EnabledColorArray  = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
                            end
                            Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "None")                            
                            Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", EnabledColorArray)
                            Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", EnabledColorArray)
                            Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")
                        end else
                            If Destructive EQ True$ then
                                DisabledColorArray = 'Red':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
                            end else
                                DisabledColorArray = 'None':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
                            end
                            Set_Property(MetStageCtrl,"OLE.CellProtection[":CurrWfrCol:";":CurrWfrRow:"]", "FUL")                            
                            Set_Property(MetStageCtrl,"OLE.CellColors[":CurrWfrCol:";":CurrWfrRow:"]", DisabledColorArray)
                        end
                    end
                end
            end
            
            RowEnabled  = MetRowsEnabled<CurrWfrRow>   
            DisabledColorArray = 'None':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
            EnabledColorArray  = 'None':@FM:'None':@FM:'None':@FM:'None':@FM:'None'
            If RowEnabled EQ False$ then
                If Destructive EQ True$ then
                    DestEditArray = 'Red':@FM:'OD':@FM:'None':@FM:'None':@FM:'None'
                    DisabledColorArray = DestEditArray
                end     
                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "FUL")                            
                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", DisabledColorArray)
            end else
                If Destructive EQ True$ then
                    DestEditArray     = 'Red':@FM:'None':@FM:'White':@FM:OI_BLUE$:@FM:'None'
                    EnabledColorArray = DestEditArray
                end
                Set_Property(MetStageCtrl,"OLE.CellProtection[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", "None")                            
                Set_Property(MetStageCtrl,"OLE.CellColors[":COL$MET_STAGE:'-':COL$MET_TOOL_ID:";":CurrWfrRow:"]", EnabledColorArray)
            end
            
        Next Stage
        
    Next WfrID
    
    Set_Property(@Window, '@METROWSENABLED', MetRowsEnabled)
    
return


Abort:
    
    Utility('DESTROY', @Window)
    
return

