rbcurse 0.1.3 → 1.1.1
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.
- data/CHANGELOG +126 -0
- data/Manifest.txt +53 -20
- data/README.markdown +423 -0
- data/Rakefile +3 -1
- data/examples/keytest.rb +177 -0
- data/examples/mpad2.rb +156 -0
- data/examples/newtesttabp.rb +121 -0
- data/examples/rfe.rb +48 -10
- data/examples/rfe_renderer.rb +4 -4
- data/examples/rvimsplit.rb +376 -0
- data/examples/sqlc.rb +97 -106
- data/examples/sqlm.rb +446 -0
- data/examples/test1.rb +4 -4
- data/examples/test2.rb +12 -12
- data/examples/testchars.rb +140 -0
- data/examples/testkeypress.rb +9 -4
- data/examples/testmulticomp.rb +72 -0
- data/examples/testscroller.rb +136 -0
- data/examples/testscrolllb.rb +86 -0
- data/examples/testscrollp.rb +87 -0
- data/examples/testscrollta.rb +80 -0
- data/examples/testscrolltable.rb +166 -0
- data/examples/testsplit.rb +87 -0
- data/examples/testsplit2.rb +123 -0
- data/examples/testsplit3.rb +215 -0
- data/examples/testsplit3_1.rb +244 -0
- data/examples/testsplit3a.rb +215 -0
- data/examples/testsplit3b.rb +237 -0
- data/examples/testsplitta.rb +148 -0
- data/examples/testsplittv.rb +142 -0
- data/examples/testsplittvv.rb +144 -0
- data/examples/testtable.rb +1 -1
- data/examples/testtabp.rb +3 -2
- data/examples/testtestw.rb +69 -0
- data/examples/testtodo.rb +5 -3
- data/examples/testtpane.rb +203 -0
- data/examples/testtpane2.rb +145 -0
- data/examples/testtpanetable.rb +199 -0
- data/examples/viewtodo.rb +5 -3
- data/lib/rbcurse.rb +1 -1
- data/lib/rbcurse/celleditor.rb +2 -2
- data/lib/rbcurse/colormap.rb +5 -5
- data/lib/rbcurse/defaultlistselectionmodel.rb +3 -3
- data/lib/rbcurse/io.rb +663 -0
- data/lib/rbcurse/listeditable.rb +306 -0
- data/lib/rbcurse/listkeys.rb +15 -15
- data/lib/rbcurse/listscrollable.rb +168 -27
- data/lib/rbcurse/mapper.rb +35 -13
- data/lib/rbcurse/rchangeevent.rb +28 -0
- data/lib/rbcurse/rform.rb +845 -0
- data/lib/rbcurse/rlistbox.rb +144 -34
- data/lib/rbcurse/rmessagebox.rb +10 -5
- data/lib/rbcurse/rmulticontainer.rb +325 -0
- data/lib/rbcurse/rmultitextview.rb +306 -0
- data/lib/rbcurse/rscrollform.rb +369 -0
- data/lib/rbcurse/rscrollpane.rb +511 -0
- data/lib/rbcurse/rsplitpane.rb +820 -0
- data/lib/rbcurse/rtabbedpane.rb +737 -109
- data/lib/rbcurse/rtabbedwindow.rb +326 -0
- data/lib/rbcurse/rtable.rb +220 -64
- data/lib/rbcurse/rtextarea.rb +340 -181
- data/lib/rbcurse/rtextview.rb +237 -101
- data/lib/rbcurse/rviewport.rb +203 -0
- data/lib/rbcurse/rwidget.rb +919 -95
- data/lib/rbcurse/scrollable.rb +7 -7
- data/lib/rbcurse/selectable.rb +4 -4
- data/lib/rbcurse/table/tablecellrenderer.rb +3 -0
- data/lib/rbcurse/undomanager.rb +181 -0
- data/lib/rbcurse/vieditable.rb +100 -0
- data/lib/ver/window.rb +471 -21
- metadata +66 -22
- data/README.txt +0 -312
- data/examples/testd.db +0 -0
- data/examples/todocsv.csv +0 -28
data/lib/rbcurse/rtabbedpane.rb
CHANGED
@@ -1,13 +1,25 @@
|
|
1
1
|
=begin
|
2
2
|
* Name: tabbed pane: can have multiple forms overlapping.
|
3
3
|
* Description:
|
4
|
+
* A tabbed pane, mostly based (iirc) on the Terminal Preferences in OSX PPC 10.5.x
|
5
|
+
* Starting a new version using pads 2009-10-25 12:05
|
4
6
|
* Author: rkumar
|
5
7
|
|
6
8
|
--------
|
7
|
-
* Date:
|
9
|
+
* Date: 2009-10-25 12:05
|
8
10
|
* License:
|
9
11
|
Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
10
12
|
|
13
|
+
* 2010-02-28 09:47 - major cleanup and rewrite.
|
14
|
+
- Allow adding of component (in addition to form)
|
15
|
+
- Ideally, even form should be created and managed itself, why should TP have to repaint it?
|
16
|
+
|
17
|
+
NOTE:
|
18
|
+
Tp now does not create a form by default, since awefun you may want to just put in one component.
|
19
|
+
Pls use tp.form(tab) to get a form associated with the tab.
|
20
|
+
You may add as many tabs as you wish. To scroll tabs, traverse into the tab form and use the usual scroll keys M-l and M-h to scroll left and right.
|
21
|
+
#
|
22
|
+
# TODO : disable/hide tab ???
|
11
23
|
=end
|
12
24
|
require 'rubygems'
|
13
25
|
require 'ncurses'
|
@@ -19,19 +31,21 @@ include RubyCurses
|
|
19
31
|
module RubyCurses
|
20
32
|
extend self
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
#
|
34
|
+
Event = Struct.new( :tab, :index, :event)
|
35
|
+
|
25
36
|
# Multiple independent overlapping forms using the tabbed metaphor.
|
26
37
|
class TabbedButton < RubyCurses::RadioButton
|
27
38
|
def getvalue_for_paint
|
28
39
|
@text
|
29
40
|
end
|
41
|
+
def selected?
|
42
|
+
@variable.value == @value
|
43
|
+
end
|
30
44
|
##
|
31
45
|
# highlight abd selected colors and attribs should perhaps be in a
|
32
46
|
# structure, so user can override easily
|
33
47
|
def repaint # tabbedbutton
|
34
|
-
|
48
|
+
$log.debug("TabbedBUTTon repaint : #{self.class()} r:#{@row} c:#{@col} #{getvalue_for_paint}" )
|
35
49
|
r,c = rowcol
|
36
50
|
attribs = @attrs
|
37
51
|
@highlight_foreground ||= $reversecolor
|
@@ -40,16 +54,24 @@ module RubyCurses
|
|
40
54
|
_state = :SELECTED if @variable.value == @value
|
41
55
|
case _state
|
42
56
|
when :HIGHLIGHTED
|
57
|
+
$log.debug("TabbedBUTTon repaint : HIGHLIGHTED #{bgcolor}, #{color}, v: #{@value}" )
|
43
58
|
bgcolor = @highlight_background
|
44
59
|
color = @highlight_foreground
|
45
60
|
bgcolor = @bgcolor
|
46
61
|
color = @color
|
47
62
|
attribs = Ncurses::A_BOLD
|
63
|
+
setrowcol r,c # show cursor on highlighted as we tab through
|
64
|
+
## but when tabbing thru selected one, then selected one doesn't show cursor
|
48
65
|
when :SELECTED
|
66
|
+
$log.debug("TabbedBUTTon repaint : SELECTED #{bgcolor}, #{color}")
|
49
67
|
bgcolor = @bgcolor
|
50
68
|
color = @color
|
51
69
|
attribs = Ncurses::A_REVERSE
|
70
|
+
if @state == :HIGHLIGHTED
|
71
|
+
setrowcol r,c # show cursor on highlighted as we tab through
|
72
|
+
end
|
52
73
|
else
|
74
|
+
$log.debug("TabbedBUTTon repaint : ELSE #{bgcolor}, #{color}")
|
53
75
|
bgcolor = @bgcolor
|
54
76
|
color = @color
|
55
77
|
end
|
@@ -59,48 +81,234 @@ module RubyCurses
|
|
59
81
|
color = ColorMap.get_color(color, bgcolor)
|
60
82
|
end
|
61
83
|
value = getvalue_for_paint
|
62
|
-
|
84
|
+
$log.debug("button repaint : r:#{r} #{@graphic.top} c:#{c} #{@graphic.left} color:#{color} bg #{bgcolor} v: #{value}, g: #{@graphic} ")
|
63
85
|
len = @display_length || value.length
|
64
|
-
|
86
|
+
# paint the tabs name in approp place with attribs
|
87
|
+
#@form.window.printstring r, c, "%-*s" % [len, value], color, attribs
|
88
|
+
#@graphic.printstring r+@graphic.top, c+@graphic.left, "%-*s" % [len, value], color, attribs
|
89
|
+
#@graphic.printstring r-@graphic.top, c-@graphic.left, "%-*s" % [len, value], color, attribs
|
90
|
+
@graphic.printstring r, c, "%-*s" % [len, value], color, attribs
|
91
|
+
@graphic.modified = true
|
65
92
|
# @form.window.mvchgat(y=r, x=c, max=len, Ncurses::A_NORMAL, bgcolor, nil)
|
93
|
+
# underline for the top tab buttons.
|
66
94
|
if @underline != nil
|
67
|
-
#
|
68
|
-
@
|
95
|
+
r -= @graphic.top # because of pad, remove if we go back to windows
|
96
|
+
c -= @graphic.left # because of pad, remove if we go back to windows
|
97
|
+
@graphic.mvchgat(y=r, x=c+@underline+0, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, color, nil)
|
69
98
|
end
|
70
99
|
end
|
100
|
+
# trying to give the option so as we tab through buttons, the relevant tab opens
|
101
|
+
# but this is getting stuck on a tab and not going on
|
102
|
+
# fire() is causing the problem
|
103
|
+
# fire takes the focus into tab area so the next TAB goes back to first button
|
104
|
+
# due to current_tab = tab (so next key stroke goes to tab)
|
105
|
+
def on_enter
|
106
|
+
$log.debug " overridden on_enter of tabbedbutton #{@name} "
|
107
|
+
super
|
108
|
+
$log.debug " calling fire overridden on_enter of tabbedbutton"
|
109
|
+
fire if @display_tab_on_traversal
|
110
|
+
end
|
111
|
+
# In order to get tab display as we traverse buttons, we need to tamper with KEY_DOWN
|
112
|
+
# since that's the only way of getting down to selected tab in this case.
|
113
|
+
def handle_key ch
|
114
|
+
case ch
|
115
|
+
when KEY_DOWN
|
116
|
+
# form will not do a next_field, it will ignore this
|
117
|
+
return :NO_NEXT_FIELD
|
118
|
+
when KEY_RIGHT
|
119
|
+
@form.select_next_field
|
120
|
+
when KEY_LEFT
|
121
|
+
@form.select_prev_field
|
122
|
+
when KEY_ENTER, 10, 13, 32 # added space bar also
|
123
|
+
if respond_to? :fire
|
124
|
+
fire
|
125
|
+
end
|
126
|
+
else
|
127
|
+
# all thrse will be re-evaluated by form
|
128
|
+
return :UNHANDLED
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
71
132
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
133
|
+
##
|
134
|
+
# extending Widget from 2009-10-08 18:45
|
135
|
+
# It should extend Widget so we can pop it in a form. In fact it should be in a form,
|
136
|
+
# we should not have tried to make it standalone like messagebox.
|
137
|
+
# This is the main TabbedPane widget that will be slung into a form
|
138
|
+
class TabbedPane < Widget
|
139
|
+
TAB_ROW_OFFSET = 3 # what row should tab start on (was 4 when printing subheader)
|
140
|
+
TAB_COL_OFFSET = 0 # what col should tab start on (to save space, flush on left)
|
77
141
|
dsl_accessor :button_type # ok, ok_cancel, yes_no
|
78
142
|
dsl_accessor :buttons # used if type :custom
|
79
143
|
attr_reader :selected_index
|
80
|
-
|
81
|
-
|
144
|
+
attr_reader :current_tab
|
145
|
+
attr_reader :window
|
146
|
+
def initialize form, aconfig={}, &block
|
147
|
+
super
|
148
|
+
@parent = form
|
149
|
+
@parentwin = form.window
|
150
|
+
@visible = true
|
151
|
+
@focusable= true
|
82
152
|
@tabs ||= []
|
83
153
|
@forms ||= []
|
84
|
-
@bgcolor ||= "black" # 0
|
85
|
-
@color ||= "white" # $datacolor
|
86
154
|
@attr = nil
|
87
155
|
@current_form = nil
|
88
156
|
@current_tab = nil
|
89
157
|
@config = aconfig
|
90
|
-
@
|
91
|
-
|
158
|
+
@col_offset = 2; @row_offset = 1 # added 2010-01-10 22:54
|
159
|
+
@recreate_buttons = true
|
160
|
+
install_keys
|
161
|
+
end
|
162
|
+
def install_keys
|
163
|
+
@form.bind_key([?d, ?d]) { ix = highlighted_tab_index; repeatm { remove_tab(ix) } }
|
164
|
+
@form.bind_key(?u) { undelete_tab; }
|
165
|
+
@form.bind_key(?p) { paste_tab 0; } # paste before or at position
|
166
|
+
@form.bind_key(?P) { paste_tab 1; } # paste deleted tab after this one
|
167
|
+
@form.bind_key([?c, ?w]) { change_label }
|
168
|
+
@form.bind_key(?C) { change_label }
|
92
169
|
end
|
93
170
|
##
|
171
|
+
# This is a public, user called method for appending a new tab
|
172
|
+
# This will be called several times for one TP.
|
94
173
|
# when adding tabs, you may use ampersand in text to create hotkey
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
tab
|
100
|
-
|
101
|
-
|
174
|
+
# XXX adding a tab later does not influence buttons array,
|
175
|
+
def add_tab text, component = nil, aconfig={}, &block
|
176
|
+
index = @tabs.size
|
177
|
+
tab = insert_tab text, component, index, aconfig, &block
|
178
|
+
return tab
|
179
|
+
end
|
180
|
+
alias :add :add_tab
|
181
|
+
## insert a component at given index
|
182
|
+
# index cannnot be greater than size of tab count
|
183
|
+
def insert_tab text, component, index, aconfig={}, &block
|
184
|
+
$log.debug " TAB insert #{text} at #{index} "
|
185
|
+
@tabs[index] = Tab.new(text, self, aconfig, &block)
|
186
|
+
tab = @tabs[index]
|
187
|
+
tab.component = component unless component.nil?
|
188
|
+
tab.index = index # so i can undelete !!!
|
189
|
+
fire_event tab, index, :INSERT
|
190
|
+
@recreate_buttons = true
|
102
191
|
return tab
|
103
192
|
end
|
193
|
+
## remove given tab based on index
|
194
|
+
# This does not unbind the key mapping, FIXME
|
195
|
+
# Currently, can be invoked by 'dd' over highlighted button
|
196
|
+
# XXX can append to deleted_tabs, then on insert or paste insert with splat.
|
197
|
+
def remove_tab index
|
198
|
+
@recreate_buttons = true
|
199
|
+
$log.debug " inside remove_tab with #{index}, #{@tabs.size} "
|
200
|
+
@deleted_tab = @tabs.delete_at(index) unless @tabs.size < index
|
201
|
+
# note this is the index it was at.
|
202
|
+
fire_event @deleted_tab, index, :DELETE
|
203
|
+
end
|
204
|
+
##
|
205
|
+
# Move this fun stuff to a util class. TODO
|
206
|
+
# If tab deleted accidentally, undelete it
|
207
|
+
# Okay, i just can stop myself from having a little fun
|
208
|
+
def undelete_tab
|
209
|
+
return unless @deleted_tab
|
210
|
+
@recreate_buttons = true
|
211
|
+
@tabs.insert(@deleted_tab.index, @deleted_tab)
|
212
|
+
fire_event @deleted_tab, @deleted_tab.index, :INSERT
|
213
|
+
@deleted_tab = nil
|
214
|
+
$log.debug " undelete over #{@tabs.size} "
|
215
|
+
end
|
216
|
+
def paste_tab pos
|
217
|
+
return unless @deleted_tab
|
218
|
+
ix = highlighted_tab_index
|
219
|
+
return if ix == -1
|
220
|
+
@recreate_buttons = true
|
221
|
+
@deleted_tab.index = ix + pos
|
222
|
+
@tabs.insert(@deleted_tab.index, @deleted_tab)
|
223
|
+
fire_event @deleted_tab, @deleted_tab.index, :INSERT
|
224
|
+
@deleted_tab = nil
|
225
|
+
$log.debug " paste over #{@tabs.size} #{ix} + #{pos} "
|
226
|
+
end
|
227
|
+
|
228
|
+
##
|
229
|
+
# prompts for a new label for a tab - taking care of mnemonics if ampersand present
|
230
|
+
# Currently, mapped to 'C' and 'cw' when cursor is on a label
|
231
|
+
# Perhaps some of this kind of utility stuff needs to go into a util class.
|
232
|
+
#
|
233
|
+
def change_label
|
234
|
+
ix = highlighted_tab_index
|
235
|
+
return if ix < 0
|
236
|
+
prompt = "Enter new label: "
|
237
|
+
label = @buttons[ix].text
|
238
|
+
config = {}
|
239
|
+
config[:default] = label.dup
|
240
|
+
maxlen = 10
|
241
|
+
ret, str = rbgetstr(@graphic, $error_message_row, $error_message_col, prompt, maxlen, config)
|
242
|
+
if ret == 0 and str != "" and str != label
|
243
|
+
@tabs[ix].text = str
|
244
|
+
@buttons[ix].text(str)
|
245
|
+
@recreate_buttons = true
|
246
|
+
end
|
247
|
+
end
|
248
|
+
##
|
249
|
+
# returns the index of the tab cursor is on (not the one that is selected)
|
250
|
+
# @return [0..] index, or -1 if some error
|
251
|
+
def highlighted_tab_index
|
252
|
+
@form.widgets.each_with_index{ |w, ix|
|
253
|
+
return ix if w.state == :HIGHLIGHTED
|
254
|
+
}
|
255
|
+
return -1
|
256
|
+
end
|
257
|
+
def selected_tab_index
|
258
|
+
@form.widgets.each_with_index{ |w, ix|
|
259
|
+
return ix if w.selected?
|
260
|
+
}
|
261
|
+
return -1
|
262
|
+
end
|
263
|
+
## remove all tabs
|
264
|
+
def remove_all
|
265
|
+
if !@buttons.empty?
|
266
|
+
@buttons.each {|e| @form.remove_widget(e) }
|
267
|
+
end
|
268
|
+
@buttons = []
|
269
|
+
@tabs = []
|
270
|
+
@recreate_buttons = true
|
271
|
+
end
|
272
|
+
|
273
|
+
## return a form for use by program - if you want to put multiple items
|
274
|
+
# Otherwise just use add_component
|
275
|
+
# private - can't use externally
|
276
|
+
def configure_component component
|
277
|
+
#component.set_form @parent <<--- definitely NOT
|
278
|
+
component.form = @parent
|
279
|
+
component.rows_panned = component.cols_panned = 0
|
280
|
+
component.parent_component = self # added 2010-02-27 so offsets can go down ?
|
281
|
+
component.should_create_buffer = true
|
282
|
+
component.row = @row + TAB_ROW_OFFSET # 2
|
283
|
+
component.col = @col + TAB_COL_OFFSET
|
284
|
+
|
285
|
+
# current_form likely to be nil XXX
|
286
|
+
scr_top = component.row # for Pad, if Pad passed as in SplitPane
|
287
|
+
scr_left = component.col # for Pad, if Pad passed as in SplitPane
|
288
|
+
ho = TAB_ROW_OFFSET + 2 # 5
|
289
|
+
component.set_buffering(:target_window => @target_window || @parentwin, :form => @current_form, :bottom => @height-ho, :right => @width-2, :screen_top => scr_top, :screen_left => scr_left)
|
290
|
+
# if left nil, then we expand the comp
|
291
|
+
component.height ||= @height - (ho - 1) # 1 keeps lower border inside by 1
|
292
|
+
component.width ||= @width - 0 # 0 keeps it flush on right border
|
293
|
+
|
294
|
+
|
295
|
+
end
|
296
|
+
## create a form for tab, if multiple components are to be placed inside tab.
|
297
|
+
# Tabbedpane has no control over placement and width etc of what's inside a form
|
298
|
+
def form tab
|
299
|
+
if tab.form.nil?
|
300
|
+
@forms << create_tab_form(tab)
|
301
|
+
tab.form = @forms.last
|
302
|
+
end
|
303
|
+
return tab.form
|
304
|
+
end
|
305
|
+
|
306
|
+
## returns the index of the current / selected tab
|
307
|
+
## @returns 0.. index of selected tab
|
308
|
+
def selected_tab_index
|
309
|
+
@tabs.index(@current_tab)
|
310
|
+
end
|
311
|
+
|
104
312
|
# private
|
105
313
|
def variable_set var, val
|
106
314
|
var = "@#{var}"
|
@@ -117,128 +325,240 @@ module RubyCurses
|
|
117
325
|
end
|
118
326
|
instance_eval &block if block_given?
|
119
327
|
end
|
328
|
+
## this is a really wierd repaint method.
|
329
|
+
# First time it creates the TP window/form which contains the buttons.
|
330
|
+
# In future calls it really doesn't do anything.
|
331
|
+
# Deos it have nothing to paint, no borders to redraw, no repaint_required ???
|
120
332
|
def repaint
|
333
|
+
$log.debug " tabbedpane repaint "
|
121
334
|
@window || create_window
|
335
|
+
_recreate_buttons if @recreate_buttons
|
336
|
+
$log.debug " tabbedpane repaint #{@window.name} "
|
122
337
|
@window.show
|
338
|
+
#x set_buffer_modified()
|
123
339
|
end
|
124
340
|
def show
|
125
341
|
repaint
|
126
342
|
end
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
@
|
135
|
-
@
|
136
|
-
@
|
137
|
-
|
138
|
-
|
343
|
+
## recreate all buttons
|
344
|
+
# We call this if even one is added : adv is we can space out accordinagly if the numbers increase
|
345
|
+
# We could also expand the pad here.
|
346
|
+
# Test it out with removing tabs to.
|
347
|
+
# XXX have to remove buttons from the form
|
348
|
+
def _recreate_buttons
|
349
|
+
$log.debug " inside recreate_buttons: #{@tabs.size} "
|
350
|
+
r = @row
|
351
|
+
col = @col + 1
|
352
|
+
@buttons ||= []
|
353
|
+
if !@buttons.empty?
|
354
|
+
@buttons.each {|e| @form.remove_widget(e) }
|
355
|
+
end
|
139
356
|
@buttons = []
|
357
|
+
button_gap = 4
|
358
|
+
# the next line necessitates a clear on the pad
|
359
|
+
# button_gap = 1 if @tabs.size > 6 # quick dirty fix, we need something that checks fit
|
360
|
+
# we may also need to truncate text to fit
|
361
|
+
|
362
|
+
@buttonpad.wclear
|
140
363
|
## create a button for each tab
|
141
|
-
$tabradio = Variable.new
|
364
|
+
$tabradio = Variable.new # so we know which is highlighted
|
142
365
|
@tabs.each do |tab|
|
143
366
|
text = tab.text
|
367
|
+
$log.debug " TABS EACH #{text} "
|
144
368
|
@buttons << RubyCurses::TabbedButton.new(@form) do
|
145
369
|
variable $tabradio
|
146
370
|
text text
|
147
371
|
name text
|
148
372
|
value text
|
149
|
-
row 1
|
373
|
+
row r + 1
|
150
374
|
col col
|
151
375
|
end
|
152
|
-
col += text.length+
|
153
|
-
#
|
154
|
-
#
|
376
|
+
col += text.length + button_gap
|
377
|
+
# if col exceeds pad_w then we need to expand pad
|
378
|
+
# but here we don't know that a pad is being used
|
379
|
+
$log.debug " button col #{col} "
|
155
380
|
form = tab.form
|
156
|
-
form.
|
157
|
-
|
158
|
-
@buttons.last
|
159
|
-
|
160
|
-
|
161
|
-
form.
|
162
|
-
|
163
|
-
|
381
|
+
form.set_parent_buffer(@window) if form
|
382
|
+
|
383
|
+
b = @buttons.last
|
384
|
+
b.command(b) {
|
385
|
+
$log.debug " calling display form from button press #{b.name} #{b.state} "
|
386
|
+
# form.rep essentially sees that buttons get correct attributes
|
387
|
+
# when triggering M-<char>. This button should get highlighted.
|
388
|
+
tab.repaint
|
389
|
+
button_form_repaint #( b.state == :HIGHLIGHTED )
|
390
|
+
if @display_tab_on_traversal
|
391
|
+
# set as old tab so ONLY on going down this becomes current_tab
|
392
|
+
@old_tab = tab
|
393
|
+
else
|
394
|
+
# next line means next key is IMMED taken by the tab not main form
|
395
|
+
@current_tab = tab
|
396
|
+
end
|
397
|
+
fire_event tab, tab.index, :OPEN
|
164
398
|
}
|
165
|
-
|
166
399
|
end
|
167
|
-
|
400
|
+
@recreate_buttons = false
|
401
|
+
# make the buttons visible now, not after next handle_key
|
168
402
|
@form.repaint
|
169
403
|
end
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
404
|
+
## This form is for the tabbed buttons on top
|
405
|
+
def create_window
|
406
|
+
set_buffer_modified() # required still ??
|
407
|
+
# first create the main top window with the tab buttons on it.
|
408
|
+
$log.debug " TPane create_buff Top #{@row}, Left #{@col} H #{@height} W #{@width} "
|
409
|
+
#$log.debug " parentwin #{@parentwin.left} #{@parentwin.top} "
|
410
|
+
|
411
|
+
r = @row
|
412
|
+
c = @col
|
413
|
+
@form = ScrollForm.new(@parentwin)
|
414
|
+
@form.set_layout(1, @width, @row+1, @col+1)
|
415
|
+
@form.display_h = 1
|
416
|
+
@form.display_w = @width-3
|
417
|
+
@buttonpad = @form.create_pad
|
418
|
+
|
419
|
+
|
420
|
+
## We will use the parent window, and not a pad. We will write absolute coordinates.
|
421
|
+
@window = @parentwin
|
422
|
+
color = $datacolor
|
423
|
+
# border around button bar. should this not be in scrollform as a border ? XXX
|
424
|
+
@window.print_border @row, @col, 2, @width, color #, Ncurses::A_REVERSE
|
425
|
+
@buttonpad.name = "Window::TPTOPPAD" # 2010-02-02 20:01
|
426
|
+
@form.name = "Form::TPTOPFORM"
|
427
|
+
$log.debug("TP WINDOW TOP ? PAD MAIN FORM W:#{@window.name}, F:#{@form.name} ")
|
428
|
+
@form.parent_form = @parent ## 2010-01-21 15:55 TRYING OUT BUFFERED
|
429
|
+
@form.navigation_policy = :NON_CYCLICAL
|
430
|
+
#xx @current_form = @form
|
431
|
+
#xx color = $datacolor
|
432
|
+
#xx @window.print_border @row, @col, @height-1, @width, color #, Ncurses::A_REVERSE
|
433
|
+
|
434
|
+
Ncurses::Panel.update_panels
|
435
|
+
_recreate_buttons
|
436
|
+
|
437
|
+
button_form_repaint true
|
438
|
+
@window.wrefresh ## ADDED 2009-11-02 23:29
|
439
|
+
@old_tab = @tabs.first
|
440
|
+
@buttons.first().fire unless @buttons.empty? # make the first form active to start with.
|
441
|
+
end
|
442
|
+
def button_form_repaint flag = true
|
443
|
+
$log.debug " INSIDE button_form_repaint #{flag} "
|
444
|
+
#if flag
|
445
|
+
#@form.repaint if flag # This paints the outer form not inner
|
446
|
+
#@buttonpad.mvwaddch(2, 0, Ncurses::ACS_LTEE) # beautify the corner 2010-02-06 19:35
|
447
|
+
#@buttonpad.mvwaddch(2, @width-1, Ncurses::ACS_RTEE)
|
448
|
+
#end
|
449
|
+
#ret = @buttonpad.prefresh(0,0, @row+0, @col+0, @row+@height, @col+@width)
|
450
|
+
#$log.debug " prefresh error buttonpad 2 " if ret < 0
|
451
|
+
#@buttonpad.modified = false
|
452
|
+
if flag
|
453
|
+
# repaint form and refresh pad
|
454
|
+
@form.repaint
|
455
|
+
else
|
456
|
+
# only refresh pad
|
457
|
+
@form.prefresh
|
458
|
+
end
|
176
459
|
end
|
460
|
+
##
|
461
|
+
# This creates a form for the tab, in case we wish to put many components in it.
|
462
|
+
# Else just pass single components in add_tab.
|
463
|
+
# @params tab tab just created for which a form is required
|
464
|
+
# @return form - a pad based form
|
177
465
|
def create_tab_form tab
|
178
|
-
|
179
|
-
|
466
|
+
mtop = 0
|
467
|
+
mleft = 0
|
468
|
+
bottom_offset = 2 # 0 will overwrite bottom line, 1 will make another line for inner form
|
469
|
+
layout = { :height => @height-(mtop+bottom_offset), :width => @width, :top => mtop, :left => mleft }
|
470
|
+
# create a pad but it must behave like a window at all times 2009-10-25 12:25
|
471
|
+
window = VER::Pad.create_with_layout(layout)
|
472
|
+
|
180
473
|
form = RubyCurses::Form.new window
|
474
|
+
$log.debug " pad created in TP create_tab_form: #{window.name} , form #{form.name} "
|
475
|
+
$log.debug " hwtl: #{layout[:height]} #{layout[:width]} #{layout[:top]} #{layout[:left]} "
|
476
|
+
## added 2010-01-21 15:46 to pass cursor up
|
477
|
+
form.parent_form = @parent
|
181
478
|
form.navigation_policy = :NON_CYCLICAL
|
182
479
|
window.bkgd(Ncurses.COLOR_PAIR($datacolor));
|
183
480
|
window.box( 0, 0);
|
184
|
-
window.
|
185
|
-
|
186
|
-
|
481
|
+
window.mvwaddch(0, 0, Ncurses::ACS_LTEE) # beautify the corner 2010-02-06 19:35
|
482
|
+
window.mvwaddch(0, @width-1, Ncurses::ACS_RTEE)
|
483
|
+
|
484
|
+
# XXX TODO this wastes space we should ditch it.
|
485
|
+
## this prints the tab name on top left
|
486
|
+
window.mvprintw(1,1, tab.text.tr('&', '')) if @print_subheader
|
487
|
+
window.name = "Tab::TAB-#{tab.text}" # 2010-02-02 19:59
|
488
|
+
form.name = "Form::TAB-#{tab.text}" # 2010-02-02 19:59
|
489
|
+
form.add_cols=@col
|
490
|
+
form.add_rows=@row
|
187
491
|
return form
|
188
492
|
end
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
493
|
+
##
|
494
|
+
# added 2009-10-08 19:39 so it can be placed in a form
|
495
|
+
# @form is the top button form
|
496
|
+
# XXX stop this nonsense about current_form and current_tab
|
497
|
+
# TP should only be concerned with tabs. what happens inside is none of its business
|
498
|
+
def handle_key(ch)
|
499
|
+
@current_tab ||= @form # first we cycle buttons
|
500
|
+
$log.debug " handle_key in tabbed pane got : #{ch}"
|
501
|
+
# needs to go to component
|
502
|
+
ret = @current_tab.handle_key(ch)
|
503
|
+
$log.debug " -- form.handle_key in tabbed pane got ret : #{ret} , #{@current_tab} , #{ch} "
|
504
|
+
|
505
|
+
# components will usually return UNHANDLED for a tab or btab
|
506
|
+
# We need to convert it so the main form can use it
|
507
|
+
if @current_tab != @form
|
508
|
+
if ret == :UNHANDLED
|
509
|
+
if ch == 9 #or ch == KEY_DOWN
|
510
|
+
ret = :NO_NEXT_FIELD
|
511
|
+
elsif ch == 353 #or ch == KEY_UP # btab
|
512
|
+
ret = :NO_PREV_FIELD
|
513
|
+
end
|
514
|
+
end
|
515
|
+
else
|
516
|
+
# key down pressed in top form, go to tab
|
517
|
+
if ch == KEY_DOWN
|
518
|
+
ret = :NO_NEXT_FIELD
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
200
522
|
case ret
|
201
523
|
when :NO_NEXT_FIELD
|
202
|
-
if @
|
203
|
-
|
204
|
-
|
205
|
-
@
|
206
|
-
|
524
|
+
if @current_tab != @form
|
525
|
+
## if no next field on a subform go to first button of main form
|
526
|
+
@old_tab = @current_tab
|
527
|
+
@current_tab = @form
|
528
|
+
@form.req_first_field
|
529
|
+
#@form.select_next_field
|
207
530
|
else
|
208
|
-
|
209
|
-
@
|
210
|
-
|
211
|
-
|
212
|
-
#@current_form.select_field -1
|
213
|
-
#ret = @current_form.handle_key(ch)
|
531
|
+
# on top button panel - no more buttons, go to tabs first field
|
532
|
+
if @old_tab # in case of empty tabbed pane old_tab was nil
|
533
|
+
@current_tab = @old_tab
|
534
|
+
@current_tab.set_focus :FIRST
|
214
535
|
end
|
215
536
|
end
|
216
537
|
when :NO_PREV_FIELD
|
217
|
-
if @
|
218
|
-
$log.debug " 1 no prev field - going to button "
|
219
|
-
@
|
220
|
-
@
|
538
|
+
if @current_tab != @form
|
539
|
+
$log.debug "TP 1 no prev field - going to last button "
|
540
|
+
@old_tab = @current_tab
|
541
|
+
@current_tab = @form
|
542
|
+
@form.req_last_field
|
221
543
|
else
|
222
|
-
|
223
|
-
@
|
224
|
-
|
225
|
-
|
544
|
+
# on top button panel - no prev buttons, go to tabs last field
|
545
|
+
if @old_tab # in case of one tab
|
546
|
+
@current_tab = @old_tab
|
547
|
+
@current_tab.set_focus :LAST
|
226
548
|
end
|
227
549
|
end
|
228
550
|
when :UNHANDLED
|
229
551
|
$log.debug " unhandled in tabbed pane #{ch}"
|
230
552
|
ret = @form.process_key ch, self # field
|
231
|
-
|
232
|
-
|
553
|
+
|
554
|
+
return ret if ret == :UNHANDLED
|
555
|
+
end
|
556
|
+
if @buttonpad.modified
|
557
|
+
button_form_repaint
|
233
558
|
end
|
234
|
-
return if @stop
|
235
|
-
@current_form.window.wrefresh
|
236
|
-
@window.refresh
|
237
|
-
end
|
238
|
-
ensure
|
239
|
-
destroy
|
240
|
-
end
|
241
559
|
end
|
560
|
+
##
|
561
|
+
# ensure that the pads are being destroyed, although we've not found a way.
|
242
562
|
def destroy
|
243
563
|
@window.destroy
|
244
564
|
@forms.each { |f| w = f.window; w.destroy unless w.nil? }
|
@@ -291,31 +611,339 @@ module RubyCurses
|
|
291
611
|
width = @layout[:width]
|
292
612
|
return (width-textlen)/2
|
293
613
|
end
|
614
|
+
def fire_event tab, index, event
|
615
|
+
# experimenting with structs, earlier we've used classes
|
616
|
+
if @tabbedpane_event.nil?
|
617
|
+
@tabbedpane_event = Event.new
|
618
|
+
end
|
619
|
+
@tabbedpane_event.tab = tab
|
620
|
+
@tabbedpane_event.index = index
|
621
|
+
@tabbedpane_event.event = event
|
622
|
+
fire_handler event, @tabbedpane_event
|
623
|
+
end
|
294
624
|
|
295
625
|
##
|
296
626
|
# nested class tab
|
627
|
+
# A user created tab, with its own form
|
297
628
|
class Tab
|
298
|
-
|
629
|
+
attr_accessor :text
|
299
630
|
attr_reader :config
|
631
|
+
attr_reader :component
|
300
632
|
attr_accessor :form
|
301
|
-
|
633
|
+
attr_accessor :parent_component
|
634
|
+
attr_accessor :index
|
635
|
+
def initialize text, parent_component, aconfig={}, &block
|
302
636
|
@text = text
|
303
637
|
@config = aconfig
|
638
|
+
@parent_component = parent_component
|
304
639
|
@config.each_pair { |k,v| variable_set(k,v) }
|
305
640
|
instance_eval &block if block_given?
|
306
641
|
end
|
642
|
+
## add a single component to the tab
|
643
|
+
# Calling this a second time will overwrite the existing component
|
644
|
+
def component=(component)
|
645
|
+
raise "Component cannot be null" unless component
|
646
|
+
raise "Component already associated with a form. Do not pass form in constructor." unless component.form.nil?
|
647
|
+
$log.debug " calling configure component "
|
648
|
+
@parent_component.configure_component component
|
649
|
+
@component = component
|
650
|
+
end
|
651
|
+
def remove_component
|
652
|
+
@component = nil
|
653
|
+
end
|
307
654
|
# private
|
308
655
|
def variable_set var, val
|
309
656
|
var = "@#{var}"
|
310
657
|
instance_variable_set(var, val)
|
311
658
|
end
|
312
|
-
|
313
|
-
|
314
|
-
|
659
|
+
# tab should handle key instead of TP.
|
660
|
+
# Pass to component or form
|
661
|
+
def handle_key ch
|
662
|
+
kh = @component || @form
|
663
|
+
ret = kh.handle_key(ch)
|
664
|
+
# forms seem to returning a nil when the pad has been updated. We need to copy it
|
665
|
+
ret ||= 0
|
666
|
+
if ret == 0
|
667
|
+
@component.repaint if @component
|
668
|
+
display_form false if @form
|
669
|
+
end
|
670
|
+
# XXX i need to call repaint of compoent if updated !!
|
671
|
+
return ret
|
672
|
+
end
|
673
|
+
def repaint # Tab
|
674
|
+
if @form
|
675
|
+
display_form
|
676
|
+
elsif @component
|
677
|
+
# we ask the component to paint its buffer only, no actual repainting
|
678
|
+
redraw
|
679
|
+
else
|
680
|
+
# pls use tp.form(tab) to get form explicity.
|
681
|
+
# It could come here if tab precreated and user is yet to assign a component.
|
682
|
+
# or has removed component and not yet set a new one.
|
683
|
+
$log.error "Got neither component nor form."
|
684
|
+
$log.error "Programmer error. A change in Tabbedpane requires you to create form explicitly using form = tpane.form(tab) syntax"
|
685
|
+
end
|
686
|
+
end
|
687
|
+
# force a redraw of a component when tabs changed
|
688
|
+
def redraw
|
689
|
+
# this kinda stuff should be inside widget or some util class
|
690
|
+
c = @component
|
691
|
+
if c.is_double_buffered?
|
692
|
+
c.set_buffer_modified
|
693
|
+
c.buffer_to_window
|
694
|
+
else
|
695
|
+
# force a repaint, if not buffered object e.g scrollpane.
|
696
|
+
$log.debug " TP: forcing repaint of non-buffered object #{c} "
|
697
|
+
c.repaint_all
|
698
|
+
c.repaint
|
699
|
+
end
|
315
700
|
end
|
701
|
+
## Set focus on a component or form field when a user has tabbed off the last or first button
|
702
|
+
def set_focus first_last
|
703
|
+
if !@form.nil?
|
704
|
+
# move to first field of existing form
|
705
|
+
#@current_form = @current_tab.form # 2010-02-27 20:22
|
706
|
+
$log.debug " calling display form from handle_key NO_NEXT_FIELD: #{first_last} "
|
707
|
+
first_last == :FIRST ? @form.req_first_field : @form.req_last_field
|
708
|
+
display_form
|
709
|
+
else
|
710
|
+
# move to component
|
711
|
+
#@current_form = @current_tab.component # temp HACK 2010-02-27 23:24
|
712
|
+
@component.set_form_row
|
713
|
+
@component.set_form_col
|
714
|
+
end
|
715
|
+
end
|
716
|
+
# On a tabbed button press, this will display the relevant form
|
717
|
+
# On why I am directyl calling copywin and not using copy_pad_to_win etc
|
718
|
+
#+ those require setting top and left. However, while printing a pad, top and left are reduced and so
|
719
|
+
#+ must be absolute r and c. But inside TP, objects have a relative coord. So the print functions
|
720
|
+
#+ were failing silently, and i was wondering why nothing was printing.
|
721
|
+
# XXX move this tab in tab.repaint and let tab decide based on component or form
|
722
|
+
# if component then pad = component.get_buffer
|
723
|
+
def display_form flag = true
|
724
|
+
return if @form.nil?
|
725
|
+
form = @form
|
726
|
+
if form.is_a? RubyCurses::Form # tempo XXX since there are components
|
727
|
+
pad = form.window
|
728
|
+
else
|
729
|
+
return
|
730
|
+
pad = form.get_buffer() # component
|
731
|
+
end
|
732
|
+
pc = @parent_component
|
733
|
+
form.repaint if flag # added 2009-11-03 23:27 paint widgets in inside form
|
734
|
+
$log.debug " TP display form before pad copy: #{pad.name}, set_backing: #{form}: #{form.name} parent: #{@parent_component} : #{pc.row} , #{pc.col}. #{pc.height} , #{pc.width}: repaint flag #{flag} "
|
735
|
+
ret = -1
|
736
|
+
pminr = pminc = 0
|
737
|
+
r = pc.row + 2
|
738
|
+
c = pc.col + 0
|
739
|
+
border_width = 0
|
740
|
+
maxr = pc.height() - 3
|
741
|
+
maxc = pc.width() - 1
|
742
|
+
$log.debug " ret = pad.copywin(pc.window.get_window, #{pminr}, #{pminc}, #{r}, #{c}, r+ #{maxr} - border_width, c+ #{maxc} -border_width,0). W:#{pc.window}, #{pc.window.get_window} "
|
743
|
+
ret = pad.copywin(pc.window.get_window, pminr, pminc, r, c, r+maxr-border_width, c+maxc-border_width,0)
|
744
|
+
$log.debug " display form after pad copy #{ret}. #{form.name} "
|
316
745
|
end
|
317
746
|
|
747
|
+
end # class Tab
|
748
|
+
|
318
749
|
end # class Tabbedpane
|
319
750
|
|
751
|
+
## An extension of Form that only displays and focuses on visible widgets
|
752
|
+
# This is minimal, and is being expanded upon as a separate class in rscrollform.rb
|
753
|
+
#
|
754
|
+
class ScrollForm < RubyCurses::Form
|
755
|
+
attr_accessor :pmincol # advance / scroll columns
|
756
|
+
attr_accessor :pminrow # advance / scroll rows (vertically)
|
757
|
+
attr_accessor :display_w
|
758
|
+
attr_accessor :display_h
|
759
|
+
attr_accessor :scroll_ctr
|
760
|
+
attr_reader :orig_top, :orig_left
|
761
|
+
attr_reader :window
|
762
|
+
attr_accessor :name
|
763
|
+
attr_reader :cols_panned, :rows_panned
|
764
|
+
def initialize win, &block
|
765
|
+
@target_window = win
|
766
|
+
super
|
767
|
+
@pminrow = @pmincol = 0
|
768
|
+
@scroll_ctr = 2
|
769
|
+
@cols_panned = @rows_panned = 0
|
770
|
+
end
|
771
|
+
def set_layout(h, w, t, l)
|
772
|
+
@pad_h = h
|
773
|
+
@pad_w = w
|
774
|
+
@top = @orig_top = t
|
775
|
+
@left = @orig_left = l
|
776
|
+
end
|
777
|
+
def create_pad
|
778
|
+
r = @top
|
779
|
+
c = @left
|
780
|
+
layout = { :height => @pad_h, :width => @pad_w, :top => r, :left => c }
|
781
|
+
@window = VER::Pad.create_with_layout(layout)
|
782
|
+
#@window = @parentwin
|
783
|
+
#@form = RubyCurses::Form.new @buttonpad
|
784
|
+
@window.name = "Pad::ScrollPad" # 2010-02-02 20:01
|
785
|
+
@name = "Form::ScrollForm"
|
786
|
+
return @window
|
787
|
+
end
|
788
|
+
## ScrollForm handle key, scrolling
|
789
|
+
def handle_key ch
|
790
|
+
$log.debug " inside ScrollForm handlekey #{ch} "
|
791
|
+
# do the scrolling thing here top left prow and pcol of pad to be done
|
792
|
+
# # XXX TODO check whether we can scroll before incrementing esp cols_panned etc
|
793
|
+
case ch
|
794
|
+
when ?\M-l.getbyte(0)
|
795
|
+
return false if !validate_scroll_col(@pmincol + @scroll_ctr)
|
796
|
+
@pmincol += @scroll_ctr # some check is required or we'll crash
|
797
|
+
@cols_panned -= @scroll_ctr
|
798
|
+
$log.debug " handled ch M-l in ScrollForm"
|
799
|
+
@window.modified = true
|
800
|
+
return 0
|
801
|
+
when ?\M-h.getbyte(0)
|
802
|
+
return false if !validate_scroll_col(@pmincol - @scroll_ctr)
|
803
|
+
@pmincol -= @scroll_ctr # some check is required or we'll crash
|
804
|
+
@cols_panned += @scroll_ctr
|
805
|
+
$log.debug " handled ch M-h in ScrollForm"
|
806
|
+
@window.modified = true
|
807
|
+
return 0
|
808
|
+
when ?\M-n.getbyte(0)
|
809
|
+
return false if !validate_scroll_row(@pminrow + @scroll_ctr)
|
810
|
+
@pminrow += @scroll_ctr # some check is required or we'll crash
|
811
|
+
@rows_panned -= @scroll_ctr
|
812
|
+
@window.modified = true
|
813
|
+
return 0
|
814
|
+
when ?\M-p.getbyte(0)
|
815
|
+
return false if !validate_scroll_row(@pminrow - @scroll_ctr)
|
816
|
+
@pminrow -= @scroll_ctr # some check is required or we'll crash
|
817
|
+
@rows_panned += @scroll_ctr
|
818
|
+
@window.modified = true
|
819
|
+
return 0
|
820
|
+
end
|
821
|
+
|
822
|
+
super
|
823
|
+
end
|
824
|
+
def repaint
|
825
|
+
$log.debug " scrollForm repaint calling parent"
|
826
|
+
super
|
827
|
+
prefresh
|
828
|
+
@window.modified = false
|
829
|
+
end
|
830
|
+
def prefresh
|
831
|
+
## reduce so we don't go off in top+h and top+w
|
832
|
+
$log.debug " start ret = @buttonpad.prefresh( #{@pminrow} , #{@pmincol} , #{@top} , #{@left} , top + #{@display_h} left + #{@display_w} ) "
|
833
|
+
if @pminrow + @display_h > @orig_top + @pad_h
|
834
|
+
$log.debug " if #{@pminrow} + #{@display_h} > #{@orig_top} +#{@pad_h} "
|
835
|
+
$log.debug " ERROR 1 "
|
836
|
+
#return -1
|
837
|
+
end
|
838
|
+
if @pmincol + @display_w > @orig_left + @pad_w
|
839
|
+
$log.debug " if #{@pmincol} + #{@display_w} > #{@orig_left} +#{@pad_w} "
|
840
|
+
$log.debug " ERROR 2 "
|
841
|
+
return -1
|
842
|
+
end
|
843
|
+
# actually if there is a change in the screen, we may still need to allow update
|
844
|
+
# but ensure that size does not exceed
|
845
|
+
if @top + @display_h > @orig_top + @pad_h
|
846
|
+
$log.debug " if #{@top} + #{@display_h} > #{@orig_top} +#{@pad_h} "
|
847
|
+
$log.debug " ERROR 3 "
|
848
|
+
return -1
|
849
|
+
end
|
850
|
+
if @left + @display_w > @orig_left + @pad_w
|
851
|
+
$log.debug " if #{@left} + #{@display_w} > #{@orig_left} +#{@pad_w} "
|
852
|
+
$log.debug " ERROR 4 "
|
853
|
+
return -1
|
854
|
+
end
|
855
|
+
# maybe we should use copywin to copy onto @target_window
|
856
|
+
$log.debug " ret = @buttonpad.prefresh( #{@pminrow} , #{@pmincol} , #{@top} , #{@left} , #{@top} + #{@display_h}, #{@left} + #{@display_w} ) "
|
857
|
+
omit = 0
|
858
|
+
# this works but if want to avoid copying border
|
859
|
+
ret = @window.prefresh(@pminrow, @pmincol, @top, @left, @top + @display_h , @left + @display_w)
|
860
|
+
|
861
|
+
$log.debug " ret = #{ret} "
|
862
|
+
# need to refresh the form after repaint over
|
863
|
+
end
|
864
|
+
def validate_scroll_row minrow
|
865
|
+
return false if minrow < 0
|
866
|
+
if minrow + @display_h > @orig_top + @pad_h
|
867
|
+
$log.debug " if #{minrow} + #{@display_h} > #{@orig_top} +#{@pad_h} "
|
868
|
+
$log.debug " ERROR 1 "
|
869
|
+
return false
|
870
|
+
end
|
871
|
+
return true
|
872
|
+
end
|
873
|
+
def validate_scroll_col mincol
|
874
|
+
return false if mincol < 0
|
875
|
+
if mincol + @display_w > @orig_left + @pad_w
|
876
|
+
$log.debug " if #{mincol} + #{@display_w} > #{@orig_left} +#{@pad_w} "
|
877
|
+
$log.debug " ERROR 2 "
|
878
|
+
return false
|
879
|
+
end
|
880
|
+
return true
|
881
|
+
end
|
882
|
+
# when tabbing through buttons, we need to account for all that panning/scrolling goin' on
|
883
|
+
def setrowcol r, c
|
884
|
+
# aha ! here's where i can check whether the cursor is falling off the viewable area
|
885
|
+
if c+@cols_panned < @orig_left
|
886
|
+
# this essentially means this widget (button) is not in view, its off to the left
|
887
|
+
$log.debug " setrowcol OVERRIDE #{c} #{@cols_panned} < #{@orig_left} "
|
888
|
+
$log.debug " aborting settrow col for now"
|
889
|
+
return
|
890
|
+
end
|
891
|
+
if c+@cols_panned > @orig_left + @display_w
|
892
|
+
# this essentially means this button is not in view, its off to the right
|
893
|
+
$log.debug " setrowcol OVERRIDE #{c} #{@cols_panned} > #{@orig_left} + #{@display_w} "
|
894
|
+
$log.debug " aborting settrow col for now"
|
895
|
+
return
|
896
|
+
end
|
897
|
+
super r+@rows_panned, c+@cols_panned
|
898
|
+
end
|
899
|
+
def add_widget w
|
900
|
+
super
|
901
|
+
#$log.debug " inside add_widget #{w.name} pad w #{@pad_w} #{w.col} "
|
902
|
+
if w.col >= @pad_w
|
903
|
+
@pad_w += 10 # XXX currently just a guess value, we need length and maybe some extra
|
904
|
+
@window.wresize(@pad_h, @pad_w)
|
905
|
+
end
|
906
|
+
end
|
907
|
+
## Is a component visible, typically used to prevent traversal into the field
|
908
|
+
# @returns [true, false] false if components has scrolled off
|
909
|
+
def visible? component
|
910
|
+
r, c = component.rowcol
|
911
|
+
return false if c+@cols_panned < @orig_left
|
912
|
+
return false if c+@cols_panned > @orig_left + @display_w
|
913
|
+
# XXX TODO for rows UNTESTED for rows
|
914
|
+
return false if r + @rows_panned < @orig_top
|
915
|
+
return false if r + @rows_panned > @orig_top + @display_h
|
916
|
+
|
917
|
+
return true
|
918
|
+
end
|
919
|
+
# returns index of first visible component. Currently using column index
|
920
|
+
# I am doing this for horizontal scrolling presently
|
921
|
+
# @return [index, -1] -1 if none visible, else index/offset
|
922
|
+
def first_visible_component_index
|
923
|
+
@widgets.each_with_index do |w, ix|
|
924
|
+
return ix if visible?(w)
|
925
|
+
end
|
926
|
+
return -1
|
927
|
+
end
|
928
|
+
def last_visible_component_index
|
929
|
+
ret = -1
|
930
|
+
@widgets.each_with_index do |w, ix|
|
931
|
+
$log.debug " reverse last vis #{ix} , #{w} : #{visible?(w)} "
|
932
|
+
ret = ix if visible?(w)
|
933
|
+
end
|
934
|
+
return ret
|
935
|
+
end
|
936
|
+
def req_first_field
|
937
|
+
select_field(first_visible_component_index)
|
938
|
+
end
|
939
|
+
def req_last_field
|
940
|
+
select_field(last_visible_component_index)
|
941
|
+
end
|
942
|
+
def focusable?(w)
|
943
|
+
w.focusable and visible?(w)
|
944
|
+
end
|
945
|
+
|
946
|
+
end # class ScrollF
|
947
|
+
|
320
948
|
|
321
949
|
end # module
|