-- FILE MANAGEMENT -- -- -- © June 2000 - October 2008, OpenSpark Interactive Ltd -- -- ---------------------------------------------------------------------- -- Kent Kersten's FileXtra3 and FileXtra4 are available at -- -- -- This movie script acts as a wrapper for FileXtra and the FileIO -- xtra. If FileXtra3 or FileXtra4 are available, it will use these -- to show Open... and Save As... dialogs, since more control is -- possible than with FileIO. -- -- In some cases, FileIO may be preferred for dialogs: -- * To display the last folder used by this application -- * To set a filter mask for a Save As... dialog on Windows -- In these cases, you can use [... xtra: #fileIO ...] as part of the -- parameter list for the File...Path() methods. ---------------------------------------------------------------------- -- 081022 JN: FileWrite() now handles error -43 (File not found) like -- error -37 (Bad file name) -- 081021 JN: FileWrite() now handles non-string values for aFilePath -- and aData more elegantly -- 050325 JN: FileConvertPath() renamed to PathForPlatform() and -- adapted to accept file names beginning with "@" -- PathMakeAbsolute() and PathExists() added -- 050303 JN: FileRawName() adapted to return a folder raw name -- 050208 JN: Renamed methods to start with File..., Folder... or -- Path... -- 040308 JN: Added file name and folder name methods, which don't -- require any xtras, and FileGetTree() and -- GetFolderContents(), which use FileXtra3 or 4. -- 040301 JN: * Converted from FileIO Handlers and FileXtra Handlers. -- FileIO and FileXtra methods are now handled together, -- using the most suitable xtra for each purpose. -- Errors in FileMask() and FileExtensionCheck() -- corrected. -- * Now depends on the Xtra Broker for a xtra instances. -- * Tested on Mac DMX authoring ---------------------------------------------------------------------- -- -- PUBLIC METHODS -- -------------- -- * FileOpenPath(aDataList) -- Returns VOID or a path to an existing file. -- * FileSavePath(aDataList) -- Returns VOID or a valid path for saving a file. -- -- * FileRead(aFilePath) -- Returns an error symbol or the string contents of the file. -- * FileWrite(aFilePath, aData, aFilterMask) -- Writes a string to a file. Returns an error symbol or 0. -- -- * FileSetType(aFilePath, aFileType) -- Sets the file type (Mac) or extension (Win) of the given file. -- Returns an error symbol or 0. -- * FileGetType(aFilePath) -- Returns the Finder Info (Mac) or extension (Win), an error -- symbol or a negative number. -- -- * FileGetTree(aFolderPath) -- Returns an error symbol or a property list with the format -- ["FolderName: ["SubfolderName": , -- "FileName": 0, ...], ...] -- * FolderGetPath(aFolderPath) -- Returns a string folder path selected by the user, or VOID if -- FileXtra4 is missing or the user cancels. -- * FolderContents(aFolderPath, aFileType, asSubLists) -- Returns a list of folders or files of a given type in -- aFolderPath, or an errorsymbol. -- -- * FileMask(aFilterMask, aMacOrWin, forFileXtra) -- Returns a platform specific filterMask string. -- -- * FileNameList(aFileName, asLocalPath) -- Returns the file name as a list: -- [#folder: , #file: , #extension: ] -- * FileRawName(aFileName) -- Returns the file name stripped of its folder path and extension, -- or an error symbol. -- * FolderFindIn(aFolderPath, aFileName) -- Returns the string absolute path to aFileName inside the given -- folder or an error symbol -- * PathGetParent(aFilePath) -- Returns the parent folder of aFilePath, or an error symbol. -- * PathNthParent(aPath, aLevel) -- Returns the path to the folder that contains the given file or -- folder, or its nth parent. -- -- * PathRelativeTo(aSourcePath, aReferencePath) -- Returns a string with the format "@:::CommonFolder:FileName", -- or an error symbol -- * PathMakeAbsolute(aPath, aList) -- Returns an absolute path the first item found, or VOID if -- the given item could not be found. -- * PathExists(aPath, isFolder -- Returns 1 of the item is found, 0 if not. May also return -- #folder if the item found at aPath is a folder, but isFolder is -- not used. -- * PathForPlatform(aFilePath) -- Returns a string representing the same absolute path as aFilePath -- converted to the current platform, or an error symbol. -- ---------------------------------------------------------------------- -- -- FILEIO ERROR CODES -- ------------------ -- 1 "Memory allocation failure" -- 0 "OK" -- -33 "File directory full" -- -34 "Volume full" -- -35 "Volume not found" -- -36 "I/O Error" -- -37 "Bad file name" -- -38 "File not open" -- -42 "Too many files open" -- -43 "File not found" -- -56 "No such drive" -- -65 "No disk in drive" -- -120 "Directory not found" -- -121 "Instance has an open file" -- -122 "File already exists" -- -123 "File is opened read-only" -- -124 "File is opened write-only" ---------------------------------------------------------------------- on FileOpenPath(aDataList) ------------------------------------------- -- INPUT: should be a property list. It could contain -- the properties | values | default -- [#mask: , -- #folder: , "" -- #title: , "" -- #mustExist: , TRUE -- #createPrompt: , TRUE -- #xtra: <#fileXtra | #FileIO> #FileXtra -- ] -- * If is a string, it is considered to be a -- valid platform-specific filter mask. -- * If is a symbol, it will be converted to a -- valid platform-specific filter mask. If you wish to -- add fileTypes, you can edit the FileMask() handler -- below. -- * If is not a string or a symbol then the -- user can choose any type of file. -- ACTION: Asks the user to select a file of type and -- returns the filePath to the chosen file. -- OUTPUT: Returns if the user cancels out of the dialog or -- if neither the FileIO xtra nor FileXtra is available, -- or filePath string if the user chooses a valid file. -- NOTE: If FileIO xtra is used (explicitly or as a backup plan) -- the items in aDataList may all be ignored. In -- particular, the initial folder will be set to the last -- folder used by the application. -------------------------------------------------------------------- if ilk(aDataList) <> #propList then aDataList = [:] end if -- Use FileXtra dialogs by default tXtra = aDataList[#xtra] if tXtra <> #fileIO then tXtra = #FileXtra end if tInstance = XtraInstance(tXtra) -- in Xtras Broker if not tInstance then -- FileXtra is not available. Use FileIO instead, if possible. tXtra = #FileIO tInstance = XtraInstance(tXtra, FALSE) -- don't show alert if not tInstance then return VOID -- as if user canceled the dialog end if end if -- Check if a filter mask is available tMask = aDataList[#mask] case ilk(tMask) of #string, #symbol, #list: tMask = FileMask(tMask, tXtra) -- Handler below end case -- Use the best available xtra to show the Open File dialog case tXtra of #FileXtra: -- tMask must be a string if not stringP(tMask) then tMask = "" end if -- Check if an initial folder is specified tFolder = aDataList[#folder] if not stringP(tFolder) then tFolder = "" end if if the platform starts "Mac" then tFilePath = tInstance.fx_FileOpenDialog(tFolder, tMask) else -- We're on Windows. Use defaults for extra parameters if -- necessary. tTitle = string(aDataList[#title]) tPrompt = aDataList[#createPrompt] tMustExist = aDataList[#mustExist] if voidP(tMustExist) then -- Default to TRUE tMustExist = TRUE tPrompt = FALSE -- since it will not be used else -- force to boolean tMustExist = (tMustExist <> 0) -- only 0 makes it FALSE if voidP(tPrompt) then tPrompt = TRUE else tPrompt = (tPrompt <> 0) end if end if tFilePath = tInstance.fx_FileOpenDialog( \ tFolder, \ tMask, \ tTitle, \ tPrompt, \ tMustExist) end if -- Mac or Win #FileIO: if stringP(tMask) then -- Assume that this is a valid platform-specific filter mask tInstance.setFilterMask(tMask) end if tFilePath = tInstance.displayOpen() end case case tFilePath of "", void: -- The user cancelled the dialog. On a Mac fileio xtra returns -- an empty string; on a PC, it returns VOID. return VOID end case return tFilePath end FileOpenPath on FileSavePath(aDataList) ------------------------------------------- -- INPUT: should be a property list. It could contain -- the properties | values | default -- [#folder: , "" -- #fileName: , "" -- #prompt: , "" -- #overwrite: , "" -- #mask: , -- #xtra: <#fileXtra | #FileIO> #FileXtra -- ] -- If a #mask string is provided and the application is -- running on Windows, FileIO will be used so that only -- files of the given type can be created. -- ACTION: Asks the user to select a file {of type } and -- returns the filePath to the chosen file. -- OUTPUT: Returns if the user cancels out of the dialog or -- if FileIO xtra is not available, or filePath string if -- the user chooses a valid file. -------------------------------------------------------------------- if ilk(aDataList) <> #propList then aDataList = [:] end if -- If we are on Windows, check if we should use a filter mask onMac = the platform starts "Mac" if not onMac then tMask = aDataList[#mask] case ilk(tMask) of #string, #symbol, #list: tMask = FileMask(tMask) -- Handler below end case end if -- Use FileXtra dialogs by default... unless a mask is to be used tXtra = aDataList[#xtra] if tXtra <> #fileIO then if stringP(tMask) then tXtra = #FileIO else tXtra = #FileXtra end if end if tInstance = XtraInstance(tXtra) -- in Xtras Broker if not tInstance then -- FileXtra is not available. Use FileIO instead, if possible. tXtra = #FileIO tInstance = XtraInstance(tXtra, FALSE) -- don't show alert if not tInstance then return VOID -- as if user canceled the dialog end if end if -- Use default "" as required tFolder = string(aDataList[#folder]) tFileName = string(aDataList[#fileName]) tPrompt = string(aDataList[#prompt]) -- Show the Save As... dialog case tXtra of #FileXtra: if onMac then tFilePath = tInstance.fx_FileSaveAsDialog( \ tFolder, \ tFileName, \ tPrompt) else tOverwrite = aDataList[#overwrite] if voidP(tOverwrite) then tOverwrite = TRUE else tOverwrite = (tOverwrite <> 0) end if tFilePath = tInstance.fx_FileSaveAsDialog( \ tFolder, \ tFileName, \ tPrompt, \ tOverwrite) end if #FileIO: -- Use FileIO, possibly with a filterMask on Windows if stringP(tMask) then tInstance.setFilterMask(tMask) end if tFilePath = tInstance.displaySave(tPrompt, tFileName) end case case tFilePath of "", void: -- The user cancelled the dialog. return VOID end case return tFilePath end FileSavePath on FileRead(aFilePath) ----------------------------------------------- -- INPUT: should be a valid string file path. If it is -- not a string, the user will be asked to choose a file. -- OUTPUT: Returns string contents of the chosen file, or an error -- symbol -------------------------------------------------------------------- tFileIO = XtraInstance(#fileio) if not tFileIO then return #xtraMissing end if -- Check that aFilePath is a string if not stringP(aFilePath) then -- Ask the user for a file path aFilePath = tFileIO.displayOpen() case aFilePath of "", VOID: -- The user canceled the dialog return #invalidString end case end if tFileIO.openFile(aFilePath, 1) -- Read only if tFileIO.status() then -- aFilePath is invalid return #invalidPath end if tContents = tFileIO.readFile() if tFileIO.status() then -- The file could not be read tContents = #illegibleFile end if tFileIO.closeFile() return tContents end FileRead on FileWrite(aFilePath, aData, aFilterMask) -------------------------- -- INPUT: should be string file path (but see below) -- should be a string. Any non-string data will be -- converted to a string. -- may be a string with the format "TYPE APPL". -- (The fifth character will be ignored), or a symbol which -- will be converted to a filterMask. This can be used to -- set the file type and creator (Mac Classic only). -- ALTERNATIVE SYNTAX: -- can be a property list with the following -- structure: -- [#filePath: "string", -- #data: "string", -- #mask: ] -- ACTION: Writes data to the chosen file. An existing file with -- the given name will be overwritten. Finder info on the -- Mac will be preserved. -- OUTPUT: Returns 0 (no error) or an error symbol. -------------------------------------------------------------------- tFileIO = XtraInstance(#fileio) -- in Xtras Broker if not tFileIO then return #xtraMissing end if -- Check that filePath is a string or a list containing a string if ilk(aFilePath) = #propList then -- Unpack the data in the list aData = aFilePath[#data] tTemp = aFilePath[#mask] case ilk(tTemp) of #string: aFilterMask = tTemp #symbol: aFilterMask = FileMask(tTemp) end case aFilePath = aFilePath[#filePath] end if if aFilePath = "" or not stringP(aFilePath) then aFilePath = FileSavePath() if not stringP(aFilePath) then return #userCanceled end if end if if not stringP(aData) then aData = string(aData) end if onMac = the platform starts "Mac" tCreateNewFile = FALSE -- declaration by default tFinderInfo = 0 -- declaration by default -- Check if the file currently exists tFileIO.openFile(aFilePath, 2) -- Write only tStatus = tFileIO.status() case tStatus of 0: -- The file could be opened, so it exists. tEndOfFile = tFileIO.getLength() if tEndOfFile > aData.char.count then -- Destroy the current file, since the new string is shorter -- than the existing one. if onMac then -- Save the finder info for when the file is recreated tFinderInfo = tFileIO.getFinderInfo() if charToNum(char 5 of tFinderInfo) < 32 then -- getFinderInfo() may return a string like "TYPE APPL" delete char 5 of tFinderInfo end if end if tFileIO.delete() tCreateNewFile = TRUE end if -37, -43: -- The file does not yet exist. tCreateNewFile = TRUE -- Ensure that the file extension is correct on PC if filterMask -- is specified. if not onMac then if stringP(WinMaskCheck(aFilterMask)) then -- The filterMask appears to be valid: apply it if not FileExtensionCheck(aFilePath, aFilterMask) then return #invalidFileType end if end if end if end case if tCreateNewFile then tFileIO.createFile(aFilePath) tStatus = tFileIO.status() if not tStatus then -- The file could be created tFileIO.openFile(aFilePath, 2) -- Write only tStatus = tFileIO.status() end if end if if tStatus then return #invalidPath end if if stringP(tFinderInfo) then -- Restore the finder info to the recreated Mac file tFileIO.setFinderInfo(tFinderInfo) end if -- Convert data to a string if necessary if not stringP(aData) then aData = string(aData) end if tFileIO.writeString(aData) tStatus = tFileIO.status() tFileIO.closeFile() if tStatus then -- The file could not be written to return #writeError end if return 0 -- No error end FileWrite on FileSetType(aFilePath, aFileType) --------------------------------- -- INPUT: should be a string file path. If the file -- does not exist, it will be created -- should be a string with the format -- "typeAPPL" (Mac) or ".ext" (Windows). It may also be a -- property list with the format: -- [#mac: "typeAPPL", #win: ".ext"] -- ACTION: Sets the file type for the chosen file. -- OUTPUT: Returns 0 for no error, or an error symbol or negative -- number. -------------------------------------------------------------------- onMac = the platform starts "Mac" -- Choose the appropriate file type if ilk(aFileType, #propList) then if onMac then aFileType = aFileType[#mac] else aFileType = aFileType[#win] end if end if -- Ensure the parameters are valid if not stringP(aFileType) then return #invalidFileType else if not stringP(aFilePath) then return #invalidString end if -- Use FileXtra by default tXtra = #FileXtra tInstance = XtraInstance(tXtra) -- in Xtras Broker if not tInstance then -- FileXtra is not available. Use FileIO instead, if possible. tXtra = #FileIO tInstance = XtraInstance(tXtra, FALSE) -- don't show alert if not tInstance then return #xtraMissing end if end if case tXtra of #FileXtra: tInstance.fx_FileSetType(aFilePath, aFileType) tStatus = tInstance.fx_ErrorNumber() #FileIO: if onMac then -- Ensure that a file exists with the correct finderInfo put " " after char 4 of aFileType -- "type APPL" tInstance.openFile(aFilePath, 1) -- Read only tStatus = tInstance.status() if tStatus = -37 then -- "Bad file name": The file does not exist. Create it -- first. tInstance.createFile(aFilePath) tStatus = tInstance.status() end if if tStatus then return #invalidPath end if -- Set the finder info tInstance.setFinderInfo(aFileType) tStatus = tInstance.status() tInstance.closeFile() else -- Windows tStatus = #cantRename end if end case return tStatus -- 0 = no error end FileSetType on FileGetType(aFilePath) -------------------------------------------- -- INPUT: should be a string file path. If the file -- does not exist, it will be created -- OTUPUT: Returns the finder info on Macintosh and the extension -- (including the dot) on Windows, or an error symbol or -- negative number. -------------------------------------------------------------------- -- Ensure the parameters are valid if not stringP(aFilePath) then return #invalidString end if -- Use FileXtra by default tXtra = #FileXtra tInstance = XtraInstance(tXtra) -- in Xtras Broker if not tInstance then -- FileXtra is not available. Use FileIO instead, if possible. tXtra = #FileIO tInstance = XtraInstance(tXtra, FALSE) -- don't show alert if not tInstance then return #xtraMissing end if end if case tXtra of #FileXtra: tFileType = tInstance.fx_FileGetType(aFilePath) #FileIO: -- Ensure that a file exists with the correct finderInfo tInstance.openFile(aFilePath, 1) -- Read only tStatus = tInstance.status() if tStatus then return #invalidPath end if if the platform starts "Mac" then -- Get the finder info tFileType = tInstance.getFinderInfo() tStatus = tInstance.status() tInstance.closeFile() if charToNum(char 5 of tFileType) < 32 then -- "type APPL" => "type APPL" delete char 5 of tFileType end if if charToNum(char 5 of tFileType) = 32 then -- "type APPL" => "typeEAPPL" delete char 5 of tFileType end if else -- Windows tDelimiter = the itemDelimiter the itemDelimiter = "." if the number of items of aFilePath > 1 then tFileType = "."& the last item of aFilePath else tFileType = "" -- neither dot nor extension end if the itemDelimiter = tDelimiter end if end case return tFileType end FileGetType on FileGetTree(aFolderPath) ------------------------------------------ -- INPUT: should be a string path name. If it is not -- a string the user will be asked to choose a folder -- OUTPUT: Returns an alphabetical list of elements in -- If an invalid is used, or if the "Locate a -- folder..." dialog is canceled, the symbol #invalidPath is -- returned. -------------------------------------------------------------------- -- Determine which character FileXtra3 will use to indicate a folder tPathDelimiter = the last char of the moviePath tFileXtra = XtraInstance(#FileXtra) if not tFileXtra then return [:] end if if not stringP(aFolderPath) then -- Ask the user to indicate a filepath in the chosen folder if tPathDelimiter = ":" then -- Mac aFolderPath = tFileXtra.fx_FolderSelectDialog("") else -- Windows tMessage = "Locate a file in the folder..." aFolderPath = tFileXtra.fx_FolderSelectDialog(tMessage) end if if aFolderPath = "" then return #invalidPath end if tSaveDelimiter = the itemDelimiter the itemDelimiter = tPathDelimiter delete the last item of aFolderPath the itemDelimiter = tSaveDelimiter end if -- Ensure that the folderPath ends with path delimiter if the last char of aFolderPath <> tPathDelimiter then put tPathDelimiter after aFolderPath end if -- Create and sort the property list that will be returned tFileTree = [:] tFileTree.sort() tContents = tFileXtra.fx_FolderToList(aFolderPath) if ilk(tContents) <> #list then return #invalidPath end if i = tContents.count() repeat while i tElement = tContents[i] case the last char of tElement of tPathDelimiter: -- Element is a folder: use recursion to get subfolder details tFileTree.addProp(tElement, FileGetTree(aFolderPath&tElement)) RETURN: -- Element is an invisible icon file on Mac: ignore it otherwise: -- Element is a file tFileTree.addProp(tElement, 0) end case i = i - 1 end repeat return tFileTree end FileGetTree on FolderGetPath(aFolderPath) --------------------------------------- -- INPUT: may be a string absolute folder path for -- the user to start searching in -- OUTPUT: Returns a string folder path selected by the user, or -- VOID if FileXtra4 is missing or the user cancels. ------------------------------------------------------------------- if not stringP(aFolderPath) then -- if the runMode = "Author" then aFolderPath = the moviePath -- else -- aFolderPath = the applicationPath -- end if end if tInstance = XtraInstance(#FileXtra) if not tInstance then return VOID -- #fileXtraMissing end if tFolderPath = tInstance.fx_FolderSelectDialog(aFolderPath) case tFolderPath of "": tFolderPath = VOID --, VOID end case return tFolderPath end FolderGetPath on FolderContents(aFolderPath, aFileType, asSubLists) ---------------- -- INPUT: should be a string absolute path to a folder -- can be VOID, #folder, a linear list of string -- extensions, or a property list with the properties -- [#mac: [], -- #win: []] -- only has an effect if it is not 0 or VOID -- and if aFileType is a list*. In this case, the output -- will be a property list, where the properties are the -- extensions or fileTypes, and the values are a list of -- file names of that type. Extensions are represented as -- symbols, to ensure case-insensitivity: fileTypes are -- represented as case-sensitive strings. -- * If is #trimExtension or #noExtension then -- the extension will be trimmed from any file names. If -- aFileType is #foldersOnly, #folder, #folders or -- #noFiles then the ":" or "\" path delimiter will be -- trimmed from folder names. -- -- OUTPUT: Returns a list of short file names which match any of the -- file types or extensions in . If aFileType is -- #folder, only folder names will be returned. -------------------------------------------------------------------- if not stringP(aFolderPath) then return #invalidString end if tFileXtra = XtraInstance(#FileXtra) if not tFileXtra then return #xtraNotInstanciated end if -- Ensure that the conditions are correct for returning subLists case asSubLists of #trimExtension, #noExtension: tTrimExtension = TRUE asSubLists = FALSE otherwise asSubLists = (listP(aFileType) AND asSubLists <> 0) if asSubLists then tOutput = [:] tOutput.sort() end if end case tList = tFileXtra.fx_FolderToList(aFolderPath) if not listP(tList) then return #invalidFolderPath end if -- Prepare to check the file type of each entry in the list tPathDelimiter = the last char of the moviePath i = tList.count case aFileType of #filesOnly, #file, #files, #noFolders: i = tList.count repeat while i if the last char of tList[i] = tPathDelimiter then -- Remove this folder from the list tList.deleteAt(i) end if i = i - 1 end repeat #foldersOnly, #folder, #folders, #noFiles: -- Remove all files from the list repeat while i tFolder = tList[i] if the last char of tFolder <> tPathDelimiter then tList.deleteAt(i) else if tTrimExtension then delete the last char of tFolder tList[i] = tFolder end if i = i - 1 end repeat otherwise: -- Filter for files onMac = (tPathDelimiter = ":") if the last char of aFolderPath <> tPathDelimiter then put tPathDelimiter after aFolderPath end if tDelimiter = the itemDelimiter the itemDelimiter = "." -- Prepare a list of extensions and fileTypes case ilk(aFileType) of #string: tExtensions = list(aFileType) if onMac then tFileTypes = tExtensions end if #list: tExtensions = aFileType if onMac then tFileTypes = tExtensions end if #propList: tExtensions = aFileType[#win] if not listP(tExtensions) then tExtensions = aFileType[#windows] if not listP(tExtensions) then tExtensions = [] end if end if if onMac then -- See if there is a list of file types to test against tFileTypes = aFileType[#mac] if not listP(tFileTypes) then tFileTypes = aFileType[#macintosh] if not listP(tFileTypes) then -- There is no list of file types: don't test for them onMac = FALSE end if end if end if otherwise: return tList end case -- Check the extension (and fileType) of each item in the list repeat while i tFile = tList[i] -- Check for a matching Windows-style extension tExtension = the last item of tFile tMatch = GetIndex(tExtensions, tExtension) --insensitive match if not tMatch then if onMac then -- Check also for a case-sensitive matching Mac fileType tFullFile = aFolderPath&tFile tFileType = tFileXtra.fx_FileGetType(tFullFile).char[1..4] tMatch = tFileTypes.getPos(tFileType) if tMatch then if asSubLists then AddToSubList(tOutput, tFileType, tFile, TRUE) end if end if end if else if asSubLists then -- Remove the extension, as it will be used as a property -- name delete the last item of tFile AddToSubList(tOutput, symbol(tExtension), tFile, TRUE) end if if not tMatch then tList.deleteAt(i) else if tTrimExtension then if the number of items of tFile > 1 then delete the last item of tFile end if tList[i] = tFile end if i = i - 1 end repeat the itemDelimiter = tDelimiter end case if asSubLists then -- Use tOutput rather than the linear tList tList = tOutput end if return tList end FolderContents -- MASK CONVERSION AND CHECKING METHODS -- on FileMask(aFilterMask, onMac, forFileXtra) ------------------------- -- OUTPUT: Returns a platform specific filtermask -- INPUT: can be a string, a symbol or a list of -- strings or symbols (see Details below). -- can be "Mac", #mac or TRUE to force your PC to -- return a Mac filterMask; or "Win", "PC", #win, #pc or -- FALSE to force your Mac to return a Windows filterMask. -- All other values will make the handler return a -- filterMask suitable for the current platform. -- can be TRUE or #fileXtra. If -- is #fileXtra, then the filterMask for FileXtra for the -- current platform will be returned. FileXtra masks for -- Windows use / characters where FileIO masks use commas. -- On the Mac, each chunk of four characters is separated by -- a /. -- DETAILS: -- * symbols: The main interest of this handler is that you can -- enter a mnemonic symbol, such as #dir, and receive a platform -- specific filterMask in return. Add your own symbols to the -- list, and remove any that you will not need in your current -- project. (Store a complete list of useful symbols and the -- corresponding filterMasks in a library cast). -- -- Example 1: put FileMask(#dir8, #mac) -- -- "MV08" -- Example 2: put FileMask(#cct, #win) -- -- "Shockwave casts,*.cct" -- -- * lists: An even neater trick is to pass in a list of symbols. -- The handler will treat each item in the list in order and build -- a compound filter path. -- -- Example 3: put FileMask([#dir8, #dir7], #mac) -- -- "MV08MV07" -- Example 4: put FileMask([#cct, #all], #win) -- -- "Shockwave casts,*.cct,All files,*.*" -- -- * Neater yet: you can get a symbol to return a list of symbols. -- -- Example 5: put FileMask(#dir, #mac) -- -- "MV08MV07MV97MV95" -- -- The #dir symbol returns the command... -- FileMask([#dir8, #dir7, #dir6, #dir5], #mac) -- ... which is then treated in a second pass to return a string. -- -- Example 6: put FileMask(#director, #win) -- -- Director movies,*.dir, -- Protected movies,*.dxr, -- Shockwave movies,*.dcr, -- Director casts,*.cst, -- Protected casts,*.cxt, -- Shockwave casts,*.cct" -- -- * strings: This handler will call either the MacMaskCheck() or -- the WinMaskCheck() handler. If your string seems valid, it -- will be returned as it is. If there is an error, the returned -- value is likely to be . (The MacMaskCheck() handler may -- be able to remove any illegal chunks, but the result may not be -- what you intended. You can also call the XxxMaskCheck() -- handlers directly. This is mainly useful while authoring. -- -- None of the symbols in the case statement below are sacrosanct. -- You can edit them as you see fit. Create your own custom symbols. ------------------------------------------------------------------- -- Allow platform conversions via the optional onMac parameter if onMac = #fileXtra then forFileXtra = TRUE onMac = the platform starts "Mac" else if integerP(onMac) then -- Keep current value else case onMac of "Mac", #mac: onMac = TRUE "Win", "PC", #win, #pc: onMac = FALSE otherwise -- Use the current platform onMac = the platform starts "Mac" end case end if if not integerP(forFileXtra) then forFileXtra = (forFileXtra = #fileXtra) end if case ilk(aFilterMask) of #string: if onMac then return MacMaskCheck(aFilterMask, forFileXtra) else return WinMaskCheck(aFilterMask, forFileXtra) end if #list: -- Treat the items in the list recursively tMask = "" tCount = aFilterMask.count() repeat with i = 1 to tCount tChunk = aFilterMask[i] tChunk = FileMask(tChunk, onMac, forFileXtra) if not stringP(tChunk) then -- tMask was of an unknown type next repeat end if if forFileXtra then put "/" after tMask else if not onMac then put "," after tMask end if put tChunk after tMask end repeat if onMac then -- A maximum of 4 file types is allowed if forFileXtra then tMask = chars(tMask, 2, 20) -- delete initial / else tMask = chars(tMask, 1, 16) end if else -- Windows delete char 1 of tMask -- , or / end if #symbol: -- Convert the symbol to a list or a string case aFilterMask of #image: tMask = FileMask([#jpg, #gif, #tif, #png, #all], onMac, forFileXtra) #jpg: if onMac then tMask = "JPEG" else tMask = "JPEG files,*.jpg" end if #gif: if onMac then tMask = "GIFf" else tMask = "GIF files,*.gif" end if #tif: if onMac then tMask = "TIFF" else tMask = "TIFF files,*.tif" end if #png: if onMac then tMask = "PNGf" else tMask = "PNG files,*.png" end if #script: if onMac then tMask = "" else tMask = "Linked scripts,*.ls" end if -- Director #director: tMask = FileMask([#movies, #casts], onMac, forFileXtra) #unprotected: tMask = FileMask([#dir, #cst], onMac, forFileXtra) #shockwave: tMask = FileMask([#dcr, #cct], onMac, forFileXtra) #movies: tMask = FileMask([#dir, #dxr, #dcr], onMac, forFileXtra) #d85movies: tMask = FileMask([#dir85, #dxr85, #dcr]) #d85casts: tMask = FileMask([#cst85, #cxt85, #cct]) #dir: if onMac then tMask = FileMask([#dir85, #dir8, #dir7, #dir6], #mac, forFileXtra) else tMask = "Director movies,*.dir" end if #dxr: if onMac then tMask = FileMask([#dxr85, #dxr8, #dxr7, #dxr6], #mac, forFileXtra) else tMask = "Protected movies,*.dxr" end if #dcr: if onMac then tMask = "FGDM" else tMask = "Shockwave movies,*.dcr" end if #casts: tMask = FileMask([#cst, #cxt, #cct], onMac, forFileXtra) #cst: if onMac then tMask = FileMask([#cst85, #cst8, #cst7, #cst6], #mac, forFileXtra) else tMask = "Director casts,*.cst" end if #cxt: if onMac then tMask = FileMask([#cxt85, #cxt8, #cxt7, #cxt6], #mac, forFileXtra) else tMask = "Protected casts,*.cxt" end if #cct: if onMac then tMask = "FGDC" else tMask = "Shockwave casts,*.cct" end if -- Mac-specific file types #dir85:tMask = "MV85" #dir8: tMask = "MV08" #dir7: tMask = "MV07" #dir6: tMask = "MV97" #dir5: tMask = "MV95" -- #dxr85:tMask = "M!85" #dxr8: tMask = "M!08" #dxr7: tMask = "M!O7" #dxr6: tMask = "M!97" #dxr5: tMask = "M!95" -- #cst85:tMask = "MC85" #cst8: tMask = "MC08" #cst7: tMask = "MC07" #cst6: tMask = "MC97" #cst5: tMask = "MC95" -- #cxt85:tMask = "M*85" #cxt8: tMask = "M*08" #cxt7: tMask = "M*07" #cxt6: tMask = "M*97" #cxt5: tMask = "M*95" #jpg: if onMac then tMask = "JPEG" else tMask = "Fichier JPEG,*.jpg" end if #text: if onMac then tMask = "TEXT" else tMask = "All text files,*.txt" end if -- Add other cases here #all: if onMac then tMask = "" else tMask = "All files,*.*" end if end case end case if forFileXtra then if not onMac then -- Replace all commas with slashes for FileXtra on Windows tOffset = offset(",", tMask) if tOffset then put "/" into char tOffset of tMask end if end if end if return tMask end FileMask on MacMaskCheck(aFilterMask, forFileXtra) ---------------------------- -- CALLED by FileMask() -- INPUT: should be a string with a format such as: -- "MV85MC85" -- can be #FileXtra or TRUE. If not it is -- considered to be FALSE (#FileIO). -- ACTION: Checks that a Mac filter mask has the correct format for -- the given xtra. -- OUTPUT: Returns if it appears valid or VOID if not. -- If any invalid character appears in , the -- four-character chunk in which it appears will be removed. -------------------------------------------------------------------- if not stringP(aFilterMask) then return VOID end if if not integerP(forFileXtra) then forFileXtra = (forFileXtra = #FileXtra) end if -- The mask must be a multiple of 4 characters... tLength = aFilterMask.char.count if forFileXtra then -- ... separated by slashes tChunk = 5 tLength = min(19, tLength - ((tLength + 1) mod tChunk)) else tChunk = 4 tLength = min(16, tLength - (tLength mod tChunk)) end if if tLength then -- Trim any extraneous characters aFilterMask = chars(aFilterMask, 1, tLength) -- Only certain characters are allowed repeat while tLength tChar = char tLength of aFilterMask if forFileXtra and not (tLength mod tChunk) then -- tChar should be a slash if tChar <> "/" then -- This chunk is invalid delete aFilterMask.char[tLength - 4..tLength] end if tLength = tLength - 5 else if not \ ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890*?!_" contains tChar) then -- This 4-character chunk is invalid tChar = tLength mod tChunk if tChar then -- The invalid character is not at the end of the chunk tLength = tLength - (tChar) else -- The invalid character is the fourth in the chunk tLength = tLength - tChunk end if delete aFilterMask.char[tLength + 1..tLength + tChunk] else tLength = tLength - 1 end if end repeat if aFilterMask.char.count > 0 then return aFilterMask end if end if -- The mask is not valid return VOID end MacMaskCheck on WinMaskCheck(aFilterMask, forFileXtra) ---------------------------- -- CALLED by FileWrite(), FileMask() -- INPUT: should be a string with a format such as: -- "Director Movies,*.dir" -- can be #FileXtra or TRUE. If not it is -- considered to be FALSE (#FileIO). -- ACTION: Checks that a Windows filter mask has the correct format -- for the given xtra -- OUTPUT: Returns if it appears valid or VOID if not. -- NOTE: This check is not entirely watertight. It may give false -- negatives for strings which use ? characters. -------------------------------------------------------------------- if not stringP(aFilterMask) then return VOID end if if not integerP(forFileXtra) then forFileXtra = (forFileXtra = #FileXtra) end if if forFileXtra then tDelimiter = "/" else tDelimiter = "," end if tMask = aFilterMask isValid = TRUE repeat while isValid -- Check that Win mask consists of at least " ,*. " or " ,?. " isValid = offset(tDelimiter, tMask) if isValid < 2 then -- No delimiter, or delimiter is the first character isValid = FALSE else -- There is a delimiter, and at least one character before it delete char 1 to isValid of tMask -- remainder is "*.ext..." isValid = offset("*", tMask) if not isValid then -- (tChar <= tOffset) then -- There is no asterisk after the delimiter: is there a "?" , isValid = offset("?", tMask) end if if isValid then -- There is either an asterisk or a "?" after the delimiter delete char 1 to isValid of tMask -- remainder is ".ext..." isValid = offset(".", tMask) if isValid then -- There is an extension dot after the fileName part delete char 1 to isValid of tMask -- remainder is "ext..." if tMask = "" then -- There is no extension after the dot: mask is invalid isValid = FALSE else if the number of chars of tMask > 3 then -- There is more than one file type isValid = offset(tDelimiter, tMask) if not isValid or isValid > 4 then isValid = FALSE else -- The dot is followed by 1 to 3 extension characters delete char 1 to isValid of tMask -- Treat the next file type definition end if else -- There are 1 - 3 extension characters, and no more -- file type definitions to treat. The filterMask is -- valid exit repeat end if end if end if end if end repeat -- The mask is not valid if isValid then return aFilterMask else return VOID end if end WinMaskCheck on FileExtensionCheck(aFilePath, aFilterMask) ------------------------ -- CALLED by FileWrite() before creating a new file, if the -- application is running on Windows and a filterMask is -- given. -- INPUT: should be a string file path with the format: -- "\." -- should be a string filterMask with the -- format: -- "Description,*.ext,All files,*. *" -- ACTION: Compares the extension of aFilePath with the items in -- aFilterMask -- OUTPUT: Returns TRUE if the extension used for aFilePath -- corresponds to one of the extensions provided by -- filterMask, or if the filterMask allows all files "*.*". -- (Does not check the file name itself: all file names are -- considered to be "*"). -------------------------------------------------------------------- tDelimiter = the itemDelimiter the itemDelimiter = "." tExtension = the last item of aFilePath the itemDelimiter = "," if aFilterMask contains "*.*" then i = 1 else -- Check the aFilePath against each possible extension i = aFilterMask.item.count / 2 repeat while i tFilter = aFilterMask.item[i * 2] the itemDelimiter = "." tFilter = the last item of tFilter the itemDelimiter = "," if tFilter = tExtension then exit repeat end if i = i - 1 end repeat end if the itemDelimiter = tDelimiter return i <> 0 end FileExtensionCheck -- FILE AND PATH NAME METHODS -- on FileNameList(aFileName, asLocalPath) ------------------------------ -- INPUT: should be a file path with one of the -- following structures (depending on the current platform): -- * "C:\Directory\SubDirectory\FileName.ext" -- * "Mac HD:Folder:SubFolder:FileName" -- may be VOID, TRUE or FALSE -- OUTPUT: a list with the structure: -- [#folder: , -- #file: , -- #extension: ] -- NOTE: the file is assumed to exist -------------------------------------------------------------------- tFileNameList = [#folder: "", #file: "", #extension: ""] if stringP(aFileName) then -- Determine which platform the file name was created on if aFileName contains "\" then -- Windows tDelimiter = "\" else -- either Macintosh or aFileName contains no folder data tDelimiter = ":" end if -- Trim the file name (+ extension) from the full path tSaveDelimiter = the itemDelimiter the itemDelimiter = tDelimiter tFileName = the last item of aFileName--may be entire name -- Add the path to the parent folder delete the last item of aFileName if aFileName <> "" then put tDelimiter after aFileName if asLocalPath then aFileName = PathForPlatform(aFileName) end if tFileNameList.folder = aFileName end if -- Add the extension and the raw file name the itemDelimiter = "." if tFileName.item.count > 1 then -- The file name has an extension: include the dot tFileNameList.extension = "."&the last item of tFileName delete the last item of tFileName end if tFileNameList.file = tFileName the itemDelimiter = tSaveDelimiter end if return tFileNameList end FileNameList on FileRawName(aFileName, isFolder) --------------------------------- -- INPUT: should be a string with one of the following -- structures (depending on the current platform): -- * "fileName.ext" -- * "C:\Directory\SubDirectory\FileName.ext" -- * "Mac HD:Folder:SubFolder:FileName.ext" -- has no effect unless it is TRUE or #folder. -- In these cases, the raw name returned may be a folder's. -- OUTPUT: The file name stripped of any folder path or extension ------------------------------------------------------------------- if not stringP(aFileName) then return #invalidString end if -- Strip the initial folder path data tDelimiter = the itemDelimiter if the platform starts "Mac" then tPathDelimiter = ":" else tPathDelimiter = "\" end if the itemDelimiter = tPathDelimiter case isFolder of TRUE, #folder: if the last char of aFileName = tPathDelimiter then delete the last char of aFileName end if end case aFileName = the last item of aFileName -- Remove the final extension the itemDelimiter = "." if aFileName.item.count > 1 then delete the last item of aFileName end if -- Put things back where we found them the itemDelimiter = tDelimiter return aFileName end FileRawName on FileChangeExtension(aFilePath, anExtension) ----------------------- -- INPUT: should be a string path name -- should be a 2 - 4 letter extension -- OUTPUT: Returns aFilePath with the new extension in place of the -- current one, or an error symbol -------------------------------------------------------------------- if not stringP(aFilePath) then return #invalidPath end if if not stringP(anExtension) then return #invalidExtension else if not (anExtension starts ".") then put "." before anExtension end if -- Strip the initial folder path data tDelimiter = the itemDelimiter -- Remove the final extension the itemDelimiter = "." if aFilePath.item.count > 1 then delete the last item of aFilePath end if put anExtension after aFilePath -- Put things back where we found them the itemDelimiter = tDelimiter return aFilePath end FileChangeExtension on FolderFindIn(aFolderPath, aFileName) ----------------------------- -- INPUT; should be a string absolute path to a -- folder -- should be a string short name of a file, -- including its extension. If searching for a folder -- rather than a file, the platform-specific path delimiter -- character should be used. -- "FileName.ext" -- "FolderName:" or "FolderName\" -- OUTPUT: Returns the string absolute path to aFileName inside the -- given folder or an error symbol ------------------------------------------------------------------- -- Ensure both inputs are strings if not stringP(aFolderPath) then return #stringFolderPathExpected else if not stringP(aFileName) then return #stringFileNameExpected end if -- Ensure parent folder exists tContents = FolderContents(aFolderPath) if not listP(tContents) then return #invalidFolderPath end if -- Ensure that folders end with the path delimiter character tDelimiter = the last char of the moviePath if the last char of aFolderPath <> tDelimiter then put tDelimiter after aFolderPath end if -- Look for aFileName in this folder i = tContents.count() repeat while i tFileName = tContents[i] if tFileName = aFileName then -- We've found the file return aFolderPath&aFileName else if the last char of tFileName <> tDelimiter then -- This is a file, not a folder. Don't attempt to search it. tContents.deleteAt(i) end if i = i - 1 end repeat -- The file was not found. Look in sub-folders i = tContents.count() repeat while i tFolderName = tContents[i] tFolderPath = aFolderPath&tFolderName tFileName = FolderFindIn(tFolderPath, aFileName) if stringP(tFileName) then return tFileName end if i = i - 1 end repeat return #fileNotFound end FolderFindIn on PathGetParent(aFilePath) ----------------------------------------- -- INPUT: should be a file path with one of the -- following structures (depending on the current platform): -- * "C:\Directory\SubDirectory\FileName.ext" -- * "Mac HD:Folder:SubFolder:FileName" -- OUTPUT: the file path to the folder that contains the given file -- NOTE: the file is assumed to exist -------------------------------------------------------------------- if the platform starts "Mac" then tDelimiter = ":" else tDelimiter = "\" end if tSaveDelimiter = the itemDelimiter the itemDelimiter = tDelimiter delete the last item of aFilePath the itemDelimiter = tSaveDelimiter return aFilePath&tDelimiter end PathGetParent on PathNthParent(aPath, aLevel) --------------------------------------- -- INPUT: should be a file or folder path with one of -- the following structures (depending on the current -- platform) -- * "C\Directory\SubDirectory {\FileName.ext}" -- * "Mac HD:Folder:SubFolder {:FileName}" -- may be VOID (for direct parent) or a positive -- integer, for the nth parent -- OUTPUT: the path to the folder that contains the given file or -- folder, or its nth parent. -- NOTE: the file or folder is assumed to exist -------------------------------------------------------------------- -- Check inputs if not ilk(aPath,#string) then return #invalidString end if -- Determine the platform if the platform starts "Mac" then tDelimiter = ":" else tDelimiter = "\" end if -- Remove the last delimiter if a full folder path is given if the last char of aPath = tDelimiter then delete the last char of aPath end if tSaveDelimiter = the itemDelimiter the itemDelimiter = tDelimiter if integerP(aLevel) then aLevel = min(the number of items of aPath, max(1, aLevel)) else aLevel = 1 end if -- Strip unwanted folder names repeat while aLevel delete the last item of aPath aLevel = aLevel - 1 end repeat the itemDelimiter = tSaveDelimiter if aPath <> "" then -- Restore the final path delimiter put tDelimiter after aPath end if return aPath end PathNthParent on PathRelativeTo(aSourcePath, aReferencePath) ----------------------- -- INPUT: should be an absolute path to a given file -- or folder -- may be the absolute path to a folder. If -- not, the moviePath is used as the reference. -- OUTPUT: Calculates the relative path to the given file, by -- comparing its full path name with that of aReferencePath -- (or the current movie). Returns the relative path in a -- format such as: "@\\\CommonFolder\File.ext", or an -- error symbol. -------------------------------------------------------------------- -- Determine the folder path delimiter if not stringP(aSourcePath) then return #invalidFilePath else if not stringP(aReferencePath) then aReferencePath = the moviePath tDelimiter = the last char of aReferencePath else if the platform starts "Mac" then tDelimiter = ":" else tDelimiter = "\" end if tPath = aSourcePath tSaveDelimiter = the itemDelimiter the itemDelimiter = tDelimiter -- Compare the paths, starting at the root, until a difference -- is found repeat while TRUE if item 1 of tPath <> item 1 of aReferencePath then -- The paths diverge here exit repeat end if -- Remove the common folder delete item 1 of tPath delete item 1 of aReferencePath end repeat if aSourcePath <> tPath then -- Both paths are on the same drive: convert aSourcePath to a -- relative pathe aSourcePath = "@" -- Determine how many levels to move up... tDepth = the number of items of aReferencePath repeat while tDepth put tDelimiter after aSourcePath tDepth = tDepth - 1 end repeat -- ... before moving back down put tPath after aSourcePath end if the itemDelimiter = tSaveDelimiter return aSourcePath end PathRelativeTo on PathMakeAbsolute(aPath, aList) ------------------------------------ -- INPUT: should be a string relative file or folder path -- may be a list of folders to be used as search -- paths. If aPath starts "@" then aList can be TRUE, in -- in which case it means "Item need not exist". (Search -- paths are ignored if aPath starts "@"). -- ACTION: Searches for a file with the given relative path, -- starting with each of the folder names in aList, in -- order. If no match is found, looks in the same folder -- as the current movie, and then in the applicationPath. -- If you want to raise the priority of the moviePath or -- the applicationPath, include these in aList. -- OUTPUT: Returns an absolute path the first item found, or VOID if -- the given item could not be found. If the found item is -- a folder, the path delimiter will be added to ensure that -- the returned path ends correctly. -- If aPath starts "@:", "@\" or "@/"then an absolute path -- defined with respect to the moviePath will be created. -- If aList is TRUE, then no test will be made to see if the -- path exists, and the path will be returned anyway. If -- not, VOID will be returned if the item does not exist. --------------------------------------------------------------------- if not stringP(aPath) then return VOID end if vParent = the moviePath vDelimiter = the last char of vParent vExists = PathExists(aPath) -- 0, 1, #folder if not vExists then -- The path is not already absolute if aPath starts "@" then -- Calculate the absolute path relative to the current movie vPath = PathForPlatform(aPath) delete char 1 of vPath -- "@" vSaveDelimiter = the itemDelimiter the itemDelimiter = vDelimiter delete the last item of vParent -- final delimiter if vPath starts vDelimiter then -- "@File" and "@:File" are considered equivalent. If more -- delimiters are used, then they indicate parent folders. delete char 1 of vPath repeat while char 1 of vPath = vDelimiter delete the last item of vParent delete char 1 of vPath end repeat end if -- vPath starts vDelimiter if vParent = "" then -- We've climbed beyond the root disk: the path is invalid. aPath = VOID else aPath = vParent&vDelimiter&vPath if aList <> TRUE then -- Check whether the calculated path exists or not vExists = PathExists(aPath) if not vExists then aPath = VOID end if end if end if the itemDelimiter = vSaveDelimiter else -- aPath does not start "@" if listP(aList) then -- Use search paths vCount = aList.count() repeat with i = 1 to vCount vFolder = aList[i] if stringP(vFolder) then if the last char of vFolder <> vDelimiter then put vDelimiter after vFolder end if vExists = PathExists(vFolder&aPath) -- 0, 1 or #folder if not vExists then -- Continue looking else aPath = vFolder&aPath vFound = TRUE exit repeat end if end if end repeat end if if not vFound then -- The given item was not found using any of the search paths vExists = PathExists(vParent&aPath) if not vExists then vParent = the applicationPath vExists = PathExists(vParent&aPath) if not vExists then aPath = VOID else -- the item is in the applicationPath aPath = vParent&aPath end if else -- the item is in the moviePath aPath = vParent&aPath end if end if end if end if if vExists = #folder then -- Ensure that the returned folder path ends correctly if the last char of aPath <> vDelimiter then put vDelimiter after aPath end if end if return aPath end PathMakeAbsolute on PathExists(aPath, isFolder) --------------------------------------- -- INPUT: should be a string file or folder name -- OUTPUT: Returns 1 of the file is found (or if the folder is found -- and isFolder is 1 or #folder), 0 if not. May also return -- #folder if the item found at aPath is a folder, but -- isFolder is not used. Returns VOID it was impossible to -- determine whether the path exists. -------------------------------------------------------------------- if not stringP(aPath) then return VOID -- #stringExpected end if vInstance = XtraInstance(#FileXtra) if not vInstance then return VOID -- #FileXtraExpected end if isFolder = [TRUE, #folder].getPos(isFolder) if isFolder then vExists = vInstance.fx_folderExists(aPath) else vExists = vInstance.fx_fileExists(aPath) end if if not vExists then vError = vInstance.fx_errorNumber() case vError of -7: if isFolder then vExists = vInstance.fx_fileExists(aPath) if vExists then vExists = #file end if else vExists = vInstance.fx_folderExists(aPath) if vExists then vExists = #folder end if end if -8: vExists = #folder -- may report incorrectly? -16: vExists = #file end case end if return vExists end PathExists on PathForPlatform(aFilePath) ---------------------------------------- -- INPUT: should be a file or folder path with one of -- the following structures (file name is optional): -- * "C:\Directory\SubDirectory\FileName.ext" -- * "Mac HD:Folder:SubFolder:FileName" -- OUTPUT: The same path with the root on the local machine -------------------------------------------------------------------- if not stringP(aFilePath) then return #invalidString end if tDelimiter = the itemDelimiter tFilePath = aFilePath if the platform starts "Mac" then if aFilePath contains "\" then -- This is a Windows path that needs converting to Mac if aFilePath starts "@" then aFilePath = "" else the itemDelimiter = "\" delete item 1 of tFilePath -- "C:\", @\" ... the itemDelimiter = ":" aFilePath = item 1 of getOSDirectory()&":" -- "Mac HD:" end if repeat while TRUE tOffset = offset("\", tFilePath) case tOffset of 0: put tFilePath after aFilePath exit repeat 1: -- network drive "..\\": swallow the second \ -- Should detect the difference between a relative path -- in "@\\Folder\File.ext" and network drive. put ":" after aFilePath otherwise: put char 1 to tOffset - 1 of tFilePath&":" after aFilePath end case delete char 1 to tOffset of tFilePath end repeat end if else if not (aFilePath contains "\") then -- This is a Mac path that needs converting to Windows if tFilePath starts "@" then aFilePath = "" else the itemDelimiter = ":" delete item 1 of tFilePath -- "Mac HD:" ... aFilePath = item 1 of getOSDirectory()&"\" -- "C:\" end if repeat while TRUE tOffset = offset(":", tFilePath) if tOffset then if tOffset = 1 then -- "@::Folder:File" => ":Folder:File" put "\" after aFilePath else put char 1 to tOffset - 1 of tFilePath&"\" after aFilePath end if delete char 1 to tOffset of tFilePath else put tFilePath after aFilePath exit repeat end if end repeat end if the itemDelimiter = tDelimiter return aFilePath end PathForPlatform -- UTILITIES -- on GetIndex(aList, anItem) ------------------------------------------ -- INPUT: must be a list -- may be any Lingo value, but this handler is only -- worth using with strings -- OUTPUT: Returns the position of the first item in aList which -- matches anItem, regardless of case. -------------------------------------------------------------------- tIndex = aList.getPos(anItem) if tIndex then return tIndex end if tCount = aList.count repeat with i = 1 to tCount if aList[i] = anItem then return i end if end repeat return 0 end GetIndex on AddToSubList(aPropList, aProp, anItem, aSorted) ------------------- -- INPUT: must be a property list -- may be any Lingo value except VOID -- may be any Lingo value -- ACTION: Adds anItem to the sublist of aPropList which is the -- value of the aProp property. If no aProp property exists -- it will be created. If it does exist, but its value is -- not a linear list, the value will be overwritten with a -- linear list. -------------------------------------------------------------------- tSubList = aPropList.getaProp(aProp) if ilk(tSubList) <> #list then tSubList = [] if aSorted then tSubList.sort() end if aPropList.setaProp(aProp, tSubList) end if tSubList.add(anItem) end AddToSubList --34567890123456789012345678901234567890123456789012345678901234567890