Inserting line breaks to hard wrap text
This lesson explains how to hard wrap text in a field.
The wordWrapped function
The wordWrapped
function takes a string and a maximum number of characters per line, if no maximum number of characters is specified a default of 75 is used.
The function walks through each line of text to see whether it's short enough to fit in the maximum length. If it's too long, the function breaks the line, at a space character, if possible, so that each line in the returned value is short enough to fit within the maximum.
The function returns the hard wrapped text.
get wordWrapped(field "Quotations",62)
put wordWrapped(myText,the length of line 1 of me) after me
put wordWrapped(it) into field "Email"
The maximum length parameter
Before starting the handler checks the pMaxLength parameter. If it's empty (meaning that the function call didn't include it) or isn't numeric, the handler substitutes a default value of 75 characters.
If it's not an integer, for example, if the function call uses a value like "22.4" then the handler truncates it to the nearest integer, since the number of characters in a line can't be a fraction. (This might happen if the value in the function call is the result of a calculation that might not give an integer result).
Looping through the text
The handler loops through each line in the pText parameter. The repeat for each form of the repeat control structure that's used here places each line in turn in the variable tLine so it can be examined. Then it builds the wrapped text, line by line, in the variable tWrappedText.
Within the loop, the handler checks whether the current line is short enough to fit. If the length of the line is less than or equal to pMaxLength, we add the line to tWrappedText, with a return character at the end for the next line. If the current line fits, we don't need to do anything else to it, and the repeat loop continues to the next iteration and the next line of pText.
The second repeat loop
If the current line is too long to fit, the handler enters a second repeat loop. This loop chops up the current line into shorter segments that will fit in the pMaxLength. The loop removes each segment from the current line as it goes, so when the variable tLine is empty, there are no segments left and the entire line has been wrapped.
The third repeat loop
To accomplish this, the second repeat loop uses a third nested loop, which works backward through the current segment, looking for space characters where the line can be broken. To do this, the third loop uses the repeat with x = start down to end form of the repeat control structure.
This loop takes a segment of the first pMaxLength characters in the current line and checks each character, from the last character in the segment backwards to the first character. If the current character is a space (or if what's left in the current line is short enough to fit in the pMaxLength), it stops and adds the current segment, up to but not including the last space, to the end of the tWrappedText variable along with a return character. Then it deletes that part (including the space) from the current line. Finally, the exit repeat statement tells the handler to go to the next iteration of the loop and start again with the next segment.
When the tLine variable is empty, it means the handler has moved all segments of the current line into the tWrappedLines variable, and it's finished processing the current line. The second loop is finished, and the outermost loop continues to the next iteration and the next line of pText.
An example
To make the process clearer, let's look at an example. Suppose the pMaxLength is 12, and the current line is "This is a test. This is only a test.", when the handler determines that the length of the line is greater than 12, it enters the second repeat loop, then immediately enters the third loop, looking at the segment consisting of the first to 12th character: "This is a te".
The third loop moves backwards through this segment, looking at character 12 and character 11, before it finds that character 10 is a space so the line can be broken here:
This is a
The handler deletes the first 10 characters (up to the space) from the current line and gets the next segment. The current line is now "test. This is only a test.", so the next segment of 12 consists of "test. This i". Going backwards from the end, the handler finds that character 11 is a space. It adds the segment (up to this breaking space) to the wrapped text:
This is a
test. This
Then it deletes the first 11 characters from the current line and gets the next segment of 12, which is "is only a te". Going backwards again, character 10 is a space, and adding the part of the current segment up to character 10 to the wrapped lines gives us:
This is a
test. This
is only a
Then the handler deletes the first ten characters from the current line, leaving "test." However, this time, when the loop checks the length of the current line, it finds that what's left is shorter than the pMaxLength. This means there's no need to check for spaces: it simply adds the remaining part of the line to the wrapped lines:
This is a
test. This
is only a
test.
As with each previous segment, the handler deletes the text it's already dealt with from the current line. This time, it's deleting all of what's left. And since the second loop starts with "repeat until thisLine is empty", it ends at this point, because the current line is now empty. The handler is now finished with that line and is ready to go on to the next line.
What if the third loop goes through the whole segment without finding any spaces? In this case, the loop variable "x" (in repeat with x = pMaxLength down to 1), which starts out being equal to pMaxLength, has been reduced by one through each iteration. If the third loop encounters a space, an exit repeat control structure stops the loop. The only way for the variable "x" to be equal to one is if the loop went through the entire segment without finding a space; otherwise, the handler would have exited the loop before "x" reached one.
Because of this, the handler can check whether x = 1 after the third loop. to find out whether the segment didn't contain any spaces. When this happens, the handler just inserts an arbitrary return character after the first pMaxLength characters, and deletes the same number of characters from the current line. This ensures that there are no lines longer than pMaxLength, even if a line has to be broken at a non-space character. Then the second loop continues with the next segment in the current line.
Once the current line has been completely processed, the second loop ends, and the next iteration of the first loop starts with the next line. The process continues until all the lines have been processed. Finally, the handler returns the contents of the theWrappedText variable, which contains the wrapped lines of text.
The wordWrapped function code
function wordWrapped pText,pMaxLength
local tWrappedText
if pMaxLength is empty or pMaxLength is not a number then
## use a default value if maxLength is not provided
put 75 into pMaxLength
end if
if pMaxLength is not an integer then
## truncate it
put trunc(pMaxLength) into pMaxLength
end if
repeat for each line tLine in pText
if length(tLine) <= pMaxLength then
## the line is shorter than the maximum
## so we can leave it as it is
put tLine & return after tWrappedText
else
## tLine is longer than the maximum
repeat until tLine is empty
## try to break the line at a space
repeat with x = pMaxLength down to 1
if char x of tLine is space or length(tLine) < pMaxLength then
put (char 1 to x-1 of tLine) & return after tWrappedText
delete char 1 to x of tLine
exit repeat
end if
end repeat
if x = 1 then
## no spaces in the first pMaxLength chars
## so we break it at that number of characters
put char 1 to pMaxLength of tLine & return after tWrappedText
delete char 1 to pMaxLength of tLine
end if
end repeat
end if
end repeat
return tWrappedText
end wordWrapped
Charles Szasz
Is this function found in LC 9.6.8?
Panos Merakos
Hello Charles,
This function (wordWrapped()) is not a LiveCode built-in function. It is a custom function, which the user has to implement, and it should work in any version of LiveCode. The implementation is shown in this lesson.
Kind regards,
Panos
--
Charles Szasz
Thanks!