rbcurse-experimental 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,418 @@
1
+ =begin
2
+ * Name: ScrollForm - a form that can take more than the screen and focus only on what's visible
3
+ * This class originated in TabbedPane for the top button form which only scrolls
4
+ * horizontally and uses objects that have a ht of 1. Here we have to deal with
5
+ * large objects and vertical scrolling.
6
+ * Description:
7
+ * Author: rkumar
8
+
9
+ --------
10
+ * Date: 2010-03-16 11:32
11
+ * License:
12
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
13
+
14
+
15
+ NOTE:
16
+ There are going to be tricky cases we have to deal with such as objects that start in the viewable
17
+ area but finish outside, or vice versa.
18
+
19
+ What if we wish some static text to be displayed at top or bottom of ScrollForm
20
+ =end
21
+ require 'logger'
22
+ require 'rbcurse'
23
+
24
+ #include Ncurses # FFI 2011-09-8
25
+ include RubyCurses
26
+ module RubyCurses
27
+ extend self
28
+ class ScrollForm < RubyCurses::Form
29
+ # the pad prints from this col to window
30
+ attr_accessor :pmincol # advance / scroll columns
31
+ # the pad prints from this row to window, usually 0
32
+ attr_accessor :pminrow # advance / scroll rows (vertically)
33
+ #attr_accessor :display_w # width of screen display NOW METHODS
34
+ #attr_accessor :display_h # ht of screen display
35
+ attr_accessor :row_offset, :col_offset
36
+ attr_accessor :scroll_unit # by how much should be scroll
37
+ attr_reader :orig_top, :orig_left
38
+ attr_reader :window
39
+ attr_accessor :name
40
+ attr_reader :cols_panned, :rows_panned
41
+ def initialize win, &block
42
+ @target_window = win
43
+ super
44
+ @pminrow = @pmincol = 0
45
+ @row_offset = @col_offset = 0
46
+ @scroll_unit = 3
47
+ @cols_panned = @rows_panned = 0
48
+ @repaint_all = true
49
+
50
+ # take display dimensions from window. It is safe to override immediately after form creation
51
+ @display_h = win.height
52
+ @display_w = win.width
53
+ if @display_h == 0
54
+ @display_h = (Ncurses.LINES - win.top - 2)
55
+ else
56
+ @display_h = win.height - 2
57
+ end
58
+ if @display_w == 0
59
+ @display_w = (Ncurses.COLS - win.left - 2)
60
+ else
61
+ # copywin fails unless u use rootwindow, so what gives in this case
62
+ @display_w = win.width - 2
63
+ end
64
+
65
+ init_vars
66
+ end
67
+ def init_vars
68
+ # maybe we should use C-x combinations rather than these keys which might be used
69
+ # by other widgets, apps
70
+ bind_key(?\M-h, :scroll_left)
71
+ bind_key(?\M-l, :scroll_right)
72
+ bind_key(?\M-n, :scroll_down)
73
+ bind_key(?\M-p, :scroll_up)
74
+ end
75
+ def should_print_border flag=true
76
+ @print_border_flag = flag
77
+ @row_offset = @col_offset = 1
78
+ end
79
+ # This is how we set size of pad and where it prints on screen
80
+ # This is all that's needed after constructor.
81
+ # @param [Fixnum] t top (row on screen to print pad on)
82
+ # @param [Fixnum] l left (col on screen to print)
83
+ # @param [Fixnum] h height (how many lines in Pad, usually more that screens actual height)
84
+ # @param [Fixnum] w width (how many cols in Pad, often more than screens width)
85
+ #
86
+ def set_pad_dimensions(t, l, h, w )
87
+ @pad_h = h
88
+ @pad_w = w
89
+ @top = @orig_top = t
90
+ @left = @orig_left = l
91
+ create_pad
92
+ end
93
+ # old program tabbed pane uses this. fix
94
+ # @deprecated use set_pad_dimensions
95
+ def set_layout h,w,t,l
96
+ set_pad_dimensions t,l,h,w
97
+ end
98
+ def display_h(*val)
99
+ if val.empty?
100
+ return @display_h
101
+ else
102
+ #raise ArgumentError "display_h should be ... " if val[0] ...
103
+ oldvalue = @display_h
104
+ @display_h = val[0]
105
+ $log.debug "XXX:given display_h to #{@display_h} "
106
+ @display_h = [@display_h, @target_window.height - 2].min unless @target_window.height == 0
107
+ $log.debug "XXX:set display_h to #{@display_h} "
108
+ #fire_property_handler(:display_h, oldvalue, @display_h)
109
+ end
110
+ self
111
+ end
112
+ #
113
+ # By default we are determining these 2 values based on window's dims.
114
+ # However, if you use a widget that is smaller than the window, then
115
+ # you will want to overwrite these values.
116
+ def display_w(*val)
117
+ if val.empty?
118
+ return @display_w
119
+ else
120
+ #raise ArgumentError "display_h should be ... " if val[0] ...
121
+ oldvalue = @display_w
122
+ @display_w = val[0]
123
+ @display_w = [@display_w, @target_window.width - 2].min unless @target_window.width == 0
124
+ $log.debug "XXX:set display_w to #{@display_w} "
125
+ #fire_property_handler(:display_h, oldvalue, @display_h)
126
+ end
127
+ self
128
+ end
129
+ ##
130
+ # create a pad to work on.
131
+ # XXX We reuse window, which is already the main window
132
+ # So if we try creating pad later, then old external window is used.
133
+ # However, many methods in superclass operate on window so we needed to overwrite. What do i do ?
134
+ #private
135
+ def create_pad
136
+ #raise "Pad already created" if @pad
137
+ return @pad if @pad
138
+ r = @top
139
+ c = @left
140
+ layout = { :height => @pad_h, :width => @pad_w, :top => r, :left => c }
141
+ @window = VER::Pad.create_with_layout(layout)
142
+
143
+ @window.name = "Pad::ScrollPad" # 2010-02-02 20:01
144
+ @name = "Form::ScrollForm"
145
+ @pad = @window
146
+ return @window
147
+ end
148
+ public
149
+ def scroll_right
150
+ s = @scroll_unit + $multiplier
151
+ $log.debug " scroll_right #{s} m: #{$multiplier} "
152
+ $multiplier = 0
153
+ return false if !validate_scroll_col(@pmincol + s)
154
+ @pmincol += s # some check is required or we'll crash
155
+ @cols_panned -= s
156
+ $log.debug " handled ch M-l in ScrollForm"
157
+ @window.modified = true
158
+ return 0
159
+ end
160
+ ##
161
+ # validate fails once unit + mult > 1. Then it won't go further
162
+ # unit should be one by default.
163
+ def scroll_left
164
+ s = @scroll_unit + $multiplier
165
+ $log.debug " scroll_left #{s} m: #{$multiplier} "
166
+ $multiplier = 0
167
+ #return false if !validate_scroll_col(@pmincol - s)
168
+ if !validate_scroll_col(@pmincol - s)
169
+ @pmincol = 0
170
+ @cols_panned = 0
171
+ else
172
+ @pmincol -= s # some check is required or we'll crash
173
+ @cols_panned += s
174
+ end
175
+ @window.modified = true
176
+ return 0
177
+ end
178
+ def scroll_down
179
+ s = @scroll_unit + $multiplier; $multiplier = 0
180
+ return false if !validate_scroll_row(@pminrow + s)
181
+ @pminrow += s # some check is required or we'll crash
182
+ @rows_panned -= s
183
+ @window.modified = true
184
+ #@repaint_all = true
185
+ return 0
186
+ end
187
+ def scroll_up
188
+ s = @scroll_unit + $multiplier; $multiplier = 0
189
+ $log.debug " scroll_up #{s} "
190
+ #return false if !validate_scroll_row(@pminrow - s)
191
+ if !validate_scroll_row(@pminrow - s)
192
+ @pminrow = 0
193
+ @rows_panned = 0
194
+ $log.debug " !valid #{@pminrow} "
195
+ else
196
+ @pminrow -= s # some check is required or we'll crash
197
+ @rows_panned += s
198
+ $log.debug " valid #{@pminrow} "
199
+ end
200
+ @window.modified = true
201
+ #@repaint_all = true
202
+ return 0
203
+ end
204
+ # print a border on the main window, just for kicks
205
+ def print_border
206
+ $log.debug " SCROLL print_border ..."
207
+ #@window.print_border_only(@top-@rows_panned, @left+@cols_panned, @display_h, @display_w, $datacolor)
208
+ @target_window.print_border_only(@top, @left, @display_h, @display_w+1, $datacolor)
209
+ end
210
+ def print_footer
211
+ footer = "Lines %d-%d (%d) Cols %d-%d (%d) " % [ @pminrow, @pminrow + @display_h, @orig_top + @pad_h, @pmincol, @pmincol + @display_w, @orig_left + @pad_w ]
212
+ @target_window.printstring(@top +@display_h, @left + 3, footer, $datacolor)
213
+ end
214
+ # XXX what if we want a static area at bottom ?
215
+ # maybe we should rename targetwindow to window
216
+ # and window to pad
217
+ # super may need target window
218
+ def repaint
219
+ print_border if @repaint_all and @print_border_flag
220
+ print_footer if @print_border_flag
221
+ $log.debug " scrollForm repaint calling parent #{@row} #{@col}+ #{@cols_panned} #{@col_offset} "
222
+ super
223
+ prefresh
224
+ if @print_border_flag
225
+ _print_more_data_marker true
226
+ _print_more_columns_marker true
227
+ end
228
+ #$log.debug " @target_window.wmove #{@row+@rows_panned+@row_offset}, #{@col+@cols_panned+@col_offset} "
229
+ @target_window.wmove @row+@rows_panned+@row_offset, @col+@cols_panned+@col_offset
230
+ @window.modified = false
231
+ @repaint_all = false
232
+ end
233
+ ## refresh pad onto window
234
+ # I am now copying onto main window, else prefresh has funny effects
235
+ def prefresh
236
+ ## reduce so we don't go off in top+h and top+w
237
+ $log.debug " start ret = @buttonpad.prefresh( #{@pminrow} , #{@pmincol} , #{@top} , #{@left} , top + #{@display_h} left + #{@display_w} ) "
238
+ if @pminrow + @display_h > @orig_top + @pad_h
239
+ $log.debug " if #{@pminrow} + #{@display_h} > #{@orig_top} +#{@pad_h} "
240
+ $log.debug " ERROR 1 "
241
+ #return -1
242
+ end
243
+ if @pmincol + @display_w > @orig_left + @pad_w
244
+ $log.debug " if #{@pmincol} + #{@display_w} > #{@orig_left} +#{@pad_w} "
245
+ $log.debug " ERROR 2 "
246
+ return -1
247
+ end
248
+ # actually if there is a change in the screen, we may still need to allow update
249
+ # but ensure that size does not exceed
250
+ if @top + @display_h > @orig_top + @pad_h
251
+ $log.debug " if #{@top} + #{@display_h} > #{@orig_top} +#{@pad_h} "
252
+ $log.debug " ERROR 3 "
253
+ return -1
254
+ end
255
+ if @left + @display_w > @orig_left + @pad_w
256
+ $log.debug " if #{@left} + #{@display_w} > #{@orig_left} +#{@pad_w} "
257
+ $log.debug " ERROR 4 "
258
+ return -1
259
+ end
260
+ # maybe we should use copywin to copy onto @target_window
261
+ $log.debug " ret = @window.prefresh( #{@pminrow} , #{@pmincol} , #{@top} , #{@left} , #{@top} + #{@display_h}, #{@left} + #{@display_w} ) "
262
+ omit = 0
263
+ # this works but if want to avoid copying border
264
+ #ret = @window.prefresh(@pminrow, @pmincol, @top+@row_offset, @left+@col_offset, @top + @display_h - @row_offset , @left + @display_w - @col_offset)
265
+ #
266
+ $log.debug "ret = @window.copywin( #{@pminrow} , #{@pmincol} , #{@top+@row_offset} , #{@left+@col_offset} , #{@top} + #{@display_h} - #{@row_offset} , #{@left} + #{@display_w} - #{@col_offset} , 0)"
267
+ ## Haha , we are back to the old notorious copywin which has given mankind
268
+ # so much grief that it should be removed in the next round of creation.
269
+ ret = @window.copywin(@target_window.get_window, @pminrow, @pmincol, @top+@row_offset, @left+@col_offset,
270
+ @top + @display_h - @row_offset , @left + @display_w - @col_offset, 0)
271
+
272
+ $log.debug " copywin ret = #{ret} "
273
+ end
274
+ private
275
+ def validate_scroll_row minrow
276
+ return false if minrow < 0
277
+ if minrow + @display_h > @orig_top + @pad_h
278
+ $log.debug " if #{minrow} + #{@display_h} > #{@orig_top} +#{@pad_h} "
279
+ $log.debug " ERROR 1 "
280
+ return false
281
+ end
282
+ return true
283
+ end
284
+ def validate_scroll_col mincol
285
+ return false if mincol < 0
286
+ if mincol + @display_w > @orig_left + @pad_w
287
+ $log.debug " if #{mincol} + #{@display_w} > #{@orig_left} +#{@pad_w} "
288
+ $log.debug " ERROR 2 "
289
+ return false
290
+ end
291
+ return true
292
+ end
293
+ # when tabbing through buttons, we need to account for all that panning/scrolling goin' on
294
+ # this is typically called by putchar or putc in editable components like field.
295
+ # XXX DELETE THIS IS SUPPOSE
296
+ def OLDsetrowcol r, c
297
+ $log.debug " SCROLL setrowcol #{r}, #{c} + #{@cols_panned}"
298
+ # aha ! here's where i can check whether the cursor is falling off the viewable area
299
+ cc = nil
300
+ rr = nil
301
+ if c
302
+ cc = c #+ @cols_panned
303
+ if c+@cols_panned < @orig_left
304
+ # this essentially means this widget (button) is not in view, its off to the left
305
+ $log.debug " setrowcol OVERRIDE #{c} #{@cols_panned} < #{@orig_left} "
306
+ $log.debug " aborting settrow col for now"
307
+ return
308
+ end
309
+ if c+@cols_panned > @orig_left + @display_w
310
+ # this essentially means this button is not in view, its off to the right
311
+ $log.debug " setrowcol OVERRIDE #{c} #{@cols_panned} > #{@orig_left} + #{@display_w} "
312
+ $log.debug " aborting settrow col for now"
313
+ return
314
+ end
315
+ end
316
+ if r
317
+ rr = r+@rows_panned
318
+ end
319
+ super rr, cc
320
+ end
321
+ public
322
+ def add_widget w
323
+ super
324
+ $log.debug " inside add_widget #{w.name} pad w #{@pad_w} #{w.col}, #{@pad_h} "
325
+ if w.col >= @pad_w
326
+ @pad_w += 10 # XXX currently just a guess value, we need length and maybe some extra
327
+ @window.wresize(@pad_h, @pad_w) if @pad
328
+ end
329
+ if w.row >= @pad_h
330
+ @pad_h += 10 # XXX currently just a guess value, we need length and maybe some extra
331
+ $log.debug " SCROLL add_widget ..."
332
+ @window.wresize(@pad_h, @pad_w) if @pad
333
+ end
334
+ end
335
+ ## Is a component visible, typically used to prevent traversal into the field
336
+ # @returns [true, false] false if components has scrolled off
337
+ def visible? component
338
+ r, c = component.rowcol
339
+ return false if c+@cols_panned < @orig_left
340
+ return false if c+@cols_panned > @orig_left + @display_w
341
+ # XXX TODO for rows UNTESTED for rows
342
+ return false if r + @rows_panned < @orig_top
343
+ return false if r + @rows_panned > @orig_top + @display_h - 2
344
+
345
+ return true
346
+ end
347
+ # returns index of first visible component. Currently using column index
348
+ # I am doing this for horizontal scrolling presently
349
+ # @return [index, -1] -1 if none visible, else index/offset
350
+ def first_visible_component_index
351
+ @widgets.each_with_index do |w, ix|
352
+ return ix if visible?(w) and focusable?(w)
353
+ end
354
+ return -1
355
+ end
356
+ def last_visible_component_index
357
+ ret = -1
358
+ @widgets.each_with_index do |w, ix|
359
+ ret = ix if visible?(w) and focusable?(w)
360
+ end
361
+ return ret
362
+ end
363
+ def req_first_field
364
+ index = first_visible_component_index
365
+ ret = select_field(index)
366
+ return ret
367
+ end
368
+ def req_last_field
369
+ select_field(last_visible_component_index)
370
+ end
371
+ def focusable?(w)
372
+ w.focusable and visible?(w)
373
+ end
374
+
375
+ # XXX needs to be called from repaint and print_border
376
+ # @param [boolean] should marker be printed or not
377
+ def _print_more_data_marker tf
378
+ tf = false
379
+ # the bottom marker meaning there's more data below
380
+ if @pminrow + @display_h < @pad_h
381
+ tf = true
382
+ end
383
+ marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_VLINE
384
+ h = @display_h; w = @display_w
385
+ r = @orig_top
386
+ c = @orig_left
387
+ $log.debug " more data #{r+h-1}, #{c+w-1} : row #{r} h #{h} w #{w} col #{c} "
388
+ @target_window.mvwaddch r+h-1, c+w-0, marker
389
+ # the top marker to show that there is data above
390
+ marker = @pminrow > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_VLINE
391
+ @target_window.mvwaddch r+1, c+w-0, marker
392
+ end
393
+
394
+ # XXX needs to be called from repaint and print_border
395
+ # @param [boolean] should marker be printed or not
396
+ def _print_more_columns_marker tf
397
+ tf = false
398
+ if @pmincol + @display_w < @pad_w
399
+ tf = true
400
+ end
401
+ marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
402
+ h = @display_h; w = @display_w
403
+ r = @orig_top
404
+ c = @orig_left
405
+ @target_window.mvwaddch r+h, c+w-2, marker
406
+ #
407
+ # show if columns to left or not
408
+ marker = @pmincol > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
409
+ @target_window.mvwaddch r+h, c+1, marker
410
+ end
411
+ end # class ScrollF
412
+
413
+ # the return of the prodigals
414
+ # The Expanding Heart
415
+ # The coming together of all those who were
416
+
417
+
418
+ end # module
@@ -0,0 +1,478 @@
1
+ =begin
2
+ * Name: stackflow.rb
3
+ A version of Container that uses stacks and flows and later grids
4
+ to place components
5
+ This is not a form. Thus it can be safely placed as a widget
6
+ without all the complicatinos of a form embedded inside another.
7
+ NOTE: Still experimental
8
+ * Description
9
+ * Author: rkumar (http://github.com/rkumar/rbcurse/)
10
+ * Date: 23.10.11 - 19:55
11
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
12
+
13
+ * Last update: 30.10.11 - 12:55
14
+
15
+ == CHANGES
16
+ Have moved most things out to a module ModStack, so this is sort of just
17
+ a skeletal container
18
+ x take care of margins
19
+ Resizing components
20
+ If window or container resized then redo the calc again.
21
+ Flow to have option of right to left orientation
22
+ == TODO
23
+ - If user specifies width, height then to be accounted when calculating weight. Also,
24
+ in such cases we must try not to overwrite h/w when calculating.
25
+ - changing an objects config not easy since it is stored in item, user may not have
26
+ handle to item
27
+ - weightx weighty
28
+ - RESET height only if expandable
29
+ - exceeding 100 will result in exceeding container.
30
+ - C-a C-e misbehaving in examples
31
+
32
+ =end
33
+
34
+ require 'rbcurse'
35
+ require 'rbcurse/core/include/bordertitle'
36
+ require 'rbcurse/core/util/basestack'
37
+
38
+ include RubyCurses
39
+ module RubyCurses
40
+ extend self
41
+
42
+ # This is a more advanced version of container
43
+ # which allows user to stack or flow components, including
44
+ # embedding stacks within flows and viceversa.
45
+
46
+
47
+ class StackFlow < Widget
48
+
49
+ include BorderTitle
50
+ include ModStack
51
+ # should container stack objects ignoring users row col
52
+ # this is esp needed since App sets row and col which is too early
53
+ # This is now the default value, till i can redo things
54
+ #dsl_accessor :stack
55
+ attr_reader :current_component
56
+ attr_reader :components
57
+
58
+ def initialize form=nil, config={}, &block
59
+ @suppress_borders = false
60
+ @row_offset = @col_offset = 1
61
+ @_events ||= []
62
+ @focusable = true
63
+ @editable = false
64
+ @components = [] # all components
65
+ @focusables = [] # focusable components, makes checks easier
66
+ @active = []
67
+ super
68
+
69
+ init_vars
70
+ end
71
+ def init_vars
72
+ @repaint_required = true
73
+ @row_offset = @col_offset = 0 if @suppress_borders
74
+ @ctr = 0
75
+
76
+ @internal_width = 2
77
+ @internal_width = 1 if @suppress_borders
78
+ @name ||= "a_stackflow"
79
+ bind_key(?\M-1, :increase_current)
80
+ bind_key(?\M-2, :decrease_current)
81
+ #raise "NO components !" if @components.empty?
82
+ calc_weightages2(@components, self) # FIXME this needs to move to basestack
83
+
84
+ end
85
+
86
+
87
+ # NOTE this is called by basestack so it cannot be here FIXME
88
+
89
+ # NOTE: since we are handling the traversal, we delink the object from any
90
+ # form's widgets array that might have been added. Whenever a form is available,
91
+ # we set it (without adding widget to it) so it can print using the form's window.
92
+ #
93
+ # @param [Widget] to add
94
+ private
95
+ def __add *items
96
+ items.each do |c|
97
+ raise ArgumentError, "Nil component passed to add" unless c
98
+ if c.is_a? Widget
99
+ if c.form && c.form != @form
100
+ $log.debug " removing widget VIMSPLIT #{c.class} wr: #{c.row} row:#{@row} ht:#{@height} "
101
+ c.form.remove_widget c
102
+ c.form = nil
103
+ # or should i just stack them myself and screw what you've asked for
104
+ end
105
+ # take it out of form's control. We will control it.
106
+ if c.form
107
+ c.form.remove_widget c
108
+ end
109
+ # shoot, what if at this point the container does not have a form
110
+ attach_form c if @form
111
+ end
112
+ # most likely if you have created both container and widgets
113
+ # inside app, it would have given row after container
114
+
115
+ #@components << c
116
+ if c.focusable
117
+ @focusables << c
118
+ @current_component ||= c # only the first else cursor falls on last on enter
119
+ end
120
+
121
+ end # items each
122
+ self
123
+ end
124
+
125
+ # When we get a form, we silently attach it to this object, without the form
126
+ # knowing. We don't want form managing this object.
127
+ def attach_form c
128
+ c.form = @form
129
+ c.override_graphic @graphic
130
+ c.parent_component = self
131
+ end
132
+ public
133
+ def widgets; @components; end
134
+ # what of by_name
135
+
136
+
137
+
138
+ # repaint object
139
+ # called by Form, and sometimes parent component (if not form).
140
+ def repaint # stackflow
141
+ my_win = @form ? @form.window : @target_window
142
+ @graphic = my_win unless @graphic
143
+ raise " #{@name} NO GRAPHIC set as yet STACKFLOW paint " unless @graphic
144
+ # actually at this level we don't have margins set -- not yet.
145
+ @margin_left ||= 0
146
+ @margin_right ||= 0
147
+ @margin_top ||= 0
148
+ @margin_bottom ||= 0
149
+ r = @row + @row_offset + @margin_top
150
+ c = @col + @col_offset + @margin_left
151
+ ht = @height-2-(@margin_top + @margin_bottom)
152
+ wd = @width -2-(@margin_left + @margin_right)
153
+ # should this not happen only if repaint_required ?
154
+ @components.each { |e|
155
+ e.parent_component = self
156
+ e.row = r
157
+ e.col = c
158
+ # check that we are not trying to print outside bounds
159
+ # by default we are stacking top level comps regardless of stack or flow
160
+ # otherwise too complicated
161
+ if e.is_a? BaseStack
162
+ # using ||= allows us to use overrides given by user
163
+ # but disallows us from calculating if size changes
164
+ e.height = (ht) * (e.weight * 0.01)
165
+ e.height = e.height.round
166
+ e.width = wd
167
+ if e.row + e.height >= @row + @height
168
+ #alert "is exceeding #{e.row} #{e.height} > #{@row} + #{@height} "
169
+ e.height = @height - e.row - 1
170
+ end
171
+ r += e.height
172
+ $log.debug "XXX: STACK r:#{e.row} e.h: #{e.height} w:#{e.weight} h: #{@height} "
173
+ #if e.type == :flow
174
+ #e.height ||= (@height-2) * (e.weight * 0.01)
175
+ #e.height = e.height.round
176
+ #e.width ||= (@width-2)
177
+ #r += e.height
178
+ #elsif e.type == :stack
179
+ #e.width ||= (@width-2) * (e.weight * 0.01)
180
+ #e.width = e.width.round
181
+ #e.height ||= (@height-2)
182
+ #c += e.width
183
+ #end
184
+ end
185
+ check_coords e
186
+ attach_form e unless e.form
187
+ } # seeme one if printing out
188
+ last = @components.last
189
+ if last.row + last.height < @row + @height
190
+ last.height += 1 # @row + @height - last.row + last.height
191
+ end
192
+
193
+ # if some major change has happened then repaint everything
194
+ # if multiple components then last row and col needs to be stored or else overlap will happen FIXME
195
+ if @repaint_required
196
+ $log.debug " STACKFLOW repaint graphic #{@graphic}, size:#{@components.size} "
197
+ print_borders unless @suppress_borders # do this once only, unless everything changes
198
+ @components.each { |e| e.repaint_all(true); e.repaint }
199
+ else
200
+ @components.each { |e| e.repaint }
201
+ end # if repaint_required
202
+
203
+ @repaint_required = false
204
+ end
205
+
206
+ private
207
+ def check_coords e # container
208
+ r = e.row
209
+ c = e.col
210
+ if r >= @row + @height
211
+ $log.warn "XXX: WARN #{e.class} is out of bounds row #{r} "
212
+ e.visible = false
213
+ end
214
+ if c >= @col + @width
215
+ $log.warn "XXX: WARN #{e.class} is out of bounds col #{c} "
216
+ e.visible = false
217
+ end
218
+ if e.row + e.height >= @height
219
+ $log.warn "XXX: WARN #{e.class} is out of bounds row #{e.row} + h #{e.height} >= #{@height} "
220
+ #e.visible = false
221
+ end
222
+ if e.col + e.width >= @width
223
+ $log.warn "XXX: WARN #{e.class} is out of bounds col #{e.col} + w #{e.width} >= #{@width} "
224
+ #e.visible = false
225
+ end
226
+ end
227
+
228
+ public
229
+ # called by parent or form, otherwise its private
230
+ def handle_key ch
231
+ $log.debug " STACKFLOW handle_key #{ch} "
232
+ return if @components.empty?
233
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
234
+
235
+ # should this go here 2011-10-19
236
+ unless @_entered
237
+ $log.warn "XXX WARN: calling ON_ENTER since in this situation it was not called"
238
+ on_enter
239
+ end
240
+ if ch == KEY_TAB
241
+ $log.debug "STACKFLOW GOTO NEXT TAB"
242
+ return goto_next_component
243
+ elsif ch == KEY_BTAB
244
+ return goto_prev_component
245
+ end
246
+ comp = @current_component
247
+ $log.debug " STACKFLOW handle_key #{ch}: #{comp}"
248
+ if comp
249
+ ret = comp.handle_key(ch)
250
+ $log.debug " STACKFLOW handle_key#{ch}: #{comp} returned #{ret} "
251
+ if ret != :UNHANDLED
252
+ comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
253
+ # in repaint of this.
254
+ return ret
255
+ end
256
+ $log.debug "XXX STACKFLOW key unhandled by comp #{comp.name} "
257
+ else
258
+ $log.warn "XXX STACKFLOW key unhandled NULL comp"
259
+ end
260
+ case ch
261
+ when ?\C-c.getbyte(0)
262
+ $multiplier = 0
263
+ return 0
264
+ when ?0.getbyte(0)..?9.getbyte(0)
265
+ $log.debug " VIM coming here to set multiplier #{$multiplier} "
266
+ $multiplier *= 10 ; $multiplier += (ch-48)
267
+ return 0
268
+ end
269
+ ret = process_key ch, self
270
+ # allow user to map left and right if he wants
271
+ if ret == :UNHANDLED
272
+ case ch
273
+ when KEY_UP
274
+ # form will pick this up and do needful
275
+ return goto_prev_component #unless on_first_component?
276
+ when KEY_LEFT
277
+ # if i don't check for first component, key will go back to form,
278
+ # but not be processes. so focussed remain here, but be false.
279
+ # In case of returnign an unhandled TAB, on_leave will happen and cursor will move to
280
+ # previous component outside of this.
281
+ return goto_prev_component unless on_first_component?
282
+ when KEY_RIGHT
283
+ return goto_next_component #unless on_last_component?
284
+ when KEY_DOWN
285
+ return goto_next_component #unless on_last_component?
286
+ else
287
+ @_entered = false
288
+ return :UNHANDLED
289
+ end
290
+ end
291
+
292
+ $multiplier = 0
293
+ return 0
294
+ end
295
+ # Actually we should only go to current component if it accepted
296
+ # a key stroke. if user tabbed thru it, then no point going back to
297
+ # it. Go to first or last depending on TAB or BACKTAB otherwise.
298
+ # NOTE: if user comes in using DOWN or UP, last traversed component will get the focus
299
+ #
300
+ def on_enter
301
+ # if BTAB, the last comp XXX they must be focusable FIXME
302
+ if $current_key == KEY_BTAB || $current_key == KEY_UP
303
+ @current_component = @focusables.last
304
+ elsif $current_key == KEY_TAB || $current_key == KEY_DOWN
305
+ @current_component = @focusables.first
306
+ else
307
+ # let current component be, since an unhandled key may have resulted
308
+ # in on_enter being called again
309
+ end
310
+ return unless @current_component
311
+ $log.debug " STACKFLOW came to ON_ENTER #{@current_component} "
312
+ set_form_row
313
+ @_entered = true
314
+ end
315
+ # we cannot be sure that this will be called especially if this is embedded
316
+ # inside some other component
317
+ def on_leave
318
+ @_entered = false
319
+ super
320
+ end
321
+ def goto_next_component
322
+ if @current_component != nil
323
+ leave_current_component
324
+ if on_last_component?
325
+ #@_entered = false
326
+ return :UNHANDLED
327
+ end
328
+ @current_index = @focusables.index(@current_component)
329
+ index = @current_index + 1
330
+ f = @focusables[index]
331
+ if f
332
+ @current_index = index
333
+ @current_component = f
334
+ return set_form_row
335
+ end
336
+ end
337
+ @_entered = false
338
+ return :UNHANDLED
339
+ end
340
+ def goto_prev_component
341
+ if @current_component != nil
342
+ leave_current_component
343
+ if on_first_component?
344
+ @_entered = false
345
+ return :UNHANDLED
346
+ end
347
+ @current_index = @focusables.index(@current_component)
348
+ index = @current_index -= 1
349
+ f = @focusables[index]
350
+ if f
351
+ @current_index = index
352
+ @current_component = f
353
+ return set_form_row
354
+ end
355
+ end
356
+ return :UNHANDLED
357
+ end
358
+ # private
359
+ # XXX why are we calling 3 methods in a row, why not OE manages these 3
360
+ # There's double calling going on.
361
+ def set_form_row
362
+ return :UNHANDLED if @current_component.nil?
363
+ cc = @current_component
364
+ $log.debug "STACKFLOW #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
365
+ $log.debug " STACKFLOW on enter sfr #{@current_component.name} #{@current_component} "
366
+
367
+ @current_component.on_enter
368
+ @current_component.set_form_row # why was this missing in vimsplit. is it
369
+ $log.debug "STACKFLOW #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
370
+ # that on_enter does a set_form_row
371
+ @current_component.set_form_col # XXX
372
+ @current_component.repaint # OMG this could happen before we've set row and col
373
+ # XXX compo should do set_form_row and col if it has that
374
+ end
375
+ #
376
+ def set_form_col
377
+ # override widget
378
+ end
379
+ # leave the component we are on.
380
+ # This should be followed by all containers, so that the on_leave action
381
+ # of earlier comp can be displayed, such as dimming components selections
382
+ def leave_current_component
383
+ begin
384
+ @current_component.on_leave
385
+ rescue FieldValidationException => fve
386
+ alert fve.to_s
387
+ end
388
+ # NOTE this is required, since repaint will just not happen otherwise
389
+ # Some components are erroneously repainting all, after setting this to true so it is
390
+ # working there.
391
+ @current_component.repaint_required true
392
+ $log.debug " after on_leave STACKFLOW XXX #{@current_component.focussed} #{@current_component.name}"
393
+ @current_component.repaint
394
+ end
395
+
396
+ # is focus on first component FIXME check for focusable
397
+ def on_first_component?
398
+ @current_component == @focusables.first
399
+ end
400
+ # is focus on last component FIXME check for focusable
401
+ def on_last_component?
402
+ @current_component == @focusables.last
403
+ end
404
+ # set focus on given component
405
+ # Sometimes you have the handle to component, and you want to move focus to it
406
+ def goto_component comp
407
+ return if comp == @current_component
408
+ leave_current_component
409
+ @current_component = comp
410
+ set_form_row
411
+ end
412
+
413
+ def increase_current
414
+ c = @current_component
415
+ p = c.config[:parent]
416
+ $log.debug "XXX: INC increase current #{c} , #{p} "
417
+ p.increase c
418
+ end
419
+ def decrease_current
420
+ c = @current_component
421
+ p = c.config[:parent]
422
+ $log.debug "XXX: INC increase current #{c} , #{p} "
423
+ p.decrease c
424
+ end
425
+ # ADD HERE ABOVe
426
+ end # class
427
+
428
+ end # module
429
+
430
+ # NOTE this is now giving an error ni basestack.rb 87 reg margin_top.
431
+ # @see teststackflow.rb in examples dir
432
+ #
433
+ if __FILE__ == $PROGRAM_NAME
434
+ require 'rbcurse/core/util/app'
435
+ App.new do
436
+
437
+ lb = Listbox.new nil, :list => ["ruby","perl","lisp","jaava", "c-blunt"] , :name => "mylist"
438
+ lb1 = Listbox.new nil, :list => ["roger","borg","haas","tsonga", "kolya","delpotro"] , :name => "mylist1"
439
+
440
+ lb2 = Listbox.new nil, :list => `gem list --local`.split("\n") , :name => "mylist2"
441
+
442
+ alist = %w[ ruby perl python java jruby macruby rubinius rails rack sinatra pylons django cakephp grails]
443
+ str = "Hello people of this world.\nThis is a textbox.\nUse arrow keys, j/k/h/l/gg/G/C-a/C-e/C-n/C-p\n"
444
+ str << alist.join("\n")
445
+ require 'rbcurse/core/widgets/rtextview'
446
+ tv = TextView.new nil, :name => "text"
447
+ tv.set_content str
448
+ =begin
449
+ f1 = field "name", :maxlen => 20, :display_length => 20, :bgcolor => :white,
450
+ :color => :black, :text => "abc", :label => " Name: ", :label_color_pair => @datacolor
451
+ f2 = field "email", :display_length => 20, :bgcolor => :white,
452
+ :color => :blue, :text => "me@google.com", :label => "Email: ", :label_color_pair => @datacolor
453
+ f3 = radio :group => :grp, :text => "red", :value => "RED", :color => :red
454
+ f4 = radio :group => :grp, :text => "blue", :value => "BLUE", :color => :blue
455
+ f5 = radio :group => :grp, :text => "green", :value => "GREEN", :color => :green
456
+ =end
457
+
458
+ f1 = Field.new nil, :maxlen => 20, :display_length => 20, :bgcolor => :white,
459
+ :color => :black, :text => "abc", :label => " Name: ", :label_color_pair => @datacolor
460
+ r = StackFlow.new @form, :row => 1, :col => 2, :width => 80, :height => 25, :title => "A container" do
461
+ stack :margin_left => 1 do
462
+ add tv, :weight => 30, :margin_left => 2
463
+ add lb, :weight => 30
464
+ flow :weight => 30 do
465
+ add lb1, :weight => 40
466
+ add lb2, :weight => 60
467
+ end
468
+ add f1
469
+ end # stack
470
+ end # r
471
+
472
+ #r.add(f1)
473
+ #r.add(f2)
474
+ #r.add(f3,f4,f5)
475
+ #sl = status_line
476
+
477
+ end # app
478
+ end # if