home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 5 / 05.iso / a / a108 / 15.ddi / GENMENU.PR_ / GENMENU.bin
Encoding:
Text File  |  1994-03-10  |  55.7 KB  |  1,935 lines

  1. *
  2. * GENMENU - Menu code generator.
  3. *
  4. * Copyright (c) 1990 - 1993 Microsoft Corp.
  5. * 1 Microsoft Way
  6. * Redmond, WA 98052
  7. *
  8. * Description:
  9. * This program generates menu code which was designed in the
  10. * FoxPro 2.5 MENU BUILDER.
  11. *
  12. * Notes:
  13. * In this program, for clarity/readability reasons, we use variable
  14. * names that are longer than 10 characters.  Note, however, that only
  15. * the first 10 characters are significant.
  16. *
  17. * Modification History:
  18. * December 13, 1990        JAC        Program Created
  19. *
  20. * Modifed for FoxPro 2.5 by WJK.
  21. *
  22. PARAMETER m.projdbf, m.recno
  23. PRIVATE ALL
  24. IF SET("TALK") = "ON"
  25.     SET TALK OFF
  26.     m.talkstate = "ON"
  27. ELSE
  28.     m.talkstate = "OFF"
  29. ENDIF
  30. m.escape = SET("ESCAPE")
  31. SET ESCAPE OFF
  32.  
  33. m.trbetween = SET("TRBET")
  34. SET TRBET OFF
  35. m.comp = SET("COMPATIBLE")
  36. SET COMPATIBLE OFF
  37. mdevice = SET("DEVICE")
  38. SET DEVICE TO SCREEN
  39.  
  40. *
  41. * Declare Constants
  42. *
  43. #DEFINE c_esc    CHR(27)
  44. #DEFINE c_null    CHR(0)
  45. #DEFINE c_aliaslen 10
  46. *
  47. * Possible values of Objtype field in SCX database.
  48. *
  49. #DEFINE c_menu        1
  50. #DEFINE c_submenu    2
  51. #DEFINE c_item        3
  52.  
  53. *
  54. * Some of the values of Objcode field in SCX database.
  55. *
  56. #DEFINE    c_global    1
  57. #DEFINE c_proc        80
  58.  
  59. #DEFINE c_maxsnippets    25
  60. #DEFINE c_maxpads        25
  61. #DEFINE c_pjx20flds        33
  62. #DEFINE c_pjxflds        31
  63. #DEFINE c_mnxflds        23
  64. #DEFINE c_20mnxflds        22
  65.  
  66. #DEFINE c_authorlen        45
  67. #DEFINE c_complen        45
  68. #DEFINE c_addrlen        45
  69. #DEFINE c_citylen        20
  70. #DEFINE c_statlen        5
  71. #DEFINE c_ziplen        10
  72. #DEFINE c_countrylen 40
  73.  
  74. #DEFINE c_error_1        "Minor"
  75. #DEFINE c_error_2        "Serious"
  76. #DEFINE c_error_3        "Fatal"
  77.  
  78. IF _MAC
  79.    m.g_dlgface     =    "Geneva"
  80.    m.g_dlgsize     =    10.000
  81.    m.g_dlgstyle =        ""
  82. ELSE
  83.    m.g_dlgface     =    "MS Sans Serif"
  84.    m.g_dlgsize     =    8.000
  85.    m.g_dlgstyle =        "B"
  86. ENDIF
  87.  
  88. #DEFINE c_replace        0
  89. #DEFINE c_append        1
  90. #DEFINE c_before        2
  91. #DEFINE c_after            3
  92.  
  93. #DEFINE c_pathsep  "\"
  94.  
  95. *
  96. * Declare Variables
  97. *
  98. STORE "" TO m.cursor, m.consol, m.bell, m.onerror, m.fields, mfieldsto, ;
  99.     m.exact, m.print, m.fixed, m.delimiters, m.mpoint, m.mcollate,m.mmacdesk
  100. STORE 0 TO m.deci, m.memowidth
  101.  
  102. m.g_error      = .F.
  103. m.g_errlog     = ""
  104. m.g_homedir    = ""
  105. m.g_location   = 0
  106. m.g_menucolor  = 0
  107. m.g_menumark   = ""
  108. m.g_nohandle   = .T.
  109. m.g_nsnippets  = 0
  110. m.g_outfile    = ""
  111. m.g_padloca    = ""
  112. m.g_projalias  = ""
  113. m.g_projdbf    = m.projdbf
  114. m.g_projpath   = ""
  115. m.g_status     = 0
  116. m.g_snippcnt   = 0
  117. m.g_thermwidth = 0
  118. m.g_workarea   = 0
  119. m.g_graphic    = .F.
  120. m.g_20mnx       = .F.
  121.  
  122. m.g_devauthor  = PADR("Author's Name",45," ")
  123. m.g_devcompany = PADR("Company Name",45, " ")
  124. m.g_devaddress = PADR("Address",45," ")
  125. m.g_devcity    = PADR("City",20," ")
  126. m.g_devstate   = "  "
  127. m.g_devzip     = PADR("Zip",10," ")
  128. m.g_devctry    = PADR("Country",40," ")
  129.  
  130. m.g_boxstrg = ['─','─','│','│','┌','┐','└','┘','─','─','│','│','┌','┐','└','┘']
  131.  
  132. STORE "" TO m.g_corn1, m.g_corn2, m.g_corn3, m.g_corn4, m.g_corn5, ;
  133.     m.g_corn6, m.g_verti2
  134. STORE "*" TO  m.g_horiz, m.g_verti1
  135.  
  136. *
  137. * Array Declarations
  138. *
  139. * g_mnxfile [1] - Normalized path + name
  140. * g_mnxfile [2] - Basename
  141. * g_mnxfile [3] - Opened originally?
  142. * g_mnxfile [4] - Alias
  143. *
  144. DIMENSION g_mnxfile[4]
  145. g_mnxfile[1] = ""
  146. g_mnxfile[2] = ""
  147. g_mnxfile[3] = .F.
  148. g_mnxfile[4] = ""
  149.  
  150. *
  151. * g_pads - names of generated menu pads
  152. *
  153. DIMENSION g_pads(c_maxpads)
  154.  
  155. *
  156. * g_snippets [*,1] - generated snippet procedure name
  157. * g_snippets [*,2] - recno()
  158. *
  159.  
  160. DIMENSION g_snippets (c_maxsnippets,2)
  161. g_snippets = ""
  162.  
  163. IF AT("WINDOWS", UPPER(VERSION())) <> 0 OR ;
  164.         AT("MAC", UPPER(VERSION())) <> 0
  165.     m.g_graphic = .T.
  166. ELSE
  167.     m.g_graphic = .F.
  168. ENDIF
  169.  
  170. *
  171. * Main program
  172. *
  173. m.onerror = ON("ERROR")
  174. ON ERROR DO errorhandler WITH MESSAGE(), LINENO(), c_error_3
  175.  
  176. IF PARAMETERS()=2
  177.     DO setup
  178.     IF validparams()
  179.         ON ESCAPE DO eschandler
  180.         SET ESCAPE ON
  181.         DO refreshprefs
  182.         DO BUILD
  183.     ENDIF
  184.     DO cleanup
  185. ELSE
  186.     DO errorhandler WITH "Invalid number of parameters passed to"+;
  187.         " the generator",LINENO(),c_error_3
  188. ENDIF
  189. ON ERROR &onerror
  190.  
  191. RETURN m.g_status
  192.  
  193. **
  194. ** Setup, Cleanup, Validparams, and Refreshprefs of Main Program
  195. **
  196.  
  197. *
  198. * STARTUP - Create program's environment.
  199. *
  200. * Description:
  201. * Save the user's environment so that we can set it back when
  202. * we are done, then issue various SET commands. The only state
  203. * we cannot conveniently save is SET TALK, because storing the
  204. * state involves an assignment statement, and assignments
  205. * generate unwanted output if TALK is set ON.
  206. *
  207. * Side Effects:
  208. * Creates a temporary file which is deleted in the Cleanup
  209. * procedure executed at the end of MENUGEN.
  210. *
  211. PROCEDURE setup
  212.     CLEAR PROGRAM
  213.     CLEAR GETS
  214.     m.g_workarea = SELECT()
  215.     m.delimiters = SET('TEXTMERGE',1)
  216.     SET TEXTMERGE DELIMITERS TO
  217.     SET UDFPARMS TO VALUE
  218.  
  219.     m.mfieldsto = SET("FIELDS",1)
  220.     m.fields = SET("FIELDS")
  221.     SET FIELDS TO
  222.     SET FIELDS OFF
  223.     m.bell = SET("BELL")
  224.     SET BELL OFF
  225.     m.consol = SET("CONSOLE")
  226.     SET CONSOLE OFF
  227.     m.cursor = SET("CURSOR")
  228.     SET CURSOR OFF
  229.     m.deci = SET("DECIMALS")
  230.     SET DECIMALS TO 0
  231.     mdevice = SET("DEVICE")
  232.     SET DEVICE TO SCREEN
  233.     m.memowidth = SET("MEMOWIDTH")
  234.     SET MEMOWIDTH TO 256
  235.     m.exact = SET("EXACT")
  236.     SET EXACT ON
  237.     m.print = SET("PRINT")
  238.     SET PRINT OFF
  239.     m.fixed = SET("FIXED")
  240.     SET FIXED ON
  241.     mpoint = SET("POINT")
  242.     SET POINT TO "."
  243.     mcollate = SET("COLLATE")
  244.     SET COLLATE TO "machine"
  245.      #if "MAC" $ UPPER(VERSION(1))
  246.         IF _MAC
  247.            m.mmacdesk = SET("MACDESKTOP")
  248.            SET MACDESKTOP ON
  249.        ENDIF
  250.      #endif
  251. *
  252. * CLEANUP - restore environment to pre-execution state.
  253. *
  254. * Description:
  255. * Close all databases opened in the course of the execution of MENUGEN.
  256. * Restore the environment to the pre-execution of MENUGEN.  Delete
  257. * the VIEW file since there is no further use for it.
  258. *
  259. * Side Effects:
  260. * Closes databases.
  261. * Deletes the temporary view file.
  262. *
  263. PROCEDURE cleanup
  264.     PRIVATE m.delilen, m.ldelimi, m.rdelimi
  265.     IF EMPTY(m.g_projalias)
  266.         RETURN
  267.     ENDIF
  268.     SELECT (m.g_projalias)
  269.     USE
  270.     IF NOT EMPTY(g_mnxfile[3])
  271.         IF USED(g_mnxfile[4])
  272.             SELECT (g_mnxfile[4])
  273.             USE
  274.         ENDIF
  275.     ENDIF
  276.     SELECT (m.g_workarea)
  277.  
  278.     m.delilen = LEN(m.delimiters)
  279.     m.ldelimi = SUBSTR(m.delimiters,1,;
  280.         IIF(MOD(m.delilen,2)=0,m.delilen/2,CEILING(m.delilen/2)))
  281.     m.rdelimi = SUBSTR(m.delimiters,;
  282.         IIF(MOD(m.delilen,2)=0,m.delilen/2+1,CEILING(m.delilen/2)+1))
  283.     SET TEXTMERGE DELIMITERS TO m.ldelimi, m.rdelimi
  284.  
  285.     SET FIELDS TO &mfieldsto
  286.     IF m.fields = "ON"
  287.            SET FIELDS ON
  288.     ELSE
  289.            SET FIELDS OFF
  290.     ENDIF
  291.     IF m.bell = "ON"
  292.         SET BELL ON
  293.     ENDIF
  294.     IF m.cursor = "ON"
  295.         SET CURSOR ON
  296.     ELSE
  297.         SET CURSOR OFF
  298.     ENDIF
  299.     IF m.consol = "ON"
  300.         SET CONSOLE ON
  301.     ENDIF
  302.     IF m.escape = "ON"
  303.         SET ESCAPE ON
  304.     ELSE
  305.         SET ESCAPE OFF
  306.     ENDIF
  307.     IF m.print = "ON"
  308.         SET PRINT ON
  309.     ENDIF
  310.     IF m.exact = "OFF"
  311.         SET EXACT OFF
  312.     ENDIF
  313.     IF m.fixed = "OFF"
  314.         SET FIXED OFF
  315.     ENDIF
  316.     SET DECIMALS TO m.deci
  317.     SET MEMOWIDTH TO m.memowidth
  318.     SET DEVICE TO &mdevice
  319.     IF m.trbetween = "ON"
  320.         SET TRBET ON
  321.     ENDIF
  322.     IF m.comp = "ON"
  323.         SET COMPATIBLE ON
  324.     ENDIF
  325.     IF m.talkstate = "ON"
  326.         SET TALK ON
  327.     ENDIF
  328.     SET POINT TO "&mpoint"
  329.     SET COLLATE TO "&mcollate"
  330.     SET MESSAGE TO
  331.     #if "MAC" $ UPPER(VERSION(1))
  332.         IF _MAC
  333.           SET MACDESKTOP &mmacdesk
  334.         ENDIF
  335.     #endif
  336.  
  337.     ON ERROR &onerror
  338.  
  339.  
  340. *
  341. * VALIDPARAMS - Validate generator parameters.
  342. *
  343. * Description:
  344. * Attempt to open the project database.  If error encountered then
  345. * on error routine takes over and issues 'CANCEL'.  The output file
  346. * cannot be erased, name not known.
  347. *
  348. FUNCTION validparams
  349.     SELECT 0
  350.     m.g_projalias = IIF(USED("projdbf"),"P"+;
  351.         SUBSTR(LOWER(SYS(3)),2,8),"projdbf")
  352.     USE (m.projdbf) ALIAS (m.g_projalias)
  353.     IF versnum() > "2.5"
  354.        SET NOCPTRANS TO devinfo, arranged, symbols, object
  355.     ENDIF
  356.  
  357.     m.g_errlog = stripext(m.projdbf)
  358.     m.g_projpath = SUBSTR(m.projdbf,1,RAT("\",m.projdbf))
  359.  
  360.     IF FCOUNT() <> c_pjxflds
  361.         DO errorhandler WITH "Generator out of date.",;
  362.             LINENO(), c_error_2
  363.         RETURN .F.
  364.     ENDIF
  365.  
  366.     GOTO RECORD m.recno
  367.  
  368.     m.g_outfile = ALLTRIM(SUBSTR(outfile,1,AT(c_null,outfile)-1))
  369.     m.g_outfile = FULLPATH(m.g_outfile, m.g_projpath)
  370.     IF _MAC AND RIGHT(m.g_outfile,1) = ":"
  371.        m.g_outfile = m.g_outfile + justfname(SUBSTR(outfile,1,AT(c_null,outfile)-1))
  372.     ENDIF
  373.     g_mnxfile[1] = FULLPATH(ALLTRIM(name), m.g_projpath)
  374.     IF _MAC AND RIGHT(g_mnxfile[1],1) = ":"
  375.        g_mnxfile[1] = g_mnxfile[1] + justfname(name)
  376.     ENDIF
  377.     g_mnxfile[2] = basename(g_mnxfile[1])
  378.  
  379. *
  380. * REFRESHPREFS - Refresh comment style and developer preferences.
  381. *
  382. * Description:
  383. * Get the newest preferences for documentation style and developer
  384. * data from the project database.
  385. *
  386. PROCEDURE refreshprefs
  387.     PRIVATE m.start, m.savrecno
  388.     m.savrecno = RECNO()
  389.     LOCATE FOR TYPE = "H"
  390.     IF NOT FOUND ()
  391.         DO errorhandler WITH "Missing header record in "+m.g_projdbf,;
  392.             LINENO(), c_error_2
  393.         GOTO RECORD m.savrecno
  394.         RETURN
  395.     ENDIF
  396.  
  397.     m.g_homedir = ALLTRIM(SUBSTR(homedir,1,AT(c_null,homedir)-1))
  398.  
  399.     m.start = 1
  400.     m.g_devauthor = subdevinfo(m.start,c_authorlen,m.g_devauthor)
  401.  
  402.     m.start = m.start + c_authorlen + 1
  403.     m.g_devcompany = subdevinfo(m.start,c_complen,m.g_devcompany)
  404.  
  405.     m.start = m.start + c_complen + 1
  406.     m.g_devaddress = subdevinfo(m.start,c_addrlen,m.g_devaddress)
  407.  
  408.     m.start = m.start + c_addrlen + 1
  409.     m.g_devcity = subdevinfo(m.start,c_citylen,m.g_devcity)
  410.  
  411.     m.start = m.start + c_citylen + 1
  412.     m.g_devstate = subdevinfo(m.start,c_statlen,m.g_devstate)
  413.  
  414.     m.start = m.start + c_statlen + 1
  415.     m.g_devzip = subdevinfo(m.start,c_ziplen,m.g_devzip)
  416.  
  417.     m.start = m.start + c_ziplen + 1
  418.     m.g_devctry = subdevinfo(m.start,c_countrylen,m.g_devctry)
  419.  
  420.     IF cmntstyle = 0
  421.         m.g_corn1 = "╓"
  422.         m.g_corn2 = "╖"
  423.         m.g_corn3 = "╙"
  424.         m.g_corn4 = "╜"
  425.         m.g_corn5 = "╟"
  426.         m.g_corn6    = "╢"
  427.         m.g_horiz = "─"
  428.         m.g_verti1 = "║"
  429.         m.g_verti2 = "║"
  430.     ENDIF
  431.     GOTO RECORD m.savrecno
  432.  
  433. *
  434. * SUBDEVINFO - Substring the DEVINFO memo filed.
  435. *
  436. FUNCTION subdevinfo
  437.     PARAMETER m.start, m.stop, m.default
  438.     PRIVATE m.string
  439.     m.string = SUBSTR(devinfo, m.start, m.stop+1)
  440.     m.string = SUBSTR(m.string, 1, AT(c_null,m.string)-1)
  441.     RETURN IIF(EMPTY(m.string), m.default, m.string)
  442.  
  443. **
  444. ** Menu Code Generator's Main Module.
  445. **
  446.  
  447. *
  448. * BUILD - Generate code for a menu.
  449. *
  450. * Description:
  451. * Call BUILDENABLE to open .MNX database specified by the user.
  452. * If the above is successfully accomplished, then proceed to generate
  453. * the menu code.  After the menu code is generated, call BUILDDISABLE
  454. * to disable code generation between SET TEXTMERGE ON and
  455. * SET TEXTMERGE OFF.
  456. *
  457. PROCEDURE BUILD
  458.     IF NOT buildenable()
  459.         RETURN
  460.     ENDIF
  461.     DO acttherm WITH "Generating Menu Code..."
  462.     DO updtherm WITH 10
  463.  
  464.     DO HEADER
  465.     DO gensetupcleanup WITH "setup"
  466.     DO definemenu
  467.     DO definepopups
  468.     DO updtherm WITH 75
  469.     DO globaldefaults
  470.     DO updtherm WITH 95
  471.     DO gensetupcleanup WITH "cleanup"
  472.     DO genprocedures
  473.  
  474.     IF m.g_graphic
  475.         SET MESSAGE TO 'Generation Complete'
  476.     ENDIF
  477.     DO builddisable
  478.     DO updtherm WITH 100
  479.     DO deactthermo
  480.  
  481. *
  482. * BUILDENABLE - Enable code generation.
  483. *
  484. * Description:
  485. * Call opendb to open .MNX database.
  486. * Call openfile to open file to hold the generated program.
  487. * If error(s) encountered in opendb or openfile then don't do
  488. * anything and exit, otherwise enable code generation with the
  489. * SET TEXTMERGE ON command.
  490. *
  491. * Returns:
  492. * .T. on success; .F. on failure
  493. *
  494. FUNCTION buildenable
  495.     PRIVATE m.stat
  496.     m.stat = opendb(g_mnxfile[1]) AND openfile()
  497.     IF m.stat
  498.         SET TEXTMERGE ON
  499.     ENDIF
  500.     RETURN m.stat
  501.  
  502. *
  503. * BUILDDISABLE - Disable code generation.
  504. *
  505. * Description:
  506. * Issue the command SET TEXTMERGE OFF.
  507. * Close the generated menu code output file.
  508. * If anything goes wrong display appropriate message to the user.
  509. *
  510. PROCEDURE builddisable
  511.     SET ESCAPE OFF
  512.     ON ESCAPE
  513.     SET TEXTMERGE OFF
  514.     IF NOT FCLOSE(_TEXT)
  515.         DO errorhandler WITH "Unable to Close the Application File",;
  516.             LINENO(), c_error_2
  517.     ENDIF
  518.  
  519. *
  520. * OPENDB - Prepare database for processing.
  521. *
  522. * Description:
  523. * Attempt to USE a database.  If attempt fails and error is reported
  524. * call ERRORHANDLER routine to display a friendly message.  Return
  525. * with a status of .F..  If attempt succeeds, return with status of .T.
  526. *
  527. * Returns:
  528. * .T. on success; .F. on failure
  529. *
  530. FUNCTION opendb
  531.     PARAMETER m.dbname
  532.     PRIVATE m.dbalias
  533.     ON ERROR DO errorhandler WITH MESSAGE(), LINENO(), c_error_2
  534.  
  535.     m.dbalias = LEFT(basename(m.dbname),c_aliaslen)
  536.     IF USED (m.dbalias)
  537.         SELECT (m.dbalias)
  538.         IF RAT(".MNX",DBF())<>0
  539.             g_mnxfile[3] = .F.
  540.             g_mnxfile[4] = m.dbalias
  541.         ELSE
  542.             g_mnxfile[4] = "M"+SUBSTR(LOWER(SYS(3)),2,8)
  543.             SELECT 0
  544.             USE (m.dbname) AGAIN ALIAS (g_mnxfile[4])
  545.             g_mnxfile[3] = .T.
  546.         ENDIF
  547.     ELSE
  548.         IF illegalname(m.dbalias)
  549.             g_mnxfile[4] = "M"+SUBSTR(LOWER(SYS(3)),2,8)
  550.         ELSE
  551.             g_mnxfile[4] = m.dbalias
  552.         ENDIF
  553.         SELECT 0
  554.         USE (m.dbname) AGAIN ALIAS (g_mnxfile[4])
  555.         g_mnxfile[3] = .T.
  556.     ENDIF
  557.  
  558.     IF FCOUNT() <> c_mnxflds
  559.         IF FCOUNT() = c_20mnxflds
  560.             m.g_20mnx = .T.
  561.         ELSE
  562.             DO errorhandler WITH "Menu "+m.dbalias+" is invalid",LINENO(),;
  563.                 c_error_2
  564.             RETURN .F.
  565.         ENDIF
  566.     ELSE
  567.         m.g_20mnx = .F.
  568.     ENDIF
  569.  
  570.     ON ERROR DO errorhandler WITH MESSAGE(), LINENO(), c_error_3
  571.     IF m.g_error = .T.
  572.         RETURN .F.
  573.     ENDIF
  574.  
  575. *
  576. * ILLEGALNAME - Check if default alias will be used when this
  577. *               database is USEd. (i.e., 1st letter is not A-Z,
  578. *                a-z or '_', or any one of ramaining letters is not
  579. *                alphanumeric.)
  580. *
  581. FUNCTION illegalname
  582.     PARAMETER m.menuname
  583.     PRIVATE m.start, m.aschar, m.length
  584.     m.length = LEN(m.menuname)
  585.     m.start  = 0
  586.     IF m.length = 1
  587.         *
  588.         * If length 1, then check if default alias can be used,
  589.         * i.e., name is different than A-J and a-j.
  590.         *
  591.         m.aschar = ASC(m.menuname)
  592.         IF (m.aschar >= 65 AND m.aschar <= 74) OR ;
  593.                 (m.aschar >= 97 AND m.aschar <= 106)
  594.             RETURN .T.
  595.         ENDIF
  596.     ENDIF
  597.     DO WHILE m.start < m.length
  598.         m.start  = m.start + 1
  599.         m.aschar = ASC(SUBSTR(m.menuname, m.start, 1))
  600.         IF m.start<>1 AND (m.aschar >= 48 AND m.aschar <= 57)
  601.             LOOP
  602.         ENDIF
  603.         IF NOT ((m.aschar >= 65 AND m.aschar <= 90) OR ;
  604.                 (m.aschar >= 97 AND m.aschar <= 122) OR m.aschar = 95)
  605.             RETURN .T.
  606.         ENDIF
  607.     ENDDO
  608.     RETURN .F.
  609.  
  610. *
  611. * OPENFILE - Create and open the application output file.
  612. *
  613. * Description:
  614. * Create a file that will hold the generated menu code.
  615. * Open the newly created file.  If error(s) encountered
  616. * at any time issue an error message and return .F.
  617. *
  618. * Returns:
  619. * .T. on success; .F. on failure
  620. *
  621. FUNCTION openfile
  622.     PRIVATE m.msg
  623.     _TEXT = FCREATE(m.g_outfile)
  624.     IF (_TEXT = -1)
  625.         m.msg = "Cannot open file "+m.g_outfile
  626.         DO errorhandler WITH m.msg, LINENO(), c_error_3
  627.         m.g_nohandle = .T.
  628.         RETURN .F.
  629.     ENDIF
  630.     m.g_nohandle = .F.
  631.  
  632. *
  633. * DEFINEMENU - Define main menu and its pads.
  634. *
  635. * Description:
  636. * Issue DEFINE MENU ... command.
  637. * Call a procedure to define all menu pads.
  638. * Call a procedure to generate ON PAD statements when appropriate.
  639. *
  640. PROCEDURE definemenu
  641.  
  642.     IF m.g_graphic
  643.         SET MESSAGE TO 'Generating menu definitions...'
  644.     ENDIF
  645.     DO commentblock WITH "menu"
  646.     SELECT (g_mnxfile[4])
  647.     LOCATE FOR objtype = c_menu
  648.     m.g_location = location
  649.     m.g_padloca  = ALLTRIM(name)
  650.  
  651.     LOCATE FOR objtype = c_submenu AND objcode = c_global
  652.  
  653.     m.g_menucolor = SCHEME
  654.     m.g_menumark  = MARK
  655.     IF m.g_location = c_replace
  656.         \SET SYSMENU TO
  657.         \
  658.     ENDIF
  659.     \SET SYSMENU AUTOMATIC
  660.     \
  661.  
  662.     DO updtherm WITH 25
  663.     DO defmenupads
  664.     DO updtherm WITH 35
  665.     DO defonpad
  666.     \
  667.     DO updtherm WITH 45
  668.  
  669. *
  670. * DEFMENUPADS - Define all pads for the menu bar.
  671. *
  672. * Description:
  673. * Scan the menu database for all objects of the type item which
  674. * have the levelname=_MSYSMENU.
  675. * For each such item, generate a statement DEFINE PAD... where
  676. * the name of the pad is the contents of NAME field or (if Name
  677. * field is empty) an automatically generated name.
  678. * Call procedures addkey, addskipfor, and mark to generate
  679. * KEY, SKIPFOR, or MARK clauses when appropriate.
  680. *
  681. PROCEDURE defmenupads
  682.     PRIVATE m.padname, m.prompt
  683.     SCAN FOR objtype=c_item AND UPPER(levelname)="_MSYSMENU"
  684.         IF NOT EMPTY(ALLTRIM(name))
  685.             g_pads[VAL(Itemnum)] = name
  686.         ELSE
  687.             g_pads[VAL(Itemnum)] = LOWER(SYS(2015))
  688.         ENDIF
  689.         \DEFINE PAD <<g_pads[VAL(Itemnum)]>> OF _MSYSMENU
  690.  
  691.         IF MOD(VAL(itemnum),25)=0
  692.             DIMENSION g_pads[VAL(Itemnum)+25]
  693.         ENDIF
  694.         m.prompt = SUBSTR(PROMPT,1,LEN(PROMPT))
  695.         \\ PROMPT "<<m.prompt>>"
  696.         \\ COLOR SCHEME <<m.g_menucolor>>
  697.  
  698.         IF m.g_menumark<>c_null AND m.g_menumark<>""
  699.             \\ ;
  700.             \    MARK "<<m.g_menumark>>"
  701.         ENDIF
  702.  
  703.         DO CASE
  704.             CASE m.g_location = c_before
  705.                 \\ ;
  706.                 \    BEFORE <<m.g_padloca>>
  707.             CASE m.g_location = c_after
  708.                 \\ ;
  709.                 \    AFTER
  710.                 IF VAL(itemnum) = 1
  711.                     \\ <<m.g_padloca>>
  712.                 ELSE
  713.                     \\ <<g_pads[VAL(Itemnum)-1]>>
  714.                 ENDIF
  715.         ENDCASE
  716.  
  717.         DO addkey
  718.         DO addskipfor
  719.         DO addmessage
  720.  
  721.     ENDSCAN
  722.  
  723. *
  724. * DEFONPAD - Generate ON PAD... statements.
  725. *
  726. * Description:
  727. * Generate ON PAD statements for each pad off of the main menu which
  728. * has a submenu associated with it.
  729. * For pads which have no submenus, but there is a command associated
  730. * with them, issue ON SELECTION PAD... statements.  If the code
  731. * associated with a pad is a snippet, then issue a call to the
  732. * generated procedure and place the snippet code in it.
  733. *
  734. PROCEDURE defonpad
  735.     PRIVATE m.padname
  736.     SCAN FOR objtype=c_item AND UPPER(levelname)="_MSYSMENU"
  737.          IF NOT EMPTY(ALLTRIM(name))
  738.                m.padname = name
  739.          ELSE
  740.                m.padname = g_pads[VAL(Itemnum)]
  741.          ENDIF
  742.          m.therec = RECNO()
  743.          SKIP
  744.          IF objtype=c_submenu AND numitems<>0
  745.                \ON PAD <<m.padname>> OF _MSYSMENU
  746.                \\ ACTIVATE POPUP <<LOWER(Name)>>
  747.                GOTO m.therec
  748.          ELSE
  749.                GOTO m.therec
  750.                DO onselection WITH "pad", m.padname, '_MSYSMENU'
  751.          ENDIF
  752.     ENDSCAN
  753.  
  754. *
  755. * DEFINEPOPUPS - Define popups and their bars.
  756. *
  757. * Description:
  758. * Scan the Menu database to find all objecttypes = submenu.
  759. * They all correspond to popups.  For each such object found, issue
  760. * command DEFINE POPUP....  Add MARK, KEY, and SKIP FOR clauses
  761. * if appropriate by calling procedures to handle these tasks.  Call
  762. * procedure Defbars to define all bars of each popup.
  763. *
  764. PROCEDURE definepopups
  765.     PRIVATE m.savrecno, m.popname, m.sch
  766.     IF m.g_graphic
  767.         SET MESSAGE TO 'Generating popup definitions...'
  768.     ENDIF
  769.     SCAN FOR objtype=c_submenu AND UPPER(levelname)<>"_MSYSMENU" ;
  770.             AND numitems <> 0
  771.  
  772.         m.savrecno = RECNO()
  773.         m.popname  = ALLTRIM(LOWER(levelname))
  774.         m.sch      = SCHEME
  775.  
  776.         \DEFINE POPUP <<LOWER(Name)>> MARGIN RELATIVE SHADOW
  777.         \\ COLOR SCHEME <<m.sch>>
  778.  
  779.         DO addmark
  780.         DO addkey
  781.         DO defbars WITH m.popname, numitems
  782.         DO defonbar WITH m.popname
  783.         \
  784.         GOTO RECORD m.savrecno
  785.     ENDSCAN
  786.  
  787. *
  788. * DEFBARS - Define bars for each popup.
  789. *
  790. * Description:
  791. * Scan the menu database for all objects of the type item whose
  792. * name equals to the current popup name.
  793. * For each such item, generate a statement DEFINE BAR....
  794. * Call procedures addkey, addskipfor, and addmark to generate
  795. * KEY, SKIPFOR, or MARK clauses when appropriate.
  796. *
  797. PROCEDURE defbars
  798.     PARAMETER m.popname, m.howmany, m.name
  799.     PRIVATE m.itemno, m.prompt
  800.     SCAN FOR objtype=c_item AND LOWER(levelname)=m.popname
  801.         m.itemno = ALLTRIM(itemnum)
  802.  
  803.         IF NOT EMPTY(ALLTRIM(name))
  804.             m.name = name
  805.             \DEFINE BAR <<m.name>> OF <<LOWER(m.popname)>>
  806.         ELSE
  807.             \DEFINE BAR <<m.itemno>> OF <<LOWER(m.popname)>>
  808.         ENDIF
  809.         m.prompt = SUBSTR(PROMPT, 1,LEN(PROMPT))
  810.         \\ PROMPT "<<m.prompt>>"
  811.  
  812.         DO addmark
  813.         DO addkey
  814.         DO addskipfor
  815.         DO addmessage
  816.  
  817.         IF VAL(m.itemno)=m.howmany
  818.             RETURN
  819.         ENDIF
  820.     ENDSCAN
  821.  
  822. *
  823. * DEFONBAR - Generate ON BAR... statements.
  824. *
  825. * Description:
  826. * Generate ON BAR statements for each popup.
  827. * For bars which have no submenus, but there is a command associated
  828. * with them, issue ON SELECTION BAR... statements.  If a snippet is
  829. * associated with the code then generate a call statement to the
  830. * generated procedure containing the snippet code.
  831. *
  832. PROCEDURE defonbar
  833.     PARAMETER m.popname
  834.     PRIVATE m.itemno
  835.     SCAN FOR objtype=c_item AND LOWER(levelname)=m.popname
  836.         IF EMPTY(ALLTRIM(name))
  837.             m.itemno = ALLTRIM(itemnum)
  838.         ELSE
  839.             m.itemno = name
  840.         ENDIF
  841.         SKIP
  842.         IF objtype=c_submenu AND numitems<>0
  843.             \ON BAR <<m.itemno>> OF <<LOWER(m.popname)>>
  844.             \\ ACTIVATE POPUP <<LOWER(Name)>>
  845.             SKIP -1
  846.         ELSE
  847.             SKIP -1
  848.             DO onselection WITH "BAR", m.itemno, m.popname
  849.         ENDIF
  850.     ENDSCAN
  851.  
  852. *
  853. * GLOBALDEFAULTS - Generate global default statements
  854. *
  855. * Description:
  856. * Search the menu database for information needed to generate any of
  857. * the following commands:
  858. * ON SELECTION MENU <name> DO <action>
  859. * ON SELECTION POPUP ALL DO <action>
  860. * ON SELECTION POPUP <name> DO <action>
  861. * It is possible that none of the above mentioned statements will be
  862. * generated.  It is also possible that the action is a snippet of
  863. * code and a call to the generated procedure containing the snippet
  864. * will be generated.
  865. *
  866. * First try to generate ON SELECTION MENU...
  867. * Then try to generate ON POPUP ALL...
  868. * Lastly, try to generate ON SELECTION POPUP...
  869. *
  870. PROCEDURE globaldefaults
  871.     LOCATE FOR objtype = c_menu
  872.     m.mrk = MARK
  873.     IF FOUND() AND MARK <> ""
  874.         IF MARK = c_null
  875.             \SET MARK OF MENU _MSYSMENU TO " "
  876.         ELSE
  877.             \SET MARK OF MENU _MSYSMENU TO "<<Mark>>"
  878.         ENDIF
  879.     ENDIF
  880.     IF FOUND() AND NOT EMPTY(PROCEDURE)
  881.         \ON SELECTION MENU _MSYSMENU
  882.         DO genproccall
  883.     ENDIF
  884.     LOCATE FOR objtype = c_submenu AND objcode = c_global
  885.     IF FOUND() AND NOT EMPTY(PROCEDURE)
  886.         \ON SELECTION POPUP ALL
  887.         DO genproccall
  888.     ENDIF
  889.     SCAN FOR (objtype=c_submenu AND UPPER(levelname)<>"_MSYSMENU";
  890.             AND NOT EMPTY(PROCEDURE))
  891.         \ON SELECTION POPUP <<ALLTRIM(LOWER(Levelname))>>
  892.         DO genproccall
  893.     ENDSCAN
  894.  
  895. **
  896. ** Subroutines for processing menu clause options.
  897. **
  898.  
  899. *
  900. * ADDMARK - Generate a MARK clause whenever appropriate.
  901. *
  902. * Description:
  903. * Add a MARK clause to the current PAD or BAR definition.
  904. * If a field named Mark is not empty, then add the continuation
  905. * character, ";", to the previous line, and then add the MARK... clause.
  906. *
  907. PROCEDURE addmark
  908.     IF MARK<>c_null AND MARK<>""
  909.         \\ ;
  910.             \    MARK "<<Mark>>"
  911.     ENDIF
  912.  
  913. *
  914. * ADDKEY - Generate KEY... clause whenever appropriate.
  915. *
  916. * Description:
  917. * Add a KEY clause to the current PAD or BAR definition.
  918. * If a field named Keyname is not empty, then add the continuation
  919. * character, ";", to the previous line, and then add the KEY... clause.
  920. *
  921. PROCEDURE addkey
  922.     IF NOT EMPTY(keyname)
  923.         \\ ;
  924.         \    KEY <<Keyname>>, "<<Keylabel>>"
  925.     ENDIF
  926.  
  927. *
  928. * ADDSKIPFOR - Generate SKIP FOR... clause whenever appropriate.
  929. *
  930. * Description:
  931. * Add a ADDSKIPFOR clause to the current PAD or BAR definition.
  932. * If a field named Addskipfor is not empty, then add the continuation
  933. * character, ";", to the previous line, and then add the SKIP FOR...
  934. * clause.
  935. *
  936. PROCEDURE addskipfor
  937.     PRIVATE m.skip
  938.     m.skip = skipfor
  939.     IF NOT EMPTY(skipfor)
  940.         \\ ;
  941.         \    SKIP FOR <<m.skip>>
  942.     ENDIF
  943.  
  944. *
  945. * ADDMESSAGE - Generate MESSAGE clause whenever appropriate.
  946. *
  947. * Description:
  948. * Add a MESSAGE clause to the current PAD or BAR definition.
  949. * If a field named MESSAGE is not empty and it is not a 2.0 menu,
  950. * then add the continuation character, ";", to the previous line,
  951. * and then add the MESSAGE clause.
  952. *
  953. PROCEDURE addmessage
  954.  
  955.     IF !m.g_20mnx AND NOT EMPTY(MESSAGE)
  956.         \\ ;
  957.         \    MESSAGE <<Message>>
  958.     ENDIF
  959.  
  960. *
  961. * HEADER - Generate generated program's header.
  962. *
  963. * Description:
  964. * As a part of the automatically generated program's header generate
  965. * program name, name of the author of the program, copyright notice,
  966. * company name and address, and the word 'Description:' which will be
  967. * followed with a short description of the generated code.
  968. *
  969. PROCEDURE HEADER
  970.     \\*       <<m.g_corn1>><<REPLICATE(m.g_horiz,57)>><<m.g_corn2>>
  971.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  972.     \*       <<m.g_verti1>> <<DATE()>>
  973.     \\<<PADC(UPPER(ALLTRIM(strippath(m.g_outfile))),IIF(SET("CENTURY")="ON",35,37))," ")>>
  974.     \\ <<TIME()>>  <<m.g_verti2>>
  975.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  976.     \*       <<m.g_corn5>><<REPLICATE(m.g_horiz,57)>><<m.g_corn6>>
  977.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  978.     \*       <<m.g_verti1>> <<m.g_devauthor>>
  979.     \\<<REPLICATE(" ",56-LEN(m.g_devauthor))>><<m.g_verti2>>
  980.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  981.     \*       <<m.g_verti1>>
  982.     \\ Copyright (c) <<YEAR(DATE())>>
  983.     IF LEN(ALLTRIM(m.g_devcompany)) <= 36
  984.         \\ <<ALLTRIM(m.g_devcompany)>>
  985.         \\<<REPLICATE(" ",37-LEN(ALLTRIM(m.g_devcompany)))>>
  986.         \\<<m.g_verti2>>
  987.     ELSE
  988.         \\ <<REPLICATE(" ",37)>><<m.g_verti2>>
  989.         \*       <<m.g_verti1>> <<m.g_devcompany>>
  990.         \\<<REPLICATE(" ",56-LEN(m.g_devcompany))>><<m.g_verti2>>
  991.     ENDIF
  992.  
  993.     \*       <<m.g_verti1>> <<m.g_devaddress>>
  994.     \\<<REPLICATE(" ",56-LEN(m.g_devaddress))>><<m.g_verti2>>
  995.  
  996.     \*       <<m.g_verti1>> <<ALLTRIM(m.g_devcity)>>, <<m.g_devstate>>
  997.     \\  <<ALLTRIM(m.g_devzip)>>
  998.     \\<<REPLICATE(" ",50-(LEN(ALLTRIM(m.g_devcity)+ALLTRIM(m.g_devzip))))>>
  999.     \\<<m.g_verti2>>
  1000.  
  1001.     IF !INLIST(ALLTRIM(UPPER(m.g_devctry)),"USA","COUNTRY") AND !EMPTY(m.g_devctry)
  1002.        \*       <<m.g_verti1>> <<ALLTRIM(m.g_devctry)>>
  1003.        \\<<REPLICATE(" ",50-(LEN(ALLTRIM(m.g_devctry))))>>
  1004.        \\<<m.g_verti2>>
  1005.     ENDIF
  1006.  
  1007.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1008.     \*       <<m.g_verti1>> Description:
  1009.     \\                                            <<m.g_verti2>>
  1010.     \*       <<m.g_verti1>>
  1011.     \\ This program was automatically generated by GENMENU.
  1012.     \\    <<m.g_verti2>>
  1013.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1014.     \*       <<m.g_corn3>><<REPLICATE(m.g_horiz,57)>><<m.g_corn4>>
  1015.     \
  1016.  
  1017. *
  1018. * GENFUNCHEADER - Generate Comment for Function/Procedure.
  1019. *
  1020. PROCEDURE genfuncheader
  1021.     PARAMETER m.procname
  1022.     PRIVATE m.place, m.prompt
  1023.     m.g_snippcnt = m.g_snippcnt + 1
  1024.     DO CASE
  1025.         CASE objtype = c_menu
  1026.             m.place = "ON SELECTION MENU _MSYSMENU"
  1027.         CASE objtype = c_submenu AND objcode = c_global
  1028.             m.place = "ON SELECTION POPUP ALL"
  1029.         CASE objtype = c_submenu AND objcode <> c_global
  1030.             m.place = "ON SELECTION POPUP "+LOWER(ALLTRIM(name))
  1031.         CASE objtype = c_item AND UPPER(levelname) = "_MSYSMENU"
  1032.             m.place = "ON SELECTION PAD "
  1033.         CASE objtype = c_item AND UPPER(levelname) <> "_MSYSMENU"
  1034.             m.place = "ON SELECTION BAR "+ALLTRIM(itemnum)+;
  1035.                 +" OF POPUP "+LOWER(ALLTRIM(levelname))
  1036.         OTHERWISE
  1037.             m.place = ""
  1038.     ENDCASE
  1039.     \
  1040.     \*       <<m.g_corn1>><<REPLICATE(m.g_horiz,57)>><<m.g_corn2>>
  1041.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1042.     \*       <<m.g_verti1>> <<UPPER(PADR(m.procname,10))>>  <<m.place>>
  1043.     \\<<REPLICATE(" ",44-LEN(m.place))>><<m.g_verti2>>
  1044.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1045.     \*       <<m.g_verti1>> Procedure Origin:
  1046.     \\<<REPLICATE(" ",39)>><<m.g_verti2>>
  1047.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1048.     \*       <<m.g_verti1>> From Menu:
  1049.     \\  <<ALLTRIM(strippath(m.g_outfile))>>
  1050.     \\,            Record:  <<STR(RECNO(),3)>>
  1051.     \\<<REPLICATE(" ",22-LEN(ALLTRIM(strippath(m.g_outfile))+STR(RECNO(),3))))>>
  1052.     \\<<m.g_verti2>>
  1053.     \*       <<m.g_verti1>> Called By:  <<m.place>>
  1054.     \\<<REPLICATE(" ",44-LEN(m.place))>><<m.g_verti2>>
  1055.     IF NOT EMPTY(PROMPT)
  1056.         m.prompt = removemeta()
  1057.         \*       <<m.g_verti1>> Prompt:     <<ALLTRIM(m.prompt)>>
  1058.         \\<<REPLICATE(" ",44-LEN(ALLTRIM(m.prompt)))>><<m.g_verti2>>
  1059.     ENDIF
  1060.     \*       <<m.g_verti1>> Snippet:
  1061.     \\    <<ALLTRIM(STR(m.g_snippcnt,2))>>
  1062.     \\<<REPLICATE(" ",44-LEN(ALLTRIM(STR(m.g_snippcnt,2))))>><<m.g_verti2>>
  1063.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1064.     \*       <<m.g_corn3>><<REPLICATE(m.g_horiz,57)>><<m.g_corn4>>
  1065.     \*
  1066.  
  1067. *
  1068. * REMOVEMETA - Remove meta characters for documentation.
  1069. *
  1070. FUNCTION removemeta
  1071.     PRIVATE m.prompt, m.hotkey
  1072.     m.prompt = PROMPT
  1073.     m.hotkey = AT("\<",m.prompt)
  1074.  
  1075.     IF m.hotkey <> 0
  1076.         m.prompt = STUFF(m.prompt,m.hotkey,2,"")
  1077.     ENDIF
  1078.  
  1079.     m.disabl = AT("\",m.prompt)
  1080.     IF m.disabl <> 0
  1081.         m.prompt = STUFF(m.prompt,m.disabl,1,"")
  1082.     ENDIF
  1083.     RETURN m.prompt
  1084.  
  1085. *
  1086. * COMMENTBLOCK - Generate a comment block.
  1087. *
  1088. PROCEDURE commentblock
  1089.     PARAMETER m.snippet
  1090.     \
  1091.     \*       <<m.g_corn1>><<REPLICATE(m.g_horiz,57)>><<m.g_corn2>>
  1092.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1093.     DO CASE
  1094.         CASE m.snippet == "setup"
  1095.             \*       <<m.g_verti1>>
  1096.             \\ <<PADC(" Setup Code",56," ")>>
  1097.         CASE m.snippet == "cleanup"
  1098.             \*       <<m.g_verti1>>
  1099.             \\ <<PADC(" Cleanup Code & Procedures",56," ")>>
  1100.         CASE m.snippet == "init"
  1101.             \*       <<m.g_verti1>>
  1102.             \\ <<PADC(" Initializing Code",56," ")>>
  1103.         CASE m.snippet == "menu"
  1104.             \*       <<m.g_verti1>>
  1105.             \\ <<PADC(" Menu Definition",56," ")>>
  1106.     ENDCASE
  1107.     \\<<m.g_verti2>>
  1108.     \*       <<m.g_verti1>><<REPLICATE(" ",57)>><<m.g_verti2>>
  1109.     \*       <<m.g_corn3>><<REPLICATE(m.g_horiz,57)>><<m.g_corn4>>
  1110.     \*
  1111.     \
  1112.  
  1113. **
  1114. ** Supporting routines
  1115. **
  1116.  
  1117. *
  1118. * ONSELECTION - Generate ON SELECTION... statements for menu items.
  1119. *
  1120. * Description:
  1121. * For pads and bars which have no submenu associated with them but
  1122. * instead have a non-empty Command field in the database, issue
  1123. * the ON SELECTION <command> statements.  If a snippet is associated
  1124. * with a pad then issue a call statement to the generated procedure
  1125. * containing the snippet.  Generated snippet procedure will be
  1126. * appended to the end of the output file.
  1127. *
  1128. PROCEDURE onselection
  1129.     PARAMETER m.which, m.name, m.ofname, m.commd
  1130.     PRIVATE m.trimname, m.basename
  1131.     IF EMPTY(PROCEDURE) AND EMPTY(COMMAND)
  1132.         RETURN
  1133.     ENDIF
  1134.     DO CASE
  1135.         CASE m.which == "pad"
  1136.             \ON SELECTION PAD <<m.name>>
  1137.         CASE m.which == "BAR"
  1138.             \ON SELECTION <<m.which+" "+m.name>>
  1139.     ENDCASE
  1140.     \\ OF <<m.ofname>>
  1141.     IF objcode = c_proc
  1142.         DO gensnippname
  1143.         m.trimname = SYS(2014,UPPER(m.g_outfile),UPPER(m.g_homedir))
  1144.         m.trimname = stripext(m.trimname)
  1145.         m.basename = basename(m.trimname)
  1146.         \\ ;
  1147.         \    DO <<g_snippets[g_nsnippets,1]>> ;
  1148.         \    IN LOCFILE("<<m.trimname>>"
  1149.         \\ ,"MPX;MPR|FXP;PRG"
  1150.         \\ ,"Where is <<m.basename>>?")
  1151.     ELSE
  1152.         m.commd = COMMAND
  1153.         \\ <<m.commd>>
  1154.     ENDIF
  1155.  
  1156. *
  1157. * GENSNIPPNAME - Generate a unique name for snippet procedure.
  1158. *
  1159. * Description:
  1160. * Lookup the #NAME name of this snippet, or alternatively
  1161. * provide a unique name for a snippet of code associated with the
  1162. * generated menu.  Save this name in an array g_snippets.
  1163. *
  1164. PROCEDURE gensnippname
  1165.     g_nsnippets = g_nsnippets + 1
  1166.     g_snippets[g_nsnippets,1] = getcname(procedure)
  1167.     g_snippets[g_nsnippets,2] = RECNO()
  1168.  
  1169.     IF MOD(g_nsnippets,25) = 0
  1170.         DIMENSION g_snippets [g_nsnippets+25,2]
  1171.     ENDIF
  1172.  
  1173. *
  1174. * GENPROCCALL - Generate a call statement to snippet procedure.
  1175. *
  1176. * Description:
  1177. * Generate a call to the snippet procedure in the menu definition
  1178. * code.
  1179. *
  1180. PROCEDURE genproccall
  1181.     PRIVATE m.trimname, m.basename, m.proc
  1182.     IF singleline()
  1183.         m.proc = PROCEDURE
  1184.         \\ <<MLINE(m.proc,1)>>
  1185.     ELSE
  1186.         DO gensnippname
  1187.         m.trimname = SYS(2014,UPPER(m.g_outfile),UPPER(m.g_homedir))
  1188.         m.trimname = stripext(m.trimname)
  1189.         m.basename = basename(m.trimname)
  1190.         \\ ;
  1191.         \    DO <<g_snippets[m.g_nsnippets,1]>> ;
  1192.         \    IN LOCFILE("<<m.trimname>>"
  1193.         \\ ,"MPX;MPR|FXP;PRG"
  1194.         \\ ,"Where is <<m.basename>>?")
  1195.     ENDIF
  1196.  
  1197. *
  1198. * SINGLELINE - Determine if Memo contains only one line.
  1199. *
  1200. * Description:
  1201. * This procedure is used to decide if an ON SELECTION... statement
  1202. * and a snippet procedure will be needed (i.e., if more than one
  1203. * line of snippet code then its a snippet, otherwise its a command)
  1204. *
  1205. FUNCTION singleline
  1206.     PRIVATE m.size, m.i
  1207.     m.size = MEMLINES(PROCEDURE)
  1208.     IF m.size = 1
  1209.         RETURN .T.
  1210.     ENDIF
  1211.     m.i = m.size
  1212.     DO WHILE m.i > 1
  1213.         m.line = MLINE(PROCEDURE, m.i)
  1214.         IF NOT EMPTY(m.line)
  1215.             RETURN .F.
  1216.         ENDIF
  1217.         m.i = m.i - 1
  1218.     ENDDO
  1219.  
  1220. *
  1221. * GENPROCEDURES - Generate procedure/snippet code.
  1222. *
  1223. * Description:
  1224. * Generate 'PROCEDURE procedurename' statement and its body.
  1225. *
  1226. PROCEDURE genprocedures
  1227.     PRIVATE m.i
  1228.     IF m.g_graphic
  1229.         SET MESSAGE TO 'Generating procedures...'
  1230.     ENDIF
  1231.     FOR m.i = 1 TO m.g_nsnippets
  1232.         GOTO RECORD (g_snippets[m.i,2])
  1233.         DO genfuncheader WITH g_snippets[m.i,1]
  1234.         \PROCEDURE <<g_snippets[m.i,1]>>
  1235.         DO writecode WITH procedure
  1236.         \
  1237.     ENDFOR
  1238.  
  1239. *
  1240. * WRITECODE - Write contents of a memo to a low level file.
  1241. *
  1242. * Description:
  1243. * Receive a memo field as a parameter and write its contents out
  1244. * to the currently opened low level file whose handle is stored
  1245. * in the system memory variable _TEXT.  Contents of the system
  1246. * memory variable _pretext will affect the positioning of the
  1247. * generated text.
  1248. *
  1249. PROCEDURE writecode
  1250.     PARAMETER m.memo
  1251.     PRIVATE m.lines, m.i, m.thisline
  1252.     m.lines = MEMLINES(m.memo)
  1253.     _MLINE = 0
  1254.     FOR m.i = 1 TO m.lines
  1255.         m.thisline = MLINE(m.memo, 1, _MLINE)
  1256.         IF LEFT(UPPER(LTRIM(m.thisline)),5) == "#INSE"   && #INSERT
  1257.            DO GenInsertCode WITH m.thisline
  1258.         ELSE
  1259.            IF LEFT(UPPER(LTRIM(m.thisline)),5) <> "#NAME"
  1260.               \<<m.thisline>>
  1261.            ENDIF
  1262.         ENDIF
  1263.     ENDFOR
  1264.  
  1265. *
  1266. * GENSETUPCLEANUP - Generate setup/cleanup code.
  1267. *
  1268. PROCEDURE gensetupcleanup
  1269.     PARAMETER m.choice
  1270.     LOCATE FOR objtype = c_menu
  1271.     DO CASE
  1272.         CASE m.choice == "setup"
  1273.             IF EMPTY(setup)
  1274.                 RETURN
  1275.             ENDIF
  1276.             IF m.g_graphic
  1277.                 SET MESSAGE TO 'Generating Menu Setup Code...'
  1278.             ENDIF
  1279.             DO commentblock WITH m.choice
  1280.             DO writecode WITH setup
  1281.         CASE m.choice == "cleanup"
  1282.             IF EMPTY(cleanup)
  1283.                 RETURN
  1284.             ENDIF
  1285.             IF m.g_graphic
  1286.                 SET MESSAGE TO 'Generating Menu Cleanup Code...'
  1287.             ENDIF
  1288.             DO commentblock WITH m.choice
  1289.             DO writecode WITH cleanup
  1290.     ENDCASE
  1291.  
  1292. *
  1293. * STRIPEXT - Strip the extension from a file name.
  1294. *
  1295. * Description:
  1296. * Use the algorithm employed by FoxPRO itself to strip a
  1297. * file of an extension (if any): Find the rightmost dot in
  1298. * the filename.  If this dot occurs to the right of a "\"
  1299. * or ":", then treat everything from the dot rightward
  1300. * as an extension.  Of course, if we found no dot,
  1301. * we just hand back the filename unchanged.
  1302. *
  1303. * Parameters:
  1304. * filename - character string representing a file name
  1305. *
  1306. * Return value:
  1307. * The string "filename" with any extension removed
  1308. *
  1309. FUNCTION stripext
  1310.     PARAMETER m.filename
  1311.     PRIVATE m.dotpos, m.terminator
  1312.     m.dotpos = RAT(".", m.filename)
  1313.     m.terminator = MAX(RAT("\", m.filename), RAT(":", m.filename))
  1314.     IF m.dotpos > m.terminator
  1315.         m.filename = LEFT(m.filename, m.dotpos-1)
  1316.     ENDIF
  1317.     RETURN m.filename
  1318.  
  1319. *
  1320. * STRIPPATH - Strip the path from a file name.
  1321. *
  1322. * Description:
  1323. * Find positions of backslash in the name of the file.  If there is one
  1324. * take everything to the right of its position and make it the new file
  1325. * name.  If there is no slash look for colon.  Again if found, take
  1326. * everything to the right of it as the new name.  If neither slash
  1327. * nor colon are found then return the name unchanged.
  1328. *
  1329. * Parameters:
  1330. * filename - character string representing a file name
  1331. *
  1332. * Return value:
  1333. * The string "filename" with any path removed
  1334. *
  1335. FUNCTION strippath
  1336.     PARAMETER m.filename
  1337.     PRIVATE m.slashpos, m.namelen, m.colonpos
  1338.     m.slashpos = RAT("\", m.filename)
  1339.     IF m.slashpos > 0
  1340.         m.namelen  = LEN(m.filename) - m.slashpos
  1341.         m.filename = RIGHT(m.filename, m.namelen)
  1342.     ELSE
  1343.         m.colonpos = RAT(":", m.filename)
  1344.         IF m.colonpos > 0
  1345.             m.namelen  = LEN(m.filename) - m.colonpos
  1346.             m.filename = RIGHT(m.filename, m.namelen)
  1347.         ENDIF
  1348.     ENDIF
  1349.     RETURN m.filename
  1350.  
  1351. *
  1352. * BASENAME - returns strippath(stripext(filespec))
  1353. *
  1354. FUNCTION basename
  1355.     PARAMETER m.filespec
  1356.     RETURN strippath(stripext(m.filespec))
  1357.  
  1358. *
  1359. * GENINSERTCODE - Emit code from the #insert file, if any
  1360. *
  1361. PROCEDURE GenInsertCode
  1362. PARAMETER strg
  1363. PRIVATE m.word1, m.filname, m.ins_fp, m.buffer
  1364.  
  1365. IF UPPER(LEFT(LTRIM(m.strg),5)) == "#INSE"
  1366.    m.word1 = wordnum(m.strg,1)
  1367.    m.filname = SUBSTR(m.strg,LEN(m.word1)+1)
  1368.    m.filname = ALLTRIM(CHRTRAN(m.filname,CHR(9),""))
  1369.  
  1370.    * Bail out if we can't find the file either explicitly or on the DOS path
  1371.    IF !FILE(m.filname)
  1372.       filname = FULLPATH(m.filname,1)
  1373.       IF !FILE(m.filname)
  1374.          \*Insert file <<m.filname>> could not be found
  1375.          RETURN
  1376.       ENDIF
  1377.    ENDIF
  1378.  
  1379.    ins_fp = FOPEN(m.filname)
  1380.    IF ins_fp > 0
  1381.       \* Inserted from <<strippath(m.filname)>>
  1382.       DO WHILE !feof(ins_fp)
  1383.          m.buffer = fgets(ins_fp)
  1384.          \<<m.buffer>>
  1385.       ENDDO
  1386.       =fclose(m.ins_fp)
  1387.       \* End of inserted lines
  1388.    ENDIF
  1389. ENDIF
  1390. *!*****************************************************************************
  1391. *!
  1392. *!       Function: JUSTPATH
  1393. *!
  1394. *!      Called by: FORCEEXT()         (function  in GENSCRN.PRG)
  1395. *!
  1396. *!*****************************************************************************
  1397. FUNCTION justpath
  1398. * Return just the path name from "filname"
  1399. PARAMETERS m.filname
  1400. PRIVATE ALL
  1401. m.filname = ALLTRIM(UPPER(m.filname))
  1402. IF '\' $ m.filname
  1403.    m.filname = SUBSTR(m.filname,1,RAT('\',m.filname))
  1404.    IF RIGHT(m.filname,1) = '\' AND LEN(m.filname) > 1 ;
  1405.             AND SUBSTR(m.filname,LEN(m.filname)-1,1) <> ':'
  1406.          filname = SUBSTR(m.filname,1,LEN(m.filname)-1)
  1407.    ENDIF
  1408.    RETURN m.filname
  1409. ELSE
  1410.    RETURN ''
  1411. ENDIF
  1412.  
  1413. **
  1414. ** Code Associated with the Thermometer
  1415. **
  1416.  
  1417. *
  1418. * ACTTHERM(<text>) - Activate thermometer.
  1419. *
  1420. * Description:
  1421. * Activates thermometer.  Update the thermometer with UPDTHERM().
  1422. * Thermometer window is named "thermometer."  Be sure to RELEASE
  1423. * this window when done with thermometer.  Creates the global
  1424. * m.g_thermwidth.
  1425. *
  1426. PROCEDURE acttherm
  1427.     PARAMETER m.text
  1428.     PRIVATE m.prompt
  1429.  
  1430.     IF m.g_graphic
  1431.         m.prompt = m.g_outfile
  1432.           m.prompt = thermfname(m.prompt)
  1433.  
  1434.         DO CASE
  1435.         CASE _WINDOWS
  1436.            DEFINE WINDOW thermomete ;
  1437.               AT  INT((SROW() - (( 5.615 * ;
  1438.               FONTMETRIC(1, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1439.               FONTMETRIC(1, WFONT(1,""), WFONT( 2,""), WFONT(3,"")))) / 2), ;
  1440.               INT((SCOL() - (( 63.833 * ;
  1441.               FONTMETRIC(6, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1442.               FONTMETRIC(6, WFONT(1,""), WFONT( 2,""), WFONT(3,"")))) / 2) ;
  1443.               SIZE 5.615,63.833 ;
  1444.               FONT m.g_dlgface, m.g_dlgsize ;
  1445.               STYLE m.g_dlgstyle ;
  1446.               NOFLOAT ;
  1447.               NOCLOSE ;
  1448.               NONE ;
  1449.               COLOR RGB(0, 0, 0, 192, 192, 192)
  1450.            MOVE WINDOW thermomete CENTER
  1451.            ACTIVATE WINDOW thermomete NOSHOW
  1452.            @ 0.5,3 SAY m.text FONT m.g_dlgface, m.g_dlgsize STYLE m.g_dlgstyle
  1453.            @ 1.5,3 SAY m.prompt FONT m.g_dlgface, m.g_dlgsize STYLE m.g_dlgstyle
  1454.            @ 0.000,0.000 TO 0.000,63.833 ;
  1455.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1456.            @ 0.000,0.000 TO 5.615,0.000 ;
  1457.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1458.            @ 0.385,0.667 TO 5.231,0.667 ;
  1459.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1460.            @ 0.308,0.667 TO 0.308,63.167 ;
  1461.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1462.            @ 0.385,63.000 TO 5.308,63.000 ;
  1463.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1464.            @ 5.231,0.667 TO 5.231,63.167 ;
  1465.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1466.            @ 5.538,0.000 TO 5.538,63.833 ;
  1467.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1468.            @ 0.000,63.667 TO 5.615,63.667 ;
  1469.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1470.            @ 3.000,3.333 TO 4.231,3.333 ;
  1471.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1472.            @ 3.000,60.333 TO 4.308,60.333 ;
  1473.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1474.            @ 3.000,3.333 TO 3.000,60.333 ;
  1475.               COLOR RGB(128, 128, 128, 128, 128, 128)
  1476.            @ 4.231,3.333 TO 4.231,60.333 ;
  1477.               COLOR RGB(255, 255, 255, 255, 255, 255)
  1478.            m.g_thermwidth = 56.269
  1479.         CASE _MAC
  1480.            DEFINE WINDOW thermomete ;
  1481.               AT  INT((SROW() - (( 5.62 * ;
  1482.               FONTMETRIC(1, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1483.               FONTMETRIC(1, WFONT(1,""), WFONT( 2,""), WFONT(3,"")))) / 2), ;
  1484.               INT((SCOL() - (( 63.83 * ;
  1485.               FONTMETRIC(6, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1486.               FONTMETRIC(6, WFONT(1,""), WFONT( 2,""), WFONT(3,"")))) / 2) ;
  1487.               SIZE 5.62,63.83 ;
  1488.               FONT m.g_dlgface, m.g_dlgsize ;
  1489.               STYLE m.g_dlgstyle ;
  1490.               NOFLOAT ;
  1491.               NOCLOSE ;
  1492.                   NONE ;
  1493.               COLOR RGB(0, 0, 0, 192, 192, 192)
  1494.            MOVE WINDOW thermomete CENTER
  1495.            ACTIVATE WINDOW thermomete NOSHOW
  1496.            @ 0.000,0.000 TO 5.62,63.83 PATTERN 1;
  1497.               COLOR RGB(192, 192, 192, 192, 192, 192)
  1498.               IF ISCOLOR()
  1499.               @ 0.000,0.000 TO 5.62,63.83 PATTERN 1;
  1500.                  COLOR RGB(192, 192, 192, 192, 192, 192)
  1501.               @ 0.000,0.000 TO 0.000,63.83 ;
  1502.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1503.               @ 0.000,0.000 TO 5.62,0.000 ;
  1504.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1505.               @ 0.385,0.67 TO 5.23,0.67 ;
  1506.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1507.               @ 0.31,0.67 TO 0.31,63.17 ;
  1508.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1509.               @ 0.385,63.000 TO 5.31,63.000 ;
  1510.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1511.               @ 5.23,0.67 TO 5.23,63.17 ;
  1512.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1513.               @ 5.54,0.000 TO 5.54,63.83 ;
  1514.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1515.               @ 0.000,63.67 TO 5.62,63.67 ;
  1516.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1517.               @ 3.000,3.33 TO 4.23,3.33 ;
  1518.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1519.               @ 3.000,60.33 TO 4.31,60.33 ;
  1520.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1521.               @ 3.000,3.33 TO 3.000,60.33 ;
  1522.                  COLOR RGB(128, 128, 128, 128, 128, 128)
  1523.               @ 4.23,3.33 TO 4.23,60.33 ;
  1524.                  COLOR RGB(255, 255, 255, 255, 255, 255)
  1525.               ELSE
  1526.               @ 0.000, 0.000 TO 5.62, 63.830  PEN 2
  1527.                @ 0.230, 0.500 TO 5.39, 63.333  PEN 1
  1528.               ENDIF
  1529.            @ 0.5,3 SAY m.text FONT m.g_dlgface, m.g_dlgsize STYLE m.g_dlgstyle+"T" ;
  1530.               COLOR RGB(0,0,0,192,192,192)
  1531.            @ 1.5,3 SAY m.prompt FONT m.g_dlgface, m.g_dlgsize STYLE m.g_dlgstyle+"T" ;
  1532.               COLOR RGB(0,0,0,192,192,192)
  1533.  
  1534.                 m.g_thermwidth = 56.27
  1535.                 IF !ISCOLOR()
  1536.                    @ 3.000,3.33 TO 4.23, (m.g_thermwidth + 1) + 3.33
  1537.                 ENDIF
  1538.         ENDCASE
  1539.         SHOW WINDOW thermomete TOP
  1540.     ELSE
  1541.         m.prompt = SUBSTR(SYS(2014,UPPER(m.g_outfile)),1,48)+;
  1542.             IIF(LEN(m.g_outfile)>48,"...","")
  1543.  
  1544.         DEFINE WINDOW thermomete;
  1545.             FROM INT((SROW()-6)/2), INT((SCOL()-57)/2) ;
  1546.             TO INT((SROW()-6)/2) + 6, INT((SCOL()-57)/2) + 57;
  1547.             DOUBLE COLOR SCHEME 5
  1548.  
  1549.         ACTIVATE WINDOW thermomete NOSHOW
  1550.  
  1551.         m.g_thermwidth = 50
  1552.         @ 0,3 SAY m.text
  1553.         @ 1,3 SAY UPPER(m.prompt)
  1554.         @ 2,1 TO 4,m.g_thermwidth+4 &g_boxstrg
  1555.  
  1556.         SHOW WINDOW thermomete TOP
  1557.     ENDIF
  1558.  
  1559. *
  1560. * UPDTHERM(<percent>) - Update thermometer.
  1561. *
  1562. PROCEDURE updtherm
  1563. PARAMETER m.percent
  1564. PRIVATE m.nblocks, m.percent
  1565. ACTIVATE WINDOW thermomete
  1566. m.nblocks = (m.percent/100) * (m.g_thermwidth)
  1567. DO CASE
  1568. CASE _WINDOWS
  1569.    @ 3.000,3.333 TO 4.231,m.nblocks + 3.333 ;
  1570.       PATTERN 1 COLOR RGB(128, 128, 128, 128, 128, 128)
  1571. CASE _MAC
  1572.    @ 3.000,3.33 TO 4.23,m.nblocks + 3.33 ;
  1573.       PATTERN 1 COLOR RGB(0, 0, 128, 0, 0, 128)
  1574. OTHERWISE
  1575.    @ 3,3 SAY REPLICATE("█",m.nblocks)
  1576. ENDCASE
  1577.  
  1578. *
  1579. * DEACTTHERMO - Deactivate and Release thermometer window.
  1580. *
  1581. PROCEDURE deactthermo
  1582.     RELEASE WINDOW thermomete
  1583.  
  1584.  
  1585. *!*****************************************************************************
  1586. *!
  1587. *!      Procedure: THERMFNAME
  1588. *!
  1589. *!*****************************************************************************
  1590. FUNCTION thermfname
  1591. PARAMETER m.fname
  1592. PRIVATE m.addelipse, m.g_pathsep, m.g_thermfface, m.g_thermfsize, m.g_thermfstyle
  1593.  
  1594. #define c_space 40
  1595. IF _MAC
  1596.     m.g_thermfface = "Geneva"
  1597.     m.g_thermfsize = 10
  1598.     m.g_thermfstyle = "B"
  1599. ELSE
  1600.     m.g_thermfface = "MS Sans Serif"
  1601.     m.g_thermfsize = 8
  1602.     m.g_thermfstyle = "B"
  1603. ENDIF
  1604.  
  1605. * Translate the filename into Mac native format
  1606. IF _MAC
  1607.     m.g_pathsep = ":"
  1608.     m.fname = LOWER(SYS(2027, m.fname))
  1609. ELSE
  1610.     m.g_pathsep = "\"
  1611. ENDIF
  1612.  
  1613. IF TXTWIDTH(m.fname,m.g_thermfface,m.g_thermfsize,m.g_thermfstyle) > c_space
  1614.     * Make it fit in c_space
  1615.     m.fname = partialfname(m.fname, c_space - 1)
  1616.     m.addelipse = .F.
  1617.     DO WHILE TXTWIDTH(m.fname+'...',m.g_thermfface,m.g_thermfsize,m.g_thermfstyle) > c_space
  1618.         m.fname = LEFT(m.fname, LEN(m.fname) - 1)
  1619.         m.addelipse = .T.
  1620.     ENDDO
  1621.     IF m.addelipse
  1622.         m.fname = m.fname + "..."
  1623.    ENDIF
  1624. ENDIF
  1625. RETURN m.fname
  1626.  
  1627.  
  1628.  
  1629. *!*****************************************************************************
  1630. *!
  1631. *!      Procedure: PARTIALFNAME
  1632. *!
  1633. *!*****************************************************************************
  1634. FUNCTION partialfname
  1635. PARAMETER m.filname, m.fillen
  1636. * Return a filname no longer than m.fillen characters.  Take some chars
  1637. * out of the middle if necessary.  No matter what m.fillen is, this function
  1638. * always returns at least the file stem and extension.
  1639. PRIVATE m.bname, m.elipse, m.remain
  1640. m.elipse = "..." + m.g_pathsep
  1641. IF _MAC
  1642.     m.bname = SUBSTR(m.filname, RAT(":",m.filname)+1)
  1643. ELSE
  1644.     m.bname = justfname(m.filname)
  1645. ENDIF
  1646. DO CASE
  1647. CASE LEN(m.filname) <= m.fillen
  1648.    m.retstr = m.filname
  1649. CASE LEN(m.bname) + LEN(m.elipse) >= m.fillen
  1650.    m.retstr = m.bname
  1651. OTHERWISE
  1652.    m.remain = MAX(m.fillen - LEN(m.bname) - LEN(m.elipse), 0)
  1653.    IF _MAC
  1654.        m.retstr = LEFT(SUBSTR(m.filname,1,RAT(":",m.filname)-1),m.remain) ;
  1655.             +m.elipse+m.bname
  1656.    ELSE
  1657.          m.retstr = LEFT(justpath(m.filname),m.remain)+m.elipse+m.bname
  1658.    ENDIF
  1659. ENDCASE
  1660. RETURN m.retstr
  1661.  
  1662. **
  1663. ** Error Handling Code
  1664. **
  1665.  
  1666. *
  1667. * ERRORHANDLER - Error Processing Center.
  1668. *
  1669. PROCEDURE errorhandler
  1670.     PARAMETERS m.messg, m.lineno, m.code
  1671.     IF ERROR() = 22
  1672.         ON ERROR &onerror
  1673.         DO cleanup
  1674.         CANCEL
  1675.     ENDIF
  1676.  
  1677.     DO CASE
  1678.         CASE m.code == "Minor"
  1679.             DO errlog WITH m.messg, m.lineno
  1680.             m.g_status = 1
  1681.         CASE m.code == "Serious"
  1682.             DO errlog  WITH m.messg, m.lineno
  1683.             DO errshow WITH m.messg, m.lineno
  1684.             m.g_error = .T.
  1685.             m.g_status = 2
  1686.             ON ERROR
  1687.         CASE m.code == "Fatal"
  1688.             IF NOT m.g_nohandle
  1689.                 DO errlog  WITH m.messg, m.lineno
  1690.             ENDIF
  1691.             DO errshow WITH m.messg, m.lineno
  1692.             IF WEXIST("Thermomete") AND WVISIBLE("Thermomete")
  1693.                 RELEASE WINDOW thermometer
  1694.             ENDIF
  1695.             ON ERROR
  1696.             DO cleanup
  1697.             CANCEL
  1698.     ENDCASE
  1699.  
  1700. *
  1701. * ESCHANDLER - Escape handler.
  1702. *
  1703. PROCEDURE eschandler
  1704.     ON ERROR
  1705.     WAIT WINDOW "Generation process stopped." NOWAIT
  1706.     DO builddisable
  1707.     IF m.g_status > 0
  1708.         ERASE (m.g_outfile)
  1709.     ENDIF
  1710.     IF WEXIST("Thermomete") AND WVISIBLE("Thermomete")
  1711.         RELEASE WINDOW thermometer
  1712.     ENDIF
  1713.     DO cleanup
  1714.     CANCEL
  1715.  
  1716. *
  1717. * ERRLOG - Insert error message into the error log.
  1718. *
  1719. PROCEDURE errlog
  1720.     PARAMETER m.messg, m.lineno
  1721.     PRIVATE m.savehandle
  1722.     m.savehandle = _TEXT
  1723.     DO openerrfile
  1724.     SET CONSOLE OFF
  1725.  
  1726.     \\GENERATOR: <<ALLTRIM(m.messg)>>
  1727.     IF NOT EMPTY(m.lineno)
  1728.         \\ LINE NUMBER: <<m.lineno>>
  1729.     ENDIF
  1730.     \
  1731.     = FCLOSE(_TEXT)
  1732.     _TEXT = m.savehandle
  1733.  
  1734. *
  1735. * ERRSHOW - Display error message in the alert box.
  1736. *
  1737. PROCEDURE errshow
  1738.     PARAMETER m.msg, m.lineno
  1739.     PRIVATE m.curcursor
  1740.  
  1741.     IF m.g_graphic
  1742.         DEFINE WINDOW alert ;
  1743.             AT  INT((SROW() - (( 5.615 * ;
  1744.             fontmetric(1, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1745.             fontmetric(1, wfont(1,""), wfont(2,""), wfont(3,"")))) / 2), ;
  1746.             INT((SCOL() - (( 63.833 * ;
  1747.             fontmetric(6, m.g_dlgface, m.g_dlgsize, m.g_dlgstyle )) / ;
  1748.             fontmetric(6, wfont(1,""), wfont(2,""), wfont(3,"")))) / 2) ;
  1749.             SIZE 5.615,63.833 ;
  1750.             font m.g_dlgface, m.g_dlgsize ;
  1751.             STYLE m.g_dlgstyle ;
  1752.             NOCLOSE ;
  1753.             DOUBLE ;
  1754.             TITLE "Genmenu Error" ;
  1755.             COLOR rgb(0, 0, 0, 255, 255, 255)
  1756.  
  1757.         ACTIVATE WINDOW alert NOSHOW
  1758.  
  1759.         m.msg = SUBSTR(m.msg,1,44)+IIF(LEN(m.msg)>44,"...","")
  1760.         @ 1,(WCOLS()-txtwidth( m.msg ))/2 SAY m.msg
  1761.  
  1762.         m.msg = "Line Number: "+STR(m.lineno, 4)
  1763.         @ 2,(WCOLS()-txtwidth( m.msg ))/2 SAY m.msg
  1764.  
  1765.         m.msg = "Press any key to cleanup and exit..."
  1766.         @ 3,(WCOLS()-txtwidth( m.msg ))/2 SAY m.msg
  1767.  
  1768.         SHOW WINDOW alert
  1769.     ELSE
  1770.         DEFINE WINDOW alert;
  1771.             FROM INT((SROW()-6)/2), INT((SCOL()-50)/2) TO INT((SROW()-6)/2) + 6, INT((SCOL()-50)/2) + 50 ;
  1772.             FLOAT NOGROW NOCLOSE NOZOOM    SHADOW DOUBLE;
  1773.             COLOR SCHEME 7
  1774.  
  1775.         ACTIVATE WINDOW alert
  1776.  
  1777.         @ 0,0 CLEAR
  1778.         @ 1,0 SAY PADC(SUBSTR(m.msg,1,44)+;
  1779.             IIF(LEN(m.msg)>44,"...",""), WCOLS())
  1780.         @ 2,0 SAY PADC("Line Number: "+STR(m.lineno, 4), WCOLS())
  1781.         @ 3,0 SAY PADC("Press any key to cleanup and exit...", WCOLS())
  1782.     ENDIF
  1783.  
  1784.     m.curcursor = SET( "CURSOR" )
  1785.     SET CURSOR OFF
  1786.  
  1787.     WAIT ""
  1788.  
  1789.     RELEASE WINDOW alert
  1790.     SET CURSOR &curcursor
  1791.  
  1792.     RELEASE WINDOW alert
  1793.  
  1794. *
  1795. * OPENERRFILE - Open error file.
  1796. *
  1797. PROCEDURE openerrfile
  1798.     PRIVATE m.errfile, m.errhandle
  1799.     m.errfile   = m.g_errlog+".ERR"
  1800.     m.errhandle = FOPEN(m.errfile,2)
  1801.     IF m.errhandle < 0
  1802.         m.errhandle = FCREATE(m.errfile)
  1803.         IF m.errhandle < 0
  1804.             DO errshow WITH ".ERR could not be opened...", LINENO()
  1805.             m.g_status = 2
  1806.             IF WEXIST("Thermomete") AND WVISIBLE("Thermomete")
  1807.                 RELEASE WINDOW thermometer
  1808.             ENDIF
  1809.             ON ERROR
  1810.             RETURN TO MASTER
  1811.         ENDIF
  1812.     ELSE
  1813.         = FSEEK(m.errhandle,0,2)
  1814.     ENDIF
  1815.     IF SET("TEXTMERGE") = "OFF"
  1816.         SET TEXTMERGE ON
  1817.     ENDIF
  1818.     _TEXT = m.errhandle
  1819.  
  1820. *
  1821. * GETCNAME - Manufacture a procedure name, unless there is a #NAME directive
  1822. *
  1823. FUNCTION getcname
  1824. PARAMETERS snippet
  1825. PRIVATE ALL
  1826. IF proctype = 1
  1827.    numlines = MEMLINES(snippet)
  1828.    IF m.numlines > 0
  1829.       _MLINE = 0
  1830.       m.i = 1
  1831.       DO WHILE m.i <= m.numlines
  1832.          m.thisline = UPPER(ALLTRIM(MLINE(snippet,1, _MLINE)))
  1833.          DO CASE
  1834.          CASE LEFT(m.thisline,5) == "#NAME"
  1835.             RETURN ALLTRIM(SUBSTR(m.thisline,6))
  1836.          CASE EMPTY(m.thisline) OR iscomment(m.thisline)
  1837.             * Do nothing.  Get next line.
  1838.          OTHERWISE
  1839.             EXIT
  1840.          ENDCASE
  1841.          m.i = m.i + 1
  1842.       ENDDO
  1843.    ENDIF
  1844. ENDIF
  1845. RETURN LOWER(SYS(2015))
  1846.  
  1847. *
  1848. * ISCOMMENT - Determine if textline is a comment line.
  1849. *
  1850. FUNCTION IsComment
  1851. PARAMETER m.textline
  1852. PRIVATE m.asterisk, m.isnote, m.ampersand, m.statement
  1853. IF EMPTY(m.textline)
  1854.    RETURN .F.
  1855. ENDIF
  1856. m.statement = UPPER(ALLTRIM(m.textline))
  1857.  
  1858. m.asterisk  = AT("*", LEFT(m.statement,1))
  1859. m.ampersand = AT(CHR(38)+CHR(38), LEFT(m.statement,2))
  1860. m.isnote    = AT("NOTE", LEFT(m.statement,4))
  1861.  
  1862. DO CASE
  1863. CASE (m.asterisk = 1 OR m.ampersand = 1)
  1864.    RETURN .T.
  1865. CASE (m.isnote = 1 ;
  1866.         AND (LEN(m.statement) <= 4 OR SUBSTR(m.statement,5,1) = ' '))
  1867.    * Don't be fooled by something like "notebook = 7"
  1868.    RETURN .T.
  1869. ENDCASE
  1870. RETURN .F.
  1871. *
  1872. * WORDNUM - Returns w_num-th word from string strg
  1873. *
  1874. FUNCTION wordnum
  1875. PARAMETERS strg,w_num
  1876. PRIVATE strg,s1,w_num,ret_str
  1877.  
  1878. m.s1 = ALLTRIM(m.strg)
  1879.  
  1880. * Replace tabs with spaces
  1881. m.s1 = CHRTRAN(m.s1,CHR(9)," ")
  1882.  
  1883. * Reduce multiple spaces to a single space
  1884. DO WHILE AT('  ',m.s1) > 0
  1885.    m.s1 = STRTRAN(m.s1,'  ',' ')
  1886. ENDDO
  1887.  
  1888. ret_str = ""
  1889. DO CASE
  1890. CASE m.w_num > 1
  1891.    DO CASE
  1892.    CASE AT(" ",m.s1,m.w_num-1) = 0   && No word w_num.  Past end of string.
  1893.       m.ret_str = ""
  1894.    CASE AT(" ",m.s1,m.w_num) = 0     && Word w_num is last word in string.
  1895.       m.ret_str = SUBSTR(m.s1,AT(" ",m.s1,m.w_num-1)+1,255)
  1896.    OTHERWISE                         && Word w_num is in the middle.
  1897.       m.strt_pos = AT(" ",m.s1,m.w_num-1)
  1898.       m.ret_str  = SUBSTR(m.s1,strt_pos,AT(" ",m.s1,m.w_num)+1 - strt_pos)
  1899.    ENDCASE
  1900. CASE m.w_num = 1
  1901.    IF AT(" ",m.s1) > 0               && Get first word.
  1902.       m.ret_str = SUBSTR(m.s1,1,AT(" ",m.s1)-1)
  1903.    ELSE                              && There is only one word.  Get it.
  1904.       m.ret_str = m.s1
  1905.    ENDIF
  1906. ENDCASE
  1907. RETURN ALLTRIM(m.ret_str)
  1908. *!*****************************************************************************
  1909. *!
  1910. *!      Function: VERSNUM
  1911. *!
  1912. *!*****************************************************************************
  1913. FUNCTION versnum
  1914. * Return string corresponding to FoxPro version number
  1915. RETURN wordnum(vers(),2)
  1916.  
  1917. *!*****************************************************************************
  1918. *!
  1919. *!       Function: JUSTFNAME
  1920. *!
  1921. *!      Called by: FORCEEXT()         (function  in GENSCRN.PRG)
  1922. *!
  1923. *!*****************************************************************************
  1924. FUNCTION justfname
  1925. PARAMETERS m.filname
  1926. PRIVATE ALL
  1927. IF RAT('\',m.filname) > 0
  1928.    m.filname = SUBSTR(m.filname,RAT('\',m.filname)+1,255)
  1929. ENDIF
  1930. IF AT(':',m.filname) > 0
  1931.    m.filname = SUBSTR(m.filname,AT(':',m.filname)+1,255)
  1932. ENDIF
  1933. RETURN ALLTRIM(UPPER(m.filname))
  1934.  
  1935.