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