/****************************** Module Header ******************************* * * Module Name: filelist.e * * Copyright (c) Netlabs EPM Distribution Project 2004 * * $Id$ * * =========================================================================== * * This file is part of the Netlabs EPM Distribution package and is free * software. You can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * Netlabs EPM Distribution. This library is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * ****************************************************************************/ /* Todo: - defproc RingAddToHistory doesn't work for wildcards in filespec; why? */ ; - Numbering files in the ring to provide the 'File # of ##' or '#/##' field ; for the titletext or statusbar ; - Save and restore a ring to/from NEPMD.INI ; - History definitions to track commands or files from EDIT, LOAD and SAVE ; --------------------------------------------------------------------------- ; RingAutoSavePos is called by 'quit' and 'ProcessAfterload'. defproc RingAutoSavePos universal CurEditCmd universal RingSavePosDisabled universal RingSavePosMaxFilesReached -- used only here, maybe set by a previous call -- Don't overwrite old ring if Disabled flag set (e.g. by RestoreRing) if RingSavePosDisabled = 1 then return endif -- Don't overwrite old ring if only .Untitled added if CurEditCmd = '' then return endif -- The CurEditCmd check avoids that situation already, but it won't hurt: -- Don't overwite old ring if only .Untitled in ring IsUnnamed = (.filename = GetUnnamedFileName()) if filesinring() = 1 & IsUnnamed then return; endif -- Check if Enabled KeyPath = '\NEPMD\User\AutoRestore\Ring\SaveLast' Enabled = QueryConfigKey( KeyPath) if Enabled <> 1 then return endif -- Handle upper limit for amount of files to save in ini KeyPath = '\NEPMD\User\AutoRestore\Ring\MaxFiles' MaxFiles = QueryConfigKey( KeyPath) if not isnum(MaxFiles) then MaxFiles = 30 endif if filesinring() > MaxFiles then -- Check if MaxFiles was already reached before for this edit window -- to suppress the msg for following files if RingSavePosMaxFilesReached <> 1 then -- Give that msg only once sayerror 'Number of files in ring exceeds max = 'MaxFiles'.' || ' Ring not saved.' RingSavePosMaxFilesReached = 1 endif return else RingSavePosMaxFilesReached = 0 endif dprintf( 'RESTORE_RING', 'call RingSavePos from RingAutoSavePos') call RingSavePos() return ; --------------------------------------------------------------------------- ; RingSavePos is called by RingAutoSavePos and by defc SaveRing, used by the ; menuitem 'Save as last ring', by defc Restart etc. defproc RingSavePos KeyPath = '\NEPMD\User\AutoRestore\Ring\MaxRings' MaxRings = QueryConfigKey( KeyPath) if not isnum(MaxRings) then MaxRings = 1 endif -- Handle MaxRings = 0 if MaxRings = 0 then return endif -- Get EPM EFrame window handle hwnd = '0x'ltoa( gethwndc(6), 16) -- EPMINFO_EDITFRAME WorkDir = directory() ThisNumber = 1 LastNumber = '' -- Search hwnd in SavedRings FoundRing = 0 do r = 1 to MaxRings -- Search hwnd in '\NEPMD\Var\SavedRings\'r'\hwnd' ThisNumber = r KeyPath = '\NEPMD\Var\SavedRings\'ThisNumber next = QueryConfigKey( KeyPath'\hwnd') if next = '' then iterate endif if next = hwnd then -- If found: KeyPath is already set FoundRing = r leave endif enddo if FoundRing = 0 then -- if current hwnd not found -- Get ThisNumber KeyPath = '\NEPMD\Var\SavedRings\LastNumber' LastNumber = QueryConfigKey( KeyPath) if LastNumber = '' or LastNumber >= MaxRings then -- if no value or MaxRings reached ThisNumber = 1 else -- Check if LastNumber has 0 Entries KeyPath = '\NEPMD\Var\SavedRings\'LastNumber next = QueryConfigKey( KeyPath'\Entries') if next = 0 then -- if Entries for LastNumber = 0 -- Replace LastNumber ThisNumber = LastNumber else -- Iterate number ThisNumber = LastNumber + 1 endif endif endif KeyPath = '\NEPMD\Var\SavedRings\'ThisNumber -- Delete old 'File'i and 'Posn'i -- This is always required. do i = 1 to 1000 -- just an upper limit to prevent looping forever DeleteConfigKey( KeyPath'\File'i) rc1 = rc DeleteConfigKey( KeyPath'\Posn'i) rc2 = rc if (rc1 <> 0 & rc2 <> 0) then leave endif enddo if ThisNumber <> LastNumber then -- Write LastNumber KeyPath = '\NEPMD\Var\SavedRings\LastNumber' WriteConfigKey( KeyPath, ThisNumber) endif KeyPath = '\NEPMD\Var\SavedRings\'ThisNumber if FoundRing = 0 then -- Set KeyPath and write hwnd WriteConfigKey( KeyPath'\hwnd', hwnd) endif -- Write WorkDir WriteConfigKey( KeyPath'\WorkDir', WorkDir) -- Write new 'File'i and 'Posn'i getfileid startfid -- Select next file to make current file topmost after restore next_file getfileid firstfid i = 0 -- Loop through all files in ring dprintf( 'RINGCMD', 'RingSavePos') do f = 1 to filesinring(1) -- Provide an upper limit; prevent looping forever -- Skip temp. files fIgnore = ((leftstr( .filename, 1) = '.') | (not .visible)) if not fIgnore then -- Write 'File'i and 'Posn'i for every file i = i + 1 WriteConfigKey( KeyPath'\File'i, .filename) WriteConfigKey( KeyPath'\Posn'i, .line .col .cursorx .cursory) endif next_file getfileid fid if fid = firstfid then leave; endif enddo -- Write 'Entries' (ammount of 'File'i and 'Posn'i) WriteConfigKey( KeyPath'\Entries', i) -- Check if file to be activated is still in ring if wordpos( ValidateFileid( startfid), '1 2') then activatefile startfid endif return ; --------------------------------------------------------------------------- defc SaveRing, RingSavePos call RingSavePos() ; --------------------------------------------------------------------------- defc RestoreRing universal RestorePosDisabled universal RingSavePosDisabled universal SelectDisabled KeyPath = '\NEPMD\User\AutoRestore\Ring\MaxRings' MaxRings = QueryConfigKey( KeyPath) if not isnum(MaxRings) then MaxRings = 1 endif RestorePosDisabled = 1 RingSavePosDisabled = 1 LastNumber = arg(1) if LastNumber = '' then KeyPath = '\NEPMD\Var\SavedRings\LastNumber' LastNumber = QueryConfigKey( KeyPath) endif hwnd = '0x'ltoa( gethwndc(6), 16) -- EPMINFO_EDITFRAME KeyPath = '\NEPMD\Var\SavedRings\'LastNumber LastNumberhwnd = QueryConfigKey( KeyPath'\hwnd') if hwnd = LastNumberhwnd then LastNumber = LastNumber - 1 if LastNumber = 0 then do r = MaxRings to 1 by -1 KeyPath = '\NEPMD\Var\SavedRings\'r Entries = QueryConfigKey( KeyPath'\Entries') if Entries = '' then iterate else LastNumber = r endif enddo endif endif -- Get last amount of entries KeyPath = '\NEPMD\Var\SavedRings\'LastNumber LastEntries = QueryConfigKey( KeyPath'\Entries') if LastEntries = 0 then SelectDisabled = 0 else SelectDisabled = 1 endif do i = 1 to LastEntries if i = LastEntries then SelectDisabled = 0 endif filename = QueryConfigKey( KeyPath'\File'i) savedpos = QueryConfigKey( KeyPath'\Posn'i) OpenNewWindow = 0 --OpenNewWindow = 1 if OpenNewWindow = 1 then dprintf( 'RESTORE_RING', "i = "i", o 'restorering "LastNumber"'") -- Problems here: "o 'restorering "LastNumber"'" return else dprintf( 'RESTORE_RING', 'i = 'i', e "'filename'"'||" 'restorepos "savedpos"'") if pos( ' ', filename) then filename = '"'filename'"' endif CurEditCmd = 'RESTORERING' 'e 'filename if rc = 0 then getfileid lastfid 'restorepos 'savedpos endif endif enddo WorkDir = QueryConfigKey( KeyPath'\WorkDir') if NepmdDirExists( WorkDir) = 1 then call directory( '\') call directory( WorkDir) endif RingSavePosDisabled = '' RestorePosDisabled = '' ; --------------------------------------------------------------------------- ; Delete all 'SavedRings' entries in NEPMD.INI ; Used to clean NEPMD.INI for testing only. defc DelSavedRings KeyPath = '\NEPMD\Var\SavedRings' DeleteConfigKey( KeyPath'\LastNumber') do r = 1 to 1000 -- just an upper limit to prevent looping forever DeleteConfigKey( KeyPath'\'r'\Entries') rc1 = rc DeleteConfigKey( KeyPath'\'r'\hwnd') rc2 = rc DeleteConfigKey( KeyPath'\'r'\WorkDir') rc3 = rc if (rc1 <> 0) & (rc2 <> 0) & (rc3 <> 0) then iterate endif do i = 1 to 1000 -- just an upper limit to prevent looping forever DeleteConfigKey( KeyPath'\'r'\File'i) rc1 = rc DeleteConfigKey( KeyPath'\'r'\Posn'i) rc2 = rc if (rc1 <> 0 & rc2 <> 0) then leave endif enddo -- i enddo -- r ; --------------------------------------------------------------------------- defc RingMaxFiles KeyPath = '\NEPMD\User\AutoRestore\Ring\MaxFiles' -- if executed with a num as arg if arg(1) <> '' & isnum(arg(1)) then WriteConfigKey( KeyPath, arg(1)) if isadefproc( 'MenuText_maxfilessavering') then MenuText_maxfilessavering() endif return endif -- else open entrybox Title = 'Set limit for Auto-save last ring ' -- add. spaces to fit the title into width Text = 'Enter max. number of files' IniValue = QueryConfigKey( KeyPath) DefaultButton = 1 parse value entrybox( Title, '/~Set/~Reset/Cancel', -- max. 4 buttons IniValue, '', 260, atoi(DefaultButton) || atoi(0000) || -- help id gethwndc(APP_HANDLE) || Text) with Button 2 NewValue \0 -- strip + or - if Button = \1 then 'RingMaxFiles' NewValue return elseif Button = \2 then DeleteConfigKey( KeyPath) return elseif Button = \3 then return endif ; --------------------------------------------------------------------------- ; Called by ProcessAfterload if filestoloadmax is less than a limit. defproc RingAddToHistory universal LoadDisabledFid KeyPath = '\NEPMD\User\History' Enabled = QueryConfigKey( KeyPath) if Enabled <> 1 then return endif --MaxLength = 65536 -- for ini entry MaxLength = 1599 -- for ETK strings MaxItems = 200 Delim = \1 -- \0 doesn't work for NepmdWriteConfigValue ListName = upcase( arg(1)) if ListName = '' then ListName = 'LOAD' endif if ListName = 'LOAD' then KeyPath = '\NEPMD\Var\History\Load' else return endif History = QueryConfigKey( KeyPath) getfileid startfid -- Select next file to make current file the first in list next_file getfileid firstfid -- Loop through all files in ring dprintf( 'RINGCMD', 'RingAddToHistory 'ListName', LoadDisabledFid = 'LoadDisabledFid) do f = 1 to filesinring(1) -- Provide an upper limit; prevent looping forever NewItem = .filename rest = History -- Skip temp files -- Additional disk access in this loop makes EPM crash for -- 110 files in the ring, so better avoid the exist check --fIgnore = ((leftstr( NewItem, 1) = '.') | (not .visible) | -- NepmdFileExists( NewItem) = 0) fIgnore = ((leftstr( NewItem, 1) = '.') | (not .visible)) if not fIgnore then -- Add NewItem i = 1 History = NewItem''Delim -- first item is NewItem fStopList = 0 do while (rest <> '' & fStopList = 0) -- Maybe append next item parse value rest with next (Delim) rest NewHistory = History''next''Delim -- Check length of string first len = length(NewHistory) + 1 if i >= MaxItems then fStopList = 1 elseif len > MaxLength then fStopList = 1 -- Append current item only if <> NewItem elseif upcase(next) <> upcase(NewItem) then -- Add next i = i + 1 History = NewHistory endif enddo -- while endif -- not fIgnore next_file getfileid fid if fid = firstfid then leave endif enddo -- f = 1 to filesinring() activatefile startfid -- required call WriteConfigKey( KeyPath, History) return ; --------------------------------------------------------------------------- ; This won't add an entry while the CommandLine is open. defproc AddToCommandBox( NewItem) universal app_hini MaxLength = 1599 -- for ETK strings Delim = \0 MarkupString = '.** CommandBox'\0 LenMarkup = length( MarkupString) -- History items are saved reversed, MarkupString''OldestItem -> NewestItem''MarkupString. History = QueryProfile( app_hini, 'EPM', 'CommandBox') LenHistory = length( History) if History = '' then -- nop -- Check presence of markup string elseif leftstr( History, LenMarkup) <> MarkupString | rightstr( History, LenMarkup) <> MarkupString then return else -- Strip MarkupString History = substr( History, LenMarkup + 1, LenHistory - (2 * LenMarkup)) endif -- Append NewItem to History History = History''NewItem''Delim -- Reverse history string. RevHistory = reverse( History) -- Limit string to MaxLength, separate at history entries rest = RevHistory NewHistory = '' do forever if rest = '' then leave endif -- Get next item parse value rest with next (Delim) rest if next = '' then iterate endif -- Check length if length( NewHistory) + 1 + length( next) > MaxLength - (2 * LenMarkup) then leave endif if next = '' then iterate endif -- Append delimiter and next item to reversed history NewHistory = NewHistory''Delim''next enddo RevHistory = NewHistory -- Reverse history string back History = reverse( RevHistory) -- Re-add MarkupString History = MarkupString''History''MarkupString -- Write to ini call SetProfile( app_hini, 'EPM', 'CommandBox', History) return ; --------------------------------------------------------------------------- ; Called by edit and save. defproc AddToHistory( Listname, NewItem) ListName = upcase( Listname) if ListName = 'COMMANDLINE' then call AddToCommandBox( NewItem) return endif KeyPath = '\NEPMD\User\History' Enabled = QueryConfigKey( KeyPath) if Enabled <> 1 then return endif --MaxLength = 65536 -- for ini entry MaxLength = 1599 -- for ETK strings MaxItems = 200 Delim = \1 fIgnore = 1 if ListName = 'LOAD' then KeyPath = '\NEPMD\Var\History\Load' fIgnore = (leftstr( NewItem, 1) = '.') elseif ListName = 'SAVE' then KeyPath = '\NEPMD\Var\History\Save' fIgnore = (leftstr( NewItem, 1) = '.') elseif ListName = 'EDIT' then KeyPath = '\NEPMD\Var\History\Edit' fIgnore = 0 endif if fIgnore then return endif History = QueryConfigKey( KeyPath) rest = History -- Add NewItem i = 1 History = NewItem''Delim -- first item is NewItem fStopList = 0 do while (rest <> '' & fStopList = 0) -- Maybe append next item parse value rest with next (Delim) rest NewHistory = History''next''Delim -- Check length of string first len = length(NewHistory) + 1 if i >= MaxItems then fStopList = 1 elseif len > MaxLength then fStopList = 1 -- Append current item only if <> NewItem elseif upcase(next) <> upcase(NewItem) then -- Add next i = i + 1 History = NewHistory endif enddo -- while WriteConfigKey( KeyPath, History) return ; --------------------------------------------------------------------------- ; For use with postme. defc AddToHistory parse arg ListName NewItem call AddToHistory( ListName, NewItem) ; --------------------------------------------------------------------------- defc History Delim = \1 ListName = strip( upcase( arg(1))) if ListName = '' then ListName = 'LOAD' endif if ListName = 'LOAD' then KeyPath = '\NEPMD\Var\History\Load' Title = 'Select a file from LOAD history' elseif ListName = 'SAVE' then KeyPath = '\NEPMD\Var\History\Save' Title = 'Select a file from SAVE history' elseif ListName = 'EDIT' then KeyPath = '\NEPMD\Var\History\Edit' Title = 'Select a file from EDIT history' else sayerror '"'ListName'" is not a valid arg for HISTORY. Specify one of LOAD, SAVE or EDIT.' return endif History = QueryConfigKey( KeyPath) HistoryList = Delim''History HistoryList = strip( HistoryList, 'T', Delim) -- Open Listbox -- No Linebreak allowed in Text Text = 'Press "Open" to load selected file into the current window.' -- Default selected item Selection = 1 -- Window coordinates in lines and columns (0 or '' are default values) top_lines = 0 --5 -- default is below or above cursor left_cols = 0 --5 -- default is at cursor -- top_lines = 0 and left_cols = 0 defaults to: -- Try to open the window at cursor, within the edit window. -- This gives the best result for larger windows, even for -- 10.System Proportional as dialog font. height_lines = min( 20, .windowheight) -- default is 4 visible entries in listbox width_cols = min( 80, .windowwidth) -- default depends on max. width of Title, Text, buttons refresh select = listbox( Title, HistoryList, '/~Open/O~pen.../Open ~folder/Cancel', -- buttons top_lines, left_cols, height_lines, width_cols, gethwnd(APP_HANDLE) || atoi(Selection) || atoi(1) || atoi(0) || Text\0) refresh -- Check result button = asc(leftstr( select, 1)) EOS = pos( \0, select, 2) -- CHR(0) signifies End Of String select= substr( select, 2, EOS - 2) -- Edit or open filename if ListName = 'LOAD' | ListName = 'SAVE' then -- Add doublequotes for filenames with spaces and if not already added if pos( ' ', select) then if not (leftstr( select, 1) = '"' & rightstr( select, 1) = '"') then select = '"'select'"' endif endif endif if button = 1 then 'e 'select elseif button = 2 then 'opendlg EDIT' elseif button = 3 then 'openfolderof 'select endif ; --------------------------------------------------------------------------- ; RingSetFileNumber is called by 'quit' and ProcessAfterLoad. ; The firstinringfid is checked. If not valid anymore (e.g. if file was ; quit), then it is set to the lowest FileNumber of all files in the ring. ; At the end all FileNumbers are rewritten, starting with firstinringfid. ; ; Note: FileNumbers are assigned according to the order in the ring, while ; fileids are assigned unsorted (but it looks like it's influenced by the ; file size). The use of firstinringfid, set by the first edit command ; together with redertermining it if not valid anymore by ; RingSetFileNumber ensures, that the first file in the ring gets the ; FileNumber 1 assigned. The 'ring_more' dialog is filled internally, its ; files are ordered according to their fileids. defproc RingSetFileNumber universal firstinringfid -- Process only, when a field is present KeyPath = '\NEPMD\User\InfoLine\TitleFields' TitleFields = QueryConfigKey( KeyPath) if pos( '', TitleFields) = 0 then KeyPath = '\NEPMD\User\InfoLine\TitleFields' StatusFields = QueryConfigKey( KeyPath) if pos( '', StatusFields) = 0 then return endif endif getfileid startfid dprintf( 'SET_FILE_NUMBER', 'startfile = 'startfid.filename) display -2 -- turn off messages activatefile firstinringfid display 2 -- turn on messages -- If firstinringfid is not in ring anymore if rc = -260 then -- Invalid fileid -- Redetermine first loaded file (file with lowest FileNumber, if any) dprintf( 'SET_FILE_NUMBER', 'startfid not found in ring, redetermining lowest file number.') activatefile startfid nextfile -- only useful in case no FileNumber exists in the ring getfileid firstfid -- start at the following file (usually the last loaded is on top) fid = firstfid firstinringfid = fid -- initialize to current file LowestFileNumber = filesinring() -- initialize to upper limit dprintf( 'RINGCMD', 'RingSetFileNumber 1') do f = 1 to filesinring(1) -- just as an upper limit -- Check if FileNumber was set by a previous call to RingSetFileNumber -- and get the lowest ThisFileNumber = GetAVar( fid'.filenumber') dprintf( 'SET_FILE_NUMBER', 'f = 'f', FileNumber = 'ThisFileNumber', FileName = '.filename) if ThisFileNumber <= LowestFileNumber & ThisFileNumber <> '' then LowestFileNumber = ThisFileNumber firstinringfid = fid endif nextfile getfileid fid if fid = firstfid then leave endif enddo endif dprintf( 'SET_FILE_NUMBER', 'firstinringfid = 'firstinringfid.filename) -- Set FileNumbers for all files in the ring, start with firstinringfid FileNumber = 0 activatefile firstinringfid fid = firstinringfid dprintf( 'RINGCMD', 'RingSetFileNumber 2') do f = 1 to filesinring(1) -- just as an upper limit FileNumber = FileNumber + 1 -- Save FileNumber in an array var call SetAVar( fid'.filenumber', FileNumber) -- Critical? If this would be processed only on defselect, the field for -- a file, selected by the internal ask-before-quit-if-modified routine -- is not updated, when it gets selected. -- (But the ring can be updated before the ring_more dialog is opened.) -- Maybe refreshing the ring should be processed on the next defselect. 'RefreshInfoLine FILELIST' nextfile getfileid fid if fid = firstinringfid then leave endif enddo activatefile startfid return ; --------------------------------------------------------------------------- defc RingSetFileNumber call RingSetFileNumber() ; --------------------------------------------------------------------------- ; Called by defproc GetInfoFieldValue('FILE'). defproc GetFileNumber -- Get FileNumber for Filename by querying an array var getfileid fid FileNumber = GetAVar( fid'.filenumber') return FileNumber ; --------------------------------------------------------------------------- ; Swap current and next views to advance the current file position in the ; ring. Original by Larry Margolis: ; http://groups.google.com/group/comp.os.os2.apps/browse_thread/thread/c88c425bd33fcf0a ; See also this message for an explanation why the Ring dialog shows the ; files in another order. defc SwapView universal firstinringfid getfileid fid this_view = fid.nextview_of_file prev_view = this_view.prevview next_view = this_view.nextview if this_view = prev_view | this_view = next_view | prev_view = next_view then sayerror 'Not enough views in the ring to swap.' return endif nextnext_view = next_view.nextview prev_view.nextview = next_view nextnext_view.prevview = this_view next_view.prevview = prev_view next_view.nextview = this_view this_view.prevview = next_view this_view.nextview = nextnext_view -- Must handle the first and last views specially: -- change firstinringfid before calling RingSetFileNumber CurFileNumber = GetAVar( fid'.filenumber') MaxFileNumber = filesinring() if CurFileNumber >= MaxFileNumber then firstinringfid = fid elseif CurFileNumber = 1 then firstinringfid = next_view endif -- Redetermine all file numbers call RingSetFileNumber() ; --------------------------------------------------------------------------- ; Moved from STDCTRL.E /* 旼컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴커 � what's it called: Ring_More � � � � what does it do : This command is called when the More... selection on � � the ring menu is selected. (Or by the Ring action bar � � item if MENU_LIMIT = 0.) It generates a listbox � � containing all the filenames, and selects the � � appropriate fileid if a filename is selected. � � � 읕컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴컴켸 */ defc Ring_More if filesinring() = 1 then sayerror ONLY_FILE__MSG return endif -- Workaround: This avoids unwanted window scrolling after closing the dialog. 'VSyncCursor' call windowmessage( 0, getpminfo(APP_HANDLE), 5141, -- EPM_POPRINGDIALOG 0, 0) -- At this point the dialog window is about to open. -- Everything else is handled by the dialog window itself. -- E code here would be processed before the dialog window -- is closed, even when its a app modal dialog. -- This is relevant, because canceling the dialog always makes -- the window scroll to the cursor. This can't be fixed here. -- Moreover, after scrolling the window the cursor becomes invisible -- until it is moved. -- -- Idea for a workaround to avoid window scrolling: -- If the cursor is not visible, place it on the middle line of current -- window for all files. This might be useful for all cases before file -- switching takes places. -- -- Note that window scolling (with the mouse wheel) is not automatically -- recognized by callback E commands. But It can be queried if the -- cursor is visible by comparing .cursory: -- visble if: (top line = 1) >= .cursory >= (bottom line = .windowheight) ; --------------------------------------------------------------------------- ; Syntax: Ring ; Executes a cmd on all files of the ring. defc Ring if arg(1) = '' then sayerror 'Specify a command to be executed on all files in the ring.' return endif display -3 getfileid startfid dprintf( 'RINGCMD', 'Ring' arg(1)) do f = 1 to filesinring(1) -- just as an upper limit if .visible then arg(1) -- execute arg(1) endif nextfile getfileid fid if fid = startfid then leave endif enddo 'postme activatefile' startfid 'postme display' 3 return