/selectable_statictext superclass /editable true def /edit_pos 0 def /rest_index 0 def /rest_length 0 def /getvalue { % -- string value edit_pos 0 exch GetInterval value rest_index 1 index length 1 index sub GetInterval append } def /close_gap { % -- rest_length 0 ne { rest_index edit_pos sub string value rest_index rest_length getinterval exch value edit_pos 4 -1 roll putinterval edit_pos rest_length add /edit_pos exch promote value edit_pos 3 -1 roll putinterval value length /rest_index exch promote /rest_length 0 promote } if } def /set_edit_pos { % int -- edit_pos 1 index edit_pos eq { pop pop } { 1 index edit_pos gt { exch rest_index neg edit_pos add add exch } if close_gap 1 index 0 ge { 1 index edit_pos lt } false ifelse { edit_pos 2 index sub /rest_length exch promote exch /edit_pos exch promote value edit_pos rest_length getinterval exch value length rest_length sub /rest_index exch promote value rest_index 4 -1 roll putinterval value edit_pos rest_index edit_pos sub string putinterval } { exch pop } ifelse edit_pos exch min index_to_selection 0 get compute_updated_display_map } ifelse } def /grow_edit_buffer { % -- value edit_pos 0 exch GetInterval 10 string append value rest_index rest_length getinterval append /value exch promote edit_pos 10 add /rest_index exch promote edit_pos index_to_selection 0 get compute_updated_display_map } def /make_editable { % -- value length /edit_pos exch promote value 10 string append /value exch promote value length /rest_index exch promote /rest_length 0 promote selection_point selection_to_index set_edit_pos compute_display_map } def /non_editable { % -- getvalue /value exch promote edit_pos index_to_selection 0 get compute_updated_display_map } def /old_del_backwards_to { % int -- selection_point 1 index 0 lt { pop pop } { edit_pos exch { 1 index 3 index lt { exch pop exit } if value 2 index 0 put exch 1 sub exch } loop exch /edit_pos exch promote edit_pos index_to_selection /selection_point exch promote selection_point 0 get exch 1 index exch 0 get ne { dup compute_updated_display_map dup paint_text_from_line dup display_top_line lt {set_top_line} {pop } ifelse } { gsave clip_textarea justification /Left eq { dup selection_point 1 get -1 BG select_part_line selection_point 1 get -1 FG draw_part_text_line } { dup BG select_line FG draw_text_line } ifelse grestore } ifelse } ifelse } def /del_backwards_to { % int -- selection_point 1 index 0 lt { pop pop } { edit_pos exch { 1 index 3 index lt { exch pop exit } if value 2 index 0 put exch 1 sub exch } loop exch /edit_pos exch promote edit_pos index_to_selection /selection_point exch promote selection_point 0 get exch display_map total_display_lines selection_point 0 get 1 add min 1 bitshift get 2 index compute_updated_display_map 2 index 3 -1 roll 0 get ne { pop true } { display_map total_display_lines selection_point 0 get 1 add min 1 bitshift get ne } ifelse { dup paint_text_from_line dup display_top_line lt {set_top_line} {pop } ifelse } { gsave clip_textarea justification /Left eq { dup selection_point 1 get -1 BG select_part_line selection_point 1 get -1 FG draw_part_text_line } { dup BG select_line FG draw_text_line } ifelse grestore } ifelse } ifelse } def /del_char { % -- edit_pos 1 sub del_backwards_to } def /del_word { % -- 0 edit_pos 1 sub max start_of_word_at del_backwards_to } def /del_line { % -- display_map selection_point 0 get start_of_line_at 1 bitshift get del_backwards_to } def /paint_changes_from { % int * int -- gsave clip_textarea display_top_line exch max { dup display_bottom_line gt { pop pop exit } if display_map 1 index 1 bitshift get 2 index 2 index 1 bitshift get ne { (Drawing line %) [2 index] sprintf DEBUG dup 1 sub BG select_line dup 1 sub FG draw_text_line } if 1 add } loop grestore } def /insert_key { % char -- selection_point 0 get value edit_pos 3 index put edit_pos 1 add /edit_pos exch promote edit_pos index_to_selection /selection_point exch promote edit_pos rest_index eq {grow_edit_buffer} if value display_map 2 index 1 bitshift get display_map 3 index 1 bitshift 1 add get getinterval text_width_proc cvx exec textarea_width le { 1 index 10 ne } false ifelse { exch is_word_space { display_map 1 index 1 bitshift get 0 2 index 1 sub max compute_updated_display_map display_map 2 index 1 bitshift get ne { 0 exch 1 sub max paint_text_from_line } { gsave clip_textarea justification /Left eq { dup 0 selection_point 1 get 1 sub max -1 BG select_part_line 0 selection_point 1 get 1 sub max -1 FG draw_part_text_line } { dup BG select_line FG draw_text_line } ifelse grestore } ifelse } { gsave clip_textarea justification /Left eq { dup 0 selection_point 1 get 1 sub max -1 BG select_part_line 0 selection_point 1 get 1 sub max -1 FG draw_part_text_line } { dup BG select_line FG draw_text_line } ifelse grestore } ifelse } { exch pop dup compute_updated_display_map paint_text_from_line selection_point 0 get display_bottom_line gt { scroll_down_line} if } ifelse } def /tmpinsert_key { % char -- selection_point 0 get value edit_pos 4 -1 roll put edit_pos 1 add /edit_pos exch promote edit_pos rest_index eq { grow_edit_buffer} if display_map 1 index 1 bitshift get display_map total_display_lines 3 index 1 add min 1 bitshift get edit_pos index_to_selection /selection_point exch promote exch display_map 3 index 1 bitshift get ne { pop 1 sub paint_text_from_line } { display_map total_display_lines 3 index 1 add min 1 bitshift get ne { paint_text_from_line} { gsave clip_textarea justification /Left eq { dup selection_point 1 get 1 sub -1 BG select_part_line selection_point 1 get 1 sub -1 FG draw_part_text_line } { dup BG select_line FG draw_text_line } ifelse grestore } ifelse } ifelse selection_point 0 get display_bottom_line gt {scroll_down_line} if } def /insert_string { % string -- dup length selection_point 0 get 1 index 0 eq { pop pop pop } { value edit_pos 0 exch GetInterval 4 -1 roll append value edit_pos value length edit_pos sub getinterval append /value exch promote edit_pos 2 index add /edit_pos exch promote rest_index 3 -1 roll add /rest_index exch promote dup compute_updated_display_map edit_pos index_to_selection /selection_point exch promote reset_range paint_text_from_line } ifelse } def /align_with_previous { % -- selection_point 0 get 1 sub dup 0 ge { false draw_cursor display_map exch 1 bitshift get { value exch dup 1 add 3 1 roll get dup 9 eq true { dup 32 eq } ifelse not { pop pop exit } if insert_key } loop } {pop} ifelse } def /clipboard //nullstring def /clear_selection { % -- /do_clear_selection gexec } def /do_clear_selection { % -- editable not not { start_selection selection_to_index end_selection selection_to_index 2 copy eq { pop pop } { false draw_cursor 1 index edit_pos eq { dup rest_index sub 1 index /rest_index exch promote rest_length exch sub /rest_length } { 1 index /edit_pos exch promote start_selection /selection_point } ifelse exch promote exch { 2 copy le { pop pop exit } if value 1 index 0 put 1 add } loop reset_range start_selection 0 get compute_updated_display_map start_selection 0 get paint_text_from_line } ifelse } if } def /copy_selection { % -- /do_copy_selection gexec } def /do_copy_selection { % -- editable not true { selection_range not } ifelse not { start_selection selection_to_index end_selection selection_to_index 1 index edit_pos eq { exch pop rest_index sub dup string value rest_index 4 -1 roll } { 1 index sub rest_index sub edit_pos add dup string value 4 -2 roll } ifelse getinterval 1 index copy pop /clipboard exch promote } if } def /paste_selection { % -- /do_paste_selection gexec } def /do_paste_selection { % -- editable not not { selection_range {do_clear_selection} if false draw_cursor clipboard insert_string } if } def /cut_selection { % -- /do_cut_selection gexec } def /do_cut_selection { % -- editable not true { selection_range not } ifelse not { false draw_cursor do_copy_selection do_clear_selection } if } def /clipboard_contents { % -- string clipboard } def /end_of_word_at { % int -- int dup edit_pos eq { pop rest_index } if //end_of_word_at exec } def /move_right_over_buffer { % -- false draw_cursor reset_range rest_length 0 ne { rest_index 1 add index_to_selection set_selection } if true draw_cursor } def /do_move_to { % name -- editable { dup /right eq } false ifelse { pop /move_right_over_buffer gexec } { //do_move_to exec } ifelse } def /fix_caret_damage { % -- editable { gsave clip_textarea selection_point 0 get 0 selection_point 1 get 1 sub max display_map selection_point 0 get 1 bitshift 1 add get rest_index index_to_selection 1 get 1 add min FG draw_part_text_line grestore } { //fix_caret_damage exec } ifelse } def /set_selection { % selection -- editable { selection_to_index set_edit_pos edit_pos index_to_selection } if //set_selection exec } def /normalize_selection { % -- start_selection end_selection gt_range { end_selection set_selection start_selection end_selection /start_selection exch promote /end_selection exch promote end_selection selection_to_index rest_index add edit_pos sub index_to_selection /end_selection exch promote } if } def /set_value { % string -- //set_value exec editable {make_editable} if } def /append_string { % string -- editable { rest_length 1 index length add /rest_length exch promote } if //append_string exec } def /set_editable { % boolean -- dup editable eq {pop} { /editable exch softpromote editable { make_editable} {non_editable} ifelse } ifelse } def /do_onkey { % int -- editable not {pop} { selection_range {do_clear_selection} if false draw_cursor dup do_onkey$SwiTch0 exch 2 copy known not { pop /$deFaUlT } if get exec true draw_cursor } ifelse } def /do_onkey$SwiTch0 6 dict dup begin /$deFaUlT {insert_key} def 21 { pop del_line } def 23 { pop del_word } def 127 { pop del_char } def 10 { pop 10 insert_key align_with_previous } def 13 { pop 10 insert_key } def end def /onkey { % int -- /do_onkey [3 -1 roll] gexeca } def /OnInit { % -- //OnInit exec true set_editable } def