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,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