Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Custom text wrapping script


  • Please log in to reply
8 replies to this topic
Gehn
  • Members
  • 13 posts
  • Last active: Jun 10 2005 06:31 PM
  • Joined: 02 Mar 2005
While working on creating a GUI for a bigger project, I found that neither text nor edit elements really wrapped like how I wanted them to for the situation. Then, I got the idea to design a wrapping system of my own, that manually put in newline statements. The prototype was successful, so I made this - a better designed and more feature complete version. Its header explains its usage.

Text Wrapper.ahk
; AutoHotkey Version: 1.0.29
; Language:       English
; Platform:       Windows XP
; Author:         Gehn WillowGlade <[email protected]>
;
; 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.
;   breakCharacterSet[*]_type - 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[1] := "\"
;   breakCharacterSet[2] := " "
;   breakCharacterSet[3] := ""
;   Gosub , WrapText
;
;   wrappedText would be: "C:\Games\`nGuild`nWars\`nGW.exe"


; Invoked externally to perform core function
WrapText:
  {
  Gosub , WrapText_Initialize
  If ErrorLevel <> 0
    Return

  Gosub , WrapText_BreakIntoLines

  Gosub , WrapText_CombineLines

  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[1] := " "
    }

  Loop
    {
    If ( breakCharacterSet[%a_index%] == "" )
      Break

    If ( !( breakCharacterSet[%a_index%]_breakType = "" OR breakCharacterSet[%a_index%]_breakType = "after" OR breakCharacterSet[%a_index%]_breakType = "before" OR breakCharacterSet[%a_index%]_breakType = "eat" ) )
      {
      MsgBox , 48 , WrapText Error , The variable 'breakCharacterSet[%a_index%]_breakType' 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[1] := textToWrap
  WrapText_lineCount := 1
  Loop
    {
    WrapText_currentLineNumber := a_index

    StringLen , WrapText_currentLineLength , 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_forceWrapThisLine := true
        Else
          WrapText_forceWrapThisLine := false

        If ( breakCharacterSet[%WrapText_currentBreakCharacterSet%]_breakType != "" )
          WrapText_currentBreakType := breakCharacterSet[%WrapText_currentBreakCharacterSet%]_breakType
        Else
          WrapText_currentBreakType := breakType

        If ( WrapText_currentBreakType = "after" )
          WrapText_currentPosition := maxCharactersPerLine
        Else
          WrapText_currentPosition := maxCharactersPerLine + 1

        Loop , %maxCharactersPerLine%
          {
          If ( WrapText_forceWrapThisLine )
            WrapText_breakCharacterPosition := 1
          Else
            {
            StringMid , WrapText_characterAtCurrentPosition , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition , 1
            StringGetPos , WrapText_breakCharacterPosition , breakCharacterSet[%WrapText_currentBreakCharacterSet%] , %WrapText_characterAtCurrentPosition%
            }

          If ( WrapText_breakCharacterPosition >= 0 )
            {
            WrapText_lineCount += 1
            If ( WrapText_currentBreakType = "after" )
              {
              StringTrimLeft , WrapText_line[%WrapText_lineCount%] , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition
              StringLeft , WrapText_line[%WrapText_currentLineNumber%] , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition
              }
            Else If ( WrapText_currentBreakType = "before" )
              {
              StringTrimLeft , WrapText_line[%WrapText_lineCount%] , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition - 1
              StringLeft , WrapText_line[%WrapText_currentLineNumber%] , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition - 1
              }
            Else ; WrapText_currentBreakType = "eat"
              {
              StringTrimLeft , WrapText_line[%WrapText_lineCount%] , WrapText_line[%WrapText_currentLineNumber%] , WrapText_currentPosition
              StringLeft , WrapText_line[%WrapText_currentLineNumber%] , WrapText_line[%WrapText_currentLineNumber%] , 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
  }

I'm mostly happy with the result. It seems fully functional - if anybody spots a bug please let me know. Any other comments would be nice too - this is the first time I've actually put out any code for public display and I imagine I could use some feedback.

However, there's three layers of looping in the WrapText_BreakIntoLines function however, and combined with the ifs in said section, there's a bit more nesting than I'd like. It makes the code less clear than it could be. I considered, and am still considering, moving some of the code from WrapText_BreakIntoLines into other functions. That could make the function much clearer in some ways, but then you'd have even more variables crossing functions and more function call overhead. Thoughts on this issue would be appreciated.

Edit: I apologize that some of the lines are really long, it makes the formatting here (and in any text editor you may paste it into) rather ugly.
The dumber people think you are, the more surprised they're going to be when you kill them.

jonny
  • Members
  • 2951 posts
  • Last active: Feb 24 2008 04:22 AM
  • Joined: 13 Nov 2004
Your sig is awesome. :wink:

A witty sig and a few good scripts will get you anything in this forum. 8)

Gehn
  • Members
  • 13 posts
  • Last active: Jun 10 2005 06:31 PM
  • Joined: 02 Mar 2005
Unfortunately, I can't take credit for my signature. It's a quote from William Clayton.
The dumber people think you are, the more surprised they're going to be when you kill them.

observer_420
  • Guests
  • Last active:
  • Joined: --
Heh... if you have Cygwin installed (and what a wonderful way to get Unix commands in your Windows box!), you can use the fmt(1) command to wrap your text in default and optional ways.


;
; AutoHotkey Version: 1.x
; Language:       English
; Platform:       Win9x/NT
; Author:         Doug Snead
;
; Script Function:
;	filter text through arbitrary unix (cygwin)
;	stdin/stdout filters (in perl)
;
menu tray, tip, HotKey: windows t`r`nfilter selected text

TMPDIR = C:\cygwin\tmp               ; = /tmp in cygwin installation
TMPFILEIN = %TMPDIR%\filtr_in.tmp    ; = /tmp/filtr_in.tmp, _out.tmp
TMPFILEOUT = %TMPDIR%\filtr_out.tmp
BINDIR = C:\cygwin\bin               ; of sh(1) executable
Command = fmt                        ; default filter command

;;
;;  run selected text through a Unix filter (stdin/stdout) command
;;
#t::  ;; = the Windows key and the t key together
    saveClipBoard = %clipboard%
    Send, ^c   ;; copy selected text into clipboard
    FileAppend, %clipboard%, %TMPFILEIN%
    InputBox, Command, select stdio filter, unix filter command:,,350,150,,,,,%Command%
    if ErrorLevel = 0
    {
            TrayTip, running:, %Command%, 120, 16
	    RunWait, %BINDIR%\sh.exe -c "dos2unix </tmp/filtr_in.tmp | %Command% | unix2dos >/tmp/filtr_out.tmp",,Hide
	    TrayTip
	    FileRead, clipboard, %TMPFILEOUT%
	    Send, ^v  ;; paste processed text back in
	    FileDelete, %TMPFILEOUT%
    }
    clipboard = %saveClipBoard%
    saveClipBoard =
    FileDelete, %TMPFILEIN%
    return


That script should work from any windows application that allows Ctl v copy and Ctl v paste operations.

Use the unix tidy command to reformat html, etc.

fsnow55
  • Members
  • 36 posts
  • Last active: Jun 07 2011 07:27 PM
  • Joined: 08 Jun 2006
Hopefully, the last person to post the cygwin filter script will reply, or somebody that knows will reply:

This script looks like it matches my need but
I did not get any useful output from this script. Perhaps the problem is with the quotes here?

RunWait, %BINDIR%\sh.exe -c "dos2unix /tmp/filtr_out.tmp",,Hide



I tried different combinations of quoting but can not get output to /tmp/filtr_out.tmp (it never got created).
I think the problem is with the %Command% variable in the string.

BTW, this works:
RunWait, %BINDIR%\sh.exe -c "echo haha > /tmp/haha.txt"

Appreciate your help.

evl
  • Members
  • 1237 posts
  • Last active: Oct 20 2010 11:41 AM
  • Joined: 24 Aug 2005
Maybe using 2 quoteded sections would work (so the variable isn't enclosed):
"dos2unix </tmp/filtr_in.tmp | " %Command% " | unix2dos > /tmp/filtr_out.tmp",,


fsnow55
  • Members
  • 36 posts
  • Last active: Jun 07 2011 07:27 PM
  • Joined: 08 Jun 2006
Thanks. I found out the problem isn't with the quotes but that I have to fully qualify all the cygwin commands e.g. this works:

RunWait, %BINDIR%\sh.exe -c "/usr/bin/dos2unix /tmp/filtr_out.tmp",,Hide


and in the popup, I type the full path for the command e.g. /usr/bin/fmt -w 20

I'm new to cygwin, so need to find out how to "auto-source" the path so XP/AHK can understand it.

fsnow55
  • Members
  • 36 posts
  • Last active: Jun 07 2011 07:27 PM
  • Joined: 08 Jun 2006
Finally solved it with help from:
http://www.astro.umd...~harris/cygwin/

i.e. need to set the C:/Cygwin/bin path in my windows path.

Kudos
  • Members
  • 39 posts
  • Last active: Sep 27 2015 09:06 AM
  • Joined: 21 Aug 2006
Gehn, thanks for this - it was very helpful to me! :)