Patch title: Release 94 bulk changes
Abstract:
File: /pliant/graphic/browser/sample/editor.pli
Key:
    Removed line
    Added line
   
module "/pliant/language/unsafe.pli"
module "/pliant/language/stream.pli"
module "/pliant/language/data/id.pli"
module "/pliant/graphic/browser/server/context.pli"
module "/pliant/graphic/browser/server/api.pli"
module "/pliant/language/type/text/str32.pli"
module "/pliant/util/encoding/utf8.pli"


type FileEditorContext
  field Pointer:BrowserServerContext context
  field Array:Str lines
  field Array:Str ids
  field (Dictionary Str Int) ys
  field CBool modified <- false
  field Int cx cy <- 0 # cursor
  field Int ax ay bx by <- undefined # selection
  field Link:Str search_pattern
  field Link:Str replace_pattern
  field List:Str copy

method e move x y
  arg_rw FileEditorContext e ; arg Int x y
  var Pointer:BrowserServerContext c :> e context
  implicit e c
    if y>=0 and y<ids:size
      focus_set "line "+ids:y (min x (shunt y>=0 and y<lines:size lines:y:len 0))
    section_overwrite "status"
      para stick cursor edit
        text "line "+(string y+1)+" column "+(string x+1)
    cx := x ; cy := y

method e insert_line y
  arg_rw FileEditorContext e ; arg Int y
  var Pointer:BrowserServerContext c :> e context
  implicit c e
    e:lines size += 1
    for (var Int i) lines:size-2 y step -1
      lines i+1 := lines i
    lines y := ""
    var Str id := "line "+generate_id
    e:ids size += 1
    for (var Int i) ids:size-2 y step -1
      ids i+1 := ids i
    ids y := id    
    each p ys
      if p>=y
        p += 1
    ys insert id y
    section_after (shunt y>0 "line "+(ids y-1) "line top")
      section "line "+id
        text ""
    
method e delete_line y
  arg_rw FileEditorContext e ; arg Int y
  var Pointer:BrowserServerContext c :> e context
  implicit c e
    var Str id := ids y
    for (var Int i) y lines:size-2
      lines i := lines i+1
    e:lines size -= 1
    for (var Int i) y ids:size-2
      ids i := ids i+1
    e:ids size -= 1
    ys remove (ys first id)
    each p ys
      if p>=y
        p -= 1
    section_delete "line "+id
    
method e set_line y l
  arg_rw FileEditorContext e ; arg Int y ; arg Str l
  var Pointer:BrowserServerContext c :> e context
  implicit c e
    lines y := l
    section_overwrite "line "+ids:y
      para stick cursor edit
        text (shunt l:len>0 l " ")
    e modified := true

method e set_line y l
  arg_rw FileEditorContext e ; arg Int y ; arg Str32 l
  e set_line y utf8_encode:l

method e cut_paste action
  arg_rw FileEditorContext e ; arg Str action
  var Pointer:BrowserServerContext c :> e context
  implicit c e
    if ay>=0 and by>=ay and by<lines:size and ax>=0 and (by>ay or bx>=ax)
      if by>ay and bx=0
        bx -= 1
      if action="cut" or action="copy"
        copy := var List:Str empty_list
        if by=ay
          copy += lines:ay ax bx+1-ax
        else
          copy += lines:ay ax lines:ay:len
          for (var Int i) ay+1 by-1
            copy += lines i
          copy += lines:by 0 bx+1
      if action="cut"
        set_line ay (lines:ay 0 ax)+(lines:by bx+1 lines:by:len)
        for (var Int i) by ay+1 step -1
          delete_line i
        move ax ay
        # FIXME window_refresh main
        ax := undefined ; ay := undefined ; bx := undefined ; by := undefined
      if action="paste"
        if not (exists copy:first)
          void
        eif not (exists (copy next copy:first))
          set_line cy (lines:cy 0 cx)+copy:first+(lines:cy cx lines:cy:len)
        else
          insert_line cy+1
          set_line cy+1 copy:last+(lines:cy cx lines:cy:len)
          set_line cy (lines:cy 0 cx)+copy:first
          var Int y := cy
          var Pointer:Str pl :> copy next copy:first
          while addressof:pl<>(addressof copy:last)
            y += 1
            insert_line y
            set_line y pl
            pl :> copy next pl
        # FIXME window_refresh main

method context text_editor filename
  arg_rw BrowserServerContext context ; arg Str filename
  ovar FileEditorContext e
  e context :> context
  implicit context e
    style_copy "" "text"
    style_set "text" text font "Bitstream Vera Sans Mono" # "Free Monospaced Bold"
    style_set "text" text wrap false
    search_pattern :> new Str
    replace_pattern :> new Str
    (var Stream s) open filename in+safe ; var Int i := 0
    while not s:atend
      var Str id := generate_id ; ids += id ; ys insert id i ; i += 1
      lines += s readline
    if i=0
      var Str id := generate_id ; ids += id ; ys insert id i
      lines += ""
    s close
    window top
      if true # FIXME center
        if true # FIXME bold
          text filename
        eol
      section "parameters"
        void
    window left
      section "menu" dynamic
        button "file" key "alt f"
          section_overwrite "parameters"
            button "save" key "alt s"
              (var Stream s2) open filename out+safe
              for (var Int i) 0 lines:size-1
                s2 writeline lines:i
              s2 close
              e modified := false
              section_replay "menu"
            button "cancel" key "escape"
              section_replay "menu"
        eol
        button "edit" key "alt e"
          section_overwrite "parameters"
            button "copy" key "alt c"
              cut_paste "copy"
            button "cut" key "alt t"
              cut_paste "cut"
            button "paste" key "alt p"
              cut_paste "paste"
            button "cancel" key "escape"
              section_replay "menu"
            eol
        eol
        button "search" key "alt s"
          focus_save
          section_overwrite "parameters"
            input "Search pattern: " search_pattern # focus true
            button "search" key "alt s"
              if search_pattern<>"" and cx>=0 and cy>=0
                var Int x := cx+1 ; var Int y := cy
                part search
                  if y>=lines:size
                    leave search
                  x += (lines:y x lines:y:len) search search_pattern undefined
                  if x<0
                    y += 1 ; x := 0
                    restart search
                  move x y
            button "cancel" key "escape"
              section_replay "menu"
            eol
            input "Replace with: " replace_pattern
            button "replace" key "alt r"
              if search_pattern<>""
                if cy<lines:size
                  if (lines:cy cx search_pattern:len)=search_pattern
                    set_line cy (lines:cy 0 cx)+replace_pattern+(lines:cy cx+search_pattern:len lines:cy:len)
                if search_pattern<>""
                  var Int x := cx+1 ; var Int y := cy
                  part search
                    if y>=lines:size
                      leave search
                    x += (lines:y x lines:y:len) search search_pattern undefined
                    if x<0
                      y += 1 ; x := 0
                      restart search
                    move x y
        eol
        button "goto" key "alt g"
          section_overwrite "parameters"
            ovar Int num := undefined
            input "Line number: " num focus true
            button "go to line" key "alt l"
              if num>=0 and num<lines:size
                move 0 num-1
              section_replay "menu"
            button "cancel" key "escape"
              section_replay "menu"
        eol
        button "exit" key "alt x"
          if modified
            section_overwrite "parameters"
              text "File as been modified, but not saved." ; eol
              button "force to leave" key "alt f"
                url_return
              button "cancel" key "escape"
                section_replay "menu"
          else
            url_return
        section_overwrite "parameters"
          void
    window main
      hook
        style use "text"
          section "line top"
            void
          for (var Int i) 0 ids:size-1
            section "line "+ids:i
              para stick cursor edit
                text (shunt i<lines:size and lines:i:len>0 lines:i " ")
      event
        # console "editor event " event " key " key " at " focus_section eol
        if event="press" and key="button1" and (pointer_section parse word:"line" any:(var Str id))
          var Int x := pointer_index
          var Pointer:Int py :> ys first id
          var Int y := shunt exists:py py (cast undefined Int)
          move x y
          ax := cx ; ay := cy
        if event="release" and key="button1" and (pointer_section parse word:"line" any:(var Str id))
          var Int x := pointer_index
          var Pointer:Int py :> ys first id
          var Int y := shunt exists:py py (cast undefined Int)
          bx := x ; by := y
        eif event="press" and key="shift"
          ax := cx ; ay := cy
        eif event="release" and key="shift"
          bx := cx ; by := cy
        if cy>=0 and cy<lines:size and cx>=0
          var Str32 l := utf8_decode lines:cy
          if event="character"
            if true
              lines cy := utf8_encode (l 0 cx)+utf8_decode:key+(l cx l:len)
              modified := true
              cx += 1
              section_overwrite "status"
                para stick cursor edit
                  text "line "+(string cy+1)+" column "+(string cx+1)
            else
              set_line cy (l 0 cx)+utf8_decode:key+(l cx l:len)
              move cx+1 cy
          eif event="press"
            if key="backspace"
              if cx>0
                set_line cy (l 0 cx-1)+(l cx l:len)
                move cx-1 cy
              eif cy>0
                var Int i := utf8_decode:(lines cy-1):len
                set_line cy-1 (lines cy-1)+lines:cy
                delete_line cy
                # window_refresh main
                # display
                move i cy-1
            eif key="enter"
              insert_line cy+1
              set_line cy+1 (l cx l:len)
              set_line cy (l 0 cx)
              if false # while { event_discard2 ; event="press" and key="enter" }
                cy += 1
                insert_line cy
              # window_refresh main
              # display
              if (lines cy+1):len=0
                cx := 0
                while (lines:cy cx 1)=" "
                  cx += 1
                set_line cy+1 (repeat cx " ")
                move cx cy+1
              else
                move 0 cy+1
            eif (key="left" or key="shift left") and cx>0
              move cx-1 cy
            eif (key="right" or key="shift right") and cx+1<=l:len
              move cx+1 cy
            eif key="home" or key="shift home"
              move 0 cy
            eif key="end" or key="shift end"
              move l:len cy
            eif (key="up" or key="shift up") and cy>0
              move cx cy-1
            eif (key="down" or key="shift down") and cy+1<lines:size
              move cx cy+1
            eif key="delete"
              cut_paste "cut"
            eif key="insert"
              cut_paste "paste"
        if false # FIXME target_x1-target_x0>hook_x1-hook_x0 
          window_refresh main
    window bottom
      section "status"
        void


export '. text_editor'