rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,1142 @@
1
+ =begin
2
+ * Name: TabularWidget
3
+ * Description A widget based on Tabular
4
+ * Author: rk (arunachalesha)
5
+ * file created 2010-09-28 23:37
6
+ FIXME:
7
+
8
+ TODO
9
+ * guess_c : have some config : NEVER, FIRST_TIME, EACH_TIME
10
+ if user has specified widths then we don't wanna guess. guess_size 20, ALL.
11
+ * move columns
12
+ * hide columns - importnat since with sorting we may need to store an identifier which
13
+ should not be displayed
14
+ x data truncation based on col wid TODO
15
+ * TODO: search -- how is it working, but curpos is wrong. This is since list does not contain
16
+ header, it only has data. so curpos is off by one _header_adjustment
17
+ * allow resize of column inside column header
18
+ * Now that we allow header to get focus, we should allow it to handle
19
+ keys, but its not an object like it was in rtable ! AARGH !
20
+ * NOTE: header could become an object in near future, but then why did we break
21
+ away from rtable ?
22
+ * TODO FIXME : after converting to convert_value_to_text and truncation etc, numbering is broken
23
+ * we are checking widths of columsn and we have added a column, so columns widths refer to wrong col
24
+ TODO : tabbing with w to take care of hidden columns and numbering. FIXME
25
+ TODO: we forgot about selection altogether. we need multiple select !!! as in gmail
26
+ subject list.
27
+ --------
28
+ * License:
29
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
30
+
31
+ =end
32
+ require 'rbhex'
33
+ require 'rbhex/core/include/listscrollable'
34
+ require 'rbhex/core/widgets/tabular'
35
+ require 'rbhex/core/include/listselectable'
36
+ require 'rbhex/core/include/bordertitle'
37
+
38
+ #include RubyCurses
39
+ module RubyCurses
40
+ extend self
41
+ # used when firing a column resize, so calling application can perhaps
42
+ # resize other columns.
43
+ class ColumnResizeEvent < Struct.new(:source, :index, :type); end
44
+
45
+ ##
46
+ # A viewable read only, scrollable table. This is supposed to be a
47
+ # +minimal+, and (hopefully) fast version of Table (@see rtable.rb).
48
+ class TabularWidget < Widget
49
+
50
+
51
+ include ListScrollable
52
+ include NewListSelectable
53
+ #dsl_accessor :title # set this on top
54
+ #dsl_accessor :title_attrib # bold, reverse, normal
55
+ dsl_accessor :footer_attrib # bold, reverse, normal
56
+ dsl_accessor :list # the array of arrays of data to be sent by user XXX RISKY bypasses set_content
57
+ dsl_accessor :maxlen # max len to be displayed
58
+ attr_reader :toprow # the toprow in the view (offsets are 0)
59
+ ##attr_reader :winrow # the row in the viewport/window
60
+ # painting the footer does slow down cursor painting slightly if one is moving cursor fast
61
+ dsl_accessor :print_footer
62
+ #dsl_accessor :suppress_borders
63
+ attr_accessor :current_index
64
+ #dsl_accessor :border_attrib, :border_color # color pair for border
65
+ dsl_accessor :header_attrib, :header_fgcolor, :header_bgcolor # 2010-10-15 13:21
66
+
67
+ # boolean, whether lines should be cleaned (if containing tabs/newlines etc)
68
+ dsl_accessor :sanitization_required
69
+ # boolean, whether column widths should be estimated based on data. If you want this,
70
+ # set to true each time you do a set_content
71
+ dsl_accessor :estimate_column_widths
72
+ # boolean, whether lines should be numbered
73
+ attr_accessor :numbering
74
+ # default or custom sorter
75
+ attr_reader :table_row_sorter
76
+
77
+ # @group select related
78
+ dsl_accessor :selection_mode
79
+ dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
80
+
81
+ dsl_property :show_selector # boolean
82
+ dsl_property :row_selected_symbol
83
+ dsl_property :row_unselected_symbol
84
+ attr_accessor :selected_index # should we use only indices ??
85
+ # index of selected rows, if multiple selection asked for
86
+ attr_reader :selected_indices
87
+ attr_reader :_header_adjustment # we need to adjust when using current_index !!! UGH
88
+ # @endgroup select related
89
+ attr_reader :columns
90
+
91
+ def initialize form = nil, config={}, &block
92
+ @focusable = true
93
+ @editable = false
94
+ @sanitization_required = true
95
+ @estimate_column_widths = true
96
+ @row = 0
97
+ @col = 0
98
+ @cw = {} # column widths keyed on column index - why not array ??
99
+ @pw = [] # preferred column widths 2010-10-20 12:58
100
+ @calign = {} # columns aligns values, on column index
101
+ @coffsets = {}
102
+ @suppress_borders = false
103
+ @row_offset = @col_offset = 1
104
+ @chash = {}
105
+ # this should have index of displayed column
106
+ # so user can reorder columns
107
+ #@column_position = [] # TODO
108
+ @separ = @columns = @numbering = nil
109
+ @y = '|'
110
+ @x = '+'
111
+ @list = []
112
+ @_header_adjustment = 0
113
+ @show_focus = false # don't highlight row under focus TODO
114
+ @selection_mode = :multiple # default is multiple, anything else given becomes single
115
+ @row_selected_symbol = '*'
116
+ @show_selector = true
117
+ super
118
+ # ideally this should have been 2 to take care of borders, but that would break
119
+ # too much stuff !
120
+ @win = @graphic
121
+
122
+ @_events.push :CHANGE # thru vieditable
123
+ @_events << :PRESS # new, in case we want to use this for lists and allow ENTER
124
+ @_events << :ENTER_ROW # new, should be there in listscrollable ??
125
+ @_events << :COLUMN_RESIZE_EVENT
126
+ install_keys # << almost jnuk now, clean off TODO
127
+ init_vars
128
+ map_keys
129
+ bordertitle_init
130
+ end
131
+ def init_vars #:nodoc:
132
+ @curpos = @pcol = @toprow = @current_index = 0
133
+ @repaint_all=true
134
+ @repaint_required=true
135
+
136
+ @row_offset = @col_offset = 0 if @suppress_borders == true
137
+ @internal_width = 2
138
+ @internal_width = 0 if @suppress_borders
139
+ # added 2010-02-11 15:11 RFED16 so we don't need a form.
140
+ @current_column = 0
141
+ # currently i scroll right only if current line is longer than display width, i should use
142
+ # longest line on screen.
143
+ @longest_line = 0 # the longest line printed on this page, used to determine if scrolling shd work
144
+ list_init_vars
145
+
146
+ end
147
+ def map_keys
148
+ require 'rbhex/core/include/listbindings'
149
+ bindings()
150
+ bind_key(?w, :next_column)
151
+ bind_key(?b, :previous_column)
152
+ bind_key(?>, :expand_column) # just trying out
153
+ list_bindings # selection bindings
154
+ end
155
+
156
+ #
157
+ # set column names
158
+ # @param [Array] column names or headings
159
+ #
160
+ def columns=(array)
161
+ @_header_adjustment = 1
162
+ @columns = array
163
+ @columns.each_with_index { |c,i|
164
+ @cw[i] ||= c.to_s.length
165
+ @calign[i] ||= :left
166
+ }
167
+ # maintains index in current pointer and gives next or prev
168
+ @column_pointer = Circular.new @columns.size()-1
169
+ end
170
+ alias :headings= :columns=
171
+ ##
172
+ # send in a list of data
173
+ # sorting will only happen if data passed using set_content
174
+ # NOTE: why doesn't set_content take in columns
175
+ # @param [Array / Tabular] data to be displayed
176
+ def set_content list, columns=nil
177
+ if list.is_a? RubyCurses::Tabular
178
+ @list = list
179
+ elsif list.is_a? Array
180
+ @list = list
181
+ else
182
+ raise "set_content expects Array not #{list.class}"
183
+ end
184
+ if @table_row_sorter
185
+ @table_row_sorter.model=@list
186
+ else
187
+ @table_row_sorter = TableRowSorter.new @list
188
+ end
189
+ # adding columns setting here 2011-10-16
190
+ self.columns = columns if columns
191
+ @current_index = @_header_adjustment # but this is set when columns passed
192
+ @toprow = 0
193
+ @second_time = false # so that reestimation of column_widths
194
+ @repaint_required = true
195
+ @recalc_required = true # is this used, if not remove TODO
196
+ self
197
+ end
198
+ def data=(data)
199
+ set_content(data, nil)
200
+ end
201
+
202
+ # add a row of data
203
+ # NOTE: this is not creating a table sorter
204
+ # @param [Array] an array containing entries for each column
205
+ def add array
206
+ @list ||= []
207
+ @list << array
208
+ @repaint_required = true
209
+ @recalc_required = true
210
+ end
211
+ alias :<< :add
212
+ alias :add_row :add
213
+ alias :append :add
214
+ def create_default_sorter
215
+ raise "Data not sent in." unless @list
216
+ @table_row_sorter = TableRowSorter.new @list
217
+ end
218
+ def remove_all
219
+ @list = []
220
+ init_vars
221
+ end
222
+ def delete_at off0
223
+ @repaint_required = true
224
+ @delete_buffer=@list.delete_at off0
225
+ return @delete_buffer
226
+ end
227
+ def []=(off0, data)
228
+ @repaint_required = true
229
+ @list[off0] = data
230
+ end
231
+ def [](off0)
232
+ @list[off0]
233
+ end
234
+ def insert off0, *data
235
+ @repaint_required = true
236
+ @list.insert off0, *data
237
+ end
238
+
239
+ # delete current line or lines
240
+ # Should be using listeditable except for _header_adjustment
241
+ # NOTE: user has to map this to some key such as 'dd'
242
+ # tw.bind_key([?\d,?\d]) { tw.delete_line }
243
+ #
244
+ def delete_line line=real_index()
245
+ #return -1 unless @editable
246
+ if !$multiplier || $multiplier == 0
247
+ @delete_buffer = @list.delete_at line
248
+ else
249
+ @delete_buffer = @list.slice!(line, $multiplier)
250
+ end
251
+ @curpos ||= 0 # rlist has no such var
252
+ $multiplier = 0
253
+ #add_to_kill_ring @delete_buffer
254
+ @buffer = @list[@current_index]
255
+ if @buffer.nil?
256
+ up
257
+ setrowcol @row + 1, nil # @form.col
258
+ end
259
+ # warning: delete buffer can now be an array
260
+ #fire_handler :CHANGE, InputDataEvent.new(@curpos,@curpos+@delete_buffer.length, self, :DELETE_LINE, line, @delete_buffer) # 2008-12-24 18:34
261
+ set_modified
262
+ #@widget_scrolled = true
263
+ @repaint_required = true
264
+ end
265
+
266
+ # undo deleted row/rows, this is a simple undo, unlike undo_managers more
267
+ # complete undo. I am not calling this <tt>undo</tt>, so there's no conflict with
268
+ # undomanager if used.
269
+ # NOTE: user has to map this to some key such as 'u'
270
+ # tw.bind_key(?\U) { tw.undo }
271
+ #
272
+ def undo_delete
273
+ return unless @delete_buffer
274
+ if @delete_buffer[0].is_a? Array
275
+ # multiple rows deleted
276
+ insert real_index(), *@delete_buffer
277
+ else
278
+ # one row deleted
279
+ insert real_index(), @delete_buffer
280
+ end
281
+ end
282
+
283
+ # TODO more methods like in listbox so interchangeable, delete_at etc
284
+ def column_width colindex, width
285
+ return if width < 0
286
+ raise ArgumentError, "wrong width value sent: #{width} " if width.nil? || !width.is_a?(Fixnum) || width < 0
287
+ @cw[colindex] = width # uncommented 2011-12-1 for expand on +
288
+ @pw[colindex] = width # XXXXX
289
+ get_column(colindex).width = width
290
+ @repaint_required = true
291
+ @recalc_required = true
292
+ end
293
+
294
+ # set alignment of given column offset
295
+ # @param [Number] column offset, starting 0
296
+ # @param [Symbol] :left, :right
297
+ def column_align colindex, lrc
298
+ raise ArgumentError, "wrong alignment value sent" if ![:right, :left, :center].include? lrc
299
+ @calign[colindex] = lrc
300
+ get_column(colindex).align = lrc
301
+ @repaint_required = true
302
+ #@recalc_required = true
303
+ end
304
+ # Set a column to hidden TODO we are not actually doing that
305
+ def column_hidden colindex, tf=true
306
+ #raise ArgumentError, "wrong alignment value sent" if ![:right, :left, :center].include? lrc
307
+ get_column(colindex).hidden = tf
308
+ @repaint_required = true
309
+ @recalc_required = true
310
+ end
311
+ def move_column
312
+
313
+ end
314
+ def expand_column
315
+ x = _convert_curpos_to_column
316
+ w = get_column(x).width || @cw[x]
317
+ # sadly it seems to be nil
318
+ column_width x, w+1 if w
319
+ end
320
+ def contract_column
321
+ x = _convert_curpos_to_column
322
+ w = get_column(x).width || @cw[x]
323
+ column_width x, w-1 if w
324
+ end
325
+ ## display this row number on top
326
+ # programmataically indicate a row to be top row
327
+ def top_row(*val)
328
+ if val.empty?
329
+ @toprow
330
+ else
331
+ @toprow = val[0] || 0
332
+ end
333
+ @repaint_required = true
334
+ end
335
+ ## ---- for listscrollable ---- ##
336
+ def scrollatrow #:nodoc:
337
+ # TODO account for headers
338
+ if @suppress_borders
339
+ @height - @_header_adjustment
340
+ else
341
+ @height - (2 + @_header_adjustment)
342
+ end
343
+ end
344
+ def row_count
345
+ #@list.length
346
+ get_content().length + @_header_adjustment
347
+ end
348
+ ##
349
+ # returns row of first match of given regex (or nil if not found)
350
+ def find_first_match regex #:nodoc:
351
+ @list.each_with_index do |row, ix|
352
+ return ix if !row.match(regex).nil?
353
+ end
354
+ return nil
355
+ end
356
+ ## returns the position where cursor was to be positioned by default
357
+ # It may no longer work like that.
358
+ def rowcol #:nodoc:
359
+ return @row+@row_offset, @col+@col_offset
360
+ end
361
+ ## print a border
362
+ ## Note that print_border clears the area too, so should be used sparingly.
363
+ def OLDprint_borders #:nodoc:
364
+ raise "#{self.class} needs width" unless @width
365
+ raise "#{self.class} needs height" unless @height
366
+
367
+ $log.debug " #{@name} print_borders, #{@graphic.name} "
368
+
369
+ bordercolor = @border_color || $datacolor
370
+ borderatt = @border_attrib || Ncurses::A_NORMAL
371
+ @graphic.print_border @row, @col, @height-1, @width, bordercolor, borderatt
372
+ print_title
373
+ end
374
+ def OLDprint_title #:nodoc:
375
+ raise "#{self.class} needs width" unless @width
376
+ $log.debug " print_title #{@row}, #{@col}, #{@width} "
377
+ @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, $datacolor, @title_attrib) unless @title.nil?
378
+ end
379
+ def print_foot #:nodoc:
380
+ @footer_attrib ||= Ncurses::A_DIM
381
+ gb = get_color($datacolor, 'green','black')
382
+ if @current_index == @toprow
383
+ footer = "%15s" % " [ header row ]"
384
+ else
385
+ footer = "%15s" % " [#{@current_index}/ #{@list.length} ]"
386
+ end
387
+ pos = @col + 2
388
+ right = true
389
+ if right
390
+ pos = @col + @width - footer.length - 1
391
+ end
392
+ @graphic.printstring( @row + @height -1 , pos, footer, gb, @footer_attrib)
393
+ @repaint_footer_required = false # 2010-01-23 22:55
394
+ #@footer_attrib ||= Ncurses::A_REVERSE
395
+ #footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
396
+ ##$log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
397
+ #@graphic.printstring( @row + @height -1 , @col+2, footer, $datacolor, @footer_attrib)
398
+ #@repaint_footer_required = false # 2010-01-23 22:55
399
+ end
400
+ ### FOR scrollable ###
401
+ def get_content
402
+ @list
403
+ #[:columns, :separator, *@list]
404
+ #[:columns, *@list]
405
+ end
406
+ def get_window #:nodoc:
407
+ @graphic
408
+ end
409
+
410
+ def repaint # Tabularwidget :nodoc:
411
+
412
+ #return unless @repaint_required # 2010-02-12 19:08 TRYING - won't let footer print for col move
413
+ paint if @repaint_required
414
+ # raise "TV 175 graphic nil " unless @graphic
415
+ print_foot if @print_footer && @repaint_footer_required
416
+ end
417
+ def getvalue
418
+ @list
419
+ end
420
+ # returns value of current row.
421
+ # NOTE: you may need to adjust it with _header_adjustment - actually you can't
422
+ # this may give wrong row -- depends what you want.
423
+ def current_value
424
+ @list[@current_index-@_header_adjustment] # XXX added header_adju 2010-11-01 11:14
425
+ end
426
+ def real_index
427
+ @current_index-@_header_adjustment # XXX added header_adju 2010-11-06 19:38
428
+ end
429
+ # Tabularwidget
430
+ def handle_key ch #:nodoc:
431
+ if header_row?
432
+ ret = header_handle_key ch
433
+ return ret unless ret == :UNHANDLED
434
+ end
435
+ case ch
436
+ when ?\C-a.getbyte(0) #, ?0.getbyte(0)
437
+ # take care of data that exceeds maxlen by scrolling and placing cursor at start
438
+ @repaint_required = true if @pcol > 0 # tried other things but did not work
439
+ set_form_col 0
440
+ @pcol = 0
441
+ when ?\C-e.getbyte(0), ?$.getbyte(0)
442
+ # take care of data that exceeds maxlen by scrolling and placing cursor at end
443
+ # This use to actually pan the screen to actual end of line, but now somewhere
444
+ # it only goes to end of visible screen, set_form probably does a sanity check
445
+ blen = @buffer.rstrip.length
446
+ set_form_col blen
447
+ # search related
448
+ when KEY_ENTER, FFI::NCurses::KEY_ENTER
449
+ #fire_handler :PRESS, self
450
+ fire_action_event
451
+ when ?0.getbyte(0)..?9.getbyte(0)
452
+ # FIXME the assumption here was that if numbers are being entered then a 0 is a number
453
+ # not a beg-of-line command.
454
+ # However, after introducing universal_argument, we can enters numbers using C-u and then press another
455
+ # C-u to stop. In that case a 0 should act as a command, even though multiplier has been set
456
+ if ch == ?0.getbyte(0) and $multiplier == 0
457
+ # copy of C-a - start of line
458
+ @repaint_required = true if @pcol > 0 # tried other things but did not work
459
+ set_form_col 0
460
+ @pcol = 0
461
+ return 0
462
+ end
463
+ # storing digits entered so we can multiply motion actions
464
+ $multiplier *= 10 ; $multiplier += (ch-48)
465
+ return 0
466
+ when ?\C-c.getbyte(0)
467
+ $multiplier = 0
468
+ return 0
469
+ else
470
+ # check for bindings, these cannot override above keys since placed at end
471
+ begin
472
+ ret = process_key ch, self
473
+ rescue => err
474
+ $error_message.value = err.to_s
475
+ # @form.window.print_error_message # changed 2011 dts
476
+ $log.error " Tabularwidget ERROR #{err} "
477
+ $log.debug(err.backtrace.join("\n"))
478
+ textdialog ["Error in TabularWidget: #{err} ", *err.backtrace], :title => "Exception"
479
+ # XXX caller app has no idea error occurred so can't do anything !
480
+ end
481
+ return :UNHANDLED if ret == :UNHANDLED
482
+ end
483
+ $multiplier = 0 # you must reset if you've handled a key. if unhandled, don't reset since parent could use
484
+ set_form_row
485
+ $status_message.value = "F10 quit, F1 Help, : menu, toprow #{@toprow} current #{@current_index} " if $log.debug?
486
+ return 0 # added 2010-01-12 22:17 else down arrow was going into next field
487
+ end
488
+ #
489
+ # allow header to handle keys
490
+ # NOTE: header could become an object in near future
491
+ # We are calling a resize event and passing column index but do we really
492
+ # have a column object that user can access and do something with ?? XXX
493
+ #
494
+ def header_handle_key ch #:nodoc:
495
+ # TODO pressing = should revert to calculated size ?
496
+ col = _convert_curpos_to_column
497
+ #width = @cw[col]
498
+ width = @pw[col] || @cw[col]
499
+ #alert "got width #{width}, #{@cw[col]} "
500
+ # NOTE: we are setting pw and chash but paint picks from cw
501
+ # TODO check for multiplier too
502
+ case ch
503
+ when ?-.getbyte(0)
504
+ column_width col, width-1
505
+ # if this event has not been used in a sample it could change in near future
506
+ e = ColumnResizeEvent.new self, col, :DECREASE
507
+ fire_handler :COLUMN_RESIZE_EVENT, e
508
+ # can fire_hander so user can resize another column
509
+ return 0
510
+ when ?\+.getbyte(0)
511
+ column_width col, width+1
512
+ # if this event has not been used in a sample it could change in near future
513
+ e = ColumnResizeEvent.new self, col, :INCREASE
514
+ return 0
515
+ end
516
+ return :UNHANDLED
517
+ end
518
+ # newly added to check curpos when moving up or down
519
+ def check_curpos #:nodoc:
520
+ # if the cursor is ahead of data in this row then move it back
521
+ # i don't think this is required
522
+ return
523
+ if @pcol+@curpos > @buffer.length
524
+ addcol((@pcol+@buffer.length-@curpos)+1)
525
+ @curpos = @buffer.length
526
+ maxlen = (@maxlen || @width-@internal_width)
527
+
528
+ # even this row is gt maxlen, i.e., scrolled right
529
+ if @curpos > maxlen
530
+ @pcol = @curpos - maxlen
531
+ @curpos = maxlen-1
532
+ else
533
+ # this row is within maxlen, make scroll 0
534
+ @pcol=0
535
+ end
536
+ set_form_col
537
+ end
538
+ end
539
+ # set cursor on correct column tview
540
+ def set_form_col col1=@curpos #:nodoc:
541
+ @cols_panned ||= 0
542
+ @pad_offset ||= 0 # added 2010-02-11 21:54 since padded widgets get an offset.
543
+ @curpos = col1
544
+ maxlen = @maxlen || @width-@internal_width
545
+ #@curpos = maxlen if @curpos > maxlen
546
+ if @curpos > maxlen
547
+ @pcol = @curpos - maxlen
548
+ @curpos = maxlen - 1
549
+ @repaint_required = true # this is required so C-e can pan screen
550
+ else
551
+ @pcol = 0
552
+ end
553
+ # the rest only determines cursor placement
554
+ win_col = 0 # 2010-02-07 23:19 new cursor stuff
555
+ col2 = win_col + @col + @col_offset + @curpos + @cols_panned + @pad_offset
556
+ #$log.debug "TV SFC #{@name} setting c to #{col2} #{win_col} #{@col} #{@col_offset} #{@curpos} "
557
+ #@form.setrowcol @form.row, col
558
+ setrowcol nil, col2
559
+ @repaint_footer_required = true
560
+ end
561
+ def cursor_forward #:nodoc:
562
+ maxlen = @maxlen || @width-@internal_width
563
+ repeatm {
564
+ if @curpos < @width and @curpos < maxlen-1 # else it will do out of box
565
+ @curpos += 1
566
+ addcol 1
567
+ else
568
+ @pcol += 1 if @pcol <= @buffer.length
569
+ end
570
+ }
571
+ set_form_col
572
+ #@repaint_required = true
573
+ @repaint_footer_required = true # 2010-01-23 22:41
574
+ end
575
+ def addcol num #:nodoc:
576
+ #@repaint_required = true
577
+ @repaint_footer_required = true # 2010-01-23 22:41
578
+ if @form
579
+ @form.addcol num
580
+ else
581
+ @parent_component.form.addcol num
582
+ end
583
+ end
584
+ def addrowcol row,col #:nodoc:
585
+ #@repaint_required = true
586
+ @repaint_footer_required = true # 2010-01-23 22:41
587
+ if @form
588
+ @form.addrowcol row, col
589
+ else
590
+ @parent_component.form.addrowcol num
591
+ end
592
+ end
593
+ def cursor_backward #:nodoc:
594
+ repeatm {
595
+ if @curpos > 0
596
+ @curpos -= 1
597
+ set_form_col
598
+ #addcol -1
599
+ elsif @pcol > 0
600
+ @pcol -= 1
601
+ end
602
+ }
603
+ #@repaint_required = true
604
+ @repaint_footer_required = true # 2010-01-23 22:41
605
+ end
606
+
607
+ ## NOTE: earlier print_border was called only once in constructor, but when
608
+ ##+ a window is resized, and destroyed, then this was never called again, so the
609
+ ##+ border would not be seen in splitpane unless the width coincided exactly with
610
+ ##+ what is calculated in divider_location.
611
+ def paint #:nodoc:
612
+ my_win = nil
613
+ if @form
614
+ my_win = @form.window
615
+ else
616
+ my_win = @target_window
617
+ end
618
+ @graphic = my_win unless @graphic
619
+ tm = get_content
620
+ rc = tm.length
621
+ _estimate_column_widths if rc > 0 # will set preferred_width 2011-10-4
622
+ @left_margin ||= @row_selected_symbol.length
623
+ @width ||= @preferred_width
624
+
625
+ @height ||= [tm.length+3, 10].min
626
+ _prepare_format
627
+
628
+ print_borders if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
629
+ _maxlen = @maxlen || @width-@internal_width
630
+ tr = @toprow
631
+ acolor = get_color $datacolor
632
+ h = scrollatrow()
633
+ r,c = rowcol
634
+ print_header
635
+ r += @_header_adjustment # for column header
636
+ @longest_line = @width #maxlen
637
+ $log.debug " #{@name} Tabularwidget repaint width is #{@width}, height is #{@height} , maxlen #{maxlen}/ #{@maxlen}, #{@graphic.name} roff #{@row_offset} coff #{@col_offset}, r #{r} top #{toprow} ci #{current_index} "
638
+ 0.upto(h - @_header_adjustment) do |hh|
639
+ crow = tr+hh
640
+ if crow < rc
641
+ #focussed = @current_index == crow ? true : false
642
+ content = tm[crow]
643
+
644
+ columnrow = false
645
+ if content == :columns
646
+ columnrow = true
647
+ end
648
+
649
+ value = convert_value_to_text content, crow
650
+
651
+ @buffer = value if crow == @current_index
652
+ # next call modified string. you may wanna dup the string.
653
+ # rlistbox does
654
+ sanitize value if @sanitization_required
655
+ truncate value
656
+ ## set the selector symbol if requested
657
+ paint_selector crow, r+hh, c, acolor, @attr
658
+
659
+ #@graphic.printstring r+hh, c, "%-*s" % [@width-@internal_width,value], acolor, @attr
660
+ #print_data_row( r+hh, c, "%-*s" % [@width-@internal_width,value], acolor, @attr)
661
+ print_data_row( r+hh, c+@left_margin, @width-@internal_width-@left_margin, value, acolor, @attr)
662
+
663
+ else
664
+ # clear rows
665
+ @graphic.printstring r+hh, c, " " * (@width-@internal_width-@left_margin), acolor,@attr
666
+ end
667
+ end
668
+ @repaint_required = false
669
+ @repaint_footer_required = true
670
+ @repaint_all = false
671
+
672
+ end
673
+
674
+ # print data rows
675
+ def print_data_row r, c, len, value, color, attr
676
+ @graphic.printstring r, c, "%-*s" % [len,value], color, attr
677
+ end
678
+ #
679
+ # Truncates data to fit into display area.
680
+ # Copied from listscrollable since we need to take care of left_margin
681
+ # 2011-10-6 This may need to be reflected in listbox and others FIXME
682
+ def truncate content #:nodoc:
683
+ #maxlen = @maxlen || @width-2
684
+ _maxlen = @maxlen || @width-@internal_width
685
+ _maxlen = @width-@internal_width if _maxlen > @width-@internal_width
686
+ _maxlen -= @left_margin
687
+ if !content.nil?
688
+ cl = content.length
689
+ if cl > _maxlen # only show maxlen
690
+ @longest_line = cl if cl > @longest_line
691
+ ## taking care of when scrolling is needed but longest_line is misreported
692
+ # So we scroll always and need to check 2013-03-06 - 00:09
693
+ #content.replace content[@pcol..@pcol+_maxlen-1]
694
+ content.replace(content[@pcol..@pcol+maxlen-1] || " ")
695
+ else
696
+ #content.replace content[@pcol..-1] if @pcol > 0
697
+ content.replace(content[@pcol..-1]||" ") if @pcol > 0
698
+ end
699
+ end
700
+ content
701
+ end
702
+
703
+ # print header row
704
+ # allows user to override
705
+ def print_header_row r, c, len, value, color, attr
706
+ #acolor = $promptcolor
707
+ @graphic.printstring r, c+@left_margin, "%-*s" % [len-@left_margin ,value], color, attr
708
+ end
709
+ def separator
710
+ #return @separ if @separ
711
+ str = ""
712
+ if @numbering
713
+ rows = @list.size.to_s.length
714
+ str = "-"*(rows+1)+@x
715
+ end
716
+ @cw.each_pair { |k,v| str << "-" * (v+1) + @x }
717
+ @separ = str.chop
718
+ end
719
+ # prints the column headers
720
+ # Uses +convert_value_to_text+ and +print_header_row+
721
+ def print_header
722
+ r,c = rowcol
723
+ value = convert_value_to_text :columns, 0
724
+ len = @width - @internal_width
725
+ truncate value # else it can later suddenly exceed line
726
+ @header_color_pair ||= get_color $promptcolor, @header_fgcolor, @header_bgcolor
727
+ @header_attrib ||= @attr
728
+ print_header_row r, c, len, value, @header_color_pair, @header_attrib
729
+ end
730
+ # convert data object to a formatted string for print
731
+ # NOTE: useful for overriding and doing custom formatting
732
+ # @param [Array] array of column data, mostly +String+
733
+ # Can also be :columns or :separator
734
+ # @param [Fixnum] index of row in data
735
+ def convert_value_to_text r, count
736
+ if r == :separator
737
+ return separator
738
+ elsif r == :columns
739
+ return "??" unless @columns # column was requested but not supplied
740
+ # FIXME putting entire header into this, take care of hidden
741
+ r = []
742
+ @columns.each_with_index { |e, i| r << e unless get_column(i).hidden }
743
+ return @headerfmtstr % r if @numbering
744
+ end
745
+ str = ""
746
+
747
+ if @numbering
748
+ #r = r.dup
749
+ #r.insert 0, count+1
750
+ # TODO get the width
751
+ str << "%*d |"% [2, count + 1]
752
+ end
753
+ # unroll r, get width and align
754
+ # This is to truncate column to requested width
755
+ fmta = []
756
+ r.each_with_index { |e, i|
757
+ next if get_column(i).hidden == true
758
+ #w = @pw[i] || @cw[i] # XXX
759
+ #$log.debug "WIDTH XXX #{i} w= #{w} , #{@pw[i]}, #{@cw[i]} :: #{e} " if $log.debug?
760
+ w = @cw[i]
761
+ l = e.to_s.length
762
+ fmt = "%-#{w}s "
763
+ # if value is longer than width, then truncate it
764
+ if l > w
765
+ fmt = "%.#{w}s "
766
+ else
767
+ # ack we don;t need to recalc this we can pull out of hash FIXME
768
+ case @calign[i]
769
+ when :right
770
+ fmt = "%#{w}s "
771
+ else
772
+ fmt = "%-#{w}s "
773
+ end
774
+ end
775
+ str << fmt % e
776
+ fmta << fmt
777
+ }
778
+ #fmstr = fmta.join(@y)
779
+ #return fmstr % r; # FIXME hidden column still goes int
780
+ return str
781
+ end
782
+ # perhaps we can delete this since it does not respect @pw
783
+ # @deprecated (see _estimate_column_widths)
784
+ def _guess_col_widths #:nodoc:
785
+ return if @second_time
786
+ @second_time = true if @list.size > 0
787
+ @list.each_with_index { |r, i|
788
+ break if i > 10
789
+ next if r == :separator
790
+ r.each_with_index { |c, j|
791
+ x = c.to_s.length
792
+ if @cw[j].nil?
793
+ @cw[j] = x
794
+ else
795
+ @cw[j] = x if x > @cw[j]
796
+ end
797
+ }
798
+ }
799
+ #sum = @cw.values.inject(0) { |mem, var| mem + var }
800
+ #$log.debug " SUM is #{sum} "
801
+ total = 0
802
+ @cw.each_pair { |name, val| total += val }
803
+ @preferred_width = total + (@cw.size() *2)
804
+ @preferred_width += 4 if @numbering # FIXME this 4 is rough
805
+ end
806
+ def _estimate_column_widths #:nodoc:
807
+ return unless @estimate_column_widths
808
+ @estimate_column_widths = false # XXX testing why its failing in gmail
809
+ @columns.each_with_index { |c, i|
810
+ if @pw[i]
811
+ @cw[i] = @pw[i]
812
+ else
813
+ @cw[i] = calculate_column_width(i)
814
+ end
815
+ }
816
+ total = 0
817
+ @cw.each_pair { |name, val| total += val }
818
+ @preferred_width = total + (@cw.size() *2)
819
+ @preferred_width += 4 if @numbering # FIXME this 4 is rough
820
+ end
821
+ # if user has not specified preferred_width for a column
822
+ # then we can calculate the same based on data
823
+ def calculate_column_width col
824
+ ret = @cw[col] || 2
825
+ ctr = 0
826
+ @list.each_with_index { |r, i|
827
+ #next if i < @toprow # this is also a possibility, it checks visible rows
828
+ break if ctr > 10
829
+ ctr += 1
830
+ next if r == :separator
831
+ c = r[col]
832
+ x = c.to_s.length
833
+ ret = x if x > ret
834
+ }
835
+ ret
836
+ end
837
+ def _prepare_format #:nodoc:
838
+ @fmtstr = nil
839
+ fmt = []
840
+ total = 0
841
+ @cw.each_with_index { |c, i|
842
+ next if get_column(i).hidden == true # added 2010-10-28 19:08
843
+ w = @cw[i]
844
+ @coffsets[i] = total
845
+ total += w + 2
846
+
847
+ case @calign[i]
848
+ when :right
849
+ fmt << "%#{w}s "
850
+ else
851
+ fmt << "%-#{w}s "
852
+ end
853
+ }
854
+ @fmstr = fmt.join(@y)
855
+ if @numbering
856
+ @rows ||= @list.size.to_s.length
857
+ @headerfmtstr = " "*(@rows+1)+@y + @fmstr
858
+ @fmstr = "%#{@rows}d "+ @y + @fmstr
859
+ @coffsets.each_pair { |name, val| @coffsets[name] = val + @rows + 2 }
860
+ end
861
+ #$log.debug " FMT : #{@fmstr} "
862
+ #alert "format: #{@fmstr} "
863
+ end
864
+ ##
865
+ # dynamically load a module and execute init method.
866
+ # Hopefully, we can get behavior like this such as vieditable or multibuffers
867
+ # TODO CUT THIS OUT AND FIX IT, there are simpler ways like extend()
868
+ def load_module requirename, includename
869
+ require "rbhex/#{requirename}"
870
+ extend Object.const_get("#{includename}")
871
+ send("#{requirename}_init") #if respond_to? "#{includename}_init"
872
+ end
873
+
874
+ # returns true if cursor is on header row
875
+ # NOTE: I have no idea why row was used here. it is not working
876
+ def header_row?
877
+ return false if @columns.nil?
878
+ #1 == @row + (@current_index-@toprow)
879
+ @current_index == @toprow
880
+ end
881
+ # on pressing ENTER we send user some info, the calling program
882
+ # would bind :PRESS
883
+ # Added a call to sort, should i still call PRESS
884
+ # or just do a sort in here and not call PRESS ???
885
+ #--
886
+ # FIXME we can create this once and reuse
887
+ #++
888
+ def fire_action_event
889
+ return unless @list
890
+ return unless @table_row_sorter
891
+ require 'rbhex/core/include/ractionevent'
892
+ # the header event must only be used if columns passed
893
+ if header_row?
894
+ # TODO we need to fire correct even for header row, including
895
+ #alert "you are on header row: #{@columns[x]} curpos: #{@curpos}, x:#{x} "
896
+ #aev = TextActionEvent.new self, :PRESS, @columns[x], x, @curpos
897
+ x = _convert_curpos_to_column
898
+ @table_row_sorter.toggle_sort_order x
899
+ @table_row_sorter.sort
900
+ @repaint_required = true
901
+ aev = TextActionEvent.new self, :PRESS,:header, x, @curpos
902
+ else
903
+ # please check this again current_value due to _header_adjustment XXX test
904
+ aev = TextActionEvent.new self, :PRESS, current_value(), @current_index, @curpos
905
+ end
906
+ fire_handler :PRESS, aev
907
+ end
908
+ # Convert current cursor position to a table column
909
+ # calculate column based on curpos since user may not have
910
+ # user w and b keys (:next_column)
911
+ # @return [Fixnum] column index base 0
912
+ def _convert_curpos_to_column #:nodoc:
913
+ x = 0
914
+ @coffsets.each_pair { |e,i|
915
+ if @curpos < i
916
+ break
917
+ else
918
+ x += 1
919
+ end
920
+ }
921
+ x -= 1 # since we start offsets with 0, so first auto becoming 1
922
+ return x
923
+ end
924
+ def on_enter
925
+ # so cursor positioned on correct row
926
+ set_form_row
927
+ super
928
+ end
929
+ # called by listscrollable, used by scrollbar ENTER_ROW
930
+ def on_enter_row arow
931
+ fire_handler :ENTER_ROW, self
932
+ @repaint_required = true
933
+ end
934
+ # move cursor to next column
935
+ # FIXME need to account for hidden columns and numbering
936
+ def next_column
937
+ c = @column_pointer.next
938
+ cp = @coffsets[c]
939
+ #$log.debug " next_column #{c} , #{cp} "
940
+ @curpos = cp if cp
941
+ next_row() if c < @column_pointer.last_index
942
+ #addcol cp
943
+ set_form_col
944
+ end
945
+ def previous_column
946
+ c = @column_pointer.previous
947
+ cp = @coffsets[c]
948
+ #$log.debug " prev_column #{c} , #{cp} "
949
+ @curpos = cp if cp
950
+ previous_row() if c > @column_pointer.last_index
951
+ #addcol cp FIXME
952
+ set_form_col
953
+ end
954
+ private
955
+ def get_column index #:nodoc:
956
+ return @chash[index] if @chash.has_key? index
957
+ @chash[index] = ColumnInfo.new
958
+ end
959
+
960
+ # Some supporting classes
961
+
962
+ # This is our default table row sorter.
963
+ # It does a multiple sort and allows for reverse sort also.
964
+ # It's a pretty simple sorter and uses sort, not sort_by.
965
+ # Improvements welcome.
966
+ # Usage: provide model in constructor or using model method
967
+ # Call toggle_sort_order(column_index)
968
+ # Call sort.
969
+ # Currently, this sorts the provided model in-place. Future versions
970
+ # may maintain a copy, or use a table that provides a mapping of model to result.
971
+ # # TODO check if column_sortable
972
+ class TableRowSorter
973
+ attr_reader :sort_keys
974
+ def initialize model=nil
975
+ self.model = model
976
+ @columns_sort = []
977
+ @sort_keys = nil
978
+ end
979
+ def model=(model)
980
+ @model = model
981
+ @sort_keys = nil
982
+ end
983
+ def sortable colindex, tf
984
+ @columns_sort[colindex] = tf
985
+ end
986
+ def sortable? colindex
987
+ return false if @columns_sort[colindex]==false
988
+ return true
989
+ end
990
+ # should to_s be used for this column
991
+ def use_to_s colindex
992
+ return true # TODO
993
+ end
994
+ # sorts the model based on sort keys and reverse flags
995
+ # @sort_keys contains indices to sort on
996
+ # @reverse_flags is an array of booleans, true for reverse, nil or false for ascending
997
+ def sort
998
+ return unless @model
999
+ return if @sort_keys.empty?
1000
+ $log.debug "TABULAR SORT KEYS #{sort_keys} "
1001
+ @model.sort!{|x,y|
1002
+ res = 0
1003
+ @sort_keys.each { |ee|
1004
+ e = ee.abs-1 # since we had offsetted by 1 earlier
1005
+ abse = e.abs
1006
+ if ee < 0
1007
+ res = y[abse] <=> x[abse]
1008
+ else
1009
+ res = x[e] <=> y[e]
1010
+ end
1011
+ break if res != 0
1012
+ }
1013
+ res
1014
+ }
1015
+ end
1016
+ # toggle the sort order if given column offset is primary sort key
1017
+ # Otherwise, insert as primary sort key, ascending.
1018
+ def toggle_sort_order index
1019
+ index += 1 # increase by 1, since 0 won't multiple by -1
1020
+ # internally, reverse sort is maintained by multiplying number by -1
1021
+ @sort_keys ||= []
1022
+ if @sort_keys.first && index == @sort_keys.first.abs
1023
+ @sort_keys[0] *= -1
1024
+ else
1025
+ @sort_keys.delete index # in case its already there
1026
+ @sort_keys.delete(index*-1) # in case its already there
1027
+ @sort_keys.unshift index
1028
+ # don't let it go on increasing
1029
+ if @sort_keys.size > 3
1030
+ @sort_keys.pop
1031
+ end
1032
+ end
1033
+ end
1034
+ def set_sort_keys list
1035
+ @sort_keys = list
1036
+ end
1037
+ end #class
1038
+ # what about is_resizable XXX
1039
+ class ColumnInfo < Struct.new(:name, :width, :align, :hidden)
1040
+ end
1041
+
1042
+ # a structure that maintains position and gives
1043
+ # next and previous taking max index into account.
1044
+ # it also circles. Can be used for traversing next component
1045
+ # in a form, or container, or columns in a table.
1046
+ class Circular < Struct.new(:max_index, :current_index)
1047
+ attr_reader :last_index
1048
+ attr_reader :current_index
1049
+ def initialize m, c=0
1050
+ raise "max index cannot be nil" unless m
1051
+ @max_index = m
1052
+ @current_index = c
1053
+ @last_index = c
1054
+ end
1055
+ def next
1056
+ @last_index = @current_index
1057
+ if @current_index + 1 > @max_index
1058
+ @current_index = 0
1059
+ else
1060
+ @current_index += 1
1061
+ end
1062
+ end
1063
+ def previous
1064
+ @last_index = @current_index
1065
+ if @current_index - 1 < 0
1066
+ @current_index = @max_index
1067
+ else
1068
+ @current_index -= 1
1069
+ end
1070
+ end
1071
+ def is_last?
1072
+ @current_index == @max_index
1073
+ end
1074
+ end
1075
+ # for some compatibility with Table
1076
+ def set_data data, colnames_array
1077
+ set_content data
1078
+ columns = colnames_array
1079
+ end
1080
+ def get_column_name index
1081
+ @columns[index]
1082
+ end
1083
+ alias :column_name :get_column_name
1084
+ alias :column :get_column
1085
+ def method_missing(name, *args)
1086
+ name = name.to_s
1087
+ case name
1088
+ when 'cell_editing_allowed', 'editing_policy'
1089
+ # silently ignore to keep compatible with Table
1090
+ else
1091
+ raise NoMethodError, "Undefined method #{name} for TabularWidget"
1092
+ end
1093
+ end
1094
+
1095
+ end # class tabluarw
1096
+
1097
+ end # modul
1098
+ if __FILE__ == $PROGRAM_NAME
1099
+
1100
+ require 'rbhex/core/util/app'
1101
+ App.new do
1102
+ t = TabularWidget.new @form, :row => 2, :col => 2, :height => 20, :width => 30
1103
+ t.columns = ["Name ", "Age ", " Email "]
1104
+ t.add %w{ rahul 33 r@ruby.org }
1105
+ t << %w{ _why 133 j@gnu.org }
1106
+ t << ["jane", "1331", "jane@gnu.org" ]
1107
+ t.column_align 1, :right
1108
+ t.create_default_sorter
1109
+
1110
+ s = TabularWidget.new @form, :row => 2, :col =>32 do |b|
1111
+ b.columns = %w{ country continent text }
1112
+ b << ["india","asia","a warm country" ]
1113
+ b << ["japan","asia","a cool country" ]
1114
+ b << ["russia","europe","a hot country" ]
1115
+ #b.column_width 2, 30
1116
+ end
1117
+ s.create_default_sorter
1118
+ s = TabularWidget.new @form , :row => 12, :col => 32 do |b|
1119
+ b.columns = %w{ place continent text }
1120
+ b << ["india","asia","a warm country" ]
1121
+ b << ["japan","asia","a cool country" ]
1122
+ b << ["russia","europe","a hot country" ]
1123
+ b << ["sydney","australia","a dry country" ]
1124
+ b << ["canberra","australia","a dry country" ]
1125
+ b << ["ross island","antarctica","a dry country" ]
1126
+ b << ["mount terror","antarctica","a windy country" ]
1127
+ b << ["mt erebus","antarctica","a cold place" ]
1128
+ b << ["siberia","russia","an icy city" ]
1129
+ b << ["new york","USA","a fun place" ]
1130
+ b.column_width 0, 12
1131
+ b.column_width 1, 12
1132
+ b.column_hidden 1, true
1133
+ b.numbering = true ## FIXME BROKEN
1134
+ end
1135
+ s.create_default_sorter
1136
+ require 'rbhex/core/widgets/scrollbar'
1137
+ sb = Scrollbar.new @form, :parent => s
1138
+ #t.column_align 1, :right
1139
+ #puts t.to_s
1140
+ #puts
1141
+ end
1142
+ end