rbcurse 1.1.5 → 1.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/CHANGELOG +45 -0
  2. data/Makefile +1 -1
  3. data/Manifest.txt +91 -0
  4. data/NOTES +349 -2
  5. data/README.markdown +12 -0
  6. data/VERSION +1 -1
  7. data/examples/abasiclist.rb +25 -0
  8. data/examples/alpmenu.rb +42 -0
  9. data/examples/app.rb +883 -0
  10. data/examples/appcombo.rb +17 -0
  11. data/examples/appdirtree.rb +73 -0
  12. data/examples/appemail.rb +164 -0
  13. data/examples/appemaillb.rb +308 -0
  14. data/examples/appgcompose.rb +303 -0
  15. data/examples/appgmail.rb +951 -0
  16. data/examples/atree.rb +56 -0
  17. data/examples/dirtree.rb +78 -0
  18. data/examples/focusmanager.rb +31 -0
  19. data/examples/imap.rb +48 -0
  20. data/examples/menu1.rb +79 -0
  21. data/examples/multispl.rb +86 -0
  22. data/examples/rfe.rb +3 -4
  23. data/examples/rmail.rb +188 -0
  24. data/examples/s.rb +10 -0
  25. data/examples/scrollbar.rb +104 -0
  26. data/examples/splitp.rb +56 -0
  27. data/examples/table1.rb +30 -0
  28. data/examples/term.rb +48 -0
  29. data/examples/term2.rb +54 -0
  30. data/examples/test1.rb +4 -2
  31. data/examples/test2.rb +9 -9
  32. data/examples/testapp.rb +44 -0
  33. data/examples/testapp2.rb +51 -0
  34. data/examples/testcombo.rb +2 -2
  35. data/examples/testgmail.rb +46 -0
  36. data/examples/testlistbox.rb +0 -1
  37. data/examples/testmultispl.rb +199 -0
  38. data/examples/testree.rb +127 -0
  39. data/examples/testscroller.rb +0 -1
  40. data/examples/testscrolllb.rb +1 -1
  41. data/examples/testscrollp.rb +2 -1
  42. data/examples/testscrollta.rb +1 -1
  43. data/examples/testscrolltable.rb +1 -2
  44. data/examples/testsplit.rb +1 -1
  45. data/examples/testsplit2.rb +1 -1
  46. data/examples/testsplit3.rb +1 -1
  47. data/examples/testsplit3_1.rb +1 -1
  48. data/examples/testsplit3a.rb +1 -1
  49. data/examples/testsplit3b.rb +1 -1
  50. data/examples/testsplitta.rb +1 -1
  51. data/examples/testsplittv.rb +1 -1
  52. data/examples/testsplittvv.rb +1 -1
  53. data/examples/testtodo.rb +491 -488
  54. data/examples/testvimsplit.rb +111 -0
  55. data/examples/todo.db +0 -0
  56. data/examples/todocsv.csv +28 -0
  57. data/examples/viewtodo.rb +408 -403
  58. data/lib/rbcurse/action.rb +1 -0
  59. data/lib/rbcurse/app.rb +1294 -0
  60. data/lib/rbcurse/applicationheader.rb +7 -2
  61. data/lib/rbcurse/checkboxcellrenderer.rb +0 -12
  62. data/lib/rbcurse/colormap.rb +34 -8
  63. data/lib/rbcurse/comboboxcellrenderer.rb +0 -11
  64. data/lib/rbcurse/defaultlistselectionmodel.rb +23 -7
  65. data/lib/rbcurse/extras/bottomline.rb +1681 -0
  66. data/lib/rbcurse/extras/directorylist.rb +445 -0
  67. data/lib/rbcurse/extras/directorytree.rb +69 -0
  68. data/lib/rbcurse/extras/divider.rb +310 -0
  69. data/lib/rbcurse/extras/focusmanager.rb +31 -0
  70. data/lib/rbcurse/extras/listselectable.rb +222 -0
  71. data/lib/rbcurse/extras/masterdetail.rb +164 -0
  72. data/lib/rbcurse/extras/menutree.rb +63 -0
  73. data/lib/rbcurse/extras/rlink.rb +27 -0
  74. data/lib/rbcurse/extras/rmenulink.rb +21 -0
  75. data/lib/rbcurse/extras/scrollbar.rb +134 -0
  76. data/lib/rbcurse/extras/stdscrwindow.rb +247 -0
  77. data/lib/rbcurse/extras/tabular.rb +258 -0
  78. data/lib/rbcurse/extras/tabularwidget.rb +1070 -0
  79. data/lib/rbcurse/extras/viewer.rb +106 -0
  80. data/lib/rbcurse/io.rb +137 -80
  81. data/lib/rbcurse/keylabelprinter.rb +4 -0
  82. data/lib/rbcurse/listcellrenderer.rb +91 -59
  83. data/lib/rbcurse/listscrollable.rb +93 -95
  84. data/lib/rbcurse/listselectable.rb +60 -7
  85. data/lib/rbcurse/ractionevent.rb +67 -0
  86. data/lib/rbcurse/rbasiclistbox.rb +688 -0
  87. data/lib/rbcurse/rcombo.rb +5 -5
  88. data/lib/rbcurse/rcommandwindow.rb +555 -0
  89. data/lib/rbcurse/rinputdataevent.rb +12 -0
  90. data/lib/rbcurse/rlistbox.rb +305 -124
  91. data/lib/rbcurse/rmenu.rb +99 -46
  92. data/lib/rbcurse/rmessagebox.rb +13 -6
  93. data/lib/rbcurse/rmulticontainer.rb +54 -93
  94. data/lib/rbcurse/rmultisplit.rb +731 -0
  95. data/lib/rbcurse/rmultitextview.rb +3 -2
  96. data/lib/rbcurse/rpopupmenu.rb +0 -1
  97. data/lib/rbcurse/rprogress.rb +117 -0
  98. data/lib/rbcurse/rscrollpane.rb +2 -1
  99. data/lib/rbcurse/rsplitpane.rb +94 -20
  100. data/lib/rbcurse/rsplitpane2.rb +1009 -0
  101. data/lib/rbcurse/rtabbedpane.rb +3 -2
  102. data/lib/rbcurse/rtabbedwindow.rb +0 -1
  103. data/lib/rbcurse/rtable.rb +92 -64
  104. data/lib/rbcurse/rtextarea.rb +91 -57
  105. data/lib/rbcurse/rtextview.rb +223 -70
  106. data/lib/rbcurse/rtree.rb +723 -0
  107. data/lib/rbcurse/rviewport.rb +2 -1
  108. data/lib/rbcurse/rvimsplit.rb +768 -0
  109. data/lib/rbcurse/rwidget.rb +524 -325
  110. data/lib/rbcurse/table/tablecellrenderer.rb +1 -1
  111. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -1
  112. data/lib/rbcurse/tree/treecellrenderer.rb +137 -0
  113. data/lib/rbcurse/tree/treemodel.rb +428 -0
  114. data/lib/rbcurse/vieditable.rb +14 -13
  115. data/lib/ver/ncurses.rb +6 -0
  116. data/lib/ver/window.rb +67 -32
  117. metadata +99 -23
  118. data/bin/rbcurse +0 -0
  119. data/examples/rvimsplit.rb +0 -376
  120. data/examples/todo.rb +0 -1
  121. data/lib/rbcurse/rform.rb +0 -845
  122. data/lib/rbcurse/selectable.rb +0 -94
  123. data/rbcurse.gemspec +0 -188
@@ -8,9 +8,9 @@
8
8
  Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
9
9
 
10
10
  =end
11
- require 'rubygems'
12
- require 'ncurses'
13
- require 'logger'
11
+ #require 'rubygems'
12
+ #require 'ncurses'
13
+ #require 'logger'
14
14
  require 'rbcurse'
15
15
 
16
16
  include Ncurses
@@ -39,8 +39,8 @@ module RubyCurses
39
39
  super
40
40
  bind_key(?\M-:, :buffer_menu)
41
41
  # bind_key([?\C-x, ?f], :file_edit)
42
- bind_key([?\C-x, ?k], :buffer_delete)
43
- bind_key([?\C-x, ?\C-b], :buffers_list)
42
+ bind_key([?\C-x, ?k], :delete_component)
43
+ bind_key([?\C-x, ?\C-b], :list_components)
44
44
  # easily cycle using p. n is used for next search.
45
45
  #bind_key(?p, :buffer_previous)
46
46
  @suppress_borders = false
@@ -49,12 +49,12 @@ module RubyCurses
49
49
  end
50
50
  ## returns current buffer
51
51
  # @return [RBuffer] current buffer
52
- def current_buffer
52
+ def current_component
53
53
  @bmanager.current
54
54
  end
55
55
  ##
56
56
  # multi-container
57
- def handle_key ch
57
+ def handle_key ch #:nodoc:
58
58
  $log.debug " MULTI handlekey #{ch}, #{@current_component} "
59
59
  ret = :UNHANDLED
60
60
  return :UNHANDLED unless @current_component
@@ -66,8 +66,9 @@ module RubyCurses
66
66
  ret = process_key ch, self
67
67
  $log.debug " MULTI = process_key returned #{ret} "
68
68
  rescue => err
69
- $error_message = err
70
- @form.window.print_error_message
69
+ # $error_message = err # changed 2010 dts
70
+ $error_message.value = err
71
+ #@form.window.print_error_message PLEASE CREATE LABEL
71
72
  $log.error " Multicomponent process_key #{err} "
72
73
  $log.debug(err.backtrace.join("\n"))
73
74
  end
@@ -82,13 +83,13 @@ module RubyCurses
82
83
  $log.debug " MULTI REPAINT "
83
84
  ret = @current_component.repaint
84
85
  end
85
- def print_border
86
+ def print_border #:nodoc:
86
87
  $log.debug " #{@name} print_borders, #{@graphic.name} "
87
88
  color = $datacolor
88
89
  @graphic.print_border_only @row, @col, @height-1, @width, color #, Ncurses::A_REVERSE
89
90
  print_title
90
91
  end
91
- def print_title
92
+ def print_title #:nodoc:
92
93
  $log.debug " print_title #{@row}, #{@col}, #{@width} "
93
94
  @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, $datacolor, @title_attrib) unless @title.nil?
94
95
  end
@@ -96,14 +97,14 @@ module RubyCurses
96
97
  # can use this for next, prev, first, last, new, delete, overwrite etc
97
98
  def buffer_menu
98
99
  menu = PromptMenu.new self
99
- menu.add(menu.create_mitem( 'l', "list buffers", "list buffers ", :buffers_list ))
100
+ menu.add(menu.create_mitem( 'l', "list buffers", "list buffers ", :list_components ))
100
101
  item = menu.create_mitem( 'b', "Buffer Options", "Buffer Options" )
101
102
  menu1 = PromptMenu.new( self, "Buffer Options")
102
- menu1.add(menu1.create_mitem( 'n', "Next", "Switched to next buffer", :buffer_next ))
103
- menu1.add(menu1.create_mitem( 'p', "Prev", "Switched to previous buffer", :buffer_previous ))
104
- menu1.add(menu1.create_mitem( 'f', "First", "Switched to first buffer", :buffer_first ))
105
- menu1.add(menu1.create_mitem( 'l', "Last", "Switched to last buffer", :buffer_last ))
106
- menu1.add(menu1.create_mitem( 'd', "Delete", "Deleted buffer", :buffer_delete ))
103
+ menu1.add(menu1.create_mitem( 'n', "Next", "Switched to next buffer", :goto_next_component ))
104
+ menu1.add(menu1.create_mitem( 'p', "Prev", "Switched to previous buffer", :goto_prev_component ))
105
+ menu1.add(menu1.create_mitem( 'f', "First", "Switched to first buffer", :goto_first_component ))
106
+ menu1.add(menu1.create_mitem( 'l', "Last", "Switched to last buffer", :goto_last_component ))
107
+ menu1.add(menu1.create_mitem( 'd', "Delete", "Deleted buffer", :delete_component ))
107
108
  item.action = menu1
108
109
  menu.add(item)
109
110
  # how do i know what's available. the application or window should know where to place
@@ -113,90 +114,50 @@ module RubyCurses
113
114
  #%w[next previous first last].each do |pos|
114
115
  #eval(
115
116
  #"def _buffer_#{pos}
116
- #@current_buffer = @bmanager.#{pos}
117
+ #@current_component = @bmanager.#{pos}
117
118
  #set_current_buffer
118
119
  #end"
119
120
  #)
120
121
  #end
121
122
 
122
- def buffer_next
123
+ def goto_next_component
123
124
  perror "No other buffer" and return if @bmanager.size < 2
124
125
 
125
- @current_buffer = @bmanager.next
126
- set_current_buffer
127
- end
128
- def buffer_previous
129
- perror "No other buffer" and return if @bmanager.size < 2
130
-
131
- @current_buffer = @bmanager.previous
132
- $log.debug " buffer_prev got #{@current_buffer} "
133
- set_current_buffer
134
- end
135
- def buffer_first
136
- @current_buffer = @bmanager.first
137
- $log.debug " buffer_first got #{@current_buffer} "
138
- set_current_buffer
139
- end
140
- def buffer_last
141
- @current_buffer = @bmanager.last
142
- $log.debug " buffer_last got #{@current_buffer} "
143
- set_current_buffer
144
- end
145
- def buffer_delete
146
- if @bmanager.size > 1
147
- @bmanager.delete_at
148
- @current_buffer = @bmanager.previous
149
- set_current_buffer
150
- else
151
- perror "Only one buffer. Cannot delete."
152
- end
126
+ @current_component = @bmanager.next
127
+ set_current_component
153
128
  end
154
129
 
155
- #%w[next previous first last].each do |pos|
156
- #eval(
157
- #"def _buffer_#{pos}
158
- #@current_buffer = @bmanager.#{pos}
159
- #set_current_buffer
160
- #end"
161
- #)
162
- #end
163
-
164
- def buffer_next
130
+ def goto_prev_component
165
131
  perror "No other buffer" and return if @bmanager.size < 2
166
132
 
167
- @current_buffer = @bmanager.next
168
- set_current_buffer
133
+ @current_component = @bmanager.previous
134
+ $log.debug " buffer_prev got #{@current_component} "
135
+ set_current_component
169
136
  end
170
- def buffer_previous
171
- perror "No other buffer" and return if @bmanager.size < 2
172
-
173
- @current_buffer = @bmanager.previous
174
- $log.debug " buffer_prev got #{@current_buffer} "
175
- set_current_buffer
137
+ def goto_first_component
138
+ @current_component = @bmanager.first
139
+ $log.debug " buffer_first got #{@current_component} "
140
+ set_current_component
176
141
  end
177
- def buffer_first
178
- @current_buffer = @bmanager.first
179
- $log.debug " buffer_first got #{@current_buffer} "
180
- set_current_buffer
142
+ def goto_last_component
143
+ @current_component = @bmanager.last
144
+ $log.debug " buffer_last got #{@current_component} "
145
+ set_current_component
181
146
  end
182
- def buffer_last
183
- @current_buffer = @bmanager.last
184
- $log.debug " buffer_last got #{@current_buffer} "
185
- set_current_buffer
186
- end
187
- def buffer_delete
147
+ def delete_component
188
148
  if @bmanager.size > 1
189
149
  @bmanager.delete_at
190
- @current_buffer = @bmanager.previous
191
- set_current_buffer
150
+ @current_component = @bmanager.previous
151
+ set_current_component
192
152
  else
193
153
  perror "Only one buffer. Cannot delete."
194
154
  end
195
155
  end
196
- def buffer_at index
197
- @current_buffer = @bmanager.element_at index
198
- $log.debug " buffer_last got #{@current_buffer} "
199
- set_current_buffer
156
+
157
+ def component_at index
158
+ @current_component = @bmanager.element_at index
159
+ $log.debug " buffer_last got #{@current_component} "
160
+ set_current_component
200
161
  end
201
162
  ##
202
163
  # Add a component with a title
@@ -209,23 +170,23 @@ module RubyCurses
209
170
  component.height = @height-2
210
171
  component.form = @form
211
172
  component.override_graphic(@graphic)
212
- @current_buffer = @bmanager.add component, title
213
- set_current_buffer
214
- $log.debug " ADD got cb : #{@current_buffer} "
173
+ @current_component = @bmanager.add component, title
174
+ set_current_component
175
+ $log.debug " ADD got cb : #{@current_component} "
215
176
  end
216
- def set_current_buffer
217
- @current_component = @current_buffer.component
218
- @current_title = @current_buffer.title
177
+ def set_current_component
178
+ @current_component = @current_component.component
179
+ @current_title = @current_component.title
219
180
  @current_component.repaint_all true
220
181
  end
221
182
  def perror errmess=$error_message
222
183
  @form.window.print_error_message errmess
223
184
  end
224
- def buffers_list
185
+ def list_components
225
186
  $log.debug " TODO buffers_list: #{@bmanager.size} "
226
187
  menu = PromptMenu.new self
227
188
  @bmanager.each_with_index{ |b, ix|
228
- aproc = Proc.new { buffer_at(ix) }
189
+ aproc = Proc.new { component_at(ix) }
229
190
  name = b.title
230
191
  num = ix + 1
231
192
  menu.add(menu.create_mitem( num.to_s, name, "Switched to buffer #{ix}", aproc ))
@@ -237,17 +198,17 @@ module RubyCurses
237
198
  def on_enter
238
199
  set_form_row
239
200
  end
240
- def set_form_row
201
+ def set_form_row #:nodoc:
241
202
  if !@current_component.nil?
242
- $log.debug " #{@name} set_form_row calling sfr for #{@current_component.name} "
203
+ #$log.debug " #{@name} set_form_row calling sfr for #{@current_component.name} "
243
204
  @current_component.set_form_row
244
205
  @current_component.set_form_col
245
206
  end
246
207
  end
247
- end # class multitextview
208
+ end # class multicontainer
248
209
  ##
249
210
  # Handles multiple buffers, navigation, maintenance etc
250
- # Instantiated at startup of MultiTextView
211
+ # Instantiated at startup of
251
212
  #
252
213
  class BufferManager
253
214
  include Enumerable
@@ -0,0 +1,731 @@
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
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
+ comp.should_create_buffer = @_child_buffering
168
+ # next 2 not sure, is it for first only
169
+ comp.ext_row_offset += @ext_row_offset + @row #- @subform1.window.top #0# screen_row
170
+ comp.ext_col_offset += @ext_col_offset + @col #-@subform1.window.left # 0# screen_col
171
+ # dang ! this can go out of bounds ! XXX tab goes out
172
+ index = @components.size - 1 # expected as base 0 in compute
173
+ #index = @max_visible - 1 if index > @max_visible - 1
174
+ # all this ado to prevent cursor going out in the first display
175
+ # when index exceeds visible, since compute now uses running balance
176
+ if index > @max_visible - 1
177
+ # we copy the coords of the previous one
178
+ prev = @components[index-1]
179
+ comp.row = prev.row
180
+ comp.col = prev.col
181
+ comp.width = prev.width
182
+ comp.height = prev.height
183
+ else
184
+ compute_component comp, index
185
+ end
186
+ comp.set_buffering(:target_window => @target_window || @form.window, :bottom => comp.height-1, :right => comp.width-1, :form => @form )
187
+ comp.set_buffering(:screen_top => @row, :screen_left => @col)
188
+ comp.min_height ||= 5
189
+ comp.min_width ||= 5
190
+ return self
191
+ end
192
+ alias :<< :add
193
+
194
+ def [](index)
195
+ raise "MultiSplit: Please add components first" unless @components
196
+ @components[index]
197
+ end
198
+ def size
199
+ @components.size
200
+ end
201
+ alias :length :size
202
+ ##
203
+ # compute component dimensions in one place
204
+ # @param [widget] a widget
205
+ # @param [Fixnum] offset in list of components
206
+ # XXX if called from outside balance can have last value !!!
207
+ # FIXME for last component, take as much as is left height or width
208
+ # otherwise odd figures will leave on row unoccupied
209
+ def compute_component comp, index
210
+ @balance ||= 0
211
+ if @orientation == :HORIZONTAL_SPLIT
212
+ # XXX NOT TESTED TODO
213
+ @comp_height = (@height / @split_count) - 0
214
+ @comp_width = @width
215
+ h = @comp_height
216
+ if @recalc_required
217
+ comp.height = h # listboxes etal calculate a height so that will stand !! XXX
218
+ else
219
+ comp.height ||= h # listboxes etal calculate a height so that will stand !! XXX
220
+ end
221
+ w = @comp_width
222
+ #r = @row + ( comp.height * index)
223
+ r = @row + @balance
224
+ if r > @row + @height
225
+ r = @row + @height
226
+ end
227
+ #alert "r #{@row} h #{@height} ::: comp row #{r} h #{h} bal:#{@balance} "
228
+ @balance += comp.height
229
+ c = @col
230
+ comp.width = w
231
+ comp.row = r
232
+ comp.col = c
233
+ else
234
+ @comp_height = @height
235
+ @comp_width = (@width / @split_count) - 0
236
+ h = @comp_height
237
+ w = @comp_width
238
+ if @recalc_required
239
+ comp.width = w
240
+ else
241
+ comp.width ||= w
242
+ end
243
+ #c = @col + ( w * index) # this makes them all equal
244
+ c = @col + @balance
245
+ if c > @col + @width
246
+ c = @col + @width
247
+ end
248
+ $log.debug "XXXX index #{index} , w #{comp.width} , c = #{c} , bal #{@balance} c+w:#{@col+@width} "
249
+ #if index < @max_visible - 1
250
+ @balance += comp.width
251
+ #end
252
+ r = @row
253
+ comp.height = h
254
+ comp.row = r
255
+ comp.col = c
256
+ #$log.debug " XXXX index r c #{r} #{c} "
257
+ end
258
+ comp
259
+ end
260
+ def increase
261
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
262
+ delta = _multiplier
263
+ c = @current_component
264
+ n = get_next_component
265
+ n = get_prev_component unless n
266
+ return unless n
267
+ if @orientation == :HORIZONTAL_SPLIT
268
+ if n.height > 3 + delta
269
+ c.height += delta
270
+ n.height -= delta
271
+ end
272
+ else
273
+ if n.width > 3 + delta
274
+ c.width += delta
275
+ n.width -= delta
276
+ end
277
+ end
278
+ @repaint_required = true
279
+ self
280
+ end
281
+ # decrease size of current component.
282
+ # if last one, then border printing exceeds right boundary. values look okay
283
+ # dunno why XXX FIXME
284
+ def decrease
285
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
286
+ delta = _multiplier
287
+ $log.debug "XXXX decrease got mult #{$_multiplier} "
288
+ c = @current_component
289
+ # if decreasing last component then increase previous
290
+ # otherwise always increase the next
291
+ n = get_next_component || get_prev_component
292
+ return unless n # if no other, don't allow
293
+ if @orientation == :HORIZONTAL_SPLIT
294
+ c.height -= delta
295
+ n.height += delta
296
+ # TODO
297
+ else
298
+ if c.width > 3 + delta
299
+ c.width -= delta
300
+ n.width += delta
301
+ end
302
+ end
303
+ @repaint_required = true
304
+ self
305
+ end
306
+ def same
307
+ @components.each do |comp|
308
+ comp.height = @comp_height
309
+ comp.width = @comp_width
310
+ end
311
+ @repaint_required = true
312
+ self
313
+ end
314
+ # @return [widget] next component or nil if no next
315
+ def get_next_component
316
+ return @components[@current_index+1]
317
+ end
318
+ # @return [widget] prev component or nil if no next
319
+ def get_prev_component
320
+ return nil if @current_index == 0
321
+ return @components[@current_index-1]
322
+ end
323
+ ##
324
+ #
325
+ # change height of splitpane
326
+ # @param val [int] new height of splitpane
327
+ # @return [int] old ht if nil passed
328
+ def height(*val)
329
+ return @height if val.empty?
330
+ oldvalue = @height || 0
331
+ super
332
+ @height = val[0]
333
+ return if @components.nil? || @components.empty?
334
+ delta = @height - oldvalue
335
+ @repaint_required = true
336
+ if !@cascade_boundary_changes.nil?
337
+ # must tell children if height changed which will happen in nested splitpanes
338
+ # must adjust to components own offsets too
339
+ if @orientation == :VERTICAL_SPLIT
340
+ @components.each do |e|
341
+ e.height += delta
342
+ e.set_buffering(:bottom => e.height-1)
343
+ end
344
+ else
345
+ e = @components.first
346
+ e.height += delta
347
+ e.set_buffering(:bottom => e.height-1)
348
+ end
349
+ end
350
+ end
351
+ ##
352
+ # change width of splitpane
353
+ # @param val [int, nil] new width of splitpane
354
+ # @return [int] old width if nil passed
355
+ # NOTE: if VERTICAL, then expand or contract only second
356
+ # If HORIZ then expand / contract both
357
+ # Actually this is very complicated since reducing should take into account min_width
358
+ def width(*val)
359
+ return @width if val.empty?
360
+ # must tell children if height changed which will happen in nested splitpanes
361
+ oldvalue = @width || 0
362
+ super
363
+ @width = val[0]
364
+ delta = @width - oldvalue
365
+ $log.debug " SPLP #{@name} width #{oldvalue}, #{@width}, #{delta} "
366
+ @repaint_required = true
367
+ if !@cascade_boundary_changes.nil?
368
+ # must adjust to components own offsets too
369
+ # NOTE: 2010-01-10 20:11 if we increase width by one, each time will both components get increased by one.
370
+ if @orientation == :HORIZONTAL_SPLIT
371
+ @components.each do |e|
372
+ e.width += delta
373
+ e.set_buffering(:right => e.width-1)
374
+ end
375
+ else
376
+ # any change in width must effect col of others too ! 2010-08-31 21:57 AUG2010
377
+ # which is why this should be done in repaint and not here
378
+ # ## next change should only happen if sc w < ...
379
+ # if @second_component.width < @width - (rc + @col_offset + @divider_offset + 1)
380
+ last = @components.last
381
+ last.width += delta
382
+ end
383
+ end
384
+ end
385
+ ##
386
+ # resets divider location based on preferred size of first component
387
+ # @return :ERROR if min sizes failed
388
+ # You may want to check for ERROR and if so, resize_weight to 0.50
389
+ def reset_to_preferred_sizes
390
+ alert "TODO THIS reset_to "
391
+ return if @components.nil?
392
+ @repaint_required = true
393
+ end
394
+ # recalculates components and calls repaint
395
+ def update_components # #:nodoc:
396
+ @balance = 0
397
+ @max_visible ||= @split_count
398
+ @_first_column_print ||= 0
399
+ @_last_column_print = @_first_column_print + @max_visible - 1
400
+ $log.debug " XXXX #{@_first_column_print} , last print #{@_last_column_print} "
401
+ @components.each_with_index do |comp,index|
402
+ next if index < @_first_column_print
403
+ break if index > @_last_column_print
404
+ compute_component comp, index
405
+ #comp.set_buffering(:target_window => @target_window || @form.window, :bottom => comp.height-1, :right => comp.width-1, :form => @form )
406
+ #comp.set_buffering(:target_window => @target_window || @form.window, :bottom => comp.height-1, :right => comp.width-1, :form => @form )
407
+ comp.set_buffering(:screen_top => comp.row, :screen_left => comp.col)
408
+ comp.repaint
409
+ end
410
+ #@balance = 0
411
+ @recalc_required = false
412
+ end
413
+ def repaint # multisplitpane #:nodoc:
414
+ if @graphic.nil?
415
+ @graphic = @target_window || @form.window
416
+ raise "graphic nil in rsplitpane #{@name} " unless @graphic
417
+ end
418
+
419
+ if @repaint_required
420
+ # repaint all ?
421
+ @components.each { |e| e.repaint_all(true) }
422
+ end
423
+ if @repaint_required
424
+ ## paint border and divider
425
+ $log.debug "MULTISPLP #{@name} repaint split H #{@height} W #{@width} "
426
+ bordercolor = @border_color || $datacolor
427
+ borderatt = @border_attrib || Ncurses::A_NORMAL
428
+ absrow = abscol = 0
429
+ if @use_absolute
430
+ absrow = @row
431
+ abscol = @col
432
+ end
433
+ if @use_absolute
434
+ $log.debug " #{@graphic} #{name} calling print_border #{@row} #{@col} #{@height}-1 #{@width}-1 "
435
+ @graphic.print_border(@row, @col, @height-1, @width-1, bordercolor, borderatt) if !@suppress_borders
436
+ else
437
+ $log.debug " #{@graphic} calling print_border 0,0"
438
+ @graphic.print_border(0, 0, @height-1, @width-1, bordercolor, borderatt) unless @suppress_borders
439
+ end
440
+ rc = -1
441
+
442
+ @graphic.attron(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
443
+ # 2010-02-14 18:23 - non buffered, have to make relative coords into absolute
444
+ #+ by adding row and col
445
+ count = @components.nil? ? @split_count : @components.size
446
+ count = @components.empty? ? @split_count : @components.size
447
+ if @orientation == :VERTICAL_SPLIT
448
+ @comp_height ||= @height
449
+ @comp_width ||= (@width / @split_count) - 0
450
+ $log.debug "SPLP #{@name} prtingign split vline divider 1, rc: #{rc}, h:#{@height} - 2 "
451
+ #@graphic.mvvline(absrow+1, rc+abscol, 0, @height-2)
452
+ # (1...count).each(){|i| @graphic.mvvline(absrow+1, (i*@comp_width)+abscol, 0, @height-2) }
453
+ # TODO put vlines here
454
+ # commented off since it uses fixed values and we are increaseing and dec
455
+
456
+ else
457
+ @comp_height ||= (@height / @split_count) - 1
458
+ @comp_width ||= @width
459
+ #$log.debug "SPLP #{@name} prtingign split hline divider rc: #{rc} , 1 , w:#{@width} - 2"
460
+ #@graphic.mvhline(rc+absrow, abscol+1, 0, @width-2)
461
+ # XXX in next line -2 at end was causing an overlap into final border col,
462
+ # this need correction in splitpane XXX
463
+ #(1...count).each(){|i| @graphic.mvhline((i*@comp_height)+absrow, abscol+1, 0, @width-3) }
464
+ # TODO put hlines here
465
+ end
466
+ @graphic.attroff(Ncurses.COLOR_PAIR(bordercolor) | borderatt)
467
+ update_components
468
+ _print_more_columns_marker true
469
+ @graphic.wrefresh # 2010-02-14 20:18 SUBWIN ONLY ??? what is this doing here ? XXX
470
+ else
471
+ # repaint only those components that may have changed
472
+ @components.each { |e| e.repaint }
473
+ end
474
+ ## XXX do not paint what is outside of bounds. See tabbedpane or scrollform
475
+ #paint
476
+ @repaint_required = false
477
+ end
478
+ def getvalue #:nodoc:
479
+ # TODO
480
+ end
481
+ # take focus to next pane (component in it)
482
+ # if its the last, return UNHANDLED so form can take to next field
483
+ # @return [0, :UNHANDLED] success, or last component
484
+
485
+ def goto_next_component
486
+ if @current_component != nil
487
+ @current_component.on_leave
488
+ if on_last_component?
489
+ return :UNHANDLED
490
+ end
491
+ @current_index += 1
492
+ @current_component = @components[@current_index]
493
+ # is it visible
494
+ #@current_index.between?(_first_column_print, _last_column_print)
495
+ if @current_index > @_last_column_print
496
+ # TODO need to check for exceeding
497
+ @_first_column_print += 1
498
+ @_last_column_print += 1
499
+ @repaint_required = true
500
+ end
501
+ # shoot if this this put on a form with other widgets
502
+ # we would never get out, should return nil -1 in handle key
503
+ unless @current_component
504
+ $log.debug " CAME HERE unless @current_component setting to first"
505
+ raise " CAME HERE unless @current_component setting to first"
506
+ @current_index = 0
507
+ @current_component = @components[@current_index]
508
+ end
509
+ else
510
+ # this happens in one_tab_expand
511
+ #@current_component = @second_component if @first_component.nil?
512
+ #@current_component = @first_component if @second_component.nil?
513
+ # XXX not sure what to do here, will it come
514
+ $log.debug " CAME HERE in else clause MSP setting to first"
515
+ raise" CAME HERE in else clause MSP setting to first"
516
+ @current_index = 0
517
+ @current_component = @components[@current_index]
518
+ end
519
+ return set_form_row
520
+ end
521
+
522
+ # take focus to prev pane (component in it)
523
+ # if its the first, return UNHANDLED so form can take to prev field
524
+ # @return [0, :UNHANDLED] success, or first component
525
+ def goto_prev_component
526
+ if @current_component != nil
527
+ @current_component.on_leave
528
+ if on_first_component?
529
+ return :UNHANDLED
530
+ end
531
+ @current_index -= 1
532
+ @current_component = @components[@current_index]
533
+ if @current_index < @_first_column_print
534
+ # TODO need to check for zero
535
+ @_first_column_print -= 1
536
+ @_last_column_print -= 1
537
+ @repaint_required = true
538
+ end
539
+ # shoot if this this put on a form with other widgets
540
+ # we would never get out, should return nil -1 in handle key
541
+ unless @current_component
542
+ @current_index = 0
543
+ @current_component = @components[@current_index]
544
+ end
545
+ else
546
+ # this happens in one_tab_expand
547
+ #@current_component = @second_component if @first_component.nil?
548
+ #@current_component = @first_component if @second_component.nil?
549
+ # XXX not sure what to do here, will it come
550
+ @current_index = 0
551
+ @current_component = @components[@current_index]
552
+ end
553
+ set_form_row
554
+ return 0
555
+ end
556
+ def on_first_component?
557
+ @current_component == @components.first
558
+ end
559
+ def on_last_component?
560
+ @current_component == @components.last
561
+ end
562
+ ## Handles key for splitpanes
563
+ ## By default, first component gets focus, not the SPL itself.
564
+ ##+ Mostly passing to child, and handling child's left-overs.
565
+ # please use bind_key for all mappings.
566
+ # Avoid adding code in here. Let this be generic
567
+ def handle_key ch #:nodoc:
568
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
569
+ @current_component ||= @first_component
570
+ @current_index ||= 0
571
+ ## 2010-01-15 12:57 this helps me switch between highest level
572
+ ## However, i should do as follows:
573
+ ## If tab on second component, return UNHA so form can take to next field
574
+ ## If B_tab on second comp, switch to first
575
+ ## If B_tab on first comp, return UNHA so form can take to prev field
576
+ if ch == KEY_TAB
577
+ return goto_next_component
578
+ #return 0
579
+ elsif ch == KEY_BTAB
580
+ return goto_prev_component
581
+ end
582
+
583
+ if @current_component != nil
584
+ ret = @current_component.handle_key ch
585
+ return ret if ret != :UNHANDLED
586
+ else
587
+ ## added 2010-01-07 18:59 in case nothing in there.
588
+ $log.debug " SPLP #{@name} - no component installed in splitpane"
589
+ #return :UNHANDLED
590
+ end
591
+ $log.debug " mmplitpane #{@name} gets KEY #{ch}"
592
+ case ch
593
+ when ?\C-c.getbyte(0)
594
+ $multiplier = 0
595
+ return 0
596
+ when ?0.getbyte(0)..?9.getbyte(0)
597
+ $multiplier *= 10 ; $multiplier += (ch-48)
598
+ return 0
599
+ end
600
+ ret = process_key ch, self
601
+ return :UNHANDLED if ret == :UNHANDLED
602
+
603
+ $multiplier = 0
604
+ return 0
605
+ end
606
+ def paint #:nodoc:
607
+ #@repaint_required = false
608
+ end
609
+ # this is executed when the component gets focus
610
+ # and will happen each time on traversal
611
+ # Used to place the focus on correct internal component
612
+ # and place cursor where component should have it.
613
+ # User can press tab, to come here, or it could be first field of form,
614
+ # or he could press a mnemonic.
615
+ def on_enter
616
+ return if @components.nil?
617
+ # cyclic means it always lands into first comp just as in rdoc
618
+ # otherwise it will always land in last visited component
619
+ if @cyclic_behavior
620
+ # if user backtabbed in place him on last comp
621
+ # else place him in first.
622
+ if $current_key == KEY_BTAB
623
+ @current_component = @components[@_last_column_print]
624
+ @current_index = @_last_column_print
625
+ else
626
+ @current_component = @components[@_first_column_print]
627
+ @current_index = @_first_column_print
628
+ end
629
+ end
630
+ @current_component ||= @components.first
631
+ set_form_row
632
+ end
633
+ # sets cursor on correct row, col
634
+ # should we raise error or throw exception if can;t enter
635
+ def set_form_row #:nodoc:
636
+ if !@current_component.nil?
637
+ c=@current_component
638
+ $log.debug "XXXXX #{@name} set_form_row calling sfr for #{@current_component.name}, #{c.row}, #{c.col} "
639
+ #@current_component.set_form_row
640
+ # trigger the on_enter handler
641
+ # my god XXX this assumes a listbox !! FIXME
642
+ # on enter should return a false or error so we don't proceed
643
+ # or throw exception
644
+ if @current_component.row_count > 0
645
+ @current_component.on_enter # typically on enter does a set_form_row
646
+ # XXX another assumption that is has this !!!
647
+ @current_component.set_form_col
648
+ return 0
649
+ end
650
+ #
651
+ end
652
+ return :UNHANDLED
653
+ end
654
+ # added 2010-02-09 10:10
655
+ # sets the forms cursor column correctly
656
+ # earlier the super was being called which missed out on child's column.
657
+ # Note: splitpane does not use the cursor, so it does not know where cursor should be displayed,
658
+ #+ the child has to decide where it should be displayed.
659
+ def set_form_col #:nodoc:
660
+ return if @current_component.nil?
661
+ #$log.debug " #{@name} set_form_col calling sfc for #{@current_component.name} "
662
+ @current_component.set_form_col
663
+ end
664
+ ## expand a split to maximum. This is the one_touch_expandable feature
665
+ # Currently mapped to C-w 1 (mnemonic for one touch), or C-w o (vim's only)
666
+ # To revert, you have to unexpand
667
+ # Note: basically, i nil the component that we don't want to see
668
+ def expand
669
+ return unless @one_touch_expandable
670
+ # TODO
671
+ #@is_expanding = true # this is required so i don't check for min_width later
672
+ #$log.debug " callign expand "
673
+ #if @current_component == @first_component
674
+ #@saved_component = @second_component
675
+ #@second_component = nil
676
+ #if @orientation == :VERTICAL_SPLIT
677
+ #set_divider_location @width - 1
678
+ #else
679
+ #set_divider_location @height - 1
680
+ #end
681
+ #$log.debug " callign expand 2 nil #{@divider_location}, h:#{@height} w: #{@width} "
682
+ #else
683
+ #@saved_component = @first_component
684
+ #@first_component = nil
685
+ #set_divider_location 1
686
+ #$log.debug " callign expand 1 nil #{@divider_location}, h:#{@height} w: #{@width} "
687
+ #end
688
+ #@repaint_required = true
689
+ end
690
+ # after expanding one split, revert to original - actually i reset, rather than revert
691
+ # This only works after expand has been done
692
+ def unexpand
693
+ #$log.debug " inside unexpand "
694
+ #return unless @saved_component
695
+ #if @first_component.nil?
696
+ #@first_component = @saved_component
697
+ #else
698
+ #@second_component = @saved_component
699
+ #end
700
+ #@saved_component = nil
701
+ #@repaint_required = true
702
+ #reset_to_preferred_sizes
703
+ end
704
+
705
+ # exchange 2 splits, bound to C-w x
706
+ # TODO
707
+ def exchange
708
+ alert "TODO"
709
+ #tmp = @first_component
710
+ #@first_component = @second_component
711
+ #@second_component = tmp
712
+ #@repaint_required = true
713
+ #reset_to_preferred_sizes
714
+ end
715
+ def tile
716
+ return unless @tiling_allowed
717
+ # TODO
718
+ end
719
+ private
720
+ def _print_more_columns_marker tf
721
+ # this marker shows that there are more columns to right
722
+ tf = @_last_column_print < @components.size - 1
723
+ marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
724
+ #@graphic.mvwaddch @row+@height-1, @col+@width-2, marker
725
+ @graphic.mvwaddch @row+@height-1, @col+@width-3, marker
726
+ # show if columns to left or not
727
+ marker = @_first_column_print > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
728
+ @graphic.mvwaddch @row+@height-1, @col+@_first_column_print+1, marker
729
+ end
730
+ end # class
731
+ end # module