rbcurse-extras 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/README.md +75 -0
  2. data/VERSION +1 -0
  3. data/examples/data/list.txt +300 -0
  4. data/examples/data/lotr.txt +12 -0
  5. data/examples/data/table.txt +36 -0
  6. data/examples/data/tasks.txt +27 -0
  7. data/examples/data/unix1.txt +21 -0
  8. data/examples/inc/qdfilechooser.rb +70 -0
  9. data/examples/inc/rfe_renderer.rb +121 -0
  10. data/examples/newtabbedwindow.rb +100 -0
  11. data/examples/rfe.rb +1236 -0
  12. data/examples/test2.rb +670 -0
  13. data/examples/testeditlist.rb +78 -0
  14. data/examples/testtable.rb +270 -0
  15. data/examples/testvimsplit.rb +141 -0
  16. data/lib/rbcurse/extras/include/celleditor.rb +112 -0
  17. data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
  18. data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
  19. data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
  20. data/lib/rbcurse/extras/include/listkeys.rb +37 -0
  21. data/lib/rbcurse/extras/include/listselectable.rb +144 -0
  22. data/lib/rbcurse/extras/include/tableextended.rb +40 -0
  23. data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
  24. data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
  25. data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
  26. data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
  27. data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
  28. data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
  29. data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
  30. data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
  31. data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
  32. data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
  33. data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
  34. data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
  35. data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
  36. data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
  37. data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
  38. metadata +94 -0
@@ -0,0 +1,21 @@
1
+ require 'rbcurse/extras/widgets/rlink'
2
+ ##
3
+ module RubyCurses
4
+ class MenuLink < Link
5
+ dsl_property :description
6
+
7
+ def initialize form, config={}, &block
8
+ super
9
+ @col_offset = -1 * @col
10
+ @row_offset = -1 * @row
11
+ end
12
+ # added for some standardization 2010-09-07 20:28
13
+ # alias :text :getvalue # NEXT VERSION
14
+ # change existing text to label
15
+
16
+ def getvalue_for_paint
17
+ "%s %-12s - %-s" % [ @mnemonic , getvalue(), @description ]
18
+ end
19
+ ##
20
+ end # class
21
+ end # module
@@ -0,0 +1,304 @@
1
+ =begin
2
+ * Name: MultiContainer
3
+ * Description View (cycle) multiple components in one container using a key or menu
4
+ * Author: rkumar (arunachalesha)
5
+ * file created 2010-03-15 10:40
6
+ --------
7
+ * License:
8
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
9
+
10
+ =end
11
+ require 'rbcurse'
12
+
13
+ include RubyCurses
14
+ module RubyCurses
15
+ extend self
16
+
17
+ ##
18
+ # Extends TextView with ability to load more than one file or content
19
+ # and switch between files (buffers).
20
+ # NOTE: ideally, i should be able to dynamically add this functionality to either Textview
21
+ # or TextArea or even ListBox or Table someday. Should then be a Module rather than a class.
22
+ class MultiContainer < Widget
23
+ dsl_accessor :title
24
+
25
+
26
+ def initialize form = nil, config={}, &block
27
+ @focusable = true
28
+ @row_offset = @col_offset = 1
29
+ super
30
+ @bmanager = BufferManager.new self
31
+ init_vars
32
+
33
+ end
34
+ def init_vars
35
+ super
36
+ # the following allows us to navigate buffers with :bn :bp etc (with Alt pressed)
37
+ bind_key(?\M-:, :buffer_menu)
38
+ bind_key(?\M-;, :buffer_menu)
39
+ # bind_key([?\C-x, ?f], :file_edit)
40
+ bind_key([?\C-x, ?k], :delete_component)
41
+ bind_key([?\C-x, ?\C-b], :list_components)
42
+ bind_key(?\M-n, :goto_next_component)
43
+ bind_key(?\M-p, :goto_prev_component)
44
+ bind_key(?\M-1, :goto_first_component)
45
+ # easily cycle using p. n is used for next search.
46
+ #bind_key(?p, :buffer_previous)
47
+ @suppress_borders = false
48
+ @repaint_all = true
49
+ @name ||= "multicontainer"
50
+ end
51
+ ## returns current buffer
52
+ # @return [RBuffer] current buffer
53
+ def current_component
54
+ @bmanager.current
55
+ end
56
+ ##
57
+ # Add a component with a title
58
+ # @param [Widget] component
59
+ # @param [String] title
60
+ def add component, title
61
+ component.row = @row+@row_offset+0 # FFI changed 1 to 0 2011-09-12
62
+ component.col = @col+@col_offset+0 # FFI changed 1 to 0 2011-09-12
63
+ component.width = @width-2
64
+ component.height = @height-2
65
+ component.form = @form
66
+ component.override_graphic(@graphic)
67
+ @current_buffer = @bmanager.add component, title
68
+ @current_component = @current_buffer.component
69
+ #set_current_component
70
+ #set_form_row ## FFI added 2011-09-12 to get cursor at start when adding
71
+ $log.debug "MULTICONT ADD got cb : #{@current_component} "
72
+ end
73
+ def set_current_component
74
+ @current_component = @current_buffer.component
75
+ @current_title = @current_component.title # NOTE: unused, don't knw what for
76
+ set_form_row
77
+ @current_component.repaint_all true
78
+ end
79
+ # required otherwise some components may not get correct cursor position on entry
80
+ # e.g. table
81
+ def on_enter
82
+ set_form_row
83
+ end
84
+ def set_form_row #:nodoc:
85
+ if !@current_component.nil?
86
+ cc = @current_component
87
+
88
+ @current_component.on_enter # 2011-10-19 why was this not there earlier
89
+
90
+ # 2011-10-21 I've tried removing next 2 lines but there are certain case
91
+ # that do need them. See testmulticontainer.rb
92
+
93
+ @current_component.set_form_row
94
+ @current_component.set_form_col
95
+
96
+ end
97
+ end
98
+ def set_form_col
99
+ # deliberately empty since Form will call this and Widgets one is unsuitable
100
+ # for us
101
+ end
102
+ ##
103
+ # multi-container
104
+ def handle_key ch #:nodoc:
105
+ $log.debug " MULTI handlekey #{ch}, #{@current_component}"
106
+ ret = :UNHANDLED
107
+ return :UNHANDLED unless @current_component
108
+
109
+ ret = @current_component.handle_key(ch)
110
+ $log.debug " MULTI = current comp #{@current_component} returned #{ret} "
111
+ if ret == :UNHANDLED
112
+ # check for bindings, these cannot override above keys since placed at end
113
+ begin
114
+ ret = process_key ch, self
115
+ $log.debug " MULTI = process_key returned #{ret} "
116
+ if ch > 177 && ch < 187
117
+ n = ch - 177
118
+
119
+ component_at(n)
120
+ ret = 0 # other unhandled goes back
121
+ # go to component n
122
+ end
123
+ rescue => err
124
+ $error_message.value = err.to_s
125
+ $log.error " Multicomponent process_key #{err} "
126
+ $log.debug(err.backtrace.join("\n"))
127
+ alert err.to_s
128
+ end
129
+ return :UNHANDLED if ret == :UNHANDLED
130
+ end
131
+ # check for any keys not handled and check our own ones
132
+ return ret #
133
+ end
134
+ def repaint
135
+ print_border if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
136
+ return unless @current_component
137
+ $log.debug " MULTI REPAINT - calling current_comps repaint #{@current_component} "
138
+ ret = @current_component.repaint
139
+ end
140
+ def print_border #:nodoc:
141
+ #$log.debug " #{@name} print_borders, #{@graphic.name} "
142
+ color = $datacolor
143
+ @graphic.print_border_only @row, @col, @height-1, @width, color #, Ncurses::A_REVERSE
144
+ print_title
145
+ end
146
+ def print_title #:nodoc:
147
+ #$log.debug " print_title #{@row}, #{@col}, #{@width} "
148
+ _title = @title || "" + @current_title
149
+ @graphic.printstring( @row, @col+(@width-_title.length)/2, _title, $datacolor, @title_attrib) unless _title.nil?
150
+ end
151
+ # this is just a test of the simple "most" menu
152
+ # can use this for next, prev, first, last, new, delete, overwrite etc
153
+ def buffer_menu
154
+ menu = PromptMenu.new self
155
+ menu.add(menu.create_mitem( 'l', "list buffers", "list buffers ", :list_components ))
156
+ item = menu.create_mitem( 'b', "Buffer Options", "Buffer Options" )
157
+ menu1 = PromptMenu.new( self, "Buffer Options")
158
+ menu1.add(menu1.create_mitem( 'n', "Next", "Switched to next buffer", :goto_next_component ))
159
+ menu1.add(menu1.create_mitem( 'p', "Prev", "Switched to previous buffer", :goto_prev_component ))
160
+ menu1.add(menu1.create_mitem( 'f', "First", "Switched to first buffer", :goto_first_component ))
161
+ menu1.add(menu1.create_mitem( 'l', "Last", "Switched to last buffer", :goto_last_component ))
162
+ menu1.add(menu1.create_mitem( 'd', "Delete", "Deleted buffer", :delete_component ))
163
+ item.action = menu1
164
+ menu.add(item)
165
+ # how do i know what's available. the application or window should know where to place
166
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor #, menu
167
+ end
168
+
169
+
170
+ def goto_next_component
171
+ perror "No other buffer" and return if @bmanager.size < 2
172
+
173
+ @current_buffer = @bmanager.next
174
+ set_current_component
175
+ end
176
+
177
+ def goto_prev_component
178
+ perror "No other buffer" and return if @bmanager.size < 2
179
+
180
+ @current_buffer = @bmanager.previous
181
+ $log.debug " buffer_prev got #{@current_buffer} "
182
+ set_current_component
183
+ end
184
+ def goto_first_component
185
+ @current_buffer = @bmanager.first
186
+ $log.debug " buffer_first got #{@current_buffer} "
187
+ set_current_component
188
+ end
189
+ def goto_last_component
190
+ @current_buffer = @bmanager.last
191
+ $log.debug " buffer_last got #{@current_buffer} "
192
+ set_current_component
193
+ end
194
+ def delete_component
195
+ if @bmanager.size > 1
196
+ @bmanager.delete_at
197
+ @current_component = @bmanager.previous
198
+ set_current_component
199
+ else
200
+ perror "Only one buffer. Cannot delete."
201
+ end
202
+ end
203
+
204
+ def component_at index
205
+ cc = @bmanager.element_at index
206
+ return unless cc
207
+ @current_component = cc
208
+ #$log.debug " buffer_last got #{@current_component} "
209
+ set_current_component
210
+ end
211
+ def perror errmess
212
+ alert errmess
213
+ #@form.window.print_error_message errmess
214
+ end
215
+ def list_components
216
+ $log.debug " TODO buffers_list: #{@bmanager.size} "
217
+ menu = PromptMenu.new self
218
+ @bmanager.each_with_index{ |b, ix|
219
+ aproc = Proc.new { component_at(ix) }
220
+ name = b.title
221
+ num = ix + 1
222
+ menu.add(menu.create_mitem( num.to_s, name, "Switched to buffer #{ix}", aproc ))
223
+ }
224
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor
225
+ end
226
+ end # class multicontainer
227
+ ##
228
+ # Handles multiple buffers, navigation, maintenance etc
229
+ # Instantiated at startup of
230
+ #
231
+ class BufferManager
232
+ include Enumerable
233
+ def initialize source
234
+ @source = source
235
+ @buffers = [] # contains RBuffer
236
+ @counter = 0
237
+ # for each buffer i need to store data, current_index (row), curpos (col offset) and title (filename).
238
+ end
239
+ def element_at index
240
+ @buffers[index]
241
+ end
242
+ def each
243
+ @buffers.each {|k| yield(k)}
244
+ end
245
+
246
+ ##
247
+ # @return [RBuffer] current buffer/file
248
+ ##
249
+ def current
250
+ @buffers[@counter]
251
+ end
252
+ ##
253
+ # Would have liked to just return next buffer and not get lost in details of caller
254
+ #
255
+ # @return [RBuffer] next buffer/file
256
+ ##
257
+ def next
258
+ @counter += 1
259
+ @counter = 0 if @counter >= @buffers.size
260
+ @buffers[@counter]
261
+ end
262
+ ##
263
+ # @return [RBuffer] previous buffer/file
264
+ ##
265
+ def previous
266
+ $log.debug " previous bs: #{@buffers.size}, #{@counter} "
267
+ @counter -= 1
268
+ return last() if @counter < 0
269
+ $log.debug " previous ctr #{@counter} "
270
+ @buffers[@counter]
271
+ end
272
+ def first
273
+ @counter = 0
274
+ @buffers[@counter]
275
+ end
276
+ def last
277
+ @counter = @buffers.size - 1
278
+ @buffers[@counter]
279
+ end
280
+ ##
281
+ def delete_at index=@counter
282
+ @buffers.delete_at index
283
+ end
284
+ def delete_by_name name
285
+ @buffers.delete_if {|b| b.filename == name }
286
+ end
287
+ def insert component, position, title=nil
288
+ anew = RComponents.new(component, title)
289
+ @buffers.insert position, anew
290
+ @counter = position
291
+ return anew
292
+ end
293
+ def add component, title=nil
294
+ $log.debug " ADD H: #{component.height} C: #{component.width} "
295
+ insert component, @buffers.size, title
296
+ end
297
+ def size
298
+ @buffers.size
299
+ end
300
+ alias :count :size
301
+ end
302
+ RComponents = Struct.new(:component, :title)
303
+
304
+ end # modul
@@ -0,0 +1,722 @@
1
+ =begin
2
+ * Name: MultiSplit
3
+ * Description: allows user to create multiple splits
4
+ * This diverges from the standard SplitPane which allowed one split only.
5
+ This is inspired by the column-browse pattern as in when we view rdoc in a browser.
6
+ A user does not need to create multiple split panes embedded inside each other, we
7
+ don't have that kind of space, and expanding can be tricky since there is no mouse
8
+ to select panes. Mostly, this makes creating apps with this pattern easy for user.
9
+
10
+
11
+ * NOTE that VERTICAL_SPLIT means the *divider* is vertical.
12
+ * Author: rkumar (arunachalesha)
13
+ * file created 2010-08-31 20:18
14
+ Todo:
15
+ --------
16
+ * License:
17
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
18
+
19
+ =end
20
+ #require 'rubygems'
21
+ #require 'ncurses'
22
+ require 'logger'
23
+ require 'rbcurse'
24
+
25
+ #include Ncurses # FFI 2011-09-8
26
+ include RubyCurses
27
+ module RubyCurses
28
+ extend self
29
+
30
+ ##
31
+ # A MultiSplit allows user to split N components vertically or horizontally.
32
+ # such as 3 listboxes, each dependent on what is selected in previous.
33
+ # This is the column-browse pattern, as in ruby's rdoc when seen in a browser.
34
+ # Also, this can be used for directory browsing, as in OSX Finder.
35
+ # One can keep adding components, and scroll
36
+ # back and forth, so we can have more components than are visible. See testmultispl.rb
37
+ # for a demo of this.
38
+ #
39
+ # This class allows for adding components in one direction, vertical or horizontal. It differs
40
+ # from Vimsplit in that you can have offscreen windows and scroll through. Vimsplit shows
41
+ # all windows but allows stacking and flowing of components.
42
+ #
43
+ # @since 1.1.5
44
+ # TODO -
45
+ # [ ] Don't print title if width less than title XXX or truncate - listbox
46
+ # x user specify max panes to show (beyond that hide and pan)
47
+ # x how many can be created
48
+ # - to squeeze panes and fit all or hide and pan
49
+ # x allow resize of panes
50
+ # - allow orientation change or not
51
+ # x some anamoly reg LEAVE and ENTER from last object
52
+ # x should we not be managing on_enter of listboxes when tabbing ?
53
+ # x how many panes to show, max to create
54
+ # x increase size - currently i recalc each time!
55
+ # x print more marker
56
+ # - allow user to specify preferred sizes and respect that
57
+ # x don't move to an empty list, can have a crash
58
+ #
59
+
60
+ class MultiSplit < Widget
61
+ dsl_property :orientation # :VERTICAL_SPLIT or :HORIZONTAL_SPLIT
62
+ dsl_accessor :border_color
63
+ dsl_accessor :border_attrib
64
+ # if no components have been added at time of repainting
65
+ #+ we could use this. This idea if that a user may want to
66
+ #+ show blank splits.
67
+ dsl_property :split_count
68
+ # should we allow adding more than split_count
69
+ # currently, we don't scroll, we narrow what is shown
70
+ dsl_accessor :unlimited
71
+ # allow user to resize components, default true
72
+ dsl_accessor :allow_resizing # XXX unused
73
+ # allow user to flip / exhange 2 components or not, default false
74
+ dsl_accessor :allow_exchanging # XXX unused
75
+ # when focus reenters this component, should it focus
76
+ # on first internal component, or last focused component.
77
+ # True means it will focus on first component (or last, if backtabbing)
78
+ dsl_accessor :cyclic_behavior
79
+ # maximum to show, if less than split_count then scrolling
80
+ dsl_property :max_visible
81
+ attr_reader :components
82
+ # should borders be suppressed or printed
83
+ dsl_accessor :suppress_borders
84
+
85
+ #attr_accessor :one_touch_expandable # boolean, default true # XXX
86
+
87
+ def initialize form, config={}, &block
88
+ @focusable = true
89
+ @editable = false
90
+ @cyclic_behavior = true
91
+ @row = 0
92
+ @col = 0
93
+ @split_count = nil
94
+ # this is the list of components
95
+ @components = []
96
+ # need to recalculate offsets and dimensions of all comps since new one added
97
+ # to be done once in repaint, and whenever a new one added (in repaint)
98
+ @recalc_required = true
99
+ @row_offset = @col_offset = 1
100
+ @suppress_borders = false
101
+ super
102
+ @orig_col = @col
103
+ @use_absolute = true; # set to true if not using subwins XXX CLEAN THIS
104
+ init_vars
105
+ end
106
+ def init_vars #:nodoc:
107
+ @_first_column_print = 0 # added 2009-10-07 11:25
108
+ @max_visible ||= @split_count
109
+ @_last_column_print = @_first_column_print + @max_visible - 1
110
+
111
+ # cascade_changes keeps the child exactly sized as per the pane which looks nice
112
+ #+ but may not really be what you want.
113
+ @cascade_changes=true
114
+ ## if this splp is increased (ht or wid) then expand the child
115
+ @cascade_boundary_changes = true
116
+ @orientation ||= :HORIZONTAL_SPLIT # added 2010-01-13 15:05 since not set
117
+
118
+ # true means will request child to create a buffer, since cropping will be needed
119
+ # FIXME: if true then increases in size are not having effect !!!
120
+ @_child_buffering = false # private, internal. not to be changed by callers.
121
+ #@one_touch_expandable = true
122
+ #@is_expanding = false
123
+ @row_offset = @col_offset = 0 if @suppress_borders
124
+
125
+ #bind_key([?\C-w, ?o], :expand)
126
+ #bind_key([?\C-w, ?1], :expand)
127
+ #bind_key([?\C-w, ?2], :unexpand)
128
+ #bind_key([?\C-w, ?x], :exchange)
129
+ bind_key(?w, :goto_next_component)
130
+ bind_key(?b, :goto_prev_component)
131
+ bind_key([?\C-w, ?-], :decrease)
132
+ bind_key([?\C-w, ?+], :increase)
133
+ bind_key([?\C-w, ?=], :same)
134
+
135
+ end
136
+ ##
137
+ # adds a component to the multisplit
138
+ # When you add a component to a container such as multisplit, be sure
139
+ # you create it with a nil form object, or else the main form will try to manage it.
140
+ # Containers typically manage their own components such as navigation and they
141
+ # give it the form/graphic object they were created with.
142
+ # @param [widget] a widget object to stack in a pane
143
+ def add comp
144
+ # for starters to make life simple, we force user to specify how many splits
145
+ # This is largely because i don;t know much about the buffering thing, is it still
146
+ # needed here or what. If we can postpone it, then we can compute this in a loop
147
+ # in repaint
148
+ raise "split_count must be given first. How many splits there will be." unless @split_count
149
+ $log.debug " multisplit: Adding a component #{@components.size} "
150
+
151
+ # until we hide those outside bounds, or are able to scroll, lets not allow add if
152
+ # exceeds
153
+ if @components.size >= @split_count
154
+ if @unlimited
155
+ #@split_count = @components.size + 1
156
+ # calc of width depending on ths
157
+ else
158
+ Ncurses.beep
159
+ return
160
+ end
161
+ end
162
+ @recalc_required = true
163
+ @components = [] if @components.nil?
164
+ @components << comp
165
+ #comp.height = nil # nuking listboxes height since it gets calculated
166
+ comp.parent_component = self
167
+ # dang ! this can go out of bounds ! XXX tab goes out
168
+ index = @components.size - 1 # expected as base 0 in compute
169
+ #index = @max_visible - 1 if index > @max_visible - 1
170
+ # all this ado to prevent cursor going out in the first display
171
+ # when index exceeds visible, since compute now uses running balance
172
+ if index > @max_visible - 1
173
+ # we copy the coords of the previous one
174
+ prev = @components[index-1]
175
+ comp.row = prev.row
176
+ comp.col = prev.col
177
+ comp.width = prev.width
178
+ comp.height = prev.height
179
+ else
180
+ compute_component comp, index
181
+ end
182
+ comp.set_buffering(:target_window => @target_window || @form.window, :bottom => comp.height-1, :right => comp.width-1, :form => @form ) # removed on 2011-09-29
183
+ comp.min_height ||= 5
184
+ comp.min_width ||= 5
185
+ return self
186
+ end
187
+ alias :<< :add
188
+
189
+ def [](index)
190
+ raise "MultiSplit: Please add components first" unless @components
191
+ @components[index]
192
+ end
193
+ def size
194
+ @components.size
195
+ end
196
+ alias :length :size
197
+ ##
198
+ # compute component dimensions in one place
199
+ # @param [widget] a widget
200
+ # @param [Fixnum] offset in list of components
201
+ # XXX if called from outside balance can have last value !!!
202
+ # FIXME for last component, take as much as is left height or width
203
+ # otherwise odd figures will leave on row unoccupied
204
+ def compute_component comp, index
205
+ @balance ||= 0
206
+ if @orientation == :HORIZONTAL_SPLIT
207
+ # XXX NOT TESTED TODO
208
+ @comp_height = (@height / @split_count) - 0
209
+ @comp_width = @width
210
+ h = @comp_height
211
+ if @recalc_required
212
+ comp.height = h # listboxes etal calculate a height so that will stand !! XXX
213
+ else
214
+ comp.height ||= h # listboxes etal calculate a height so that will stand !! XXX
215
+ end
216
+ w = @comp_width
217
+ #r = @row + ( comp.height * index)
218
+ r = @row + @balance
219
+ if r > @row + @height
220
+ r = @row + @height
221
+ end
222
+ #alert "r #{@row} h #{@height} ::: comp row #{r} h #{h} bal:#{@balance} "
223
+ @balance += comp.height
224
+ c = @col
225
+ comp.width = w
226
+ comp.row = r
227
+ comp.col = c
228
+ else
229
+ @comp_height = @height
230
+ @comp_width = (@width / @split_count) - 0
231
+ h = @comp_height
232
+ w = @comp_width
233
+ if @recalc_required
234
+ comp.width = w
235
+ else
236
+ comp.width ||= w
237
+ end
238
+ #c = @col + ( w * index) # this makes them all equal
239
+ c = @col + @balance
240
+ if c > @col + @width
241
+ c = @col + @width
242
+ end
243
+ $log.debug "XXXX index #{index} , w #{comp.width} , c = #{c} , bal #{@balance} c+w:#{@col+@width} "
244
+ #if index < @max_visible - 1
245
+ @balance += comp.width
246
+ #end
247
+ r = @row
248
+ comp.height = h
249
+ comp.row = r
250
+ comp.col = c
251
+ #$log.debug " XXXX index r c #{r} #{c} "
252
+ end
253
+ comp
254
+ end
255
+ def increase
256
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
257
+ delta = _multiplier
258
+ c = @current_component
259
+ n = get_next_component
260
+ n = get_prev_component unless n
261
+ return unless n
262
+ if @orientation == :HORIZONTAL_SPLIT
263
+ if n.height > 3 + delta
264
+ c.height += delta
265
+ n.height -= delta
266
+ end
267
+ else
268
+ if n.width > 3 + delta
269
+ c.width += delta
270
+ n.width -= delta
271
+ end
272
+ end
273
+ @repaint_required = true
274
+ self
275
+ end
276
+ # decrease size of current component.
277
+ # if last one, then border printing exceeds right boundary. values look okay
278
+ # dunno why XXX FIXME
279
+ def decrease
280
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
281
+ delta = _multiplier
282
+ $log.debug "XXXX decrease got mult #{$_multiplier} "
283
+ c = @current_component
284
+ # if decreasing last component then increase previous
285
+ # otherwise always increase the next
286
+ n = get_next_component || get_prev_component
287
+ return unless n # if no other, don't allow
288
+ if @orientation == :HORIZONTAL_SPLIT
289
+ c.height -= delta
290
+ n.height += delta
291
+ # TODO
292
+ else
293
+ if c.width > 3 + delta
294
+ c.width -= delta
295
+ n.width += delta
296
+ end
297
+ end
298
+ @repaint_required = true
299
+ self
300
+ end
301
+ def same
302
+ @components.each do |comp|
303
+ comp.height = @comp_height
304
+ comp.width = @comp_width
305
+ end
306
+ @repaint_required = true
307
+ self
308
+ end
309
+ # @return [widget] next component or nil if no next
310
+ def get_next_component
311
+ return @components[@current_index+1]
312
+ end
313
+ # @return [widget] prev component or nil if no next
314
+ def get_prev_component
315
+ return nil if @current_index == 0
316
+ return @components[@current_index-1]
317
+ end
318
+ ##
319
+ #
320
+ # change height of splitpane
321
+ # @param val [int] new height of splitpane
322
+ # @return [int] old ht if nil passed
323
+ def height(*val)
324
+ return @height if val.empty?
325
+ oldvalue = @height || 0
326
+ super
327
+ @height = val[0]
328
+ return if @components.nil? || @components.empty?
329
+ delta = @height - oldvalue
330
+ @repaint_required = true
331
+ if !@cascade_boundary_changes.nil?
332
+ # must tell children if height changed which will happen in nested splitpanes
333
+ # must adjust to components own offsets too
334
+ if @orientation == :VERTICAL_SPLIT
335
+ @components.each do |e|
336
+ e.height += delta
337
+ end
338
+ else
339
+ e = @components.first
340
+ e.height += delta
341
+ end
342
+ end
343
+ end
344
+ ##
345
+ # change width of splitpane
346
+ # @param val [int, nil] new width of splitpane
347
+ # @return [int] old width if nil passed
348
+ # NOTE: if VERTICAL, then expand or contract only second
349
+ # If HORIZ then expand / contract both
350
+ # Actually this is very complicated since reducing should take into account min_width
351
+ def width(*val)
352
+ return @width if val.empty?
353
+ # must tell children if height changed which will happen in nested splitpanes
354
+ oldvalue = @width || 0
355
+ super
356
+ @width = val[0]
357
+ delta = @width - oldvalue
358
+ $log.debug " SPLP #{@name} width #{oldvalue}, #{@width}, #{delta} "
359
+ @repaint_required = true
360
+ if !@cascade_boundary_changes.nil?
361
+ # must adjust to components own offsets too
362
+ # NOTE: 2010-01-10 20:11 if we increase width by one, each time will both components get increased by one.
363
+ if @orientation == :HORIZONTAL_SPLIT
364
+ @components.each do |e|
365
+ e.width += delta
366
+ end
367
+ else
368
+ # any change in width must effect col of others too ! 2010-08-31 21:57 AUG2010
369
+ # which is why this should be done in repaint and not here
370
+ # ## next change should only happen if sc w < ...
371
+ # if @second_component.width < @width - (rc + @col_offset + @divider_offset + 1)
372
+ last = @components.last
373
+ last.width += delta
374
+ end
375
+ end
376
+ end
377
+ ##
378
+ # resets divider location based on preferred size of first component
379
+ # @return :ERROR if min sizes failed
380
+ # You may want to check for ERROR and if so, resize_weight to 0.50
381
+ def reset_to_preferred_sizes
382
+ alert "TODO THIS reset_to "
383
+ return if @components.nil?
384
+ @repaint_required = true
385
+ end
386
+ # recalculates components and calls repaint
387
+ def update_components # #:nodoc:
388
+ @balance = 0
389
+ @max_visible ||= @split_count
390
+ @_first_column_print ||= 0
391
+ @_last_column_print = @_first_column_print + @max_visible - 1
392
+ $log.debug " XXXX #{@_first_column_print} , last print #{@_last_column_print} "
393
+ @components.each_with_index do |comp,index|
394
+ next if index < @_first_column_print
395
+ break if index > @_last_column_print
396
+ compute_component comp, index
397
+ comp.set_buffering(:target_window => @target_window || @form.window, :form => @form ) # 2011-09-29
398
+ #comp.set_buffering(:target_window => @target_window || @form.window, :bottom => comp.height-1, :right => comp.width-1, :form => @form )
399
+ comp.repaint
400
+ end
401
+ #@balance = 0
402
+ @recalc_required = false
403
+ end
404
+ def repaint # multisplitpane #:nodoc:
405
+ if @graphic.nil?
406
+ @graphic = @target_window || @form.window
407
+ raise "graphic nil in rsplitpane #{@name} " unless @graphic
408
+ end
409
+
410
+ if @repaint_required
411
+ # repaint all ?
412
+ @components.each { |e| e.repaint_all(true) }
413
+ end
414
+ if @repaint_required
415
+ ## paint border and divider
416
+ $log.debug "MULTISPLP #{@name} repaint split H #{@height} W #{@width} "
417
+ bordercolor = @border_color || $datacolor
418
+ borderatt = @border_attrib || Ncurses::A_NORMAL
419
+ absrow = abscol = 0
420
+ if @use_absolute
421
+ absrow = @row
422
+ abscol = @col
423
+ end
424
+ if @use_absolute
425
+ $log.debug " #{@graphic} #{name} calling print_border #{@row} #{@col} #{@height}-1 #{@width}-1 "
426
+ @graphic.print_border(@row, @col, @height-1, @width-1, bordercolor, borderatt) if !@suppress_borders
427
+ else
428
+ $log.debug " #{@graphic} calling print_border 0,0"
429
+ @graphic.print_border(0, 0, @height-1, @width-1, bordercolor, borderatt) unless @suppress_borders
430
+ end
431
+ rc = -1
432
+
433
+ @graphic.attron(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
434
+ # 2010-02-14 18:23 - non buffered, have to make relative coords into absolute
435
+ #+ by adding row and col
436
+ count = @components.nil? ? @split_count : @components.size
437
+ count = @components.empty? ? @split_count : @components.size
438
+ if @orientation == :VERTICAL_SPLIT
439
+ @comp_height ||= @height
440
+ @comp_width ||= (@width / @split_count) - 0
441
+ $log.debug "SPLP #{@name} prtingign split vline divider 1, rc: #{rc}, h:#{@height} - 2 "
442
+ #@graphic.mvvline(absrow+1, rc+abscol, 0, @height-2)
443
+ # (1...count).each(){|i| @graphic.mvvline(absrow+1, (i*@comp_width)+abscol, 0, @height-2) }
444
+ # TODO put vlines here
445
+ # commented off since it uses fixed values and we are increaseing and dec
446
+
447
+ else
448
+ @comp_height ||= (@height / @split_count) - 1
449
+ @comp_width ||= @width
450
+ #$log.debug "SPLP #{@name} prtingign split hline divider rc: #{rc} , 1 , w:#{@width} - 2"
451
+ #@graphic.mvhline(rc+absrow, abscol+1, 0, @width-2)
452
+ # XXX in next line -2 at end was causing an overlap into final border col,
453
+ # this need correction in splitpane XXX
454
+ #(1...count).each(){|i| @graphic.mvhline((i*@comp_height)+absrow, abscol+1, 0, @width-3) }
455
+ # TODO put hlines here
456
+ end
457
+ @graphic.attroff(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
458
+ update_components
459
+ _print_more_columns_marker true
460
+ @graphic.wrefresh # 2010-02-14 20:18 SUBWIN ONLY ??? what is this doing here ? XXX
461
+ else
462
+ # repaint only those components that may have changed
463
+ @components.each { |e| e.repaint }
464
+ end
465
+ ## XXX do not paint what is outside of bounds. See tabbedpane or scrollform
466
+ #paint
467
+ @repaint_required = false
468
+ end
469
+ def getvalue #:nodoc:
470
+ # TODO
471
+ end
472
+ # take focus to next pane (component in it)
473
+ # if its the last, return UNHANDLED so form can take to next field
474
+ # @return [0, :UNHANDLED] success, or last component
475
+
476
+ def goto_next_component
477
+ if @current_component != nil
478
+ @current_component.on_leave
479
+ if on_last_component?
480
+ return :UNHANDLED
481
+ end
482
+ @current_index += 1
483
+ @current_component = @components[@current_index]
484
+ # is it visible
485
+ #@current_index.between?(_first_column_print, _last_column_print)
486
+ if @current_index > @_last_column_print
487
+ # TODO need to check for exceeding
488
+ @_first_column_print += 1
489
+ @_last_column_print += 1
490
+ @repaint_required = true
491
+ end
492
+ # shoot if this this put on a form with other widgets
493
+ # we would never get out, should return nil -1 in handle key
494
+ unless @current_component
495
+ $log.debug " CAME HERE unless @current_component setting to first"
496
+ raise " CAME HERE unless @current_component setting to first"
497
+ @current_index = 0
498
+ @current_component = @components[@current_index]
499
+ end
500
+ else
501
+ # this happens in one_tab_expand
502
+ #@current_component = @second_component if @first_component.nil?
503
+ #@current_component = @first_component if @second_component.nil?
504
+ # XXX not sure what to do here, will it come
505
+ $log.debug " CAME HERE in else clause MSP setting to first"
506
+ raise" CAME HERE in else clause MSP setting to first"
507
+ @current_index = 0
508
+ @current_component = @components[@current_index]
509
+ end
510
+ return set_form_row
511
+ end
512
+
513
+ # take focus to prev pane (component in it)
514
+ # if its the first, return UNHANDLED so form can take to prev field
515
+ # @return [0, :UNHANDLED] success, or first component
516
+ def goto_prev_component
517
+ if @current_component != nil
518
+ @current_component.on_leave
519
+ if on_first_component?
520
+ return :UNHANDLED
521
+ end
522
+ @current_index -= 1
523
+ @current_component = @components[@current_index]
524
+ if @current_index < @_first_column_print
525
+ # TODO need to check for zero
526
+ @_first_column_print -= 1
527
+ @_last_column_print -= 1
528
+ @repaint_required = true
529
+ end
530
+ # shoot if this this put on a form with other widgets
531
+ # we would never get out, should return nil -1 in handle key
532
+ unless @current_component
533
+ @current_index = 0
534
+ @current_component = @components[@current_index]
535
+ end
536
+ else
537
+ # this happens in one_tab_expand
538
+ #@current_component = @second_component if @first_component.nil?
539
+ #@current_component = @first_component if @second_component.nil?
540
+ # XXX not sure what to do here, will it come
541
+ @current_index = 0
542
+ @current_component = @components[@current_index]
543
+ end
544
+ set_form_row
545
+ return 0
546
+ end
547
+ def on_first_component?
548
+ @current_component == @components.first
549
+ end
550
+ def on_last_component?
551
+ @current_component == @components.last
552
+ end
553
+ ## Handles key for splitpanes
554
+ ## By default, first component gets focus, not the SPL itself.
555
+ ##+ Mostly passing to child, and handling child's left-overs.
556
+ # please use bind_key for all mappings.
557
+ # Avoid adding code in here. Let this be generic
558
+ def handle_key ch #:nodoc:
559
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
560
+ @current_component ||= @first_component
561
+ @current_index ||= 0
562
+ ## 2010-01-15 12:57 this helps me switch between highest level
563
+ ## However, i should do as follows:
564
+ ## If tab on second component, return UNHA so form can take to next field
565
+ ## If B_tab on second comp, switch to first
566
+ ## If B_tab on first comp, return UNHA so form can take to prev field
567
+ if ch == KEY_TAB
568
+ return goto_next_component
569
+ #return 0
570
+ elsif ch == KEY_BTAB
571
+ return goto_prev_component
572
+ end
573
+
574
+ if @current_component != nil
575
+ ret = @current_component.handle_key ch
576
+ return ret if ret != :UNHANDLED
577
+ else
578
+ ## added 2010-01-07 18:59 in case nothing in there.
579
+ $log.debug " SPLP #{@name} - no component installed in splitpane"
580
+ #return :UNHANDLED
581
+ end
582
+ $log.debug " mmplitpane #{@name} gets KEY #{ch}"
583
+ case ch
584
+ when ?\C-c.getbyte(0)
585
+ $multiplier = 0
586
+ return 0
587
+ when ?0.getbyte(0)..?9.getbyte(0)
588
+ $multiplier *= 10 ; $multiplier += (ch-48)
589
+ return 0
590
+ end
591
+ ret = process_key ch, self
592
+ return :UNHANDLED if ret == :UNHANDLED
593
+
594
+ $multiplier = 0
595
+ return 0
596
+ end
597
+ def paint #:nodoc:
598
+ #@repaint_required = false
599
+ end
600
+ # this is executed when the component gets focus
601
+ # and will happen each time on traversal
602
+ # Used to place the focus on correct internal component
603
+ # and place cursor where component should have it.
604
+ # User can press tab, to come here, or it could be first field of form,
605
+ # or he could press a mnemonic.
606
+ def on_enter
607
+ return if @components.nil?
608
+ # cyclic means it always lands into first comp just as in rdoc
609
+ # otherwise it will always land in last visited component
610
+ if @cyclic_behavior
611
+ # if user backtabbed in place him on last comp
612
+ # else place him in first.
613
+ if $current_key == KEY_BTAB
614
+ @current_component = @components[@_last_column_print]
615
+ @current_index = @_last_column_print
616
+ else
617
+ @current_component = @components[@_first_column_print]
618
+ @current_index = @_first_column_print
619
+ end
620
+ end
621
+ @current_component ||= @components.first
622
+ set_form_row
623
+ end
624
+ # sets cursor on correct row, col
625
+ # should we raise error or throw exception if can;t enter
626
+ def set_form_row #:nodoc:
627
+ if !@current_component.nil?
628
+ c=@current_component
629
+ $log.debug "XXXXX #{@name} set_form_row calling sfr for #{@current_component.name}, #{c.row}, #{c.col} "
630
+ #@current_component.set_form_row
631
+ # trigger the on_enter handler
632
+ # my god XXX this assumes a listbox !! FIXME
633
+ # on enter should return a false or error so we don't proceed
634
+ # or throw exception
635
+ if @current_component.row_count > 0
636
+ @current_component.on_enter # typically on enter does a set_form_row
637
+ # XXX another assumption that is has this !!!
638
+ @current_component.set_form_col
639
+ return 0
640
+ end
641
+ #
642
+ end
643
+ return :UNHANDLED
644
+ end
645
+ # added 2010-02-09 10:10
646
+ # sets the forms cursor column correctly
647
+ # earlier the super was being called which missed out on child's column.
648
+ # Note: splitpane does not use the cursor, so it does not know where cursor should be displayed,
649
+ #+ the child has to decide where it should be displayed.
650
+ def set_form_col #:nodoc:
651
+ return if @current_component.nil?
652
+ #$log.debug " #{@name} set_form_col calling sfc for #{@current_component.name} "
653
+ @current_component.set_form_col
654
+ end
655
+ ## expand a split to maximum. This is the one_touch_expandable feature
656
+ # Currently mapped to C-w 1 (mnemonic for one touch), or C-w o (vim's only)
657
+ # To revert, you have to unexpand
658
+ # Note: basically, i nil the component that we don't want to see
659
+ def expand
660
+ return unless @one_touch_expandable
661
+ # TODO
662
+ #@is_expanding = true # this is required so i don't check for min_width later
663
+ #$log.debug " callign expand "
664
+ #if @current_component == @first_component
665
+ #@saved_component = @second_component
666
+ #@second_component = nil
667
+ #if @orientation == :VERTICAL_SPLIT
668
+ #set_divider_location @width - 1
669
+ #else
670
+ #set_divider_location @height - 1
671
+ #end
672
+ #$log.debug " callign expand 2 nil #{@divider_location}, h:#{@height} w: #{@width} "
673
+ #else
674
+ #@saved_component = @first_component
675
+ #@first_component = nil
676
+ #set_divider_location 1
677
+ #$log.debug " callign expand 1 nil #{@divider_location}, h:#{@height} w: #{@width} "
678
+ #end
679
+ #@repaint_required = true
680
+ end
681
+ # after expanding one split, revert to original - actually i reset, rather than revert
682
+ # This only works after expand has been done
683
+ def unexpand
684
+ #$log.debug " inside unexpand "
685
+ #return unless @saved_component
686
+ #if @first_component.nil?
687
+ #@first_component = @saved_component
688
+ #else
689
+ #@second_component = @saved_component
690
+ #end
691
+ #@saved_component = nil
692
+ #@repaint_required = true
693
+ #reset_to_preferred_sizes
694
+ end
695
+
696
+ # exchange 2 splits, bound to C-w x
697
+ # TODO
698
+ def exchange
699
+ alert "TODO"
700
+ #tmp = @first_component
701
+ #@first_component = @second_component
702
+ #@second_component = tmp
703
+ #@repaint_required = true
704
+ #reset_to_preferred_sizes
705
+ end
706
+ def tile
707
+ return unless @tiling_allowed
708
+ # TODO
709
+ end
710
+ private
711
+ def _print_more_columns_marker tf
712
+ # this marker shows that there are more columns to right
713
+ tf = @_last_column_print < @components.size - 1
714
+ marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
715
+ #@graphic.mvwaddch @row+@height-1, @col+@width-2, marker
716
+ @graphic.mvwaddch @row+@height-1, @col+@width-3, marker
717
+ # show if columns to left or not
718
+ marker = @_first_column_print > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
719
+ @graphic.mvwaddch @row+@height-1, @col+@_first_column_print+1, marker
720
+ end
721
+ end # class
722
+ end # module