--*************************************************************************
-- Script: Text Format Class
-- Version: 0.1
-- Date: 2016-01-10
-- Author: Valentin Schmidt
--
-- Implements an abstract representation (as propList) of formatted text
-- that can be read from existing text (or field) members, assigned to text
-- (or field) members, and also be merged, allowing copy/paste operations
-- of formatted text.
--*************************************************************************
property props
----------------------------------------
--
----------------------------------------
on new me
-- Only add text properties you really need to preserve, more properties means: slower.
-- Notice: When used for field instead of text members,the property list has to be changed to only
-- include properties that are actually available for fields
me.props = [#font, #fontStyle, #fontSize, #color, #alignment, #fixedLineSpace, #charSpacing, #tabs]
return me
end
----------------------------------------
-- Returns txtformat property list of specified text or field member
----------------------------------------
on getTxtFormat (me, mem)
-- TODO:
-- for text imported/pasted from external source:
-- only allow RETURN, replace all numtochar(10) with "" ???
fmt = [:]
fmt[#global] = [:]
fmt[#text] = mem.text
fmt[#sections] = []
state = [:]
repeat with p in props
state[p] = 0
fmt[#global][p] = mem.getProp(p)
end repeat
cnt = mem.text.length
repeat with i = 1 to cnt
changed = []
newState = [:]
repeat with p in props
test = mem.char[i].getProp(p)
newState[p] = test
if test<>state[p] then changed.add(p)
end repeat
if changed.count then
-- find end of section (next change or end of text)
startPos = i
bool = true
repeat while bool
if i+1>cnt then
bool = false
exit repeat
else
repeat with p in props
if mem.char[i+1].getProp(p)<>newState[p] then
bool = false
exit repeat
end if
end repeat
if bool then i=i+1
end if
end repeat
endPos = i
section = [:]
section[#range] = [startPos,endPos]
repeat with p in changed
section[p] = newState[p]
end repeat
fmt[#sections].add(section)
state = newState
end if
end repeat
return fmt
end
----------------------------------------
-- Assigns txtformat property list to specified text or field member
----------------------------------------
on setTxtFormat (me, fmt, mem)
repeat with p in props
mem.setProp(p, fmt[#global][p])
end repeat
lastProps = fmt[#global]
mem.text = fmt[#text]
repeat with s in fmt[#sections]
repeat with p in props
if not voidP(s[p]) then lastProps[p] = s[p]
mem.char[s.range[1]..s.range[2]].setProp(p, lastProps[p])
end repeat
end repeat
end
----------------------------------------
-- Returns txtformat property list of specified selection (as [from, to] list)
-- of specified text or field member
----------------------------------------
on getSelection (me, mem, sel)
fmt = [:]
fmt[#global] = [:]
fmt[#text] = mem.text.char[sel[1]+1..sel[2]]
fmt[#sections] = []
state = [:]
repeat with p in props
state[p] = 0
fmt[#global][p] = mem.getProp(p)
end repeat
repeat with i = sel[1]+1 to sel[2]
changed = []
newState = [:]
repeat with p in props
test = mem.char[i].getProp(p)
newState[p] = test
if test<>state[p] then changed.add(p)
end repeat
if changed.count then
-- find end of section (next change or end of text)
startPos = i
bool = true
repeat while bool
if i+1>sel[2] then
bool = false
exit repeat
else
repeat with p in props
if mem.char[i+1].getProp(p)<>newState[p] then
bool = false
exit repeat
end if
end repeat
if bool then i=i+1
end if
end repeat
endPos = i
section = [:]
section[#range] = [startPos-sel[1], endPos-sel[1]]
repeat with p in changed
section[p] = newState[p]
end repeat
fmt[#sections].add(section)
state = newState
end if
end repeat
return fmt
end
----------------------------------------
-- Puts text specified by txtformat propList <fmt2> into selection <sel> of text specifed by txtformat <fmt1>.
-- Returns new txtformat propList.
----------------------------------------
on mergeTxtFormats (me, fmt1, fmt2, sel)
fmt = [:]
fmt[#global] = fmt1[#global]
str = ""
if sel[1]>0 then put fmt1[#text].char[1..sel[1]] after str
put fmt2[#text] & fmt1[#text].char[sel[2]+1..fmt1[#text].length] after str
fmt[#text] = str
fmt[#sections] = []
offset = fmt2[#text].length - (sel[2]-sel[1])
repeat with s in fmt1[#sections]
if s.range[2]<sel[1] then
-- nothing
fmt[#sections].add(s)
else if s.range[1]>sel[2] then
--s = s.duplicate() -- uncomment for non-destructive mode
s.range = [s.range[1]+offset, s.range[2]+offset]
fmt[#sections].add(s)
else if s.range[1]<sel[1] then
s1 = s.duplicate()
s1.range = [s.range[1], sel[1]]
fmt[#sections].add(s1)
if s.range[2]>sel[2] then
-- add new section after sel
s2 = s.duplicate()
s2.range = [sel[2], s.range[2]+offset]
fmt[#sections].add(s2)
end if
else -- = sel[1] < s.range[1] < sel[2]
if s.range[2]<sel[2] then
--nothing
else
--s = s.duplicate() -- uncomment for non-destructive mode
s.range = [sel[2], s.range[2]+offset]
fmt[#sections].add(s)
end if
end if
end repeat
repeat with s in fmt2[#sections]
--s = s.duplicate() -- uncomment for non-destructive mode
s.range = [s.range[1]+sel[1], s.range[2]+sel[1]]
fmt[#sections].add(s)
end repeat
return fmt
end