rbcurse-experimental 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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