home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / POV-Ray 3.0.2 / src / MacSource / TemplateMenu.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-14  |  13.0 KB  |  473 lines  |  [TEXT/CWIE]

  1. /*
  2. ==============================================================================
  3. Project:    POV-Ray
  4.  
  5. Version:    3
  6.  
  7. File Name:    TemplateMenu.c
  8.  
  9. Description:
  10.     Template text insertion menus for POV-Ray.  Reads a specially formatted
  11.     text file, and adds a hierarchy of menus into the menu bar.  When the
  12.     user chooses a menu item, the corresponding text (read from the file)
  13.     is inserted into the scene file at the insertion point.
  14.  
  15.     This is the main source file, containing the private definitions and
  16.     code to implement all the needed external and internal support functions.
  17.  
  18. Related Files:
  19.     TemplateMenu.h    - Header for these routines
  20. ------------------------------------------------------------------------------
  21. Author:
  22.     Eduard [esp] Schwan
  23. ------------------------------------------------------------------------------
  24.     from Persistence of Vision(tm) Ray Tracer
  25.     Copyright 1996 Persistence of Vision Team
  26. ------------------------------------------------------------------------------
  27.     NOTICE: This source code file is provided so that users may experiment
  28.     with enhancements to POV-Ray and to port the software to platforms other 
  29.     than those supported by the POV-Ray Team.  There are strict rules under
  30.     which you are permitted to use this file.  The rules are in the file
  31.     named POVLEGAL.DOC which should be distributed with this file. If 
  32.     POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  33.     Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  34.     Forum.  The latest version of POV-Ray may be found there as well.
  35.  
  36.     This program is based on the popular DKB raytracer version 2.12.
  37.     DKBTrace was originally written by David K. Buck.
  38.     DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  39. ------------------------------------------------------------------------------
  40. Change History:
  41.     920901    [esp]    Created
  42.     920906    [esp]    Added auto-scroll to new insertion point
  43.     921226    [esp]    More error checking (& returning) in InitTemplateMenu
  44.     931001    [esp]    version 2.0 finished (Released on 10/4/93)
  45.     931001    [esp]    Added code to delete current selection upon insert of new template
  46.     950901    [esp]    Totally rewrote to read & dynamically build menus from a text file
  47.     951007    [esp]    Checked for raw text at beginning of file without valid command.
  48.     960522    [esp]    Moved SetCurrentDirToAppDir() out to pov.c for others to use
  49. ==============================================================================
  50. */
  51.  
  52. /*==== POV-Ray std headers ====*/
  53. #include "TemplateMenu.h"
  54. #include "LnkLst.h"            // for LL_ stuff
  55. #include "AppPrefs.h"        // for kSTRI_FileNames, kSTRIi_Templates
  56. #include "TextEditor.h"
  57. #include "POVMacProto.h"    // for SetCurrentDirToAppDir()
  58.  
  59.  
  60. /*==== Macintosh-specific headers ====*/
  61. #include <errors.h>
  62. #include <resources.h>
  63. #include <memory.h>
  64. #include <menus.h>
  65. #include <processes.h>
  66. #include <ctype.h> // tolower
  67.  
  68.  
  69. #define    STARTING_MENU_ID        150        // submenu to start at
  70. #define    MAX_TEMPLATE_LEVELS        3        // Max nested menu levels
  71. #define    MAX_TEMPLATES_PER_MENU    200        // Max items in one menu
  72. #define TEMPLATE_CMD_CHAR        '@'        // char used to flag a command line
  73.  
  74.  
  75. /*==== global variables (global scope) ====*/
  76.  
  77.  
  78. /*==== global variables (local scope) ====*/
  79.  
  80. // The menu-level placeholder
  81. typedef struct
  82. {
  83.     short    menuID;
  84.     short    menuItem;
  85. } MenuLevelRec_t, *MenuLevelPtr_t;
  86.  
  87. // The linked-list records of text
  88. typedef struct
  89. {
  90.     short    menuID;
  91.     short    menuItem;
  92.     short    menuLevel;
  93.     Handle    textH;
  94. } TemplateQRec_t, *TemplateQPtr_t;
  95.  
  96.  
  97. static Boolean        gTemplateImported;
  98. static LLHeadPtr    gTemplateQueue;
  99.  
  100. static MenuHandle    gMainTemplateMenuH;    // main template menu
  101. // track template menu allocation
  102. static short        gCurrMenuID;
  103. static short        gCurrMenuLevel;
  104. static MenuLevelRec_t    gMenuLevel[MAX_TEMPLATE_LEVELS];
  105.  
  106. static Handle        gCurrTextH;
  107.  
  108. static short        gLineCounter;
  109. static char            *gTemplateFNameRef; // temp reference for error messages
  110.  
  111.  
  112.  
  113. // ==============================================
  114. // initialize all the variables
  115. OSErr InitTemplateMenu(void)
  116. {
  117.     short    anError = noErr;
  118.     gTemplateFNameRef = NULL;
  119.     // Read in the main template menu
  120.     gMainTemplateMenuH = GetMenuHandle(temn_ID);
  121.     anError = ResError();
  122.     if (gMainTemplateMenuH == NULL)
  123.         anError = resNotFound;
  124.     // set up the menu level tracker
  125.     gCurrMenuID = STARTING_MENU_ID-1;
  126.     gCurrMenuLevel = 0; // start out pointing at main template menu
  127.     gMenuLevel[gCurrMenuLevel].menuID    = temn_ID;    // BASE menu!
  128.     gMenuLevel[gCurrMenuLevel].menuItem    = 2;
  129.     // init globals
  130.     gTemplateImported = FALSE;
  131.     gTemplateQueue = NULL;
  132.     return anError;
  133. }
  134.  
  135. // ==============================================
  136. // dispose of all memory storage, prepare for exiting application
  137. void KillTemplateMenu(void)
  138. {
  139.     int        k;
  140.     TemplateQPtr_t    p;
  141.     if (gTemplateQueue)
  142.     {
  143.         // Loop through entire list, getting each of OUR elements and disposing
  144.         for (k=0; k<LLGetNumElements(gTemplateQueue); k++)
  145.             {
  146.             // find k'th data record
  147.             p = (TemplateQPtr_t)LLGetElementByIndex(gTemplateQueue, k);
  148.             // if found, delete it (data attached to element, not the list element itself)
  149.             if (p)
  150.                 DisposePtr((Ptr)p);
  151.             }
  152.         // now destroy the linked list elements, and the list itself
  153.         LLDestroyList(gTemplateQueue);
  154.     }
  155.     gTemplateQueue = NULL;
  156. }
  157.  
  158. // ==============================================
  159. // tell caller whether the templates are read, useful for AdjustMenus call
  160. Boolean TemplatesImported(void)
  161. {
  162.     return gTemplateImported;
  163. }
  164.  
  165.  
  166. // ==============================================
  167. // Add a template menu item text record to the list
  168. static void TemplateQ_Put(short menuID, short menuItem, short menuLevel, Handle textH)
  169. {
  170.     TemplateQPtr_t    newTptr;
  171.  
  172.     // create new record
  173.     newTptr = (TemplateQPtr_t)NewPtr(sizeof(TemplateQRec_t));
  174.  
  175.     if (newTptr)
  176.     {
  177.         // fill it up
  178.         newTptr->menuID        = menuID;
  179.         newTptr->menuItem    = menuItem;
  180.         newTptr->menuLevel    = menuLevel;
  181.         newTptr->textH        = textH;
  182.     
  183.         // append it to the list
  184.         LLAppendElement(gTemplateQueue, newTptr);
  185.     }
  186. }
  187.  
  188.  
  189.  
  190. // ==============================================
  191. // Open the template text file for reading
  192. static FILE * OpenTemplateFile(void)
  193. {
  194.     OSErr        anError = noErr;
  195.     short    saveVRefNum;
  196.     long    saveDirID;
  197.     FILE *        aFile;
  198. static    char    fileName[32];    // persistent, we point to it for later error reporting
  199.  
  200.     // fill in the template file name from resource
  201.     GetIndString((StringPtr)fileName, kSTRI_FileNames, kSTRIi_Templates);
  202.     anError = ResError();
  203.     p2cstr((StringPtr)fileName);
  204.     gTemplateFNameRef = fileName; // remember it for later error reporting
  205.  
  206.     // remember current folder, since we're about to change it
  207.     anError = HGetVol((StringPtr)NULL, &saveVRefNum, &saveDirID);
  208.  
  209.     // set current directory to app directory
  210.     SetCurrentDirToAppDir();
  211.  
  212.     // open it
  213.     aFile = fopen(fileName, "r");
  214.  
  215.     // reset current folder
  216.     anError = HSetVol((StringPtr)NULL, saveVRefNum, saveDirID);
  217.  
  218.     return aFile;
  219. }
  220.  
  221.  
  222. // ==============================================
  223. // Close the template text file
  224. static void CloseTemplateFile(FILE * aFile)
  225. {
  226.     if (aFile)
  227.         fclose(aFile);
  228. }
  229.  
  230. // ==============================================
  231. // Close the template text file
  232. static void ShowTemplateError(char * errorText)
  233. {
  234.     printf("### Error in Template file '%s', line %d\n",
  235.             gTemplateFNameRef, gLineCounter);
  236.     printf("### -- %s\n", errorText);
  237. }
  238.  
  239.  
  240. // ==============================================
  241. // Handle a template MENU-COMMAND text line
  242. static Boolean DoTCMenu(char * aLine)
  243. {
  244.     short        newMenuLevel;
  245.     MenuHandle    theMenuH;
  246.     MenuHandle    theParentMenuH;
  247.     char        *menuTitle;
  248.  
  249.     newMenuLevel = (aLine[3] - '0') - 1; // convert ASCII # to number (zero based)
  250.     if ((newMenuLevel < 0) || (newMenuLevel >= MAX_TEMPLATE_LEVELS))
  251.     {
  252.         // ERROR, menu level out of range
  253.         ShowTemplateError("Menu level out of range (1-3)."); // MAX_TEMPLATE_LEVELS
  254.     }
  255.     else if (newMenuLevel > gCurrMenuLevel+1)
  256.     {
  257.         // ERROR, can't increment by more than one
  258.         ShowTemplateError("Cannot increment menu level by more than one.");
  259.     }
  260.     else
  261.     { // should be OK here
  262.  
  263.         // If incrementing, create new sub menu first
  264.         if (newMenuLevel > gCurrMenuLevel)
  265.         {
  266.             // Create new menu
  267.             gCurrMenuID++;
  268.             theMenuH = NewMenu(gCurrMenuID, "\p");
  269.             InsertMenu(theMenuH, -1); // add it to menu list invisibly
  270.             // set up this new menu level
  271.             gMenuLevel[newMenuLevel].menuID   = gCurrMenuID;
  272.             gMenuLevel[newMenuLevel].menuItem = 0; // reset
  273.             // point parent's current menu item to this new submenu
  274.             theParentMenuH = GetMenuHandle(gMenuLevel[gCurrMenuLevel].menuID);
  275.             SetItemCmd(theParentMenuH, gMenuLevel[gCurrMenuLevel].menuItem, 0x1b); // this menu item has a submenu
  276.             SetItemMark(theParentMenuH, gMenuLevel[gCurrMenuLevel].menuItem, gCurrMenuID); // set submenu ID
  277.         }
  278.  
  279.         // now move current menu index to this level (up, down or same)
  280.         gCurrMenuLevel =  newMenuLevel;
  281.  
  282.         // just add a new menu item to this current menu
  283.         theMenuH = GetMenuHandle(gMenuLevel[gCurrMenuLevel].menuID);
  284.         menuTitle = &aLine[5]; // point to start of menu title on line
  285.         c2pstr(menuTitle); // convert it to a Pascal String (in place, ugh)
  286.         AppendMenu(theMenuH, (StringPtr)menuTitle); // add item
  287.         gMenuLevel[gCurrMenuLevel].menuItem++; // increment item count
  288.     }
  289.     return TRUE;
  290. }
  291.  
  292.  
  293. // ==============================================
  294. // Handle any COMMAND text line
  295. static Boolean DoTemplateCommand(char * aLine)
  296. {
  297.     Boolean    retVal;
  298.     short    lineLen = strlen(aLine);
  299.  
  300.     // extract command and info, case insensitive
  301.     switch (tolower(aLine[1]))
  302.     {
  303.         case 'c': // @COM (Comment, ignored)
  304.             retVal = FALSE;
  305.             break;
  306.  
  307.         case 'm': // @mt#.title (menu title)
  308.             if (lineLen < 6)
  309.             { // line too short
  310.             ShowTemplateError("Line too short");
  311.             }
  312.             else
  313.             { // parse the command
  314.                 DoTCMenu(aLine);
  315.             }
  316.             retVal = FALSE;
  317.             break;
  318.  
  319.         case '@': // @@@@ (eof)
  320.             retVal = TRUE;
  321.             break;
  322.  
  323.         default:
  324.             ShowTemplateError("Unknown command in file.");
  325.             retVal = TRUE;
  326.             break;
  327.     }
  328.     return retVal;
  329. }
  330.  
  331.  
  332. // ==============================================
  333. // Handle a TEXT line
  334. static void DoTemplateLine(char * aLine, Boolean doNewLine)
  335. {
  336.     long    currLineSize;
  337.     long    currTextSize;
  338.  
  339.     if (doNewLine)
  340.     {
  341.         gCurrTextH        = NewHandle(0);
  342.         // add new menu list record
  343.         TemplateQ_Put(
  344.                 gMenuLevel[gCurrMenuLevel].menuID,
  345.                 gMenuLevel[gCurrMenuLevel].menuItem,
  346.                 gCurrMenuLevel,
  347.                 gCurrTextH);
  348.     }
  349.  
  350.     currLineSize = strlen(aLine);
  351.     // add a CR to end of line
  352.     aLine[currLineSize++] = 0x0d;
  353.     aLine[currLineSize] = '\0';
  354.     // add text to existing block
  355.     currTextSize = GetHandleSize(gCurrTextH);
  356.     // increase current buffer
  357.     SetHandleSize(gCurrTextH, currTextSize+currLineSize);
  358.     // lock it down and move data into it, then let it float again
  359.     HLock(gCurrTextH);
  360.     BlockMoveData(aLine, (*gCurrTextH)+currTextSize, currLineSize);
  361.     HUnlock(gCurrTextH);
  362. }
  363.  
  364.  
  365. // ==============================================
  366. // read the lines from the template file and process them
  367. static void ReadTemplateFile(FILE * aFile)
  368. {
  369.     OSErr    anError        = noErr;
  370.     short    lineLen;
  371.     Boolean isEOF        = FALSE;
  372.     Boolean    foundCmd    = FALSE;
  373.     Boolean    doNewLine    = TRUE; // starting a new line after command
  374.     char    aLine[256];
  375.  
  376.     // create a linked list to hold the text info
  377.     gTemplateQueue = LLCreateList(1);
  378.  
  379.     // read the lines
  380.     gLineCounter = 0;
  381.     while (!isEOF && !anError)
  382.     {
  383.         // read next line
  384.         fgets(aLine, 256, aFile);
  385.         gLineCounter++;
  386.         // delete any trailing CR
  387.         lineLen = strlen(aLine);
  388.         if (lineLen > 0)
  389.             if (aLine[lineLen-1] == '\n')
  390.                 aLine[lineLen-1] = '\0';
  391.         // handle line
  392.         if (aLine[0] == TEMPLATE_CMD_CHAR)
  393.         {
  394.             isEOF = DoTemplateCommand(aLine);
  395.             // hit a command, start a new line next time
  396.             doNewLine = TRUE;
  397.             foundCmd = TRUE; // OK to read lines now...
  398.         }
  399.         else
  400.         {
  401.             if (!foundCmd)
  402.             {
  403.                 // ERROR, raw text in first part of file before any commands!
  404.                 ShowTemplateError("File must start with a valid command.");
  405.                 anError = -1; // force an exit
  406.             }
  407.             else
  408.                 DoTemplateLine(aLine, doNewLine);
  409.             // start appending until we get another command
  410.             doNewLine = FALSE;
  411.         }
  412.         // check for real eof
  413.         if (!isEOF)
  414.             isEOF = feof(aFile);
  415.     }
  416.     gTemplateImported = TRUE;
  417. }
  418.  
  419.  
  420. // ==============================================
  421. // Read the template file, parse it, and build the menus from it
  422. void ImportTemplates(void)
  423. {
  424.     FILE *        tFile;
  425.     tFile = OpenTemplateFile();
  426.     if (tFile)
  427.     {
  428.         ReadTemplateFile(tFile);
  429.         CloseTemplateFile(tFile);
  430.     }
  431.     else
  432.         ShowTemplateError("Cannot open Template file.");
  433. }
  434.  
  435. // ==============================================
  436. void HandleTemplateMenu(short theMenuID, short theItem)
  437. {
  438.     short            anError = noErr;
  439.     short            k;
  440.     TemplateQPtr_t    p;
  441.     Handle            theTextHandle;
  442.  
  443.     // The menu ID must be one of the submenus
  444.     if ((theMenuID < STARTING_MENU_ID) || (theMenuID > gCurrMenuID))
  445.         return;
  446.  
  447.     // loop through the records in the list, looking for a menu match
  448.     theTextHandle = NULL;
  449.     // Loop through entire list, getting each of OUR elements and disposing
  450.     for (k=1; (k<=LLGetNumElements(gTemplateQueue)) && !theTextHandle; k++)
  451.     {
  452.         p = (TemplateQPtr_t)LLGetElementByIndex(gTemplateQueue, k);
  453.         if (p)
  454.         {
  455.             if ((theMenuID==p->menuID) && (theItem==p->menuItem))
  456.                 theTextHandle = p->textH;
  457.         }
  458.     }
  459.     if (gSrcWind_visible && theTextHandle)
  460.     {
  461.         HLock(theTextHandle);
  462.         // call text editor fn to insert it
  463.         DoSrcWindTextInsert(&(**theTextHandle), GetHandleSize(theTextHandle));
  464.         HUnlock(theTextHandle);
  465.         gSrcWind_dirty = true;
  466.         // scroll to new insertion point
  467.         ShowSelect();
  468.     }
  469.     else
  470.         SysBeep(1);
  471. }
  472.  
  473.