/************************************************************************* EditFile Enhanced EditFile command Author: Ian Campbell (Contributing User) Date: May 2, 1994 (Original: Ian Campbell) Jul 15, 1994 Update. Mar 5, 1995 Revised by Ian Campbell Feb 9, 2002 Made helpdef non-OEM font compliant May 29, 2002 SEM: Added QuotePath to try to handle spaces in filenames. Jul 2003 Updated pickfile footer display to include f10-Menu. Thanks to Jose Adriano Baltieri Oct 2003 SEM: Removed QuotePath in mEditFile, as it broke other code. Thanks to Jose Adriano Baltieri for the report. Jun 21, 2008: Richard Blackburn - fix for quoted filenames - recognize the quotes in routine ParseSpacedWords() SEM - in mSmartEditfile(), do a quick check to see if the file exists. If so, go ahead and load it. This allows filenames with spaces in the name to be loaded without being quoted. Overview: This macro enhances EditFile() by trying to load the file from the current directory first, and if unsuccessful, it then tries the current file's directory. If wildcards are used, two picklists will be presented -- one for the current directory and one for the current file's directory. Simply load this macro and press to pull up the SmartEditFile() prompt. Keys: SmartEditfile() Usage notes: With this macro, you can literally tag and load thousands of filenames in mere seconds (for example, it took just 12 seconds to mark and load all 433 filenames in one of my larger directories). Major features include: 1. File tagging for multiple file loading and deletions. While in an Edit PickFile, multiple files may be tagged by first highlighting a file and then by pressing the SPACEBAR. A tag will appear in the "tag" position of the PickFile in the column along the left. Tagging key alternatives to the SPACEBAR include the GREY PLUS key, the GREY MINUS key, the INSERT key, and the LEFT mouse button. Press ENTER or click the RIGHT mouse button on a filename, to load all of the tagged files, or just the selected file if no tags are present. Both normal files, as well as binary and hex files, may be tagged and loaded in this manner. Press DELETE to delete all tagged files, or just the selected file if no files are tagged. In addition, subdirectories may also be deleted, provided that they are empty. Subdirectories may NOT be tagged. 2. PickFile Sorting If a wildcard is utilized, or if a directory is referenced, then TSE will provide a PickFile showing multiple file choices. This PickFile may now be sorted. Files may be sorted by name, extension, date, or size. 3. Dual Directory Searching If a file is not found in the current directory, and if the current file at the top of TSE's ring of files is in a directory different from the current directory, then an attempt will be made to locate the file(s) in this directory. This action will apply to both single files as will as wildcard PickFiles and with multiple filenames. *************************************************************************/ /* EDITFILE -- EDIT ENHANCEMENT MACRO SOFTWARE: EDITFILE VERSION: 1.11 AUTHOR: Ian Campbell TYPE: External Macro Adapted for TSE Version 2.5 (and tested under beta-2.01y) This is an EXTENSIVE rewrite of the original macro that shipped with TSE version 2.0. It is extremely useful when you want to tag and load (or delete) multiple files, yet it mimics the existing native EditFile() command to near perfection. */ /* Change EXTENDEDHOOK to TRUE if you want the PickFile enhancements to apply globally to all of your other macros' PickFiles. Change I_WANT_SLASH to FALSE if you do NOT want the leading slash in front of your directory names. */ constant EXTENDEDHOOK = FALSE constant I_WANT_SLASH = TRUE constant TAG_COL = 1, // column in pick list of tag ATTR_COL = 2, // column in pick list of attribute DATE_COL = 5, // column in pick list of date SIZE_COL = 7, // column in pick list of size NAME_COL = 11, // column in pick list of name EXT_COL = 21, // column in pick list of extension TAG_CHAR = 26, // character to use for marking TAG_DONE_CHAR = 29 // character to flag a tagged action integer ReverseFlg = 0 integer TagCount = 0 string OptionFlg[64] = "" integer proc mEditFile(string fn) return (EditFile(fn)) end /* Present a message centered on the screen in a box. The message is automatically wrapped to multiple lines if necessary. The "`" character may be used to force a new line if desired. Called by: mSortListFiles(), mDeleteSelectedFile() Enter With: The box tile, and the box contents. Returns: The keystroke that terminated it. */ integer proc mMessageBox(string BoxTitle, string s) integer MyID = GetBufferID() integer TempBuffer = CreateTempBuffer() integer PopX1, PopY1, PopX2, PopY2 integer OldRightMargin = Set(RightMargin, 72) integer WinHeight integer OldCursor = Set(Cursor, OFF) integer ReturnCode = , SizeLongestLine = 0 integer OldAttr = Set(Attr, Color(BRIGHT WHITE ON BLACK)) string s2[80] if TempBuffer HideMouse() InsertText(s) BegFile() while lFind("`", "") DelChar() if CurrCol() <> 1 SplitLine() endif SplitLine() endwhile BegFile() while WrapPara() if CurrLineLen() == 0 KillLine() endif endwhile Begfile() repeat if CurrLineLen() > SizeLongestLine SizeLongestLine = CurrLineLen() endif until not Down() BegFile() if Length(BoxTitle) + 2 > SizeLongestLine SizeLongestLine = Length(BoxTitle) + 2 endif BegFile() WinHeight = NumLines() PopX1 = (((Query(ScreenCols) - (SizeLongestLine)) / 2) - 1) PopX2 = PopX1 + SizeLongestLine + 3 PopY1 = ((Query(ScreenRows) - (WinHeight + 2)) / 2) + 1 PopY2 = PopY1 + WinHeight + 1 PopWinOpen(PopX1, PopY1, PopX2, PopY2, 1, BoxTitle, Query(MenuTextLtrAttr)) ClrScr() While CurrLine() < WinHeight s2 = GetText(1, SizeLongestLine) WriteLine(" " + s2) Down() endwhile s2 = GetText(1, SizeLongestLine) Write(" " + s2) GotoBufferID(MyID) AbandonFile(TempBuffer) Set(RightMargin, OldRightMargin) Set(Attr, OldAttr) ReturnCode = GetKey() ShowMouse() PopWinClose() Set(Cursor, OldCursor) endif return(ReturnCode) end mMessageBox /* This help information is displayed when the user selects "Help" from the EditFile() PickFile prompt. */ helpdef PopupHelp TITLE = "EditFile Help" WIDTH = 65 X = 7 Y = 2 "SORTING" "" " Keys used to invoke the sort:" "" " " " or" " " "" " Files may be sorted by Date, Extension, Name, or Size." "" "" " Some supported Norton Commander shortcut sort keys include:" "" " Sort by Name" " Sort by Extension" " Reverse sort by Date" " Reverse sort by Size" "________________________________________________________________" "" "TAGGING" "" " Keys used for tagging include:" "" " toggle a tag On or Off" " Click the left mouse button to toggle a tag" " ALL tags On" " ALL tags Off" " Current tag On" " Current tag On" " Current tag Off" " Go to First tag" " Go to Last tag" " Go to Next tag" " Go to Previous tag" "________________________________________________________________" "" 'LOADING' '' " Press or click the mouse to load all " " tagged filenames into TSE's ring of files. Control will be" ' returned to the PickFile when this operation has completed.' '' ' If no files are tagged, then the selected file will be' ' loaded and switched to in the normal way.' "________________________________________________________________" '' 'FILE DELETION/DIRECTORY REMOVAL' '' " Press to erase ALL marked files, or the selected" ' file if no files are marked. A confirmation prompt will' ' be issued before any action is taken.' '' ' can also remove a selected subdirectory, provided' ' that it is empty (contains no files or subdirectories). A' ' confirmation prompt will be issued before any action is' ' taken.' "________________________________________________________________" '' 'EXIT' '' ' Press or mouse click on a background window to exit' ' without taking any further action. If any files are tagged,' ' a warning prompt will be presented before the command is' ' accepted.' '' end PopupHelp /* Return the expanded file name from the list, appending the PickFile path to the beginning of the file name. Called by: mAcceptFiles(), mDeleteFiles() Returns the full filename, including the path, from the selected picklist file. */ string proc GetFileName() return (SplitPath(Query(PickFilePath), _DRIVE_ | _PATH_) + PBName()) end GetFileName /* This routine returns the length of an extention if it exists. Called by: mGetFileFromDisk() Enter With: the filename to strip and examine the extention. Returns: the number of characters in the extention. */ integer proc mExtentionLength(string s) return(Length(SplitPath(s, _EXT_))) end mExtentionLength /* Used to toggle the string contents of the Sort Menu. Called by SortMenu() Returns the string based on the state of ReverseFlg. */ string proc ShowSortFlag() return (iif(ReverseFlg & 1, "Descending", "Ascending ")) end ShowSortFlag /* Used to toggle the direction of the sort. Called by SortMenu() */ proc ToggleSortFlag(integer which) if ReverseFlg & which ReverseFlg = ReverseFlg & ~ which else ReverseFlg = ReverseFlg | which endif end ToggleSortFlag /* Provides a sort menu while viewing the list buffer. Filenames may be sorted in a number of ways. Called by: mSort() */ Menu SortMenu() Title = " File Sort Menu " History "&Name" "&Extension" "&Size" "&Date" "", , Divide "Sort &Order" [ShowSortFlag() : 10], ToggleSortFlag(1), DontClose end SortMenu /* Add some info to the bottom of the pickfile window Called by: OnPickFileStartup(), mReTag(), mTagAllFiles(), mDeleteFiles(), mDeleteCurrentPickFile(), mAcceptFiles(), mToggleTags() */ proc FooterMsg(integer index, integer count) string s[50] = " {F1}-Help {Ctrl S}-Sort {Spacebar}-Tag {F10}-Menu {" string TagName[10] = "Tag" + iif(count <> 1, "s ", " ") string FileName[10] = "File" + iif(count <> 1, "s ", " ") case index when 1 ListFooter(s + format(Str(count) + "} " + FileName)) when 2 ListFooter(s + format(Str(count) + "} " + TagName)) when 3 ListFooter("Loading Files, Press {} To Stop {" + format(Str(count) + "} " + TagName)) when 4 ListFooter("Erasing File(s), Press {} To Stop{" + format(Str(count) + "} " + TagName)) endcase end FooterMsg /* Enter while in the PickFile. This macro will position the cursor to the first file (the line just after the directories), and return TRUE. If no filename exists, then the cursor will be placed on the last directory name, and FALSE will be returned. Called by: ValiDateFooterMsg(), mTagAllFiles() */ integer proc mPositionPickFileAtFiles() BegFile() do NumLines() times // check the attribute for the non-directory bit if not (PBAttribute() & 0x10) return(1) endif Down() enddo return(0) end mPositionPickFileAtFiles /* Place the appropriate message at the bottom of the PickFile. Called by: mToggleTags(), mAcceptFiles(), mDeleteFiles(), mTagAllFiles(), mReTag() */ proc ValiDateFooterMsg() integer FileStartLine if TagCount FooterMsg(2, TagCount) else PushPosition() FileStartLine = iif(mPositionPickFileAtFiles(), CurrLine(), CurrLine() + 1) FooterMsg(1, NumLines() + 1 - FileStartLine) PopPosition() endif end ValiDateFooterMsg /* Sort the PickFile, and center the selected filename Called by: AdditionalKeys */ proc mSort(integer NeedMenu) string PickFn[80] integer OldY1 = Set(Y1, ((Query(ScreenRows) - 6) / 2) - 4) integer OldX1 = Set(X1, (Query(ScreenCols) - 23) / 2) integer Row = CurrRow() PickFn = GetText(NAME_COL, 13) if NeedMenu case SortMenu() when 1 // by name Set(PickFileSortOrder, iif(ReverseFlg, "Fe", "xfe")) when 2 // by extension Set(PickFileSortOrder, iif(ReverseFlg, "Ef", "xef")) when 3 // by size Set(PickFileSortOrder, iif(ReverseFlg, "Sfe", "xsfe")) when 4 // by date Set(PickFileSortOrder, iif(ReverseFlg, "DTfe", "xdtfe")) endcase endif Sort(_PICK_SORT_) // sort it now lFind(PickFn, "G") ScrollToRow(Row) Set(X1, OldX1) Set(Y1, OldY1) end mSort /* Place/remove the appropriate tag to the left of the selected filename in the filelist. Track the tag count on the bottom of the PickFile window. Called by: mLeftBtn(), AdditionalKeys */ proc mToggleTags(integer function) integer CurrentPosition = CurrPos() string tag[1] = GetText(TAG_COL, 1) tag = iif(tag == Chr(TAG_DONE_CHAR), ' ', tag) if not (PBAttribute() & 0x10) // avoid directory tagging BegLine() case function when 0 // tag off InsertText(" ", _OVERWRITE_) when 1 // tag on InsertText(Chr(TAG_CHAR), _OVERWRITE_) when 2 // toggle tag InsertText(iif(tag == Chr(TAG_CHAR), " ", Chr(TAG_CHAR)), _OVERWRITE_) endcase if tag <> GetText(TAG_COL, 1) TagCount = iif(tag <> Chr(TAG_CHAR), TagCount + 1, TagCount - 1) endif GotoPos(CurrentPosition) endif ValiDateFooterMsg() end mToggleTags /* Load all tagged files into TSE's ring of files, and update the tag count at the bottom of the window. If no files are tagged, then pass the name of the selected file and exit the pickfile. Called by: mRightBtn(), AdditionalKeys */ proc mAcceptFiles() integer id = GetBufferID() string CurrentFileName[_MAXPATH_] integer row = CurrRow() if TagCount PushPosition() BegFile() lFind(Chr(TAG_CHAR), "^") repeat ScrollToRow(row) UpdateDisplay() if not TagCount break endif // file tagged? if GetText(TAG_COL, 1) == Chr(TAG_CHAR) BegLine() TagCount = TagCount - 1 FooterMsg(3, TagCount) PrevFile(_DONT_LOAD_) // get the current file's filename CurrentFileName = CurrFilename() GotoBufferID(id) // back to pickfile mEditFile(CurrentFileName + " " + iif(Length(OptionFlg), OptionFlg + " ", "") + GetFileName()) GotoBufferID(id) // back to the control buffer BegLine() InsertText(Chr(TAG_DONE_CHAR), _OVERWRITE_) endif if KeyPressed() if GetKey() == break endif endif until not Down() PopPosition() else // accept the single selection and edit that file EndProcess(1) endif ValiDateFooterMsg() end mAcceptFiles /* Delete the selected filename in the PickFile and mark it with an '*'. Called by: mDeleteFiles() Returns TRUE if the file is deleted, otherwise FALSE. */ integer proc mDeleteCurrentPickFile(string file) FooterMsg(4, TagCount) if not EraseDiskFile(file) warn('Error, Unable to delete "' + file +'"') return(FALSE) else BegLine() InsertText(Chr(TAG_DONE_CHAR), _OVERWRITE_) endif return(TRUE) end mDeleteCurrentPickFile /* Rebuild the pick buffer. This in used when the file content changes, currently in response to file(s) deletion. Called by: mDeleteFiles() Returns TRUE if at least one matching file was found, otherwise FALSE */ integer proc RebuildPickBuffer() PushPosition() EmptyBuffer() BuildPickBufferEx(Query(PickFilePath), _NORMAL_|_READONLY_|_ARCHIVE_|_DIRECTORY_) Sort(_PICK_SORT_) PopPosition() return(NumLines()) end RebuildPickBuffer /* Delete all tagged files, or the highlighted file or directory if no files are tagged. Called by: AdditionalKeys */ proc mDeleteFiles() string s[80], tag[1] integer row = CurrRow() if TagCount loop case mMessageBox("EditFile", "Preparing to delete ALL marked files`" + "`OK to proceed (y/n?)") when , , , break when , PushPosition() BegFile() lFind(Chr(TAG_CHAR), "^") repeat ScrollToRow(row) UpdateDisplay() if not TagCount break endif // file tagged with a ctrl-z? if GetText(TAG_COL, 1) == Chr(TAG_CHAR) if mDeleteCurrentPickFile(GetFileName()) TagCount = TagCount - 1 endif endif if KeyPressed() if GetKey() == break endif endif until not Down() PopPosition() if not RebuildPickBuffer() EndProcess() endif break endcase endloop else // one single file or directory // a directory? if (PBAttribute() & 0x10) if not Pos(".", GetText(NAME_COL, 13)) // get the directory name (null terminated) s = GetFileName() + Chr(0) loop case mMessageBox("EditFile", 'Remove directory: "' + s[1:Length(s) - 1] + '" (y/n?)') when , , , break when , if RmDir(s) if not RebuildPickBuffer() EndProcess() endif endif break endcase endloop endif else // a filename s = GetFileName() loop case mMessageBox("EditFile", 'Delete "' + s + '" (y/n?)') when , , , break when , // get the current tag state tag = GetText(TAG_COL, 1) if mDeleteCurrentPickFile(s) // file was tagged? if tag == Chr(TAG_CHAR) TagCount = TagCount - 1 endif if not RebuildPickBuffer() EndProcess() endif endif break endcase endloop endif endif ValiDateFooterMsg() end mDeleteFiles /* Tag all filenames in the pickfile Called by: AdditionalKeys */ proc mTagAllFiles(integer TagChar) PushBlock() UnmarkBlock() // clear any possible existing block PushPosition() if mPositionPickFileAtFiles() // get past the directories at the top MarkColumn(CurrLine(), TAG_COL, NumLines(), TAG_COL) TagCount = iif(TagChar == TAG_CHAR, NumLines() - CurrLine() + 1, 0) // fill with either TAG_CHAR or space FillBlock(Chr(TagChar)) endif PopPosition() PopBlock() ValiDateFooterMsg() end mTagAllFiles /* Count any files with a TAG_DONE_CHAR in the tag column, and update the footer message when done. Called by: */ proc mCountTags() PushPosition() TagCount = 0 BegFile() if lFind(Chr(TAG_CHAR), "^") repeat TagCount = TagCount + 1 until not lFind(Chr(TAG_CHAR), "^+") endif PopPosition() ValiDateFooterMsg() end mCountTags /* Retag any files with an "*" in the tag column. Called by: Assigned to a key in AdditionalKeys */ proc mReTag() PushPosition() lReplace(Chr(TAG_DONE_CHAR), Chr(TAG_CHAR), "^gn") mCountTags() PopPosition() end mReTag /* Assert a QuickHelp type screen. The text is recolored to bright white on black, and the border is recolored to bright red on black. Called by: keydef AdditionalKeys */ proc mPopupHelp() integer OldMenuTextAttr = Set(MenuTextAttr, Color(BRIGHT WHITE ON BLACK)) integer OldMenuBorderAttr = Set(MenuBorderAttr, Color(BRIGHT RED ON BLACK)) QuickHelp(PopUpHelp) Set(MenuTextAttr, OldMenuTextAttr) Set(MenuBorderAttr, OldMenuBorderAttr) end mPopupHelp /* Drop the pickfile list, usually in response to an escape key. Called by: AdditionalKeys, mRightBtn(), mLeftBtn() */ proc mDropPickFile() if TagCount loop case mMessageBox("EditFile", "Abandon tagged files (y/n?)") when , , , break when , EndProcess() break endcase endloop else EndProcess() endif end mDropPickFile /* This handles the left mouse click while in the PickFile. Called by: AdditionalKeys */ proc mLeftBtn() ProcessHotSpot() if Query(MouseY) if (MouseWindowID()) mToggleTags(2) else mDropPickFile() endif else mDropPickFile() endif end mLeftBtn /* This handles the right mouse click while in the PickFile. Called by: AdditionalKeys */ proc mRightBtn() ProcessHotSpot() if Query(MouseY) if (MouseWindowID()) mAcceptFiles() else mDropPickFile() endif else mDropPickFile() endif end mRightBtn /* Find one of the following: 1. the first tag 2. the last tag 3. the next tag 4. the previous tag Called by: AdditionalKeys */ proc mFindTag(integer key) integer row if TagCount row = CurrRow() case key when lFind(Chr(TAG_CHAR), '^g') when lFind(Chr(TAG_CHAR), '^bg') when lFind(Chr(TAG_CHAR), '^+') when lFind(Chr(TAG_CHAR), '^b') endcase ScrollToRow(row) UpdateDisplay() endif end mFindTag /* These keys are linked in during the pickfile Called by: OnPickFileStartup() */ keydef AdditionalKeys mSort(TRUE) mSort(TRUE) Set(PickFileSortOrder, "fe") mSort(FALSE) // name Set(PickFileSortOrder, "ef") mSort(FALSE) // extension Set(PickFileSortOrder, "DTfe") mSort(FALSE)// rev size Set(PickFileSortOrder, "Sfe") mSort(FALSE) // rev date mToggleTags(2) Down() mToggleTags(1) Down() mToggleTags(1) Down() mToggleTags(1) Down() mToggleTags(0) Down() mDeleteFiles() mDeleteFiles() mAcceptFiles() mAcceptFiles() mTagAllFiles(TAG_CHAR) mTagAllFiles(Asc(" ")) mReTag() mFindTag() mFindTag() mFindTag() mFindTag() mLeftBtn() mRightBtn() mDropPickFile() mPopupHelp() end AdditionalKeys /* Color the directory name on the top line. Add an appropriate footer message to the bottom line. Reselect the filename on any restart condition. Called by: Hooked to _PICKFILE_STARTUP_ in routine mPickFile() */ proc OnPickFileStartup() // string TitleMessage[80] = Lower(Query(PickFilePath)) TagCount = 0 // VGotoXY(Query(WindowX1) // + ((Query(WindowCols)) / 2) // - ((Length(TitleMessage) / 2)+ Length(TitleMessage) Mod 2), // Query(WindowY1) - 1) // PutHelpLine(TitleMessage) // highlight the title ValiDateFooterMsg() // output a footer message Enable(AdditionalKeys) end OnPickFileStartup /* Assert the pickfile list, and handle any restart requests. Called by: mGetFileFromDisk() Returns the PickFile string selected, or an empty string if escape was pressed. */ string proc mPickFile(string pf) integer OldY1 = Query(Y1) string PickString[_MAXPATH_] Set(Y1, iif(Query(StatusLineAtTop), 2, 1)) if not EXTENDEDHOOK Hook(_PICKFILE_STARTUP_, OnPickFileStartup) endif PickString = PickFile(pf) if not EXTENDEDHOOK UnHook(OnPickFileStartup) endif Set(Y1, OldY1) return(PickString) end mPickFile /* CurrFilePath returns the drive and path from the current filename Called by: mResolveFilename() Enter With: nothing Returns: a string with the drive and path information. */ string proc CurrFilePath() return(SplitPath(CurrFilename(), _DRIVE_ | _PATH_ )) end CurrFilePath /* Take a string of words separated by spaces, parse and return them one word at a time. Update the index, i, to point to the next word. Eat the spaces. Called by: mGetFileFromDisk() and mSmartEditfile() Returns the next word indexed via i in string s. */ string proc ParseSpacedWords(string s, var integer i) string wordst[_MAXPATH_] = "" integer quoted = false // first, eat all leading spaces while i <= Length(s) if s[i] <> ' ' break endif i = i + 1 endwhile // extract the word, character by character while i <= Length(s) if s[i] == '"' quoted = NOT quoted endif if ( s[i] == ' ' ) and ( not quoted ) break else wordst = wordst + s[i] endif i = i + 1 endwhile return(wordst) end ParseSpacedWords /* Check to see if the file exists on disk, and return the filename if it exists. Take the DefaultExt variable into account if no extension is given. Called by: mResolveFilename() Enter With: the filename string to check. Returns: a qualified filename if it exists, otherwise an empty string. */ string proc mGetFileFromDisk(string fn, var integer PickFilePresented) string name[_MAXPATH_] string DefaultExt[60] string SingleExt[4] integer DefaultIndex name = Splitpath(fn, _NAME_ | _EXT_) // first, check for a wildcard needed for the PickFile if Pos('*', name) or Pos('?', name) if FileExists(fn + iif(mExtentionLength(fn) == 0, ".*", "")) PickFilePresented = TRUE return(mPickFile(fn)) else return("") // file not found endif endif if mExtentionLength(fn) // extension? if FileExists(fn) return(fn) endif else // try to use extension defaults DefaultExt = Query(DefaultExt) DefaultIndex = 1 // point to the start of the list loop SingleExt = ParseSpacedWords(DefaultExt, DefaultIndex) if Length(SingleExt) if FileExists(fn + "." + SingleExt) return(fn + "." + SingleExt) endif else // end of list return("") // file not found endif endloop endif return("") // file not found end mGetFileFromDisk /* An attempt is made to find the file on the disk in the current subdirectory. If unsuccessful, then a further attempt will be made to find the file, this time using the same directory as the file currently at the top of the editor's ring. If the user aborts the file (via a PickFile), then then return an empty string. Called by: mSmartEditfile() Enter With: string with the requested filename Returns: the filename string or else an empty string */ string proc mResolveFilename(STRING fn) string filename[_MAXPATH_] = fn string fn1[_MAXPATH_] string fn2[_MAXPATH_] integer PickFilePresented = FALSE if isDirSeparator(filename[Length(filename)]) or filename[Length(filename)] == ':' filename = filename + "*.*" elseif (FileExists(filename) & _DIRECTORY_) if not Length(SplitPath(filename, _EXT_)) if filename[Length(filename)] == '*' filename = filename + ".*" else filename = filename + GetDirSeparator() + "*.*" endif endif endif fn1 = mGetFileFromDisk(filename, PickFilePresented) if not Length(fn1) // if no drive or path specified, try the current file's drive & path if not Length(SplitPath(filename , _DRIVE_ | _PATH_)) // if current path different from the filename's path, try again! if CurrFilePath() <> SplitPath(ExpandPath(filename), _DRIVE_ | _PATH_) // see if file exists in the same directory as the current file fn2 = CurrFilePath() + SplitPath(filename, _NAME_ | _EXT_) fn1 = mGetFileFromDisk(fn2, PickFilePresented) endif endif endif if not Length(fn1) if Pos('*', filename) or Pos('?', filename) // if a wildcard, then just return the empty string if not PickFilePresented warn("File not found:" + filename) endif return(fn1) else // must be a new file - just pass the original filename back return(filename) endif endif return(fn1) end mResolveFilename /* Used for tab-key filename expansion. Called by: mPromptStartup */ keydef PromptKeys EndProcess(2) // tab key pressed end PromptKeys /* This routine adds the tab keydef for use in filename expansion. Hooked to _PROMPT_STARTUP_ in mSmartEditfile() */ proc mPromptStartup() Enable(PromptKeys) end mPromptStartup /* This macro routine asserts its Ask box, requesting "File(s) to edit". The intention is that this box mimic the original EditFile, which this routine replaces, as much as possible in order to maintain user familiarity. This includes multiple filenames and tab completion. If no path is specified, an attempt will be made to load the file off the disk in the current directory. If unsuccessful, then a further attempt will be made to load the file, this time using the same directory as the file currently at the top of the editor's ring. Multifile selection via PickFile tagging is provided, as is multifile PickFile sorting. Called by: A keyboard assignment. This is a replacement for EditFile() (default = . Notes: This macro is a replacement for "EditFile()" which, by default, is assigned to Alt-E. */ proc mSmartEditfile() integer ReturnFlg string EditString[255] = "" string fn[_MAXPATH_] string fn2[_MAXPATH_] string fn3[_MAXPATH_] string FirstFile[_MAXPATH_] = "" integer i, SuccessFlg = FALSE, ForceOnePickFile = FALSE, attribute AskAgain: fn = "" fn2 = fn fn3 = fn Hook(_PROMPT_STARTUP_, mPromptStartup) ReturnFlg = Ask("File(s) to edit:", EditString, _EDIT_HISTORY_) UnHook(mPromptStartup) EditString = Lower(Trim(EditString)) if not Length(EditString) ForceOnePickFile = TRUE endif // First, check to see if the tab key was pressed while in the prompt if ReturnFlg == 2 i = 1 loop fn2 = ParseSpacedWords(EditString, i) if Length(fn2) fn3 = fn3 + fn + " " fn = fn2 else fn2 = SplitPath(fn, _EXT_) if Length(fn2) case fn2[Length(fn2)] when "*" // nothing more to do otherwise fn = fn + "*" endcase else fn = fn + "*.*" endif fn2 = mResolveFilename(fn) if Length(fn2) EditString = fn3 + fn2 break endif goto AskAgain endif endloop endif // now parse the entire prompt line and sequentially process all items if ReturnFlg // see if the file as entered exists on disk attribute = FileExists(EditString) // and make sure it isn't a directory if attribute and (attribute & _DIRECTORY_) == 0 FirstFile = QuotePath(EditString) goto edit_the_file endif i = 1 loop fn2 = ParseSpacedWords(EditString, i) fn = fn2 if not Length(fn) if not SuccessFlg if ForceOnePickFile ForceOnePickFile = FALSE fn = "*.*" else goto AskAgain endif else break endif endif OptionFlg = "" // initialize the flag OptionSpin: if fn[1] == "-" or fn[1] == "/" if Length(fn) > 1 case lower(fn[2]) when 'l' LoadMacro(Trim(fn[3:Length(fn)])) when 'e' ExecMacro(Trim(fn[3:Length(fn)])) otherwise // combine all options into one string OptionFlg = OptionFlg + iif(Length(OptionFlg), " ", "") + fn endcase // get the next word fn = ParseSpacedWords(EditString, i) goto OptionSpin endif endif fn2 = "" if Length(fn) fn2 = mResolveFilename(fn) if Length(fn2) if Length(OptionFlg) fn2 = OptionFlg + " " + fn2 endif AddHistoryStr(fn2, _EDIT_HISTORY_) if not SuccessFlg SuccessFlg = TRUE FirstFile = fn2 else mEditFile(CurrFilename() + " " + fn2) endif endif endif endloop edit_the_file: AddHistoryStr(EditString, _EDIT_HISTORY_) if (Length(FirstFile)) mEditFile(FirstFile) endif endif OptionFlg = "" // clean up for external hooks end mSmartEditfile /* This routine is called when TSE loads EDITFILE. It sets up a permanent _PICKFILE_STARTUP_ hook if constant EXTENDEDHOOK has been changed from FALSE to TRUE. This allows file tagging, sorting, and file deletion capabilities to globally extend to other macros besides EDITFILE. */ proc WhenLoaded() if I_WANT_SLASH Set(PickFileFlags, _ADD_SLASH_ | _DIRS_AT_TOP_) else Set(PickFileFlags, _DIRS_AT_TOP_) endif // set up a permanent hook if constant EXTENDEDHOOK is changed to TRUE if EXTENDEDHOOK Hook(_PICKFILE_STARTUP_, OnPickFileStartup) endif end WhenLoaded /* This routine is called when TSE first executes EDITFILE. The following command may be used to remotely connect EDITFILE.MAC to TSE's UI (and the mouse), or to another macro altogether: ExecMacro("editfile") Called by: TSE when it first executes the macro. */ proc Main() mSmartEditfile() end Main /* Here is where the key is assigned. Called when the user presses the key combination. */ mSmartEditfile()