home *** CD-ROM | disk | FTP | other *** search
- /*
- ==============================================================================
- Project: POV-Ray
-
- Version: 3
-
- File Name: TemplateMenu.c
-
- Description:
- Template text insertion menus for POV-Ray. Reads a specially formatted
- text file, and adds a hierarchy of menus into the menu bar. When the
- user chooses a menu item, the corresponding text (read from the file)
- is inserted into the scene file at the insertion point.
-
- This is the main source file, containing the private definitions and
- code to implement all the needed external and internal support functions.
-
- Related Files:
- TemplateMenu.h - Header for these routines
- ------------------------------------------------------------------------------
- Author:
- Eduard [esp] Schwan
- ------------------------------------------------------------------------------
- from Persistence of Vision(tm) Ray Tracer
- Copyright 1996 Persistence of Vision Team
- ------------------------------------------------------------------------------
- NOTICE: This source code file is provided so that users may experiment
- with enhancements to POV-Ray and to port the software to platforms other
- than those supported by the POV-Ray Team. There are strict rules under
- which you are permitted to use this file. The rules are in the file
- named POVLEGAL.DOC which should be distributed with this file. If
- POVLEGAL.DOC is not available or for more info please contact the POV-Ray
- Team Coordinator by leaving a message in CompuServe's Graphics Developer's
- Forum. The latest version of POV-Ray may be found there as well.
-
- This program is based on the popular DKB raytracer version 2.12.
- DKBTrace was originally written by David K. Buck.
- DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
- ------------------------------------------------------------------------------
- Change History:
- 920901 [esp] Created
- 920906 [esp] Added auto-scroll to new insertion point
- 921226 [esp] More error checking (& returning) in InitTemplateMenu
- 931001 [esp] version 2.0 finished (Released on 10/4/93)
- 931001 [esp] Added code to delete current selection upon insert of new template
- 950901 [esp] Totally rewrote to read & dynamically build menus from a text file
- 951007 [esp] Checked for raw text at beginning of file without valid command.
- 960522 [esp] Moved SetCurrentDirToAppDir() out to pov.c for others to use
- ==============================================================================
- */
-
- /*==== POV-Ray std headers ====*/
- #include "TemplateMenu.h"
- #include "LnkLst.h" // for LL_ stuff
- #include "AppPrefs.h" // for kSTRI_FileNames, kSTRIi_Templates
- #include "TextEditor.h"
- #include "POVMacProto.h" // for SetCurrentDirToAppDir()
-
-
- /*==== Macintosh-specific headers ====*/
- #include <errors.h>
- #include <resources.h>
- #include <memory.h>
- #include <menus.h>
- #include <processes.h>
- #include <ctype.h> // tolower
-
-
- #define STARTING_MENU_ID 150 // submenu to start at
- #define MAX_TEMPLATE_LEVELS 3 // Max nested menu levels
- #define MAX_TEMPLATES_PER_MENU 200 // Max items in one menu
- #define TEMPLATE_CMD_CHAR '@' // char used to flag a command line
-
-
- /*==== global variables (global scope) ====*/
-
-
- /*==== global variables (local scope) ====*/
-
- // The menu-level placeholder
- typedef struct
- {
- short menuID;
- short menuItem;
- } MenuLevelRec_t, *MenuLevelPtr_t;
-
- // The linked-list records of text
- typedef struct
- {
- short menuID;
- short menuItem;
- short menuLevel;
- Handle textH;
- } TemplateQRec_t, *TemplateQPtr_t;
-
-
- static Boolean gTemplateImported;
- static LLHeadPtr gTemplateQueue;
-
- static MenuHandle gMainTemplateMenuH; // main template menu
- // track template menu allocation
- static short gCurrMenuID;
- static short gCurrMenuLevel;
- static MenuLevelRec_t gMenuLevel[MAX_TEMPLATE_LEVELS];
-
- static Handle gCurrTextH;
-
- static short gLineCounter;
- static char *gTemplateFNameRef; // temp reference for error messages
-
-
-
- // ==============================================
- // initialize all the variables
- OSErr InitTemplateMenu(void)
- {
- short anError = noErr;
- gTemplateFNameRef = NULL;
- // Read in the main template menu
- gMainTemplateMenuH = GetMenuHandle(temn_ID);
- anError = ResError();
- if (gMainTemplateMenuH == NULL)
- anError = resNotFound;
- // set up the menu level tracker
- gCurrMenuID = STARTING_MENU_ID-1;
- gCurrMenuLevel = 0; // start out pointing at main template menu
- gMenuLevel[gCurrMenuLevel].menuID = temn_ID; // BASE menu!
- gMenuLevel[gCurrMenuLevel].menuItem = 2;
- // init globals
- gTemplateImported = FALSE;
- gTemplateQueue = NULL;
- return anError;
- }
-
- // ==============================================
- // dispose of all memory storage, prepare for exiting application
- void KillTemplateMenu(void)
- {
- int k;
- TemplateQPtr_t p;
- if (gTemplateQueue)
- {
- // Loop through entire list, getting each of OUR elements and disposing
- for (k=0; k<LLGetNumElements(gTemplateQueue); k++)
- {
- // find k'th data record
- p = (TemplateQPtr_t)LLGetElementByIndex(gTemplateQueue, k);
- // if found, delete it (data attached to element, not the list element itself)
- if (p)
- DisposePtr((Ptr)p);
- }
- // now destroy the linked list elements, and the list itself
- LLDestroyList(gTemplateQueue);
- }
- gTemplateQueue = NULL;
- }
-
- // ==============================================
- // tell caller whether the templates are read, useful for AdjustMenus call
- Boolean TemplatesImported(void)
- {
- return gTemplateImported;
- }
-
-
- // ==============================================
- // Add a template menu item text record to the list
- static void TemplateQ_Put(short menuID, short menuItem, short menuLevel, Handle textH)
- {
- TemplateQPtr_t newTptr;
-
- // create new record
- newTptr = (TemplateQPtr_t)NewPtr(sizeof(TemplateQRec_t));
-
- if (newTptr)
- {
- // fill it up
- newTptr->menuID = menuID;
- newTptr->menuItem = menuItem;
- newTptr->menuLevel = menuLevel;
- newTptr->textH = textH;
-
- // append it to the list
- LLAppendElement(gTemplateQueue, newTptr);
- }
- }
-
-
-
- // ==============================================
- // Open the template text file for reading
- static FILE * OpenTemplateFile(void)
- {
- OSErr anError = noErr;
- short saveVRefNum;
- long saveDirID;
- FILE * aFile;
- static char fileName[32]; // persistent, we point to it for later error reporting
-
- // fill in the template file name from resource
- GetIndString((StringPtr)fileName, kSTRI_FileNames, kSTRIi_Templates);
- anError = ResError();
- p2cstr((StringPtr)fileName);
- gTemplateFNameRef = fileName; // remember it for later error reporting
-
- // remember current folder, since we're about to change it
- anError = HGetVol((StringPtr)NULL, &saveVRefNum, &saveDirID);
-
- // set current directory to app directory
- SetCurrentDirToAppDir();
-
- // open it
- aFile = fopen(fileName, "r");
-
- // reset current folder
- anError = HSetVol((StringPtr)NULL, saveVRefNum, saveDirID);
-
- return aFile;
- }
-
-
- // ==============================================
- // Close the template text file
- static void CloseTemplateFile(FILE * aFile)
- {
- if (aFile)
- fclose(aFile);
- }
-
- // ==============================================
- // Close the template text file
- static void ShowTemplateError(char * errorText)
- {
- printf("### Error in Template file '%s', line %d\n",
- gTemplateFNameRef, gLineCounter);
- printf("### -- %s\n", errorText);
- }
-
-
- // ==============================================
- // Handle a template MENU-COMMAND text line
- static Boolean DoTCMenu(char * aLine)
- {
- short newMenuLevel;
- MenuHandle theMenuH;
- MenuHandle theParentMenuH;
- char *menuTitle;
-
- newMenuLevel = (aLine[3] - '0') - 1; // convert ASCII # to number (zero based)
- if ((newMenuLevel < 0) || (newMenuLevel >= MAX_TEMPLATE_LEVELS))
- {
- // ERROR, menu level out of range
- ShowTemplateError("Menu level out of range (1-3)."); // MAX_TEMPLATE_LEVELS
- }
- else if (newMenuLevel > gCurrMenuLevel+1)
- {
- // ERROR, can't increment by more than one
- ShowTemplateError("Cannot increment menu level by more than one.");
- }
- else
- { // should be OK here
-
- // If incrementing, create new sub menu first
- if (newMenuLevel > gCurrMenuLevel)
- {
- // Create new menu
- gCurrMenuID++;
- theMenuH = NewMenu(gCurrMenuID, "\p");
- InsertMenu(theMenuH, -1); // add it to menu list invisibly
- // set up this new menu level
- gMenuLevel[newMenuLevel].menuID = gCurrMenuID;
- gMenuLevel[newMenuLevel].menuItem = 0; // reset
- // point parent's current menu item to this new submenu
- theParentMenuH = GetMenuHandle(gMenuLevel[gCurrMenuLevel].menuID);
- SetItemCmd(theParentMenuH, gMenuLevel[gCurrMenuLevel].menuItem, 0x1b); // this menu item has a submenu
- SetItemMark(theParentMenuH, gMenuLevel[gCurrMenuLevel].menuItem, gCurrMenuID); // set submenu ID
- }
-
- // now move current menu index to this level (up, down or same)
- gCurrMenuLevel = newMenuLevel;
-
- // just add a new menu item to this current menu
- theMenuH = GetMenuHandle(gMenuLevel[gCurrMenuLevel].menuID);
- menuTitle = &aLine[5]; // point to start of menu title on line
- c2pstr(menuTitle); // convert it to a Pascal String (in place, ugh)
- AppendMenu(theMenuH, (StringPtr)menuTitle); // add item
- gMenuLevel[gCurrMenuLevel].menuItem++; // increment item count
- }
- return TRUE;
- }
-
-
- // ==============================================
- // Handle any COMMAND text line
- static Boolean DoTemplateCommand(char * aLine)
- {
- Boolean retVal;
- short lineLen = strlen(aLine);
-
- // extract command and info, case insensitive
- switch (tolower(aLine[1]))
- {
- case 'c': // @COM (Comment, ignored)
- retVal = FALSE;
- break;
-
- case 'm': // @mt#.title (menu title)
- if (lineLen < 6)
- { // line too short
- ShowTemplateError("Line too short");
- }
- else
- { // parse the command
- DoTCMenu(aLine);
- }
- retVal = FALSE;
- break;
-
- case '@': // @@@@ (eof)
- retVal = TRUE;
- break;
-
- default:
- ShowTemplateError("Unknown command in file.");
- retVal = TRUE;
- break;
- }
- return retVal;
- }
-
-
- // ==============================================
- // Handle a TEXT line
- static void DoTemplateLine(char * aLine, Boolean doNewLine)
- {
- long currLineSize;
- long currTextSize;
-
- if (doNewLine)
- {
- gCurrTextH = NewHandle(0);
- // add new menu list record
- TemplateQ_Put(
- gMenuLevel[gCurrMenuLevel].menuID,
- gMenuLevel[gCurrMenuLevel].menuItem,
- gCurrMenuLevel,
- gCurrTextH);
- }
-
- currLineSize = strlen(aLine);
- // add a CR to end of line
- aLine[currLineSize++] = 0x0d;
- aLine[currLineSize] = '\0';
- // add text to existing block
- currTextSize = GetHandleSize(gCurrTextH);
- // increase current buffer
- SetHandleSize(gCurrTextH, currTextSize+currLineSize);
- // lock it down and move data into it, then let it float again
- HLock(gCurrTextH);
- BlockMoveData(aLine, (*gCurrTextH)+currTextSize, currLineSize);
- HUnlock(gCurrTextH);
- }
-
-
- // ==============================================
- // read the lines from the template file and process them
- static void ReadTemplateFile(FILE * aFile)
- {
- OSErr anError = noErr;
- short lineLen;
- Boolean isEOF = FALSE;
- Boolean foundCmd = FALSE;
- Boolean doNewLine = TRUE; // starting a new line after command
- char aLine[256];
-
- // create a linked list to hold the text info
- gTemplateQueue = LLCreateList(1);
-
- // read the lines
- gLineCounter = 0;
- while (!isEOF && !anError)
- {
- // read next line
- fgets(aLine, 256, aFile);
- gLineCounter++;
- // delete any trailing CR
- lineLen = strlen(aLine);
- if (lineLen > 0)
- if (aLine[lineLen-1] == '\n')
- aLine[lineLen-1] = '\0';
- // handle line
- if (aLine[0] == TEMPLATE_CMD_CHAR)
- {
- isEOF = DoTemplateCommand(aLine);
- // hit a command, start a new line next time
- doNewLine = TRUE;
- foundCmd = TRUE; // OK to read lines now...
- }
- else
- {
- if (!foundCmd)
- {
- // ERROR, raw text in first part of file before any commands!
- ShowTemplateError("File must start with a valid command.");
- anError = -1; // force an exit
- }
- else
- DoTemplateLine(aLine, doNewLine);
- // start appending until we get another command
- doNewLine = FALSE;
- }
- // check for real eof
- if (!isEOF)
- isEOF = feof(aFile);
- }
- gTemplateImported = TRUE;
- }
-
-
- // ==============================================
- // Read the template file, parse it, and build the menus from it
- void ImportTemplates(void)
- {
- FILE * tFile;
- tFile = OpenTemplateFile();
- if (tFile)
- {
- ReadTemplateFile(tFile);
- CloseTemplateFile(tFile);
- }
- else
- ShowTemplateError("Cannot open Template file.");
- }
-
- // ==============================================
- void HandleTemplateMenu(short theMenuID, short theItem)
- {
- short anError = noErr;
- short k;
- TemplateQPtr_t p;
- Handle theTextHandle;
-
- // The menu ID must be one of the submenus
- if ((theMenuID < STARTING_MENU_ID) || (theMenuID > gCurrMenuID))
- return;
-
- // loop through the records in the list, looking for a menu match
- theTextHandle = NULL;
- // Loop through entire list, getting each of OUR elements and disposing
- for (k=1; (k<=LLGetNumElements(gTemplateQueue)) && !theTextHandle; k++)
- {
- p = (TemplateQPtr_t)LLGetElementByIndex(gTemplateQueue, k);
- if (p)
- {
- if ((theMenuID==p->menuID) && (theItem==p->menuItem))
- theTextHandle = p->textH;
- }
- }
- if (gSrcWind_visible && theTextHandle)
- {
- HLock(theTextHandle);
- // call text editor fn to insert it
- DoSrcWindTextInsert(&(**theTextHandle), GetHandleSize(theTextHandle));
- HUnlock(theTextHandle);
- gSrcWind_dirty = true;
- // scroll to new insertion point
- ShowSelect();
- }
- else
- SysBeep(1);
- }
-
-