]> git.cworth.org Git - sup/blob - lib/sup/textfield.rb
bugfix for input history (stop adding all those excessive blank entries)
[sup] / lib / sup / textfield.rb
1 module Redwood
2
3 ## a fully-functional text field supporting completions, expansions,
4 ## history--everything!
5 ## 
6 ## writing this fucking sucked. if you thought ncurses was some 1970s
7 ## before-people-knew-how-to-program bullshit, wait till you see
8 ## ncurses forms.
9 ##
10 ## completion comments: completion is done emacs-style, and mostly
11 ## depends on outside support, as we merely signal the existence of a
12 ## new set of completions to show (#new_completions?)  or that the
13 ## current list of completions should be rolled if they're too large
14 ## to fill the screen (#roll_completions?).
15 ##
16 ## in sup, completion support is implemented through BufferManager#ask
17 ## and CompletionMode.
18 class TextField
19   def initialize window, y, x, width
20     @w, @x, @y = window, x, y
21     @width = width
22     @i = nil
23     @history = []
24
25     @completion_block = nil
26     reset_completion_state
27   end
28
29   bool_reader :new_completions, :roll_completions
30   attr_reader :completions
31
32   def value; @value || get_cursed_value end
33
34   def activate question, default=nil, &block
35     @question = question
36     @completion_block = block
37     @field = Ncurses::Form.new_field 1, @width - question.length,
38                                      @y, @x + question.length, 0, 0
39     @form = Ncurses::Form.new_form [@field]
40     @value = default
41     Ncurses::Form.post_form @form
42     set_cursed_value default if default
43   end
44
45   def position_cursor
46     @w.attrset Colormap.color_for(:none)
47     @w.mvaddstr @y, 0, @question
48     Ncurses.curs_set 1
49     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_END_FIELD
50     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_NEXT_CHAR if @value && @value =~ / $/ # fucking RETARDED!!!!
51   end
52
53   def deactivate
54     reset_completion_state
55     @form.unpost_form
56     @form.free_form
57     @field.free_field
58     @field = nil
59     Ncurses.curs_set 0
60   end
61
62   def handle_input c
63     ## short-circuit exit paths
64     case c
65     when Ncurses::KEY_ENTER # submit!
66       @value = get_cursed_value
67       @history.push @value unless @value =~ /^\s*$/
68       return false
69     when Ncurses::KEY_CANCEL # cancel
70       @value = nil
71       return false
72     when Ncurses::KEY_TAB # completion
73       return true unless @completion_block
74       if @completions.empty?
75         v = get_cursed_value
76         c = @completion_block.call v
77         if c.size > 0
78           @value = c.map { |full, short| full }.shared_prefix(true)
79           set_cursed_value @value
80           position_cursor
81         end
82         if c.size > 1
83           @completions = c
84           @new_completions = true
85           @roll_completions = false
86         end
87       else
88         @new_completions = false
89         @roll_completions = true
90       end
91       return true
92     end
93
94     reset_completion_state
95     @value = nil
96
97     d =
98       case c
99       when Ncurses::KEY_LEFT
100         Ncurses::Form::REQ_PREV_CHAR
101       when Ncurses::KEY_RIGHT
102         Ncurses::Form::REQ_NEXT_CHAR
103       when Ncurses::KEY_BACKSPACE
104         Ncurses::Form::REQ_DEL_PREV
105       when 1 #ctrl-a
106         Ncurses::Form::REQ_BEG_FIELD
107       when 5 #ctrl-e
108         Ncurses::Form::REQ_END_FIELD
109       when 11 # ctrl-k
110         Ncurses::Form::REQ_CLR_EOF
111       when Ncurses::KEY_UP, Ncurses::KEY_DOWN
112         unless @history.empty?
113           value = get_cursed_value
114           @i ||= @history.size
115           #Redwood::log "history before #{@history.inspect}"
116           @history[@i] = value #unless value =~ /^\s*$/
117           @i = (@i + (c == Ncurses::KEY_UP ? -1 : 1)) % @history.size
118           @value = @history[@i]
119           #Redwood::log "history after #{@history.inspect}"
120           set_cursed_value @value
121           Ncurses::Form::REQ_END_FIELD
122         end
123       else
124         c
125       end
126
127     Ncurses::Form.form_driver @form, d if d
128     true
129   end
130
131 private
132
133   def reset_completion_state
134     @completions = []
135     @new_completions = @roll_completions = @clear_completions = false
136   end
137
138   ## ncurses inanity wrapper
139   ##
140   ## DO NOT READ THIS CODE. YOU WILL GO MAD.
141   def get_cursed_value
142     return nil unless @field
143
144     x = Ncurses.curx
145     Ncurses::Form.form_driver @form, Ncurses::Form::REQ_VALIDATION
146     v = @field.field_buffer(0).gsub(/^\s+|\s+$/, "")
147
148     ## cursor <= end of text
149     if x - @question.length - v.length <= 0
150       v
151     else # trailing spaces
152       v + (" " * (x - @question.length - v.length))
153     end
154   end
155
156   def set_cursed_value v
157     @field.set_field_buffer 0, v
158   end
159 end
160 end