Unfortunately, the script doesn't work on the current AutoHotkey version, I think probably because it uses array-style reference syntax (square brackets) in pseudo-array variable names within function calls (e.g. StringLen), which I'm guessing probably worked prior to the implementation of associative arrays (see https://autohotkey.com/docs/misc/Arrays.htm).
But I really wanted it, so I took the time to correct the bugs and spruce it up a bit. Since this took me most of a day and I found it pretty useful, I thought I should share (see below)
Suggested improvements would be appreciated. If anyone is interested in seeing the hotkey I wrote that utilizes this script, I can post that too.
Code: Select all
; TextWrapper.ahk
;
; AutoHotkey Version: 1.0.29
; Language: English
; Platform: Windows XP
; Author: Gehn WillowGlade <[email protected]>
;
; Updates made 09/14/2017 by kkwillmert as follows:
; - Updated to work with AutoHotkey Versions 1.0.90+ (associative arrays broke
; the original script); The input arrays are now associative arrays, so
; they must be initialized by the caller using Object() before they're
; populated with their input values (see the revised Example below).
; - Added limited ability to handle line breaks in the input string. Two
; consecutive line breaks will be treated as a delimiter which is preserved,
; and the content between them is what gets wrapped. Single line breaks are
; converted to whatever the first character in breakCharacterSet[1] is, with
; an attempt made at preventing this conversion from creating two
; consecutive instances of that character. This does, however, cause some
; probably-undesirable behavior when there are an odd number of consecutive
; line breaks in the original string, since only double line breaks are
; preserved.
;
; Script Function:
; Wraps text manually by adding in newline statements to a string using
; a configurable methodology. Configuration is done by setting specific
; variables, detailed below. Settings are remembered between calls. Most
; variables have a default and hence do not absolutely require setting.
; Text is wrapped by breaking the lines as long as possible, at certain
; 'break characters' defined in sets. These sets have priority, starting
; at 1. Wrap Text will attempt to break the line using set 1, and if it
; fails, it will continue on to set 2, and so forth, until it runs out of
; sets. The result is that it will prefer wrapping very short lines to
; wrapping at lower priority break characters.
;
; Variables:
; textToWrap - The text to be wrapped. No default.
; maxCharactersPerLine - The number of characters that are allowed
; on a line before a wrap is attempted. This
; setting defaults to 80.
; breakType - This can be set to either 'after', 'before'
; or 'eat'. 'after' will have lines break after
; the break character. 'before' will have the
; lines break before the break character. 'eat'
; will destroy the break character entirely.
; This setting defaults to 'after'.
; breakCharacterSet[*] - A set of individual characters which all
; have the same priority, starting at 1. Do
; not include spaces unless you want space to
; be a break character. Use a blank set to end
; the set list.
; breakCharacterSetType[*] - This allows the break type to be set on a per
; break character set basis. This setting
; defaults to the breakType setting.
; forceWrap - This will force wrapping of the text, even if
; no break characters are found. In such a case
; the line will break right at the limit. Set
; this option using true or false. The default
; is false.
; wrappedText - This variable is not set. Instead, this is the
; text, wrapped, returned by Wrap Text.
;
; Example:
; textToWrap := "C:\Games\Guild Wars\GW.exe"
; maxCharactersPerLine := 10
;
; breakCharacterSet := Object()
; breakCharacterSet[1] := "\"
; breakCharacterSet[2] := " "
; breakCharacterSet[3] := ""
;
; Gosub , WrapText
; MsgBox % wrappedText
;
; wrappedText would be: "C:\Games\`nGuild`nWars\`nGW.exe"
; Invoked externally to perform core function
WrapText:
{
Gosub , WrapText_Initialize
If ErrorLevel <> 0
{
Return
}
; Preserve double-line breaks, but re-wrap single-breaks
originalTextToWrap := textToWrap
newWrappedText := ""
StringReplace, textToWrap, textToWrap, `n`n, ``, All
StringSplit, inputTextLines, textToWrap, ``
Loop, %inputTextLines0%
{
textToWrap := inputTextLines%a_index%
; Use the first specified wrap character instead of `n; `n charactersseem to goof up
; the wrapping logic somewhere along the way.
; Also, try to prevent this conversion from creating two of the wrap character in a row
wrapChar := SubStr( breakCharacterSet[1], 1, 1 )
StringReplace, textToWrap, textToWrap, %wrapChar%`n, %wrapChar%, All
StringReplace, textToWrap, textToWrap, `n%wrapChar%, %wrapChar%, All
StringReplace, textToWrap, textToWrap, `n, %wrapChar%, All
Gosub , WrapText_BreakIntoLines
Gosub , WrapText_CombineLines
newWrappedText := newWrappedText "`n`n" wrappedText
}
StringTrimLeft , newWrappedText , newWrappedText , 2
wrappedText := newWrappedText
Return
}
; Makes sure all variables are user set or filled with defaults (where applicable)
WrapText_Initialize:
{
If ( textToWrap == "" )
{
MsgBox , 48 , WrapText Error , The variable 'textToWrap' must be set when WrapText is called.
ErrorLevel := 1
Return
}
If ( maxCharactersPerLine == "" )
{
maxCharactersPerLine := 80
}
Else If maxCharactersPerLine is not integer
{
MsgBox , 48 , WrapText Error , The variable 'maxCharactersPerLine' must be a positive, nonzero integer.
ErrorLevel := 1
Return
}
Else If ( maxCharactersPerLine <= 0 )
{
MsgBox , 48 , WrapText Error , The variable 'maxCharactersPerLine' must be a positive, nonzero integer.
ErrorLevel := 1
Return
}
If ( breakType == "" )
{
breakType := "after"
}
Else If ( !( breakType = "after" OR breakType = "before" OR breakType = "eat" ) )
{
MsgBox , 48 , WrapText Error , The variable 'breakType' may only be set to 'after', 'before' or 'eat'.
ErrorLevel := 1
Return
}
If ( breakCharacterSet[1] == "" )
{
breakCharacterSet := Object()
breakCharacterSet[1] := " "
breakCharacterSet[2] := ""
}
Loop
{
If ( breakCharacterSet[a_index] == "" )
{
Break
}
If ( !( breakCharacterSetType[a_index] == "" OR breakCharacterSetType[a_index] == "after" OR breakCharacterSetType[a_index] == "before" OR breakCharacterSetType[a_index] == "eat" ) )
{
MsgBox , 48 , WrapText Error , The variable 'breakCharacterSetType[%a_index%]' may only be set to 'after', 'before' or 'eat'.
ErrorLevel := 1
Return
}
}
If ( forceWrap == "" )
{
forceWrap := false
}
Else If ( !( forceWrap == true OR forceWrap == false ) )
{
MsgBox , 48 , WrapText Error , The variable 'forceWrap' must be either true or false.
ErrorLevel := 1
Return
}
Return
}
; Breaks the text into lines, as needed
WrapText_BreakIntoLines:
{
; Chop off the ends of lines, to create new lines, until all lines are under the character limit
WrapText_line := Object()
WrapText_line[1] := textToWrap
WrapText_lineCount := 1
Loop
{
WrapText_currentLineNumber := a_index
WrapText_currentLineLength := StrLen( WrapText_line[WrapText_currentLineNumber] )
If ( WrapText_currentLineLength <= maxCharactersPerLine )
{
Break
}
Else
{
; Loop through break character sets until a break character is found, or we run out of sets
WrapText_breakCharacterPosition := -1
Loop
{
; Search backwards from farthest possible character to find a break character
WrapText_currentBreakCharacterSet := a_index
If ( ( WrapText_breakCharacterPosition >= 0 ) OR ( ( breakCharacterSet[WrapText_currentBreakCharacterSet] == "" ) AND !forceWrap ) )
{
Break
}
If ( ( breakCharacterSet[WrapText_currentBreakCharacterSet] == "" ) AND forceWrap )
{
WrapText_forceWraptextToWrap := true
}
Else
{
WrapText_forceWraptextToWrap := false
}
If ( breakCharacterSetType[WrapText_currentBreakCharacterSet] != "" )
{
WrapText_currentBreakType := breakCharacterSetType[WrapText_currentBreakCharacterSet]
}
Else
{
WrapText_currentBreakType := breakType
}
If ( WrapText_currentBreakType == "after" )
{
WrapText_currentPosition := maxCharactersPerLine
}
Else
{
WrapText_currentPosition := maxCharactersPerLine + 1
}
Loop , %maxCharactersPerLine%
{
If ( WrapText_forceWraptextToWrap )
{
WrapText_breakCharacterPosition := 1
}
Else
{
WrapText_characterAtCurrentPosition := SubStr( WrapText_line[WrapText_currentLineNumber], WrapText_currentPosition, 1 )
WrapText_breakCharacterPosition := InStr( breakCharacterSet[WrapText_currentBreakCharacterSet], WrapText_characterAtCurrentPosition )
}
If ( WrapText_breakCharacterPosition > 0 )
{
WrapText_lineCount += 1
If ( WrapText_currentBreakType == "after" )
{
WrapText_line[WrapText_lineCount] := SubStr( WrapText_line[WrapText_currentLineNumber], WrapText_currentPosition + 1 )
WrapText_line[WrapText_currentLineNumber] := SubStr( WrapText_line[WrapText_currentLineNumber], 1, WrapText_currentPosition )
}
Else If ( WrapText_currentBreakType == "before" )
{
WrapText_line[WrapText_lineCount] := SubStr( WrapText_line[WrapText_currentLineNumber], WrapText_currentPosition )
WrapText_line[WrapText_currentLineNumber] := SubStr( WrapText_line[WrapText_currentLineNumber], 1, WrapText_currentPosition - 1 )
}
Else ; WrapText_currentBreakType == "eat"
{
WrapText_line[WrapText_lineCount] := SubStr( WrapText_line[WrapText_currentLineNumber], WrapText_currentPosition + 1 )
WrapText_line[WrapText_currentLineNumber] := SubStr( WrapText_line[WrapText_currentLineNumber], 1, WrapText_currentPosition - 1 )
}
Break
}
WrapText_currentPosition -= 1
}
}
}
}
Return
}
; Recombines the text into a single variable
WrapText_CombineLines:
{
wrappedText := ""
Loop , %WrapText_lineCount%
{
WrapText_currentLine := WrapText_line[a_index]
wrappedText := wrappedText "`n" WrapText_currentLine
}
StringTrimLeft , wrappedText , wrappedText , 1
Return
}