/****************************** Module Header ******************************* * * Module Name: wrap.e * * Copyright (c) Netlabs EPM Distribution Project 2002 * * $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. * ****************************************************************************/ const map_WindowToDoc = 1 -- x, y in; x, y out map_DocToLCO = 2 -- x, y in; l, c, o out map_LCOToDoc = 3 -- l, c, o in; x, y out map_Doc2Win = 4 -- x, y in; x, y out map_Win2LCO = 5 -- x, y in; l, c, o out map_LCO2Win = 6 -- l, c, o in; x, y out WRAP_CHARS = ' '\9',;>' ; --------------------------------------------------------------------------- ; Return values ; 1 = MAXLNSIZE_UNTERMINATED ; unterminated ; 2 = CR ; 3 = LF ; 4 = CRLF ; 5 = CRCRLF ; 6 = NULL_TERMINATED ; 7 = CTRLZ_TERMINATED ; 8 = NOMOREDATA_TERMINATED ; 9 = INHERITED_TERMINATED ; newly added line, not loaded from disk, last line of file ; 10 = CRLFEOF_TERMINATED ; 18 = newly added line, not loaded from disk, other line ; sometimes also used for a modified line, 4 is changed to 18 ; 16 ... 31 = user-defined. In the meantime 18 and 20 seem to be used. ; 21 ... 31 = better user these values for user settings. Due to the ; amount of remaing numbers, an additional bit is not ; possible. Therefore support only 3 and 4, which, in ; combination with user-defined line options, use 2 values. ; 9 and 18 are not changed by Save. ; 9 and 18 change to 4 after Save and Revert. Revert deletes all prior ; Undo states. defproc QueryLineTerm LineNum = arg( 1) if LineNum = '' then LineNum = .line endif getfileid fid TermType = '?' rc = dynalink32( E_DLL, 'EtkQueryLineTerminator', GethWndC( EPMINFO_EDITCLIENT) || atol( fid) || atol( LineNum) || address( TermType)) TermId = 0 if not rc then TermId = asc( leftstr( TermType, 1)) endif return TermId ; --------------------------------------------------------------------------- defproc SetLineTerm( TermId) LineNum = arg( 2) if LineNum = '' then LineNum = .line endif getfileid fid rc = dynalink32( E_DLL, 'EtkChangeLineTerminator', GethWndC( EPMINFO_EDITCLIENT) || atol( fid) || atol( LineNum) || atol( TermId)) return ; --------------------------------------------------------------------------- ; Return values ; Unix - all lines have LF line ends ; DOS - all lines have CRLF line ends, also used for other types ; mixed - both line ends (or others) are found ; unterm - not terminated, used for binary views defproc QueryFileLineEnd universal default_save_options -- Internal default + default_save_options + fid.saveoptions Options = '/ns /ne /nt 'default_save_options GetFileAVar( 'saveoptions') Options = NormalizeSaveOptions( Options) TermId = '' fUnixFound = 0 fDosFound = 0 fUntermFound = 0 fAddedFound = 0 fOtherFound = 0 do LineNum = 1 to .last TermId = QueryLineTerm( LineNum) if rc then iterate endif if wordpos( TermId, '1 8') then fUntermFound = 1 elseif TermId = 3 then fUnixFound = 1 elseif TermId = 4 then fDosFound = 1 elseif wordpos( TermId, '9 18 20') then fAddedFound = 1 else fOtherFound = 1 endif enddo MixedSum = fOtherFound + fDosFound + fUnixFound if fUntermFound & not fUnixFound & not fDosFound & not fOtherFound then FileLineEnd = 'unterm' elseif fUnixFound & not fDosFound & not fOtherFound then FileLineEnd = 'Unix' elseif fDosFound & not fUnixFound & not fOtherFound then FileLineEnd = 'DOS' elseif fOtherFound | MixedSum > 1 then FileLineEnd = 'mixed' else if wordpos( '/l', Options) then SaveType = 'Unix' else SaveType = 'DOS' -- Default if not /l endif FileLineEnd = SaveType endif return FileLineEnd ; --------------------------------------------------------------------------- defproc SetFileTerm( TermId) getfileid fid do LineNum = 1 to fid.last CurTermId = QueryLineTerm( LineNum) if wordpos( CurTermId, '1 6 7 8') then iterate endif call SetLineTerm( TermId, LineNum) if rc then leave endif enddo return ; --------------------------------------------------------------------------- defc WordProc OldMargins = .margins KeyPath = '\NEPMD\User\Format\HeaderWidth' HeaderWidth = QueryConfigKey( KeyPath) .margins = '2 'HeaderWidth' 1' -- This should make it look nice... TermId = 1 call ChangeLineEnd( TermId, 1, -1) -- 2nd arg = 1 means: reflow -- 3rd arg = -1 means: offset at the end = -1 -- New save options: -- /k keep removes /o, /l and /a -- /a auto removes /o, /l and /k, adds /o or /l from file loading -- /bin bin shortcut for /k /ne /ns /nt -- They are expanded in NormalizeSaveOptions. 'SetSaveOptions /k' .margins = OldMargins ; --------------------------------------------------------------------------- defc DosLineEnds TermId = 4 call ChangeLineEnd( TermId) 'SetSaveOptions /o' ; --------------------------------------------------------------------------- defc UnixLineEnds TermId = 3 call ChangeLineEnd( TermId) 'SetSaveOptions /l' ; --------------------------------------------------------------------------- defc ChangeFileLineEnd SelectedLineEnd = GetAVar( 'lineendoption') if upcase( SelectedLineEnd) = 'UNIX' then TermId = 3 SaveOption = '/l' else TermId = 4 SaveOption = '/o' endif SetFileTerm( TermId) 'SetSaveOptions 'SaveOption ; --------------------------------------------------------------------------- defproc ChangeLineEnd( TermId) fReflow = (arg( 2) = 1) OffsetEndMark = 0 if IsNum( arg( 3)) then if arg( 3) < 0 then OffsetEndMark = arg( 3) -- Must be < 0 endif endif getfileid fid call pSave_Pos( savepos) oldmodify = .modify oldautosave = .autosave .autosave = 0 -- Don't want to go crazy autosaving... top call NextCmdAltersText() if not fReflow then -- Process line-wise. do LineNum = 1 to .last + OffsetEndMark CurTermId = QueryLineTerm( LineNum) if wordpos( CurTermId, '1 6 7 8') then iterate endif call SetLineTerm( TermId, LineNum) enddo else -- Process paragraph-wise: Mark paragraph and reflow. call pSave_Mark( savemark) stopit = 0 do forever getline line do while line = '' -- Skip over blank lines if .line = .last then stopit = 1 leave endif down getline line enddo if stopit then leave endif startline = .line -- Startline is first line of paragraph unmark mark_line call pFind_Blank_Line() if .line <> startline then up else bottom endif mark_line if fReflow then reflow endif getmark firstline, lastline -- New first and last lines of marked region. do LineNum = firstline to lastline + OffsetEndMark CurTermId = QueryLineTerm( LineNum) if wordpos( CurTermId, '1 6 7 8') then iterate endif call SetLineTerm( TermId, LineNum) enddo if lastline = .last then leave endif .line = Min( .last, lastline - OffsetEndMark) enddo call pRestore_Mark( savemark) endif .modify = oldmodify + 1 .autosave = oldautosave call pRestore_Pos( savepos) ; --------------------------------------------------------------------------- ; Query soft-wrapped state of current or specified file. Returns 1 or 0. defproc GetWrapped Filename = arg( 1) if Filename = '' then getfileid fid else getfileid fid, Filename endif return (GetAVar( fid'.wrapped') = 1) ; --------------------------------------------------------------------------- ; Soft wrap or umwrap current file depending on the soft-wrapped state. defc ToggleWrap if GetWrapped() then 'PostMe UnWrap' else 'PostMe SoftWrap' endif ; --------------------------------------------------------------------------- ; Soft-wrap current file. ; Syntax: SoftWrap [] ; = 0 Wrap to header width ; = 1 Wrap to window width ; = w[indow] Wrap to window width ; = Wrap to columns ; = '' Get split type from config ; - Save won't destroy the file, because the additional line terminators ; are removed internally. That makes it even possible to unwrap the file. ; - Derived from Flow2Win.E by Larry Margolis. defc SoftWrap -- Check if already wrapped if GetWrapped() then -- Unwrap first 'UnWrap' 'PostMe SoftWrap2 'arg( 1) else 'SoftWrap2 'arg( 1) endif defc SoftWrap2 universal vepm_pointer KeyPath = '\NEPMD\User\Format\HeaderWidth' SplitCol = QueryConfigKey( KeyPath) Args = strip( arg( 1)) if Args = 0 then fToWindow = 0 elseif Args = 1 then fToWindow = 1 elseif IsUInt( Args) then -- Col specified fToWindow = 0 SplitCol = Args elseif Args <> '' & abbrev( 'window', lowcase( Args)) then fToWindow = 1 else -- Query from config KeyPath = '\NEPMD\User\Format\Wrap\ToWindow' fToWindow = QueryConfigKey( KeyPath) endif if fToWindow then -- Check if monospaced parse value queryfont( .font) with fontname '.' fontsize '.' if .levelofattributesupport bitand 4 then -- Mixed fonts? fMonospaced = 0 -- Can't assume monospaced regardless of font else fMonospaced = IsMonoFont() endif swp1 = copies( \0, 36) call dynalink32( 'PMWIN', '#837', -- Win32QueryWindowPos GethWndC( EPMINFO_EDITCLIENT) || address( swp1)) WindowWidth = ltoa( substr( swp1, 9, 4), 10) -- Calculate limit = split col for a monospaced font if fMonospaced then -- Calculate once, outside of the loop x = 1 y = 1 map_point map_LCOToDoc, x, y -- Get y position of currentline. x = WindowWidth y = y + 5 map_point map_DocToLCO, x, y -- Get column corresponding to pel pos. wM SplitCol = y - 2 endif endif SavedReadOnly = .readonly if SavedReadOnly then .readonly = 0 -- need to disable .readonly temporarily endif -- No additional undo state supression required call NextCmdAltersText() SavedModify = .modify mouse_setpointer WAIT_POINTER display -1 -- disable update of text area call pSave_Pos( SavedPos) Mode = GetMode() -- Split lines, start at line 1 TabWidth = word( .tabs, 1) TermId = 1 NumWrapped = 0 -- number of wrapped lines LineNum = 1 do while LineNum <= .last -- Get line and convert tabs to spaces getline ThisLineStr, LineNum ThisLineStr = TabExpandStr( ThisLineStr, TabWidth, fChanged) if fToWindow then -- Calculate limit = split col for a proportional font if not fMonospaced then -- Have to calculate for each line individually x = LineNum y = 1 map_point map_LCOToDoc, x, y -- Get y position of current line. x = WindowWidth y = y + 5 map_point map_DocToLCO, x, y -- Get column corresponding to pel pos. wM SplitCol = y - 2 endif endif if length( strip( ThisLineStr, 'T')) > SplitCol then -- Split line p = 0 -- Split at last space before SplitCol if p = 0 then first_nonblank_p = Max( 1, verify( ThisLineStr, ' '\9)) do c = 1 to length( WRAP_CHARS) ch = substr( WRAP_CHARS, c, 1) -- Split at last char before SplitCol p = lastpos( ch, ThisLineStr, SplitCol) if p > 0 then if ch <> ' ' then p = p + 1 -- Go after the WrapChar if not a space endif leave endif enddo -- Fixed: endless loop if a line starts with a space. if p < first_nonblank_p then -- No spaces in the line after indent? p = SplitCol endif endif .line = LineNum .col = p split -- Change line terminator id to 1 = unterminated call SetLineTerm( TermId, LineNum) NumWrapped = NumWrapped + 1 endif LineNum = LineNum + 1 enddo call pRestore_Pos( SavedPos) display 1 mouse_setpointer vepm_pointer if SavedReadOnly then .readonly = 1 endif -- Save wrapped state in an array var if NumWrapped > 0 then fWrapped = 1 'SayError Wrapped 'NumWrapped' lines (restored on file save)' 'SetSaveOptions /k' else fWrapped = 0 sayerror 'No wrap required' endif .modify = SavedModify call SetFileAVar( 'wrapped', fWrapped) ; --------------------------------------------------------------------------- ; Set line terminator of type MaxLnSize_unterminated. Acts on current line ; or, when an arg was specified, a line relative to current line. 'NoWrap -1' ; removes the line end from previous line, 'NoWrap' from current line. defc NoWrap if arg( 1) = '' then LineNum = .line else LineNum = Min( Max( .line + arg( 1), 1), .last) endif call NoWrapLine( LineNum) defproc NoWrapLine if arg( 1) = '' then LineNum = .line else LineNum = arg( 1) endif getfileid fid TermId = 1 call SetLineTerm( TermId, LineNum) call SetAVar( fid'.wrapped', 1) return ; --------------------------------------------------------------------------- ; Joins current or specified line number with next line. Must wrap at 1599. ; Todo: ; Restore line end for LineNum + 1 if it was MAXLNSIZE_UNTERMINATED. ; Join more lines, if cursor col not reached. Therefore specify ; a col num as required arg. defc UnWrapLine SavedLine = .line SavedCol = .col parse arg Col LineNum Col = strip( Col) LineNum = strip( LineNum) if LineNum = '' then LineNum = .line else .line = LineNum endif if Col = '' then Col = .col else .col = Col endif do forever if LineNum = .last then return endif getline ThisLineStr, LineNum getline NextLineStr, LineNum + 1 LenThisLine = length( ThisLineStr) LenNextLine = length( NextLineStr) if LenThisLine >= Col then return endif TermId = QueryLineTerm( LineNum) if TermId = 1 then -- Append next line to current .line = LineNum join else leave endif enddo .line = SavedLine .col = SavedCol ; --------------------------------------------------------------------------- ; Unused. ; Splits line at column under cursor, if required. After that, move to the ; next line that is longer than the cursor column. Tries to split at last ; space before the cursor. ; Todo: Unwrap before wrap, split at more chars defc SoftWrapAtCursor SavedReadOnly = .readonly if SavedReadOnly then .readonly = 0 -- need to disable .readonly temporarily endif SavedModify = .modify SplitCol = .col ; -- Unwrap first ; 'UnwrapLine 'splitcol endline getline ThisLineStr if .col > SplitCol then first_nonblank_p = Max( 1, verify( ThisLineStr, ' '\9)) c = 0 do c = 1 to length( WRAP_CHARS) ch = substr( WRAP_CHARS, c, 1) -- Split at last char before SplitCol p = lastpos( ch, ThisLineStr, SplitCol) if p > 0 then if ch <> ' ' then p = p + 1 -- Go after the WrapChar if not a space endif leave endif enddo if p < first_nonblank_p then -- No spaces in the line after indent? p = SplitCol endif .col = p split -- splits and keeps leading spaces if not GetWrapped() then 'SetSaveOptions /k' endif call NoWrapLine() endif PrevLine = .line do forever if .line < .last then down endif if .line = PrevLine then leave endif PrevLine = .line endline if .col > SplitCol then leave endif enddo .col = SplitCol if SavedReadOnly then .readonly = 1 endif .modify = SavedModify ; --------------------------------------------------------------------------- ; Soft-unwrap lines of current file. Determine line terminators of type ; MaxLnSize_unterminated and join these lines. defc UnWrap universal vepm_pointer getfileid fid ClientFid = GethWndC( EPMINFO_EDITCLIENT)''atol( fid) -- no additional undo state supression required SavedReadOnly = .readonly if SavedReadOnly then .readonly = 0 -- need to disable .readonly temporarily endif call NextCmdAltersText() SavedModify = .modify mouse_setpointer WAIT_POINTER display -1 -- disable update of text area call pSave_Pos( SavedPos) Mode = GetMode() -- Try to re-join lines -- Start at last line - 1 LineNum = .last - 1 do while LineNum > 0 -- Try to join previously split lines first TermId = QueryLineTerm( LineNum) if TermId = 1 then if LineNum > 0 then -- Get line terminator id of next line NextTermId = QueryLineTerm( LineNum + 1) -- Append next line to current .line = LineNum join -- Set line terminator id to the id of the next line. -- This is required, because the line terminator id doesn't -- change automatically after a line join to the id of the -- next line. call SetLineTerm( NextTermId, LineNum) endif endif LineNum = LineNum - 1 enddo call pRestore_Pos( SavedPos) display 1 mouse_setpointer vepm_pointer if SavedReadOnly then .readonly = 1 endif .modify = SavedModify -- Save wrapped state in an array var fWrapped = 0 call SetAVar( fid'.wrapped', Wrapped) ; --------------------------------------------------------------------------- ; Reads options from config. Executed by newmenu. ; TODO: Combine this and Wrap. Then read either args or config keys. ; Add fPar and fMark options to Wrap. defc WrapMenuCmd universal vepm_pointer KeyPath = '\NEPMD\User\Format\Wrap' fSoftWrap = QueryConfigKey( KeyPath'\SoftWrap') fToWindow = QueryConfigKey( KeyPath'\ToWindow') fMark = QueryConfigKey( KeyPath'\Mark') fPar = QueryConfigKey( KeyPath'\Par') fKeepIndent = QueryConfigKey( KeyPath'\KeepIndent') KeyPath = '\NEPMD\User\Format\HeaderWidth' SplitCol = QueryConfigKey( KeyPath) if fSoftWrap then -- Process soft wrap separately SavedReadOnly = .readonly if SavedReadOnly then .readonly = 0 endif if GetWrapped() then 'Unwrap' elseif fToWindow then 'Softwrap window' -- At least 'w' else 'Softwrap 'SplitCol endif if SavedReadOnly then .readonly = SavedReadOnly endif if isadefc( 'menuinit_linewraps') then 'menuinit_linewraps' endif return endif -- Normal wrap WrapArgs = '' if fMark then WrapArgs = WrapArgs 'mark' endif if fPar then WrapArgs = WrapArgs 'par' endif if fKeepIndent then WrapArgs = WrapArgs 'keepindent' endif WrapArgs = strip( WrapArgs) 'Wrap 'WrapArgs if isadefc( 'menuinit_linewraps') then 'menuinit_linewraps' endif ; --------------------------------------------------------------------------- ; Hard wrap: split lines. Tries to split at words first. ; Syntax: Wrap [splitcol] [mark] [par] [keepindent] ; splitcol Number of columns or '*'. ; '*' means: open command line with default value for user ; input. ; Default SplitCol = header width. ; A line is wrapped at spaces or tabs before SplitCol. If no ; split point was found, a line is split exactly at SplitCol. ; mark Process marked lines if file is marked. Char and block ; marked are changed to line mark before that. ; Without mark: The complete file is processed, unless par ; is specified. ; par Processes current paragraph only. After that, the cursor ; is moved to the next paragraph for repeated processing. ; This is only applied without mark. ; Without par: The complete file is processed, unless mark ; is specified. ; keepindent The split part gets the indent of the original line. ; Without keepindent: The split parts gets no indent. defc Wrap universal vepm_pointer KeyPath = '\NEPMD\User\Format\HeaderWidth' DefaultSplitCol = QueryConfigKey( KeyPath) Args = strip( arg( 1)) UpArgs = upcase( Args) wp = wordpos( '*', Args) if wp > 0 then Args = delword( Args, wp) 'CommandLine' strip( 'Wrap 'Args) DefaultSplitCol return endif fKeepIndent = 0 wp = wordpos( 'KEEPINDENT', UpArgs) if wp > 0 then Args = delword( Args, wp) fKeepIndent = 1 endif fPar = 0 wp = wordpos( 'PAR', UpArgs) if wp > 0 then Args = delword( Args, wp) fPar = 1 endif fMark = 0 wp = wordpos( 'MARK', UpArgs) if wp > 0 then Args = delword( Args, wp) fMark = 1 endif if IsUInt( Args) then SplitCol = Args else SplitCol = DefaultSplitCol endif do once = 1 to 1 if .readonly then 'SayError File has .readonly field set. Edit is diabled, toggle .readonly first.' leave endif -- No additional undo state supression required call NextCmdAltersText() mouse_setpointer WAIT_POINTER display -1 -- disable update of text area call pSave_Pos( SavedPos) if fMark & not FileIsMarked() then fMark = 0 endif if fMark then fPar = 0 endif if fPar then -- Find current par: Empty line before cursor and empty line after cursor firstl = 1 do l = .line - 1 to 1 by -1 if StripBlanks( textline( l)) = '' then firstl = l + 1 leave endif enddo lastl = .last do l = .line + 1 to .last getline LineStr, l if StripBlanks( textline( l)) = '' then lastl = l - 1 leave endif enddo elseif fMark then -- Marked lines getmark firstl, lastl, firstc, lastc, markfid MarkToLineMark() else -- All lines firstl = 1 lastl = .last endif m = 0 LineNum = firstl do forever -- do forever is faster then using a condition -- Check if line counter is > last (marked) line if LineNum > lastl then leave endif if LineNum > .last then leave endif -- Process lines getline LineStr, LineNum if length( strip( LineStr, 'T')) > SplitCol then if fKeepIndent then -- Search last occurence of space or tab, starting at SplitCol SpaceP = lastpos( ' ', LineStr, SplitCol) TabP = lastpos( \9, LineStr, SplitCol) p = Max( SpaceP, TabP) first_nonblank_p = Max( 1, verify( LineStr, ' '\9)) if p < first_nonblank_p then -- No spaces in the line after indent? p = SplitCol endif .line = LineNum -- Set cursor on line LineNum .col = p -- Set cursor on col p (at a possible space or tab) call SplitLines() -- Copies indent of current line, removes spaces and tabs at split pos. before else p = lastpos( ' ', LineStr, SplitCol) first_nonblank_p = Max( 1, verify( LineStr, ' '\9)) if p < first_nonblank_p then -- No spaces in the line after indent? p = SplitCol endif .line = LineNum -- Set cursor on line LineNum .col = p -- Set cursor on col p (before a possible space) SplitWithIndent( '') endif lastl = lastl + 1 if fMark then if LineNum = lastl - 1 then -- Extend mark to include wrapped line setmark firstl, lastl, firstc, lastc, 0, markfid endif endif m = m + 1 endif -- length( strip( LineStr, 'T')) > SplitCol LineNum = LineNum + 1 enddo -- forever call pRestore_Pos( SavedPos) if fPar then -- Go to next par EmptyLine = '' do l = Min( .last, .line + 1) to .last oldcursory = .cursory if not EmptyLine then if StripBlanks( textline( l)) = '' then EmptyLine = l endif else if StripBlanks( textline( l)) <> '' then -- Next par reached. Scroll vertically to previous .cursory. --dprintf( '.last = '.last', l = 'l', .windowheight = '.windowheight', oldcursory = 'oldcursory) .line = l if .last - l > .windowheight - oldcursory then .cursory = oldcursory endif leave endif endif enddo endif enddo -- once display 1 mouse_setpointer vepm_pointer 'HighlightCursor' if not fPar then Msg = 'Wrap at column 'SplitCol':' if m = 1 then Msg = Msg' 'm' change.' else Msg = Msg' 'm' changes.' endif 'SayError 'Msg endif