/*----------------------------------------------------------------------------*/
/*                                                                            */
/* Copyright (c) 2007-2019 Rexx Language Association. All rights reserved.    */
/*                                                                            */
/* This program and the accompanying materials are made available under       */
/* the terms of the Common Public License v1.0 which accompanies this         */
/* distribution. A copy is also available at the following address:           */
/* https://www.oorexx.org/license.html                                        */
/*                                                                            */
/* Redistribution and use in source and binary forms, with or                 */
/* without modification, are permitted provided that the following            */
/* conditions are met:                                                        */
/*                                                                            */
/* Redistributions of source code must retain the above copyright             */
/* notice, this list of conditions and the following disclaimer.              */
/* Redistributions in binary form must reproduce the above copyright          */
/* notice, this list of conditions and the following disclaimer in            */
/* the documentation and/or other materials provided with the distribution.   */
/*                                                                            */
/* Neither the name of Rexx Language Association nor the names                */
/* of its contributors may be used to endorse or promote products             */
/* derived from this software without specific prior written permission.      */
/*                                                                            */
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS        */
/* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT          */
/* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS          */
/* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT   */
/* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,      */
/* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED   */
/* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,        */
/* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING    */
/* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS         */
/* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.               */
/*                                                                            */
/*----------------------------------------------------------------------------*/

/*  oorexxtry.rex  */
/*
What:   21st Century version of Rexxtry
Who:    Lee Peedin
See documentation for version control

10/04/07    Extended fileNameDialog mask for open (*.*)
10/05/07    Added charout method to sayCatcher class
            Added lineout method to sayCatcher class

10/15/07    Directives now work - return values aren't captured
10/16/07    Added header to copy all

11/29/07    Modified ~getText for occasions when lines should not be stripped.
            Place in the incubator

04/19/09    Removed deprecated createFont() and replaced it with createFontEx().
04/19/09    Enhanced the menu to use check marks for font name, size, and silent.
*/

    -- Capture the location of our source code program
    discard = locate()

    parse arg isDefault
    .local~useDefault = .false
    if isDefault~translate = 'DEFAULT' then
        .local~useDefault = .true

    call LoadEnvironment                        -- Set up the environment to work with
    code = .oort_dialog~new()                   -- Create the dialog
    if code~initCode \= 0 then
        do
            call errorDialog 'Error creating code dialog. initCode:' code~initCode
            exit
        end
    say
    say "This console will receive the output of your system commands."
    say "Try 'dir' for example."
    say
    code~execute('ShowTop')                     -- Execute the dialog
exit

::requires "ooDialog.cls"                       -- Needed for the dialog
::requires 'winsystm.cls'                       -- Needed for the Windows clipboard

::class oort_dialog subclass userdialog
::method init
    self~init:super
    rc = self~create(.dx,.dy,.dwidth,.dheight,.title,'ThickFrame MinimizeBox MaximizeBox')
    self~initCode = (rc=0)
    self~fontMenuHelper

::method defineDialog
    expose u menuBar
    u = .dlgAreaU~new(self)
    if .nil \= u~lastError then
        call errorDialog u~lastError

---------- Arguments title & dialog area
    at = .dlgArea~new(0,0,u~w,10)
    self~createStaticText(17,at~x,at~y,at~w,at~h,'CENTER','Arguments')
    ad = .dlgArea~new(0,at~y + 10,u~w,u~h('15%'))
    self~createEdit(12,ad~x,ad~y,ad~w,ad~h,'multiline hscroll vscroll','args_data')

---------- Code title & dialog area
    ct = .dlgArea~new(0,ad~y + ad~h,u~w,10)
    self~addText(ct~x,ct~y,ct~w,ct~h,'Code','CENTER',18)
    cd = .dlgArea~new(0,ct~y + ct~h,u~w,u~h('40%'))
    self~addEntryLine(13,'code_data',cd~x,cd~y,cd~w,cd~h,'multiline hscroll vscroll')

---------- Says title & dialog area
    st = .dlgArea~new(0,cd~y + cd~h,u~w('50%'),10)
    self~createStaticText(19,st~x,st~y,st~w,st~h,'CENTER','Says')
    sd = .dlgArea~new(0,st~y + st~h,u~w('50%'),u~h('43%'))
    self~createEdit(14,sd~x,sd~y,sd~w,sd~h,'notab readonly multiline hscroll vscroll','say_data')

---------- Returns title & dialog area
    rt = .dlgArea~new(sd~x + sd~w,cd~y + cd~h,u~w('50%'),10)
    self~createStaticText(20,rt~x,rt~y,rt~w,rt~h,'CENTER','Returns')
    rd = .dlgArea~new(rt~x,st~y + st~h,u~w('50%'),u~h('15%'))
    self~createEdit(15,rd~x,rd~y,rd~w,rd~h,'notab readonly multiline hscroll vscroll','results_data')

---------- Errors/Information title & dialog area
    et = .dlgArea~new(rt~x,rd~y + rd~h,u~w('50%'),10)
    self~createStaticText(21,et~x,et~y,et~w,et~h,'CENTER','Errors / Information')
    ed = .dlgArea~new(rt~x,et~y + et~h,u~w('50%'),u~h('17%'))
    self~createEdit(16,ed~x,ed~y,ed~w,ed~h,'notab readonly multiline hscroll vscroll','error_data')

---------- Run & Exit buttons for easier execution
    self~createPushButton(80,ed~x     ,ed~y + ed~h + 2,35,10,,'&Run','RunIt')
    self~createPushButton(IDCANCEL,ed~x + 40,ed~y + ed~h + 2,35,10,,'E&xit')

----------
    menuBar = .UserMenuBar~new(500,0,65)

    menuBar~addPopup(510, '&File')
       menuBar~addItem(22,'&Run'   ,     ,'RunIt')
       menuBar~addItem(23,'&SaveAs',     ,'FileDialog')
       menuBar~addItem(25,'&Open'  ,     ,'FileDialog')
       menuBar~addItem(24,'E&xit'  ,'END','Cancel')

    menuBar~addPopup(520,'&Edit')
       menuBar~addPopup(530,'Font&Name')
           menuBar~addItem(30,'&Lucida Console',     ,'onFontMenuClick')
           menuBar~addItem(31,'&Courier New'   ,'END','onFontMenuClick')
       menuBar~addSeparator(535)
       menuBar~addPopup(540,'Font&Size','END')
           menuBar~addItem(40,'&8' ,     ,'onFontMenuClick')
           menuBar~addItem(41,'1&0',     ,'onFontMenuClick')
           menuBar~addItem(42,'1&2',     ,'onFontMenuClick')
           menuBar~addItem(43,'1&4',     ,'onFontMenuClick')
           menuBar~addItem(44,'1&6',     ,'onFontMenuClick')
           menuBar~addItem(45,'1&8','END','onFontMenuClick')
    menuBar~addPopup(550,'&Tools')
       menuBar~addPopup(560,'&Copy')
           menuBar~addItem(50,'&Args'   ,     ,'Clipboard')
           menuBar~addItem(51,'&Code'   ,     ,'Clipboard')
           menuBar~addItem(52,'&Says'   ,     ,'ClipBoard')
           menuBar~addItem(53,'&Returns',     ,'ClipBoard')
           menuBar~addItem(54,'&Errors' ,     ,'ClipBoard')
           menuBar~addItem(55,'A&ll'    ,'END','ClipBoard')
       menuBar~addSeparator(565)
       menuBar~addPopup(570,'C&lear')
           menuBar~addItem(60,'&Args'   ,     ,'ClearAll')
           menuBar~addItem(61,'&Code'   ,     ,'ClearAll')
           menuBar~addItem(62,'&Says'   ,     ,'ClearAll')
           menuBar~addItem(63,'&Returns',     ,'ClearAll')
           menuBar~addItem(64,'&Errors' ,     ,'ClearAll')
           menuBar~addItem(65,'A&ll'    ,'END','ClearAll')
       menuBar~addSeparator(575)
       menuBar~addPopup(580,'&Silent')
           menuBar~addItem(66,'&No'     ,     ,'Silent')
           menuBar~addItem(67,'&Yes'    ,'END','Silent')
       menuBar~addSeparator(585)
       menuBar~addPopup(590,'Sa&ve Settings','END')
           menuBar~addItem(72,'Sa&ve'   ,'END','SaveSettings')
    menuBar~addPopup(600,'&Help','END')
       menuBar~addItem(71,'Current &Settings',     ,'Settings')
       menuBar~addItem(70,'&About'           ,'END','Help')

   if \ menuBar~complete then
       statusText = 'User menu bar completion error:' .SystemErrorCode SysGetErrortext(.SystemErrorCode)

::method initDialog unguarded
    expose args_input code_input result_input say_input errors_input menuBar
    -- Use font data from .ini file or defaults if .ini not present yet

    -- FW_EXTRALIGHT == 200
    d = .Directory~new
    d~weight = 200
    hfont = self~createFontEx(.fontname,.fontsize,d)

    if menuBar \== .nil then menuBar~attachTo(self)

    -- Set the font and silent menu item check marks.
    self~setFontMenuChecks
    if .silent then menuBar~check(67)
    else menuBar~check(66)

    -- Get the controls for all dialog elements that will need to be adjusted
    args_input   = self~newEdit(12)
    code_input   = self~newEdit(13)
    say_input    = self~newEdit(14)
    result_input = self~newEdit(15)
    errors_input = self~newEdit(16)
    args_title   = self~newStatic(17)
    code_title   = self~newStatic(18)
    say_title    = self~newStatic(19)
    result_title = self~newStatic(20)
    errors_title = self~newStatic(21)

    -- Set the color of the titles
    args_title  ~setColor(5,10)
    code_title  ~setColor(16,10)
    say_title   ~setColor(15,0)
    result_title~setColor(14,0)
    errors_title~setColor(13,10)

    -- Set the font name/size to the default values
    self~reDraw

    if \.useDefault then
        do
            -- Read oorexxtry.ini position & size the dialog based on its values
            sd = .application~srcDir
            k1 = SysIni(sd'oorexxtry.ini','oorexxtry','k1')
            k2 = SysIni(sd'oorexxtry.ini','oorexxtry','k2')
            k3 = SysIni(sd'oorexxtry.ini','oorexxtry','k3')
            k4 = SysIni(sd'oorexxtry.ini','oorexxtry','k4')
            if k1 = 'ERROR:' | k2 = 'ERROR:' | k3 = 'ERROR:' | k4 = 'ERROR:' then
                nop -- First exection will not find the ini file
            else
                do
                    r = .Rect~new(k1,k2,k3-k1,k4-k2)
                    self~setWindowPos(TOP,r)
                    self~ensureVisible
                end
        end

    self~connectResize('onResize', 'sync')

-- Run menu option
::method RunIt
    expose args_input code_input result_input say_input errors_input

    -- Calculate the center point of the code_input edit control, in screen
    -- co-ordinates.
    r = code_input~clientRect
    centerPoint = .Point~new((r~right - r~left) % 2, (r~bottom - r~top) % 2)
    code_input~client2screen(centerPoint)

    -- Get and save the current mouse position.
    mouse = .Mouse~new(code_input)
    oldCursorPos = mouse~getCursorPos

    -- Set the cursor to the busy symbol / move the cursor to our center point
    oldCursor = mouse~wait
    mouse~setCursorPos(centerPoint)

    arg_array = self~getText(args_input,.true)
    .local~si = say_input
    w1 = code_input~selected~word(1)
    w2 = code_input~selected~word(2)
    code_array = self~getText(code_input,.true)
    if (w1 = .max_length & w2 = .max_length) | (w1 = w2) then
        nop     -- we've already loaded our code_array
    else
        do
            code_string = code_array~makeString
            code_array  = code_string~substr(w1,w2-w1)~makearray
        end

    -- Clear any previous error data
    self~error_data    = ''
    errors_input~title = self~error_data

    -- Clear any previous say data
    self~say_data      = ''
    say_input~title    = self~say_data

    -- Clear any previous returns data
    self~results_data  = ''
    result_input~title = self~results_data

    .local~emsg = ''
    .local~imsg = ''

    self~error_data  = 'Code Is Executing'
    errors_input~title = self~error_data

    .local~Error? = .false
    .local~badarg = ''
    -- Interpret each argument so that expressions can be used
    signal on syntax name ArgSyntax
    do i = 1 to arg_array~items
        .local~badarg = i arg_array[i]
        interpret 'arg_array['i'] =' arg_array[i]
    end
    signal off syntax

    exec = .executor~new('oorexxtry.code',code_array)
    exec~run(arg_array)

    if .emsg \= '' then
        do
            if \.silent then
                call beep 600,100
            self~error_data    = .emsg
            errors_input~title = self~error_data
            self~results_data  = ''
            result_input~title = self~results_data
        end
    else
        do
            self~error_data  = 'Code Execution Complete'
            errors_input~title = self~error_data
        end

    if .nil \= .run_results['returns'] then
        do
            self~results_data  = .run_results['returns']
            result_input~title = self~results_data
        end
    if \.silent then        -- Let the user know when code execution is complete
        call beep 150,150

    mouse~restoreCursor(oldCursor)
    mouse~setCursorPos(oldCursorPos)

    self~returnFocus
return

ArgSyntax:
    msg = 'Trapped In'~right(11)'..: ArgSyntax'
    obj = condition('o')
    msg = msg||.endOfLine||'Message'~right(11)'..:' obj['MESSAGE']
    msg = msg||.endOfLine||'ErrorText'~right(11)'..:' obj['ERRORTEXT']
    msg = msg||.endOfLine||'Code'~right(11)'..:' obj['CODE']
    msg = msg||.endOfLine||('Argument' .badarg~word(1))~right(11)'..:' .badarg~subword(2)
    .local~emsg = msg
    if \.silent then
        call beep 600,100
    self~error_data    = .emsg
    errors_input~title = self~error_data
    self~results_data      = ''
    result_input~title = self~results_data
    .local~emsg = ''
    self~focusItem(12)
    args_input~select(1,1)

    mouse~restoreCursor(oldCursor)
    mouse~setCursorPos(oldCursorPos)
return

::method cancel

    r = self~windowRect
    -- Write out the size,position,fontname,fontsize, & silent to the .ini file
    sd = .application~srcDir
    if \self~isMinimized & \self~isMaximized then
        do
            rv = SysIni(sd'oorexxtry.ini','oorexxtry','k1',r~left)
            rv = SysIni(sd'oorexxtry.ini','oorexxtry','k2',r~top)
            rv = SysIni(sd'oorexxtry.ini','oorexxtry','k3',r~right)
            rv = SysIni(sd'oorexxtry.ini','oorexxtry','k4',r~bottom)
        end
    rv = SysIni(sd'oorexxtry.ini','oorexxtry','fn',.fontname)
    rv = SysIni(sd'oorexxtry.ini','oorexxtry','fs',.fontsize)
    rv = SysIni(sd'oorexxtry.ini','oorexxtry','sl',.silent)
return self~ok:super

-- Clipboard menu option
::method ClipBoard
    expose args_input errors_input code_input say_input result_input
    use arg msg, args
    cp = .WindowsClipBoard~new
    select
        when msg = 50 then
            do
                cp_array  = self~getText(args_input,.false)
                cp_string = cp_array~makestring
                cp~copy(cp_string)
                .local~imsg = 'Arguments Are On ClipBoard'
            end
        when msg = 51 then
            do
                cp_array  = self~getText(code_input,.false)
                cp_string = cp_array~makestring
                cp~Copy(cp_string)
                .local~imsg = 'Code Is On ClipBoard'
            end
        when msg = 52 then
            do
                cp_array  = self~getText(say_input,.false)
                cp_string = cp_array~makestring
                cp~copy(cp_string)
                .local~imsg = 'Says Are On The ClipBoard'
            end
        when msg = 53 then
            do
                cp_array  = self~getText(result_input,.false)
                cp_string = cp_array~makestring
                cp~Copy(cp_string)
                .local~imsg = 'Returns Are On ClipBoard'
            end
        when msg = 54 then
            do
                cp_array  = self~getText(errors_input,.false)
                cp_string = cp_array~makestring
                cp~copy(cp_string)
                .local~imsg = 'Errors Are On ClipBoard'
            end
        when msg = 55 then
            do
                allData = 'The Following Output Was Generated With ooRexxTry'.endOfLine||.endOfLine
                cp_array  = self~getText(args_input,.false)
                cp_string = cp_array~makestring
                allData = alldata||'Arguments'||.endOfLine||cp_string||.endOfLine||'-'~copies(20)||.endOfLine

                cp_array  = self~getText(code_input,.false)
                cp_string = cp_array~makestring
                allData = allData||'Code'||.endOfLine||cp_string||.endOfLine||'-'~copies(20)||.endOfLine

                cp_array  = self~getText(say_input,.false)
                cp_string = cp_array~makestring
                allData = allData||'Says'||.endOfLine||cp_string||.endOfLine||'-'~copies(20)||.endOfLine

                cp_array  = self~getText(result_input,.false)
                cp_string = cp_array~makestring
                allData = allData||'Results'||.endOfLine||cp_string||.endOfLine||'-'~copies(20)||.endOfLine

                cp_array  = self~getText(errors_input,.false)
                cp_string = cp_array~makestring
                allData = allData||'Errors/Information'||.endOfLine||cp_string||.endOfLine||'-'~copies(20)||.endOfLine

                cp~copy(allData)
                .local~imsg = 'All Data Is On ClipBoard'
            end
        otherwise
            nop
    end
    self~error_data    = .imsg
    errors_input~title = self~error_data
    self~returnFocus

-- Code2File menu option
::method FileDialog
    expose code_input errors_input
    use arg msg, args
    if msg = 23 then
        do
            c_array = self~getText(code_input,.false)
            c_string = c_array~makestring
            action = 'S'
            dtitle = 'ooRexxTry File Save'
        end
    else
        do
            action = 'L'
            dtitle = 'ooRexxTry File Open'
        end

    delimiter = '0'x
    filemask      = 'ooRexx Files (*.rex)'delimiter'*.rex'delimiter||-
                    'All Files (*.*)'delimiter'*.*'delimiter
    handle = self~getSelf
    a_file = FileNameDialog(.preferred_path,handle,filemask,action,dtitle,'.rex')
    if a_file \= 0 then
        do
            ostream = .stream~new(a_file)
            if msg = 23 then
                do
                    ostream~open('Write Replace')
                    ostream~lineout(c_string)
                    ostream~close
                    .local~imsg        = 'Code Saved As' a_file
                    self~error_data    = .imsg
                    errors_input~title = self~error_data
                end
            else
                do
                    oarray = ostream~charin(,ostream~chars)~makearray
                    ostream~close
                    mycode = ''
                    do i = 1 to oarray~items
                        mycode = mycode||oarray[i]
                        if i < oarray~items then
                            mycode = mycode||.endOfLine
                    end
                    self~code_data   = mycode
                    code_input~title = self~code_data

                    .local~imsg        = 'Code From' a_file 'In Code Dialog'
                    self~error_data    = .imsg
                    errors_input~title = self~error_data
                end
        end
    self~returnFocus

-- ClearAll menu option
::method ClearAll
    expose args_input code_input result_input say_input errors_input
    use arg msg, args
    select
        when msg = 60 then
            do
                self~args_data   = ''
                args_input~title = self~args_data
            end
        when msg = 61 then
            do
                self~code_data   = ''
                code_input~title = self~code_data
            end
        when msg = 62 then
            do
                self~say_data = ''
                say_input~title = self~say_data
            end
        when msg = 63 then
            do
                self~results_data = ''
                result_input~title = self~results_data
            end
        when msg = 64 then
            do
                self~error_data = ''
                errors_input~title = self~error_data
            end
        when msg = 65 then
            do
                self~args_data   = ''
                args_input~title = self~args_data

                self~code_data   = ''
                code_input~title = self~code_data

                self~results_data = ''
                result_input~title = self~results_data

                self~say_data = ''
                say_input~title = self~say_data

                self~error_data = ''
                errors_input~title = self~error_data
            end
        otherwise
            nop
    end
    self~returnFocus

::method Silent
    expose menuBar
    use arg msg, args
    select
        when msg = 66 then do
            .local~silent = .false
            menuBar~check(66)
            menuBar~unCheck(67)
        end
        when msg = 67 then do
            .local~silent = .true
            menuBar~check(67)
            menuBar~unCheck(66)
        end
        otherwise
            nop
    end
    self~returnFocus

::method SaveSettings
    use arg msg, args

    r = self~windowRect
    select
        when msg = 72 then
            do
                sd = .application~srcDir
                if \self~isMinimized & \self~isMaximized then
                    do
                        rv = SysIni(sd'oorexxtry.ini','oorexxtry','k1',r~left)
                        rv = SysIni(sd'oorexxtry.ini','oorexxtry','k2',r~top)
                        rv = SysIni(sd'oorexxtry.ini','oorexxtry','k3',r~right)
                        rv = SysIni(sd'oorexxtry.ini','oorexxtry','k4',r~bottom)
                    end
                rv = SysIni(sd'oorexxtry.ini','oorexxtry','fn',.fontname)
                rv = SysIni(sd'oorexxtry.ini','oorexxtry','fs',.fontsize)
                rv = SysIni(sd'oorexxtry.ini','oorexxtry','sl',.silent)
            end
        otherwise
            nop
    end
    self~returnFocus

::method ReturnFocus
    expose code_input
    w1 = code_input~selected~word(1)
    w2 = code_input~selected~word(2)
    code_array = self~getText(code_input,.true)
    if w1 = .max_length & w2 = .max_length then
        do
            sel_start = .max_length
            sel_end   = .max_length
        end
    else
        do
            sel_start = w1
            sel_end   = w2
        end
    self~focusItem(13)
    code_input~select(sel_start,sel_end)

-- Method to handle all the resizing
::method onResize
    expose u
    use arg dummy,sizeinfo
    u~resize(self,sizeinfo)

::method Help
    expose code_input u
    handle = self~getSelf
    parse value self~GetWindowRect(handle) with var1 var2 var3 var4
    .local~dw = (var3 - var1) / self~factorX
    .local~dh = (var4 - var2) / self~factorY

    parse value self~GetPos with dx dy
    .local~dx = dx
    .local~dy = dy

    help = .help_dialog~new()
    if help~initCode \= 0 then
        do
            call errorDialog 'Error creating help dialog. initCode:' help~initCode
            exit
        end
    help~execute('ShowTop')

::method Settings
    expose code_input u
    handle = self~getSelf
    parse value self~getWindowRect(handle) with var1 var2 var3 var4
    .local~dw = (var3 - var1) / self~factorX
    .local~dh = (var4 - var2) / self~factorY

    parse value self~getPos with dx dy
    .local~dx = dx
    .local~dy = dy

    settings = .settings_dialog~new()
    if settings~initCode \= 0 then
        do
            call errorDialog 'Error creating help dialog. initCode:' settings~initCode
            exit
        end
    settings~execute('ShowTop')

-- Redraw applicable areas of the dialog based on the font menu choice
::method ReDraw
    expose args_input code_input say_input result_input errors_input

    -- FW_EXTRALIGHT == 200
    d = .Directory~new
    d~weight = 200
    hfont = self~createFontEx(.fontname,.fontsize,d)
    args_input  ~setFont(hfont)
    code_input  ~setFont(hfont)
    say_input   ~setFont(hfont)
    result_input~setFont(hfont)
    errors_input~setFont(hfont)

::method getText
    use arg the_input,stripIt
    iarray = .array~new()
    max_length = 0
    do i = 1 to the_input~lines
        if stripIt then
            do
                if the_input~getLine(i)~strip() \= '' then
                    do
                        iarray~append(the_input~getLine(i))
                        max_length += iarray[iarray~last]~length
                    end
            end
        else
            do
                iarray~append(the_input~getLine(i))
                max_length += iarray[iarray~last]~length
            end
    end
    .local~max_length = max_length + 1
return iarray

-- This method is invoked whenever the user clicks on one of the font menu items.
-- The first arg to the method is the resource id of the menu id that was clicked.
::method onFontMenuClick
    expose fontMenuIDs
    use arg id

    -- Map the menu item id to a font setting.  Could be size of name.
    item = fontMenuIDs~index(id)

    -- If item is a number, then it is a font size menu item, otherwis a font name.
    if item~datatype('W') then .local~fontSize = item
    else .local~fontname = item

    -- Reset the menu item check marks and redraw in the new font.
    self~setFontMenuChecks
    self~reDraw

-- This method sets the appropriate font menu item check state.  Checked for selected
-- and unchecked for unselected.
::method setFontMenuChecks private
    expose fontMenuIDs menuBar

    -- Iterate over all items in the table unchecking each menu item.  Brute force,
    -- but easy, and there are not many items. The alternative is to keep track of
    -- which items are checked and uncheck / check the correct ones.
    do id over fontMenuIDs~allItems
        menuBar~unCheck(id)
    end

    -- Now check the menu item that matches what font name and size is currently
    -- in use.
    menuBar~check(fontMenuIDs[.fontname])
    menuBar~check(fontMenuIDs[.fontsize])

-- A private help method that sets up things to make working with the font menu easier
::method fontMenuHelper private
    expose fontMenuIDs

    -- Create a table that maps menu item IDs to the matching font setting.  Since
    -- the Table class has the index() method, the mapping works both ways.
    fontMenuIDs = .Table~new
    fontMenuIDs["Lucida Console"] = 30
    fontMenuIDs["Courier New"]    = 31

    fontMenuIDs[ 8] = 40
    fontMenuIDs[10] = 41
    fontMenuIDs[12] = 42
    fontMenuIDs[14] = 43
    fontMenuIDs[16] = 44
    fontMenuIDs[18] = 45

-- Class that dynamically creates a method to take the arguments and execute the code
::class executor public
::method init
    expose rt_routine
    use arg routine_name,code
    .local~Error? = .false
    signal on syntax name ExecSyntax
    rt_routine = .routine~new(routine_name, code)
return

-- Syntax trap similiar to rexxc.exe
ExecSynTax:
    msg = 'Trapped In'~right(11)'..: ExecSyntax'
    obj = condition('o')
    msg = msg||.endOfLine||'Message'~right(11)'..:' obj['MESSAGE']
    msg = msg||.endOfLine||'ErrorText'~right(11)'..:' obj['ERRORTEXT']
    msg = msg||.endOfLine||'Code'~right(11)'..:' obj['CODE']
    msg = msg||.endOfLine||'Line #'~right(11)'..:' obj['POSITION']
    .local~emsg = msg
    .local~Error? = .true
return

-- Method that actually runs our code
::method run
    expose rt_routine say_string
    signal on syntax name RunSyntax
    .local~run_results = .directory~new
    .local~say_stg = ''
    my_result = ''
    if \.Error? then
        do
            -- Redirect STDOUT for say statements
            scrnOut = .SayCatcher~New('STDOUT')~~Command('open write nobuffer')
            theSayMonitor = .monitor~New(scrnOut)
            .output~destination(theSayMonitor)
            -- Run the Code
            args = arg(1)
            rt_routine~callWith(args)
            -- Test if there was anything returned by the code
            if symbol('result') = 'VAR' then
                my_result = result
            -- Load the says and returns into environment variables for updating the dialog areas
            .local~run_results['returns'] = my_result
            -- Redirect STDOUT back to what it was (probably the screen)
            .output~Destination()
        end
return .run_results

-- Syntax trap for errors in the code
RunSyntax:
    msg = 'Trapped In'~right(11)'..: RunSyntax'
    obj = condition('o')
    msg = msg||.endOfLine||'Message'~right(11)'..:' obj['MESSAGE']
    msg = msg||.endOfLine||'ErrorText'~right(11)'..:' obj['ERRORTEXT']
    msg = msg||.endOfLine||'Code'~right(11)'..:' obj['CODE']
    msg = msg||.endOfLine||'Line #'~right(11)'..:' obj['POSITION']
    .local~emsg = msg
return

-- Class to "catch" all say,charout,lineout statements in the code
-- Will not catch charout & lineout if the first argument is supplied
::class SayCatcher subclass stream
::method say
    use arg input
    .local~say_stg = .say_stg||input||.endOfLine
    .si~title = .say_stg
return

::method charout
    use arg input
    .local~say_stg = .say_stg||input
    .si~title = .say_stg
return 0

::method lineout
    use arg input
    .local~say_stg = .say_stg||input||.endOfLine
    .si~title = .say_stg
return 0


::class help_dialog subclass userdialog
::method init
    self~init:super
    lp = (.dx + (.dw / 2) - 50)~format( , 0)
    tp = (.dy + (.dh / 2) - 30)~format( , 0)
    rc = self~create(lp,tp,100,60,.title,"THICKFRAME")
    self~initCode = (rc=0)

::method defineDialog
    expose h sizing
    h = .dlgAreaU~new(self)
    if .nil \= h~lastError then
        call errorDialog h~lastError
    h~updateOnResize = .false
    sizing = .false

    vt = .dlgArea~new(h~x,0,h~w,10)
    self~createStaticText(20,vt~x,vt~y,vt~w,vt~h,'CENTER','Version')
    vd = .dlgArea~new(h~x,vt~y + vt~h,h~w,10)
    self~createStaticText(21,vd~x,vd~y,vd~w,vd~h,'CENTER',.version)

    at = .dlgArea~new(h~x,vd~y + vd~h,h~w,10)
    self~createStaticText(22,at~x,at~y,at~w,at~h,'CENTER','Author')
    ad = .dlgArea~new(h~x,at~y + at~h,h~w,10)
    self~createStaticText(23,ad~x,ad~y,ad~w,ad~h,'CENTER','Lee Peedin')

    dt = .dlgArea~new(h~x,ad~y + ad~h,h~w,10)
    self~createStaticText(24,dt~x,dt~y,dt~w,dt~h,'CENTER','Documentation')
    dd = .dlgArea~new(h~x,dt~y + dt~h,h~w,10)
    self~createPushButton(25,dd~x,dd~y,dd~w,10,,'&PDF','Help')

::method initDialog unguarded
    v_title = self~newStatic(20)
    a_title = self~newStatic(22)
    d_title = self~newStatic(24)

    v_title~setColor(5,10)
    a_title~setColor(5,10)
    d_title~setColor(5,10)

    self~connectResize('onResize', 'sync')
    self~connectSizeMoveEnded('onSizeMoveEnded')

::method help
    -- The help doc is supposed to be in the 'doc' subdirectory, but we will also check the
    -- current directory, then the Rexx home directory.
    if SysFileExists('doc\ooRexxTry.pdf') then do
        helpDoc = 'doc\ooRexxTry.pdf'
    end
    else if SysFileExists('ooRexxTry.pdf') then do
        helpDoc = 'ooRexxTry.pdf'
    end
    else if SysfileExists(value("REXX_HOME",,"ENVIRONMENT")||"\doc\ooRexxTry.pdf") then do
        helpDoc = value("REXX_HOME",,"ENVIRONMENT")||"\doc\ooRexxTry.pdf"
    end
    else do
        msg = "The ooRexxTry.pdf help documentation could not be located."                     || .endOfLine -
              || .endOfLine || -
              "Tried:"                                                                         || .endOfLine -
              "  doc subdirectory:  "||directory()||"\doc\ooRexxTry.pdf"                       || .endOfLine -
              "  current directory: "||directory()||"\ooRexxTry.pdf"                           || .endOfLine -
              "  Rexx home:         "||value("REXX_HOME",,"ENVIRONMENT")||"\doc\ooRexxTry.pdf" || .endOfLine -
              || .endOfLine || -
              "Sorry, no help is available"
        title = 'ooRexx Try for the 21st Century - Error'
        j = MessageDialog(msg, self~hwnd, title, 'Ok', 'WARNING')
        return
    end
    'start "ooRexxTry Online Documentation"' '"'||helpDoc||'"'

::method onResize unguarded
    expose h sizing
  	use arg sizingType, sizeinfo

    sizing = .true
    h~resize(self, sizeinfo)

::method onSizeMoveEnded unguarded
  expose sizing

  -- If we were resizing, force the dialog controls to redraw themselves.
  if sizing then self~update

  -- We are not resizing anymore.
  sizing = .false
  return 0


::class settings_dialog subclass userdialog
::method Init
    self~init:super
    lp = (.dx + (.dw / 2) - 50)~format( , 0)
    tp = (.dy + (.dh / 2) - 30)~format( , 0)
    rc = self~Create(lp,tp,100,60,.title)
    self~InitCode = (rc=0)

::method DefineDialog
    expose h
    h = .dlgAreaU~new(self)
    if .nil \= h~lastError then
        call errorDialog h~lastError
    vt = .dlgArea~new(h~x,0,h~w,10)
    self~addText(vt~x,vt~y,vt~w,vt~h,'Font Name','CENTER',20)
    vd = .dlgArea~new(h~x,vt~y + vt~h,h~w,10)
    self~addText(vd~x,vd~y,vd~w,vd~h,.fontname,'CENTER',21)

    at = .dlgArea~new(h~x,vd~y + vd~h,h~w,10)
    self~addText(at~x,at~y,at~w,at~h,'Font Size','CENTER',22)
    ad = .dlgArea~new(h~x,at~y + at~h,h~w,10)
    self~addText(ad~x,ad~y,ad~w,ad~h,.fontsize,'CENTER',23)

    dt = .dlgArea~new(h~x,ad~y + ad~h,h~w,10)
    self~addText(dt~x,dt~y,dt~w,dt~h,'Silent','CENTER',24)
    dd = .dlgArea~new(h~x,dt~y + dt~h,h~w,10)
    self~addText(dd~x,dd~y,dd~w,dd~h,.silent,'CENTER',23)

::method InitDialog
    v_title = self~newStatic(20)
    a_title = self~newStatic(22)
    d_title = self~newStatic(24)

    v_title~setColor(14,0)
    a_title~setColor(14,0)
    d_title~setColor(14,0)

::routine LoadEnvironment
    .local~px = ScreenSize()[3]
    .local~py = ScreenSize()[4]

-- Establish dialog area sizes based of the user's screen resolution - will only be used for the first
-- execution - from then on data is retrieved from the .ini file, unless user specifies default as an
-- execution argument from the command line
    .local~dwidth  = ((.px / 2.5) * .65)~format(,0)
    .local~dheight = ((.py / 2.5) * .65)~format(,0)
    .local~dx      = 0
    .local~dy      = 0


-- Need a few generic variables
    .local~version        = '1.0'             -- Internal version control
    .local~preferred_path = ''                  -- Default starting path for Save As dialog - will be either
                                                -- the folder ooRexxTry is executed from or the last folder that
                                                -- was accessed using the Windows File Dialog
    .local~title          = 'ooRexxTry'       -- Title to use for the dialogs

-- If the .ini file is present, use it for font/silent variables
    sd = .application~srcDir
    .local~fontname = SysIni(sd'oorexxtry.ini','oorexxtry','fn')
    .local~fontsize = SysIni(sd'oorexxtry.ini','oorexxtry','fs')
    .local~silent   = SysIni(sd'oorexxtry.ini','oorexxtry','sl')

-- Else use some defaults
    if .fontname = 'ERROR:' | .useDefault then
        .local~fontname = 'Lucida Console'
    if .fontsize = 'ERROR:' | .useDefault then
        .local~fontsize = 12
    if .silent   = 'ERROR:' | .useDefault then
        .local~silent   = .false
return



