rbcurse-extras 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.
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,800 @@
1
+ # This is a new kind of splitpane, inspired by the vim editor.
2
+ # I was deeply frustrated with the Java kind of splitpane,
3
+ # which requires splitpanes within splitpanes to get several split.
4
+ # This is an attempt at getting many splits, keeping them at one level
5
+ # and keeping the interface as simple as possible, with minimal input
6
+ # from user.
7
+ # It usually takes a listbox or textview or textarea.
8
+ # It can also take an array, or string or hash.
9
+ # It supports moving the split, and increasing or decreasing the current box to some extent.
10
+ # Typically if the split is vertical, add stacks the components, one below the other.
11
+ # If horizontal, if will flow the components, to the right of previous. This can be overriden by passing
12
+ # type as :STACK or :FLOW.
13
+ # See examples/testvimsplit.rb
14
+ #
15
+ # This does not support changing the orientation at run time, that's nice for demos, but a pain
16
+ # to get right, and results in a lot of extra code, meaning more bugs.
17
+ # TODO: create a class that contains component array and a pointer so it can give next/prev
18
+ # i am tired of maintaining this everywhere.
19
+ require 'rbcurse'
20
+ require 'rbcurse/extras/widgets/rlistbox'
21
+ require 'rbcurse/core/widgets/divider'
22
+ require 'rbcurse/core/util/focusmanager'
23
+
24
+ include RubyCurses
25
+ module RubyCurses
26
+ extend self
27
+ class Coord < Struct.new(:row, :col, :h, :w); end
28
+ # Split contains info for a component added. weight is preferred weight
29
+ # and can contain value :AUTO. act_weight has the weight calculated.
30
+ # Often, last component can be nil, remainder will be assigned to it.
31
+ class Split < Struct.new(:which, :type, :weight, :act_weight); end
32
+ class ResizeEvent < Struct.new(:source, :type); end
33
+
34
+ # A simpler replacement for the java-esque SplitPane. This can take multiple splits
35
+ # and does not require splits within splits as SplitPane does.
36
+ # This is less functional, but should be easier to use, setup and hack.
37
+ class VimSplit < Widget
38
+
39
+ # split orientation :V or :H
40
+ dsl_accessor :orientation
41
+ # min and max weight of main split. do not allow user to exceed these
42
+ dsl_accessor :min_weight, :max_weight
43
+ dsl_accessor :suppress_borders #to_print_borders
44
+ dsl_accessor :border_attrib, :border_color
45
+ attr_reader :current_component
46
+ def initialize form, config={}, &block
47
+ if config[:width] == :EXPAND
48
+ config[:width] = Ncurses.COLS - config[:col]
49
+ end
50
+ if config[:orientation] == nil
51
+ config[:orientation] = :HORIZONTAL_SPLIT
52
+ else
53
+ # if first char is V or H then fill it in.
54
+ char = config[:orientation].to_s[0,1].upcase
55
+ if char == "V"
56
+ config[:orientation] = :VERTICAL_SPLIT
57
+ else
58
+ config[:orientation] = :HORIZONTAL_SPLIT
59
+ end
60
+ end
61
+ @max_weight ||= 0.8
62
+ @min_weight ||= 0.1 # earlier 0.2 but i wanted 0.15, someone may want 0.05 ??
63
+ @suppress_borders = false
64
+ @_use_preferred_sizes = true
65
+ @row_offset = @col_offset = 1
66
+ # type can be :INCREASE, :DECREASE, :EXPAND, :UNEXPAND :EQUAL
67
+ @_events ||= []
68
+ @_events.push :COMPONENT_RESIZE_EVENT
69
+ @_events.push :DRAG_EVENT
70
+ super
71
+ @focusable = true
72
+ @editable = false
73
+ @components = [] # all components
74
+ @c1 =[] # first split's comps
75
+ @c2 =[] # second split's comps
76
+ # coordinates of a split, i calculate and increment row col as i go.
77
+ @c1rc = nil # TODO create once only
78
+ @c2rc = nil
79
+
80
+ # hash, keyed on component, contains Split (which side, flow or stack, weight)
81
+ @ch = {}
82
+ @weight ||= 0.50
83
+
84
+ init_vars
85
+ bind_key([?\C-w,?o], :expand)
86
+ bind_key([?\C-w,?1], :expand)
87
+ bind_key([?\C-w,?2], :unexpand)
88
+ bind_key([?\C-w,?\C-w], :goto_other_split)
89
+ bind_key([?\C-w,?-], :decrease_height)
90
+ bind_key([?\C-w,?+], :increase_height)
91
+ bind_key([?\C-w,?<], :decrease_width)
92
+ bind_key([?\C-w,?>], :increase_width)
93
+ bind_key([?\C-w,?i], :increase_weight)
94
+ bind_key([?\C-w,?d], :decrease_weight)
95
+ bind_key([?\C-w,?6], :increase_current_component)
96
+ bind_key([?\C-w,?5], :decrease_current_component)
97
+ # this needs to be set at application level
98
+ bind_key(FFI::NCurses::KEY_F3) {RubyCurses::FocusManager.toggle_focusable}
99
+ end
100
+ def init_vars
101
+ @repaint_required = true
102
+ @recalculate_splits = true # convert weight to size
103
+ @row_offset = @col_offset = 0 if @suppress_borders # FIXME supposed to use this !!
104
+
105
+ @internal_width = 2
106
+ @internal_width = 1 if @suppress_borders
107
+
108
+ end
109
+ # uses intelligent default a vertical split would prefer stacks and
110
+ # a horizontal split would go with flows
111
+ # @param [Widget, Array, String, Hash, Variable] to add
112
+ # @param [:FIRST, :SECOND]
113
+ def add c, which, weight=:AUTO, type=:AUTO
114
+ if type == :AUTO
115
+ if v?
116
+ type = :STACK
117
+ else
118
+ type = :FLOW
119
+ end
120
+ end
121
+ _add type, c, which, weight
122
+ #return self # lets return component created for christ's sake and keep it simple
123
+ end
124
+ # set the weight of outer split
125
+ def weight(*val)
126
+ if val.empty?
127
+ return @weight
128
+ else
129
+ # raise ArgumentError
130
+ newval = val[0]
131
+ # this is since, using numeric multipliers he can go beyond, so lets give him the best
132
+ if val[0] < @min_weight
133
+ newval = @min_weight
134
+ elsif val[0] > @max_weight
135
+ newval = @max_weight
136
+ end
137
+ oldvalue = @weight
138
+ @weight = newval
139
+ # orientation can be nil, so we cannot calculate rc here
140
+ #if v?
141
+ #@rc = (@width * @weight).to_i
142
+ #else
143
+ #@rc = (@height * @weight).to_i
144
+ #end
145
+ @rc = nil # so recalculated in repaint
146
+ fire_property_change(:weight, oldvalue, @weight)
147
+ end
148
+ self
149
+ end
150
+ # stack components, one over another, useful in a vertical split
151
+ # @param [Widget] component
152
+ # @param [:FIRST :SECOND] first or second split
153
+ # @param [Float, nil, :AUTO] weight of object, nil for last will expand it to full
154
+ # :AUTO will give equal weight to all
155
+ def stack c, which, weight
156
+ _add :STACK, c, which, weight
157
+ #return self # lets return component created for christ's sake and keep it simple
158
+ end
159
+ # place components on right of previous. Useful in horizontal split
160
+ def flow c, which, weight
161
+ _add :FLOW, c, which, weight
162
+ #return self # lets return component created for christ's sake and keep it simple
163
+ end
164
+ private
165
+ def _add type, c, which, weight
166
+ raise ArgumentError, "Nil component passed to add" unless c
167
+ raise ArgumentError, "which must be :FIRST or :SECOND" if which != :FIRST && which != :SECOND
168
+ # trying out wt of 0 means it will see height of object and use that.
169
+ if weight.nil? || weight == :AUTO || (weight >= 0 && weight <= 1.0)
170
+ else
171
+ raise ArgumentError, "weight must be >0 and <=1.0 or nil or :AUTO"
172
+ end
173
+ if c.is_a? Widget
174
+ if c.form && c.form != @form
175
+ $log.debug " removing widget VIMSPLIT #{c.class} "
176
+ c.form.remove_widget c
177
+ c.form = nil
178
+ end
179
+ #$log.debug " XXXX VIM is a widget"
180
+ else
181
+ case c
182
+ when Array
183
+ lb = Listbox.new nil, :list => c , :name => "list#{@components.size}"
184
+ c = lb
185
+ when String
186
+ require 'rbcurse/core/widgets/rtextview'
187
+ lb = TextView.new nil, :name => "text#{@components.size}"
188
+ lb.set_content c
189
+ c = lb
190
+ when Hash
191
+ lb = Listbox.new nil, :list => c.keys , :name => "list#{@components.size}"
192
+ c = lb
193
+ when Variable
194
+ # TODO
195
+ else
196
+ if c == :grabbar || c == :divider
197
+ side = :bottom
198
+ case type
199
+ when :STACK
200
+ side = :bottom
201
+ when :FLOW
202
+ side = :left
203
+ end
204
+ c = Divider.new nil, :parent => @components.last, :side => side
205
+ c.focusable(false)
206
+ RubyCurses::FocusManager.add c
207
+ c.bind :DRAG_EVENT do |ev|
208
+ source = ev.source
209
+ case ev.type
210
+ when KEY_UP
211
+ # CHECK BOUNDS TODO
212
+ # TODO what about KEY_LEFT and RIGHT ?
213
+ if source.next_component && source.next_component.row > 1 && source.parent.height > 1
214
+ source.parent.height -= 1
215
+ source.next_component.height +=1
216
+ source.next_component.row -= 1
217
+ source.parent.repaint_required
218
+ source.next_component.repaint_required
219
+ source.parent.repaint
220
+ source.next_component.repaint
221
+ end
222
+ when KEY_DOWN
223
+ # CHECK BOUNDS TODO check with appemail.rb
224
+ if source.next_component && source.next_component.height > 1
225
+ source.parent.height += 1
226
+ source.next_component.height -=1
227
+ source.next_component.row += 1
228
+ source.parent.repaint_required
229
+ source.next_component.repaint_required
230
+ source.parent.repaint
231
+ source.next_component.repaint
232
+ end
233
+ end
234
+ end
235
+ end
236
+ end
237
+ end
238
+ c.parent_component = self
239
+
240
+ @components << c
241
+ if which == :FIRST
242
+ @c1 << c
243
+ else
244
+ @c2 << c
245
+ end
246
+ #@ch[c] = [which, type, weight]
247
+ @ch[c] = Split.new(which, type, weight)
248
+ @repaint_required = true
249
+ return c
250
+ end
251
+ public
252
+ def split_info_for(c = @current_component)
253
+ @ch[c]
254
+ end
255
+ # get the current split focus is on
256
+ # @return [:FIRST, :SECOND] which split are we on
257
+ def current_split
258
+ split_info_for(@current_component).which
259
+ end
260
+ # returns the other split.
261
+ def other_split
262
+ which = current_split
263
+ return which == :FIRST ? :SECOND : :FIRST
264
+ end
265
+ # returns list of components for FIRST or SECOND split
266
+ def components_for which
267
+ return which == :FIRST ? @c1 : @c2
268
+ end
269
+
270
+ public
271
+ # repaint object
272
+ # called by Form, and sometimes parent component (if not form).
273
+ def repaint
274
+ my_win = @form ? @form.window : @target_window
275
+ @graphic = my_win unless @graphic
276
+ raise " #{@name} NO GRAPHIC set as yet VIMSPLIT paint " unless @graphic
277
+
278
+ #return unless @repaint_required
279
+ @recalculate_splits = true if @rc.nil?
280
+
281
+ # if some major change has happened then repaint everything
282
+ if @repaint_required
283
+ $log.debug " VIM repaint graphic #{@graphic} "
284
+ print_borders unless @suppress_borders # do this once only, unless everything changes
285
+ r,c = rowcol
286
+
287
+ bordercolor = @border_color || $datacolor
288
+ borderatt = @border_attrib || Ncurses::A_NORMAL
289
+
290
+
291
+ @graphic.attron(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
292
+
293
+ ## The following calculations are only calcing the 2 split areas
294
+ ## and divider locations based on V or H and weight.
295
+
296
+ @gbwid ||= 0 # grabbar width
297
+ roffset = 1
298
+ loffset = 2
299
+ if @suppress_borders
300
+ loffset = roffset = 0
301
+ end
302
+ # vertical split
303
+ if v?
304
+ @rc ||= (@width * @weight).to_i
305
+ rc = @rc # divider location
306
+ $log.debug "SPLP #{@name} prtingign split vline divider 1, rc: #{rc}, h:#{@height} - 2 "
307
+ unless @vb # if grabbar not created
308
+ @gbwid = 1
309
+ _create_divider
310
+ else # created, so set it
311
+ @vb.row @row+roffset
312
+ @vb.col rc+@col
313
+ #@vb.repaint
314
+ end
315
+ #@graphic.mvvline(@row+1, rc+@col, 0, @height-2)
316
+ # TODO don;t keep recreating, if present, reset values
317
+ ## calculate cordinated of both split areas/boxes
318
+ @c1rc = Coord.new(@row,@col, @height -0, rc-@gbwid)
319
+ @c2rc = Coord.new(@row,rc+@col+@gbwid,@height-0, @width - rc-@gbwid)
320
+ else # horizontal split
321
+ @rc ||= (@height * @weight).to_i
322
+ rc = @rc # dividers row col location
323
+ $log.debug "SPLP #{@name} prtingign split hline divider rc: #{rc} , 1 , w:#{@width} - 2"
324
+ unless @vb
325
+ @gbwid = 1
326
+ _create_divider
327
+ else
328
+ #@vb = Divider.new nil, :row => @row+rc-1, :col => @col+1, :length => @width-loffset, :side => :bottom
329
+ @vb.row @row+@rc-1
330
+ @vb.col @col+roffset
331
+ #@vb.repaint # getting wiped out by vimsplit ?
332
+ end
333
+ #@graphic.mvhline(rc+@row, @col+1, 0, @width-@internal_width)
334
+ #@neat = true
335
+ if @neat
336
+ a = 1
337
+ @c1rc = Coord.new(@row+a,@col+a, rc-a, @width-@internal_width)
338
+ @c2rc = Coord.new(@row+rc+a,@col+a, @height-rc-2, @width - @internal_width)
339
+ else
340
+ # flush against border
341
+ #@c1rc = Coord.new(@row,@col, @height -0, rc-@gbwid)
342
+ #@c2rc = Coord.new(@row,rc+@col+@gbwid,@height-0, @width - rc-@gbwid)
343
+ a = 0
344
+ @c1rc = Coord.new(@row,@col, rc-@gbwid, @width)
345
+ @c2rc = Coord.new(@row+rc, @col, @height-rc-@gbwid, @width)
346
+ end
347
+ end
348
+ @graphic.attroff(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
349
+ @components.each { |e| e.repaint_all(true) }
350
+ $log.debug " XXX VIM REPAINT ALL "
351
+ # FIXME do this only once, or when major change happends, otherwise
352
+ # i cannot increase decrease size on user request.
353
+ recalculate_splits @_use_preferred_sizes if @recalculate_splits
354
+ # vimsplit often overwrites this while divider is being moved so we must
355
+ # again call it.
356
+ @vb.repaint if @vb
357
+ else
358
+ # only repaint those that are needing repaint
359
+ # 2010-09-22 18:09 its possible somenoe has updated an internal
360
+ # component, but this container does not know. So we've got to call
361
+ # repaint on all components, only those which are changed will
362
+ # actually be repainted
363
+ @components.each { |e| e.repaint }
364
+ end # if repaint_required
365
+ # NOTE: at present one cannot change from flow to stack inside a pane
366
+
367
+ @repaint_required = false
368
+ end
369
+ def v?
370
+ @orientation == :VERTICAL_SPLIT
371
+ end
372
+ def h?
373
+ !@orientation == :VERTICAL_SPLIT
374
+ end
375
+ # convert weight to height and length
376
+ # we should only do this once, or if major change
377
+ # otherwise changes that user may have effected in size will be lost
378
+ # NOTE: this resets all components to preferred weights (given when component was added.
379
+ # If user has resized components
380
+ # then those changes in size will be lost.
381
+ def reset_to_preferred_size
382
+ recalculate_splits use_preferred_sizes=true
383
+ end
384
+ def recalculate_splits use_preferred_sizes=false
385
+ # i've made it true so that moving the divider can recalculate all, otherwise
386
+ # testvimsplit was failing to recalc 2 lists. 2011-12-29
387
+ use_preferred_sizes=true
388
+ @recalculate_splits = false
389
+ [@c1,@c2].each_with_index do |c,i|
390
+ rca = @c1rc
391
+ if i == 1
392
+ #$log.debug " XXX VIM moving to second"
393
+ rca = @c2rc
394
+ end
395
+ totalw = 0 # accumulative weight
396
+ totalwd = 0 # accumulative weight for width (in case someone switches)
397
+ totalht = 0 # accumulative weight for height (in case someone switches)
398
+ sz = c.size
399
+ auto = 1.0/sz # even this is wrong since we have stacks and flows mixed
400
+ # calculate a more accurate auto than just an average
401
+ # SHIT this won't work since c contains flow and stacks, all objects
402
+ #used = 0.0
403
+ #usedct = 0
404
+ #c.each {|e| info = @ch[e]; wt = info.weight;
405
+ #if wt && wt != :AUTO
406
+ #used += wt
407
+ #usedct += 1
408
+ #end
409
+ #}
410
+ #if usedct > 0
411
+ #$log.debug "XXX: COMES HERE 1.0 - #{used} / #{sz} - #{usedct} "
412
+ #auto = (1.0 - used) / (sz - usedct)
413
+ #end
414
+ frem = srem = 0
415
+ c.each do |e|
416
+ r = rca.row
417
+ c = rca.col
418
+ info = @ch[e]
419
+ type = info.type
420
+ wt = info.weight
421
+ wt = auto if wt == :AUTO
422
+ e.row = r
423
+ if e.row <= @row
424
+ # TODO do something here !!
425
+ $log.warn "XXX: WARN VIMPSPLIT row #{e.row} going less than #{@row} "
426
+ end
427
+ e.col = c
428
+ if type == :STACK
429
+ # store actual weight that was calculated, so if user reduces or increases
430
+ # we can use this, although ... we have no method that uses actual weights
431
+ # NOTE: If calling program increases one comp's weight, will have to reduce other.
432
+ info.act_weight = wt
433
+
434
+ e.width = (rca.w * (1 - totalwd)).to_i
435
+ # recaclulate height only in this case, otherwise we will overwrite changes
436
+ # made by user
437
+ if use_preferred_sizes
438
+ if wt != 0
439
+ if wt
440
+ m = rca.h * wt
441
+ mf = m.floor
442
+ srem += (m - mf)
443
+ if srem >= 1
444
+ mf += 1
445
+ srem = 0
446
+ end
447
+ e.height = mf #((rca.h * wt).to_i)
448
+ else
449
+ a = 0
450
+ a = 1 if @suppress_borders
451
+ e.height = rca.h - rca.row + a # take exactly rows left
452
+ end
453
+ # else use its own height
454
+ end
455
+ end
456
+ $log.warn "XXX: WARN VIMSPLIT height = 0 " if e.height == 0
457
+ rca.row += e.height
458
+ totalht += wt if wt
459
+ else # FLOW
460
+ # TODO THIS PART AS PER ABOVE CASE , TO TEST
461
+ # this is a horizontal split or flow
462
+ info.act_weight = wt
463
+ #e.height = rca.h
464
+ e.height = (rca.h * (1- totalht)).to_i
465
+ if use_preferred_sizes
466
+ if wt != 0
467
+ if wt
468
+ m = rca.w * wt
469
+ mf = m.floor
470
+ frem += (m - mf)
471
+ if frem >= 1
472
+ mf += 1
473
+ frem = 0
474
+ end
475
+ e.width = mf # ((rca.w * wt).to_i)
476
+ else
477
+ a = 0
478
+ a = 1 if @suppress_borders
479
+ e.width = rca.w - rca.col + a # take exactly rows left
480
+ end
481
+ end
482
+ end
483
+ rca.col += e.width
484
+ totalwd += wt if wt
485
+ end
486
+ e.set_buffering(:target_window => @target_window || @form.window, :bottom => e.height-1, :right => e.width-1, :form => @form ) # removed on 2011-09-29
487
+ #$log.debug " XXXXX VIMS R #{@row} : #{e.row} C #{e.col} H #{e.height} W #{e.width} "
488
+ e.repaint
489
+ e._object_created = true # added 2010-09-16 13:02 now prop handlers can be fired
490
+ end
491
+ end
492
+ @_use_preferred_sizes = false
493
+ end
494
+
495
+ private
496
+ def print_borders
497
+ width = @width
498
+ height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
499
+ window = @graphic # 2010-01-04 12:37 BUFFERED
500
+ startcol = @col
501
+ startrow = @row
502
+ @color_pair = get_color($datacolor)
503
+ #$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
504
+ window.print_border startrow, startcol, height, width, @color_pair, @attr
505
+ print_title
506
+ end
507
+ def print_title
508
+ @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
509
+ end
510
+
511
+ public
512
+ # called by parent or form, otherwise its private
513
+ def handle_key ch
514
+ $log.debug " VIMSPLIT handle_key #{ch} "
515
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
516
+ if ch == KEY_TAB
517
+ #$log.debug " GOTO NEXT"
518
+ return goto_next_component
519
+ elsif ch == KEY_BTAB
520
+ return goto_prev_component
521
+ end
522
+ comp = @current_component
523
+ $log.debug " VIMSPL handle_k #{ch}: #{comp}"
524
+ if comp
525
+ ret = comp.handle_key(ch)
526
+ if ret != :UNHANDLED
527
+ comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
528
+ # in repaint of this.
529
+ return ret
530
+ end
531
+ end
532
+ $log.debug "XXX VIM key unhandled by comp #{comp.name} "
533
+ case ch
534
+ when ?\C-c.getbyte(0)
535
+ $multiplier = 0
536
+ return 0
537
+ when ?0.getbyte(0)..?9.getbyte(0)
538
+ $log.debug " VIM coming here to set multiplier #{$multiplier} "
539
+ $multiplier *= 10 ; $multiplier += (ch-48)
540
+ return 0
541
+ end
542
+ ret = process_key ch, self
543
+ # allow user to map left and right if he wants
544
+ if ret == :UNHANDLED
545
+ case ch
546
+ when KEY_UP
547
+ # form will pick this up and do needful
548
+ return goto_prev_component #unless on_first_component?
549
+ when KEY_LEFT
550
+ # if i don't check for first component, key will go back to form,
551
+ # but not be processes. so focussed remain here, but be false.
552
+ # In case of returnign an unhandled TAB, on_leave will happen and cursor will move to
553
+ # previous component outside of this.
554
+ return goto_prev_component unless on_first_component?
555
+ when KEY_RIGHT
556
+ return goto_next_component #unless on_last_component?
557
+ when KEY_DOWN
558
+ return goto_next_component #unless on_last_component?
559
+ else
560
+ return :UNHANDLED
561
+ end
562
+ end
563
+
564
+ $multiplier = 0
565
+ return 0
566
+ end
567
+ # private
568
+ def on_enter
569
+ # TODO if BTAB the last comp
570
+ if $current_key == KEY_BTAB
571
+ # FIXME last is not focusable, then ??
572
+ @current_component = @components.last
573
+ else
574
+ @current_component = @components.first
575
+ end
576
+ $log.debug " VIM came to on_enter #{@current_component} "
577
+ set_form_row
578
+ end
579
+ def on_leave
580
+ super
581
+ end
582
+ def goto_next_component
583
+ if @current_component != nil
584
+ leave_current_component
585
+ if on_last_component?
586
+ return :UNHANDLED
587
+ end
588
+ @current_index = @components.index(@current_component)
589
+ index = @current_index + 1
590
+ index.upto(@components.length-1) do |i|
591
+ f = @components[i]
592
+ if f.focusable
593
+ @current_index = i
594
+ @current_component = f
595
+ return set_form_row
596
+ end
597
+ end
598
+ end
599
+ return :UNHANDLED
600
+ end
601
+ def goto_prev_component
602
+ if @current_component != nil
603
+ leave_current_component
604
+ if on_first_component?
605
+ return :UNHANDLED
606
+ end
607
+ @current_index = @components.index(@current_component)
608
+ index = @current_index -= 1
609
+ index.downto(0) do |i|
610
+ f = @components[i]
611
+ if f.focusable
612
+ @current_index = i
613
+ @current_component = f
614
+ return set_form_row
615
+ end
616
+ end
617
+ end
618
+ return :UNHANDLED
619
+ end
620
+ # private
621
+ def set_form_row
622
+ #return :UNHANDLED if @current_component.nil?
623
+ $log.debug " VIM on enter sfr #{@current_component} "
624
+ @current_component.on_enter
625
+ @current_component.set_form_col # XXX
626
+ @current_component.repaint
627
+ # XXX compo should do set_form_row and col if it has that
628
+ end
629
+ # private
630
+ def set_form_col
631
+ return if @current_component.nil?
632
+ $log.debug " #{@name} set_form_col calling sfc for #{@current_component.name} "
633
+ @current_component.set_form_col
634
+ end
635
+ # leave the component we are on.
636
+ # This should be followed by all containers, so that the on_leave action
637
+ # of earlier comp can be displayed, such as dimming components selections
638
+ def leave_current_component
639
+ @current_component.on_leave
640
+ # NOTE this is required, since repaint will just not happen otherwise
641
+ # Some components are erroneously repainting all, after setting this to true so it is
642
+ # working there.
643
+ @current_component.repaint_required true
644
+ $log.debug " after on_leave VIMS XXX #{@current_component.focussed} #{@current_component.name}"
645
+ @current_component.repaint
646
+ end
647
+
648
+ # is focus on first component
649
+ def on_first_component?
650
+ @current_component == @components.first
651
+ end
652
+ # is focus on last component
653
+ def on_last_component?
654
+ @current_component == @components.last
655
+ end
656
+ def goto_other_split
657
+ c = components_for(other_split)
658
+ leave_current_component
659
+ @current_component = c.first
660
+ set_form_row
661
+ end
662
+ # set focus on given component
663
+ # Sometimes you have the handle to component, and you want to move focus to it
664
+ def goto_component comp
665
+ return if comp == @current_component
666
+ leave_current_component
667
+ @current_component = comp
668
+ set_form_row
669
+ end
670
+ # decrease the weight of the split
671
+ def decrease_weight
672
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
673
+ weight(weight - 0.05*_multiplier)
674
+ end
675
+ # increase the weight of the split
676
+ def increase_weight
677
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
678
+ weight(weight + 0.05*_multiplier)
679
+ end
680
+ # FIXME - i can only reduce if i've increased
681
+ def decrease_current_component
682
+ info = split_info_for
683
+ #info.add_weight = 0 if info.add_weight.nil?
684
+ #if info.add_weight > 0.0
685
+ #info.add_weight = info.add_weight - 0.05
686
+ #end
687
+ e = ResizeEvent.new @current_component, :DECREASE
688
+ fire_handler :COMPONENT_RESIZE_EVENT, e
689
+ #@repaint_required = true
690
+ end
691
+ # fires handler to request app to resize component
692
+ # @param [:INCREASE, :DECREASE]
693
+ # @param [:HEIGHT, :WIDTH]
694
+ def resize_component incdec, hw #:nodoc:
695
+ type = incdec.to_s + '_' + hw.to_s
696
+ #info = split_info_for
697
+ #info.add_weight = 0 if info.add_weight.nil?
698
+ e = ResizeEvent.new @current_component, type.to_sym
699
+ fire_handler :COMPONENT_RESIZE_EVENT, e
700
+ #@repaint_required = true
701
+ end
702
+ # fires handler to request app to resize current component
703
+ #
704
+ def decrease_height
705
+ resize_component :DECREASE, :HEIGHT
706
+ end
707
+ # fires handler to request app to resize current component
708
+ def decrease_width
709
+ resize_component :DECREASE, :WIDTH
710
+ end
711
+ # fires handler to request app to resize current component
712
+ def increase_width
713
+ resize_component :INCREASE, :WIDTH
714
+ end
715
+ # fires handler to request app to resize current component
716
+ def increase_height
717
+ resize_component :INCREASE, :HEIGHT
718
+ end
719
+ def increase_current_component
720
+ info = split_info_for
721
+ #info.add_weight = 0 if info.add_weight.nil?
722
+ #if info.add_weight < 0.3
723
+ #info.add_weight = info.add_weight + 0.05
724
+ #end
725
+ e = ResizeEvent.new @current_component, :INCREASE
726
+ fire_handler :COMPONENT_RESIZE_EVENT, e
727
+ #@repaint_required = true
728
+ end
729
+ # calling application need to handle this, since it knows
730
+ # how many windows its has and what the user would mean
731
+ def expand # maximize
732
+ e = ResizeEvent.new @current_component, :EXPAND
733
+ fire_handler :COMPONENT_RESIZE_EVENT, e
734
+ end
735
+ # calling application need to handle this, since it knows
736
+ # how many windows its has and what the user would mean
737
+ def unexpand
738
+ e = ResizeEvent.new @current_component, :UNEXPAND
739
+ fire_handler :COMPONENT_RESIZE_EVENT, e
740
+ end
741
+
742
+ private
743
+ def _create_divider
744
+ return if @vb
745
+ roffset = 1
746
+ loffset = 2
747
+ if @suppress_borders
748
+ loffset = roffset = 0
749
+ end
750
+ rc = @rc
751
+ if v?
752
+ @vb = Divider.new nil, :row => @row+roffset, :col => rc+@col-1, :length => @height-loffset, :side => :right
753
+ else
754
+ @vb = Divider.new nil, :row => @row+rc-1, :col => @col+1, :length => @width-loffset, :side => :bottom
755
+ end
756
+ @vb.focusable(false)
757
+ RubyCurses::FocusManager.add @vb
758
+ @vb.parent_component = self
759
+ @components << @vb
760
+ @vb.set_buffering(:target_window => @target_window || @form.window, :form => @form ) # removed on 2011-09-29
761
+ @vb.bind :DRAG_EVENT do |ev|
762
+ if v?
763
+ case ev.type
764
+ when KEY_RIGHT
765
+ $log.debug "VIMSPLIT RIGHT "
766
+ if @rc < @width - 3
767
+ @recalculate_splits = true
768
+ @rc += 1
769
+ @repaint_required = true # WHY ! Did prop handler not fire ?
770
+ end
771
+ when KEY_LEFT
772
+ if @rc > 3
773
+ @recalculate_splits = true
774
+ @repaint_required = true
775
+ @rc -= 1
776
+ end
777
+ end
778
+ else
779
+ # horizontal
780
+ case ev.type
781
+ when KEY_DOWN
782
+ if @rc < @height - 3
783
+ @recalculate_splits = true
784
+ @rc += 1
785
+ @repaint_required = true # WHY ! Did prop handler not fire ?
786
+ end
787
+ when KEY_UP
788
+ if @rc > 3
789
+ @recalculate_splits = true
790
+ @repaint_required = true
791
+ @rc -= 1
792
+ end
793
+ end
794
+ end # v?
795
+ end
796
+ end
797
+
798
+ # ADD HERE ABOVe
799
+ end # class
800
+ end # module