1. --*************************************************************************
  2. -- Script:   Text Format Class
  3. -- Version:  0.1
  4. -- Date:     2016-01-10
  5. -- Author:   Valentin Schmidt
  6. --
  7. -- Implements an abstract representation (as propList) of formatted text
  8. -- that can be read from existing text (or field) members, assigned to text
  9. -- (or field) members, and also be merged, allowing copy/paste operations
  10. -- of formatted text.
  11. --*************************************************************************
  12.  
  13. property props
  14.  
  15. ----------------------------------------
  16. --
  17. ----------------------------------------
  18. on new me
  19.   -- Only add text properties you really need to preserve, more properties means: slower.
  20.   -- Notice: When used for field instead of text members,the property list has to be changed to only
  21.   -- include properties that are actually available for fields
  22.   me.props = [#font, #fontStyle, #fontSize, #color, #alignment, #fixedLineSpace, #charSpacing, #tabs]
  23.   return me
  24. end
  25.  
  26. ----------------------------------------
  27. -- Returns txtformat property list of specified text or field member
  28. ----------------------------------------
  29. on getTxtFormat (me, mem)
  30.  
  31.   -- TODO:
  32.   -- for text imported/pasted from external source:
  33.   -- only allow RETURN, replace all numtochar(10) with "" ???
  34.  
  35.   fmt = [:]
  36.   fmt[#global] = [:]
  37.   fmt[#text] = mem.text
  38.   fmt[#sections] = []
  39.  
  40.   state = [:]
  41.   repeat with p in props
  42.     state[p] = 0
  43.     fmt[#global][p] = mem.getProp(p)
  44.   end repeat
  45.  
  46.   cnt = mem.text.length
  47.   repeat with i = 1 to cnt
  48.    
  49.     changed = []
  50.     newState = [:]
  51.     repeat with p in props
  52.       test = mem.char[i].getProp(p)
  53.       newState[p] = test
  54.       if test<>state[p] then changed.add(p)
  55.     end repeat
  56.    
  57.     if changed.count then
  58.      
  59.       -- find end of section (next change or end of text)
  60.       startPos = i
  61.       bool = true
  62.       repeat while bool
  63.         if i+1>cnt then
  64.           bool = false
  65.           exit repeat
  66.         else
  67.           repeat with p in props
  68.             if mem.char[i+1].getProp(p)<>newState[p] then
  69.               bool = false
  70.               exit repeat
  71.             end if
  72.           end repeat
  73.           if bool then i=i+1
  74.         end if
  75.       end repeat
  76.       endPos = i
  77.      
  78.       section = [:]
  79.       section[#range] = [startPos,endPos]
  80.       repeat with p in changed
  81.         section[p] = newState[p]
  82.       end repeat
  83.      
  84.       fmt[#sections].add(section)
  85.      
  86.       state = newState
  87.      
  88.     end if
  89.   end repeat
  90.  
  91.   return fmt
  92. end
  93.  
  94. ----------------------------------------
  95. -- Assigns txtformat property list to specified text or field member
  96. ----------------------------------------
  97. on setTxtFormat (me, fmt, mem)
  98.   repeat with p in props
  99.     mem.setProp(p, fmt[#global][p])
  100.   end repeat
  101.   lastProps = fmt[#global]
  102.   mem.text = fmt[#text]
  103.   repeat with s in fmt[#sections]
  104.     repeat with p in props
  105.       if not voidP(s[p]) then lastProps[p] = s[p]
  106.       mem.char[s.range[1]..s.range[2]].setProp(p, lastProps[p])
  107.     end repeat
  108.   end repeat
  109. end
  110.  
  111. ----------------------------------------
  112. -- Returns txtformat property list of specified selection (as [from, to] list)
  113. -- of specified text or field member
  114. ----------------------------------------
  115. on getSelection (me, mem, sel)
  116.   fmt = [:]
  117.   fmt[#global] = [:]
  118.   fmt[#text] = mem.text.char[sel[1]+1..sel[2]]
  119.   fmt[#sections] = []
  120.  
  121.   state = [:]
  122.   repeat with p in props
  123.     state[p] = 0
  124.     fmt[#global][p] = mem.getProp(p)
  125.   end repeat
  126.  
  127.   repeat with i = sel[1]+1 to sel[2]
  128.    
  129.     changed = []
  130.     newState = [:]
  131.     repeat with p in props
  132.       test = mem.char[i].getProp(p)
  133.       newState[p] = test
  134.       if test<>state[p] then changed.add(p)
  135.     end repeat
  136.    
  137.     if changed.count then
  138.      
  139.       -- find end of section (next change or end of text)
  140.       startPos = i
  141.       bool = true
  142.       repeat while bool
  143.         if i+1>sel[2] then
  144.           bool = false
  145.           exit repeat
  146.         else
  147.           repeat with p in props
  148.             if mem.char[i+1].getProp(p)<>newState[p] then
  149.               bool = false
  150.               exit repeat
  151.             end if
  152.           end repeat
  153.           if bool then i=i+1
  154.         end if
  155.       end repeat
  156.       endPos = i
  157.      
  158.       section = [:]
  159.       section[#range] = [startPos-sel[1], endPos-sel[1]]
  160.       repeat with p in changed
  161.         section[p] = newState[p]
  162.       end repeat
  163.      
  164.       fmt[#sections].add(section)
  165.      
  166.       state = newState
  167.      
  168.     end if
  169.   end repeat
  170.  
  171.   return fmt
  172. end
  173.  
  174. ----------------------------------------
  175. -- Puts text specified by txtformat propList <fmt2> into selection <sel> of text specifed by txtformat <fmt1>.
  176. -- Returns new txtformat propList.
  177. ----------------------------------------
  178. on mergeTxtFormats (me, fmt1, fmt2, sel)
  179.  
  180.   fmt = [:]
  181.   fmt[#global] = fmt1[#global]
  182.  
  183.   str = ""
  184.   if sel[1]>0 then put fmt1[#text].char[1..sel[1]] after str
  185.   put fmt2[#text] & fmt1[#text].char[sel[2]+1..fmt1[#text].length] after str
  186.   fmt[#text] = str
  187.  
  188.   fmt[#sections] = []
  189.  
  190.   offset = fmt2[#text].length - (sel[2]-sel[1])
  191.  
  192.   repeat with s in fmt1[#sections]
  193.    
  194.     if s.range[2]<sel[1] then
  195.       -- nothing
  196.       fmt[#sections].add(s)
  197.     else if s.range[1]>sel[2] then
  198.       --s = s.duplicate() -- uncomment for non-destructive mode
  199.       s.range = [s.range[1]+offset, s.range[2]+offset]
  200.       fmt[#sections].add(s)
  201.     else if s.range[1]<sel[1] then
  202.       s1 = s.duplicate()
  203.       s1.range = [s.range[1], sel[1]]
  204.       fmt[#sections].add(s1)
  205.      
  206.       if s.range[2]>sel[2] then
  207.         -- add new section after sel
  208.         s2 = s.duplicate()
  209.         s2.range = [sel[2], s.range[2]+offset]
  210.         fmt[#sections].add(s2)
  211.       end if
  212.     else -- = sel[1] < s.range[1] < sel[2]
  213.       if s.range[2]<sel[2] then
  214.         --nothing
  215.       else
  216.         --s = s.duplicate() -- uncomment for non-destructive mode
  217.         s.range = [sel[2], s.range[2]+offset]
  218.         fmt[#sections].add(s)
  219.       end if
  220.     end if
  221.   end repeat
  222.  
  223.   repeat with s in fmt2[#sections]
  224.     --s = s.duplicate() -- uncomment for non-destructive mode
  225.     s.range = [s.range[1]+sel[1], s.range[2]+sel[1]]
  226.     fmt[#sections].add(s)
  227.   end repeat
  228.  
  229.   return fmt
  230.  
  231. end
  232.  
[raw code]