ncumbra 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/CHANGELOG +14 -0
- data/README.md +21 -6
- data/examples/data/atp_matches_2017.csv +2887 -0
- data/examples/data/atp_matches_2018.csv +460 -0
- data/examples/data/import.sh +13 -0
- data/examples/data/schema.txt +51 -0
- data/examples/ex1.rb +2 -0
- data/examples/ex2.rb +1 -0
- data/examples/ex5.rb +1 -1
- data/examples/exbox.rb +3 -2
- data/examples/exm1.rb +1 -2
- data/examples/extab2.rb +170 -0
- data/examples/extab3.rb +306 -0
- data/examples/extabular.rb +123 -0
- data/examples/tasks.csv +88 -0
- data/lib/umbra/box.rb +38 -21
- data/lib/umbra/checkbox.rb +2 -2
- data/lib/umbra/dialog.rb +1 -2
- data/lib/umbra/eventhandler.rb +52 -4
- data/lib/umbra/field.rb +7 -3
- data/lib/umbra/form.rb +119 -55
- data/lib/umbra/keymappinghandler.rb +4 -4
- data/lib/umbra/label.rb +31 -17
- data/lib/umbra/labeledfield.rb +7 -8
- data/lib/umbra/listbox.rb +114 -341
- data/lib/umbra/messagebox.rb +93 -42
- data/lib/umbra/multiline.rb +476 -0
- data/lib/umbra/pad.rb +20 -14
- data/lib/umbra/radiobutton.rb +2 -2
- data/lib/umbra/table.rb +260 -0
- data/lib/umbra/tabular.rb +431 -0
- data/lib/umbra/textbox.rb +36 -223
- data/lib/umbra/togglebutton.rb +3 -5
- data/lib/umbra/version.rb +1 -1
- data/lib/umbra/widget.rb +72 -13
- data/lib/umbra/window.rb +59 -15
- data/lib/umbra.rb +82 -11
- data/umbra.gemspec +2 -2
- metadata +18 -4
data/lib/umbra/table.rb
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
# ----------------------------------------------------------------------------- #
|
2
|
+
# File: table.rb
|
3
|
+
# Description: widget for tabular data
|
4
|
+
# Author: j kepler http://github.com/mare-imbrium/umbra/
|
5
|
+
# Date: 2018-05-06 - 09:56
|
6
|
+
# License: MIT
|
7
|
+
# Last update: 2018-05-22 14:46
|
8
|
+
# ----------------------------------------------------------------------------- #
|
9
|
+
# table.rb Copyright (C) 2018 j kepler
|
10
|
+
|
11
|
+
##--------- Todo section ---------------
|
12
|
+
## DONE w - next column, b - previous column
|
13
|
+
## TODO paint lines as column separators. issue is panning.
|
14
|
+
## TODO starting visual column (required when scrolling)
|
15
|
+
## DONE change a value value_at(x,y, value) ; << ; delete_at
|
16
|
+
## TODO change column width interactively, hide column , move column
|
17
|
+
## TODO maybe even column_color(n, color_pair, attr)
|
18
|
+
## TODO sort on column/s.
|
19
|
+
## TODO selection will have to be added. maybe we should have extended listbox after all. Or made multiline selectable.
|
20
|
+
## DONE how to format the header
|
21
|
+
## DONE formatting rows
|
22
|
+
## DONE if we want to color specific columns based on values then I think we have to format (render) the row at the last
|
23
|
+
## moment in print_row and not in advance
|
24
|
+
## NOTE: we are setting the data in tabular, not list. So calling list() will give nil until a render has happened.
|
25
|
+
## callers will have to use data() instead of list() which is not consistent.
|
26
|
+
## NOTE: current_index in this object refers to index including header and separator. It is not the offset in the data array.
|
27
|
+
## For that we need to adjust with @data_offset.
|
28
|
+
#
|
29
|
+
require 'forwardable'
|
30
|
+
require 'umbra/tabular'
|
31
|
+
require 'umbra/multiline'
|
32
|
+
|
33
|
+
module Umbra
|
34
|
+
##
|
35
|
+
## A table of columnar data.
|
36
|
+
## This uses Tabular as a table model and extends Multiline.
|
37
|
+
#
|
38
|
+
class Table < Multiline
|
39
|
+
|
40
|
+
extend Forwardable
|
41
|
+
|
42
|
+
|
43
|
+
## tabular is the data model for Table.
|
44
|
+
## It may be passed in in the constructor, or else is created when columns and data are passed in.
|
45
|
+
attr_accessor :tabular
|
46
|
+
|
47
|
+
## color pair and attribute for header row
|
48
|
+
attr_accessor :header_color_pair, :header_attr
|
49
|
+
|
50
|
+
attr_accessor :rendered ## boolean, if data has changed, we need to re-render
|
51
|
+
|
52
|
+
|
53
|
+
## Create a Table object passing either a Tabular object or columns and list
|
54
|
+
## e.g. Table.new tabular: tabular
|
55
|
+
## Table.new columns: cols, list: mylist
|
56
|
+
##
|
57
|
+
def initialize config={}, &block
|
58
|
+
if config.key? :tabular
|
59
|
+
@tabular = config.delete(:tabular)
|
60
|
+
else
|
61
|
+
cols = config.delete(:columns)
|
62
|
+
data = config.delete(:list)
|
63
|
+
@tabular = Tabular.new cols
|
64
|
+
if data
|
65
|
+
@tabular.data = data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
@rendered = nil
|
69
|
+
super
|
70
|
+
|
71
|
+
bind_key(?w, "next column") { self.next_column }
|
72
|
+
bind_key(?b, "prev column") { self.prev_column }
|
73
|
+
bind_key(KEY_RETURN, :fire_action_event)
|
74
|
+
## NOTE: a tabular object should be existing at this point.
|
75
|
+
end
|
76
|
+
|
77
|
+
## returns the raw data as array of arrays in tabular
|
78
|
+
def data
|
79
|
+
@tabular.list
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def data=(list)
|
84
|
+
@rendered = false
|
85
|
+
@tabular.data = list
|
86
|
+
@repaint_required = true
|
87
|
+
self.focusable = true
|
88
|
+
@pstart = @current_index = 0
|
89
|
+
@pcol = 0
|
90
|
+
#$log.debug " before table data= CHANGED "
|
91
|
+
#fire_handler(:CHANGED, self) ## added 2018-05-08 -
|
92
|
+
end
|
93
|
+
|
94
|
+
## render the two-dimensional array of data as an array of Strings.
|
95
|
+
## Calculates data_offset which is the row offset from which data starts.
|
96
|
+
def render
|
97
|
+
@data_offset = 0
|
98
|
+
@data_offset +=1 if @tabular.use_separator
|
99
|
+
@data_offset +=1 if @tabular.columns
|
100
|
+
self.list = @tabular.render
|
101
|
+
end
|
102
|
+
|
103
|
+
## paint the table
|
104
|
+
def repaint
|
105
|
+
render if !@rendered
|
106
|
+
super
|
107
|
+
|
108
|
+
@rendered = true
|
109
|
+
end
|
110
|
+
|
111
|
+
## Specify how to print the header and separator.
|
112
|
+
## index can be 0 or 1
|
113
|
+
## returns an array of color_pair and attribute
|
114
|
+
def color_of_header_row index, state
|
115
|
+
arr = [ @header_color_pair || CP_MAGENTA, @header_attr || REVERSE ]
|
116
|
+
return arr if index == 0
|
117
|
+
[ arr[0], NORMAL ]
|
118
|
+
end
|
119
|
+
|
120
|
+
## Specify how the data rows are to be coloured.
|
121
|
+
## Override this to have customised row coloring.
|
122
|
+
## @return array of color_pair and attrib.
|
123
|
+
def color_of_data_row index, state, data_index
|
124
|
+
color_of_row(index, state) ## calling superclass here
|
125
|
+
end
|
126
|
+
|
127
|
+
## Print the row which could be header or data
|
128
|
+
## @param index [Integer] - index of list, starting with header and separator
|
129
|
+
def print_row(win, row, col, str, index, state)
|
130
|
+
if index <= @data_offset - 1
|
131
|
+
_print_headings(win, row, col, str, index, state)
|
132
|
+
else
|
133
|
+
data_index = index - @data_offset ## index into actual data object
|
134
|
+
_print_data(win, row, col, str, index, state, data_index)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
## Print the header row
|
139
|
+
## index [Integer] - should be 0 or 1 (1 for optional separator)
|
140
|
+
def _print_headings(win, row, col, str, index, state)
|
141
|
+
arr = color_of_header_row(index, state)
|
142
|
+
win.printstring(row, col, str, arr[0], arr[1])
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
|
147
|
+
## Print the data.
|
148
|
+
## index is index into visual row, starting 0 for headings, and 1 for separator
|
149
|
+
## data_index is index into actual data object. Use this if checking actual data array
|
150
|
+
def _print_data(win, row, col, str, index, state, data_index)
|
151
|
+
data_index = index - @data_offset ## index into actual data object
|
152
|
+
arr = color_of_data_row(index, state, data_index)
|
153
|
+
|
154
|
+
win.printstring(row, col, str, arr[0], arr[1])
|
155
|
+
end
|
156
|
+
def color_of_column ix, value, defaultcolor
|
157
|
+
raise "unused yet"
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
|
162
|
+
def row_count
|
163
|
+
@tabular.list.size
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
## return rowid (assumed to be first column)
|
168
|
+
def current_id
|
169
|
+
data = current_row_as_array()
|
170
|
+
return nil unless data
|
171
|
+
data.first
|
172
|
+
end
|
173
|
+
# How do I deal with separators and headers here - return nil
|
174
|
+
## This returns all columns including hidden so rowid can be accessed
|
175
|
+
def current_row_as_array
|
176
|
+
data_index = @current_index - @data_offset ## index into actual data object
|
177
|
+
return nil if data_index < 0 ## separator and heading
|
178
|
+
data()[data_index]
|
179
|
+
end
|
180
|
+
|
181
|
+
## returns the current row as a hash with column name as key.
|
182
|
+
def current_row_as_hash
|
183
|
+
data = current_row_as_array
|
184
|
+
return nil unless data
|
185
|
+
columns = @tabular.columns
|
186
|
+
hash = columns.zip(data).to_h
|
187
|
+
end
|
188
|
+
|
189
|
+
## Move cursor to next column
|
190
|
+
def next_column
|
191
|
+
@coffsets = @tabular._calculate_column_offsets unless @coffsets
|
192
|
+
#c = @column_pointer.next
|
193
|
+
current_column = current_column_offset() +1
|
194
|
+
if current_column > @tabular.column_count-1
|
195
|
+
current_column = 0
|
196
|
+
end
|
197
|
+
cp = @coffsets[current_column]
|
198
|
+
@curpos = cp if cp
|
199
|
+
$log.debug " next_column #{@coffsets} :::: #{cp}, curpos=#{@curpos} "
|
200
|
+
set_col_offset @curpos
|
201
|
+
#down() if c < @column_pointer.last_index
|
202
|
+
#fire_column_event :ENTER_COLUMN
|
203
|
+
end
|
204
|
+
|
205
|
+
## Move cursor to previous column
|
206
|
+
def prev_column
|
207
|
+
@coffsets = @tabular._calculate_column_offsets unless @coffsets
|
208
|
+
#c = @column_pointer.next
|
209
|
+
current_column = current_column_offset() -1
|
210
|
+
if current_column < 0 #
|
211
|
+
current_column = @tabular.column_count-1
|
212
|
+
end
|
213
|
+
cp = @coffsets[current_column]
|
214
|
+
@curpos = cp if cp
|
215
|
+
$log.debug " next_column #{@coffsets} :::: #{cp}, curpos=#{@curpos} "
|
216
|
+
set_col_offset @curpos
|
217
|
+
#down() if c < @column_pointer.last_index
|
218
|
+
#fire_column_event :ENTER_COLUMN
|
219
|
+
end
|
220
|
+
|
221
|
+
# Convert current cursor position to a table column
|
222
|
+
# calculate column based on curpos since user may not have
|
223
|
+
# used w and b keys (:next_column)
|
224
|
+
# @return [Integer] column index base 0
|
225
|
+
def current_column_offset
|
226
|
+
_calculate_column_offsets unless @coffsets
|
227
|
+
x = 0
|
228
|
+
@coffsets.each_with_index { |i, ix|
|
229
|
+
if @curpos < i
|
230
|
+
break
|
231
|
+
else
|
232
|
+
x += 1
|
233
|
+
end
|
234
|
+
}
|
235
|
+
x -= 1 # since we start offsets with 0, so first auto becoming 1
|
236
|
+
return x
|
237
|
+
end
|
238
|
+
|
239
|
+
def header_row?
|
240
|
+
@current_index == 0 and @data_offset > 0
|
241
|
+
end
|
242
|
+
|
243
|
+
## Handle case where ENTER/RETURN pressed on header row (so sorting can be done).
|
244
|
+
def fire_action_event
|
245
|
+
if header_row?
|
246
|
+
# TODO sorting here
|
247
|
+
$log.debug " PRESSED ENTER on header row, TODO sorting here"
|
248
|
+
end
|
249
|
+
super
|
250
|
+
end
|
251
|
+
|
252
|
+
## delegate calls to the tabular object
|
253
|
+
def_delegators :@tabular, :headings=, :columns= , :add, :add_row, :<< , :column_width, :column_align, :column_hide, :convert_value_to_text, :separator, :to_string, :x=, :y=, :column_unhide
|
254
|
+
def_delegators :@tabular, :columns , :numbering
|
255
|
+
def_delegators :@tabular, :column_hidden, :delete_at, :value_at
|
256
|
+
|
257
|
+
end # class
|
258
|
+
end # module
|
259
|
+
|
260
|
+
# vim: comments=sr\:##,mb\:##,el\:#/,\:## :
|
@@ -0,0 +1,431 @@
|
|
1
|
+
#!/usr/bin/env ruby -w
|
2
|
+
=begin
|
3
|
+
* Name : A Quick take on tabular data. Readonly.
|
4
|
+
* Description : To show tabular data inside a control, rather than going by the huge
|
5
|
+
Table object, I want to create a simple, minimal table data generator.
|
6
|
+
This will be thrown into a TextView for the user to navigate, select
|
7
|
+
etc.
|
8
|
+
I would use this applications where the tabular data is fairly fixed
|
9
|
+
not where i want the user to select columns, move them, expand etc.
|
10
|
+
* :
|
11
|
+
* Author : jkepler
|
12
|
+
* Date :
|
13
|
+
* Last Update : 2018-05-20 14:24
|
14
|
+
* License : MIT
|
15
|
+
=end
|
16
|
+
|
17
|
+
## Todo Section --------------
|
18
|
+
## What if user wishes to supply formatstring and override ours
|
19
|
+
## ---------------------------
|
20
|
+
|
21
|
+
|
22
|
+
# A simple tabular data generator. Given table data in arrays and a column heading row in arrays, it
|
23
|
+
# quickely generates tabular data. It only takes left and right alignment of columns into account.
|
24
|
+
# You may specify individual column widths. Else it will take the widths of the column names you supply
|
25
|
+
# in the startup array. You are encouraged to supply column widths.
|
26
|
+
# If no columns are specified, and no widths are given, it take the widths of the first row
|
27
|
+
# as a model to determine column widths.
|
28
|
+
#
|
29
|
+
module Umbra
|
30
|
+
|
31
|
+
class Tabular
|
32
|
+
GUESSCOLUMNS = 30
|
33
|
+
|
34
|
+
def yield_or_eval &block
|
35
|
+
return unless block
|
36
|
+
if block.arity > 0
|
37
|
+
yield self
|
38
|
+
else
|
39
|
+
self.instance_eval(&block)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
## stores column info internally: name, width and alignment
|
45
|
+
class ColumnInfo < Struct.new(:name, :width, :index, :offset, :align, :hidden)
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
|
50
|
+
|
51
|
+
## an array of column titles
|
52
|
+
attr_reader :columns
|
53
|
+
|
54
|
+
## data which is array of arrays: rows and columns
|
55
|
+
attr_reader :list
|
56
|
+
|
57
|
+
# boolean, does user want lines numbered
|
58
|
+
attr_accessor :numbering
|
59
|
+
|
60
|
+
attr_accessor :use_separator ## boolean. Use a separator line after heading or not.
|
61
|
+
|
62
|
+
# x is the + character used a field delim in separators
|
63
|
+
# y is the field delim used in data rows, default is pipe or bar
|
64
|
+
attr_accessor :x, :y
|
65
|
+
|
66
|
+
# takes first optional argument as array of column names
|
67
|
+
# second optional argument as array of data arrays
|
68
|
+
# @yield self
|
69
|
+
#
|
70
|
+
def initialize cols=nil, *args, &block
|
71
|
+
@chash = [] # hash of column info, not used
|
72
|
+
@_skip_columns = {} # internal, which columns not to calc width of since user has specified
|
73
|
+
@separ = @columns = @numbering = nil
|
74
|
+
@y = '|'
|
75
|
+
@x = '+'
|
76
|
+
@use_separator = false
|
77
|
+
@_hidden_columns_flag = false
|
78
|
+
self.columns = cols if cols
|
79
|
+
if !args.empty?
|
80
|
+
self.data = args
|
81
|
+
end
|
82
|
+
yield_or_eval(&block) if block_given?
|
83
|
+
end
|
84
|
+
#
|
85
|
+
# set columns names .
|
86
|
+
## NOTE that we are not clearing chash here. In case, someone changes table and columns.
|
87
|
+
# @param [Array<String>] column names, preferably padded out to width for column
|
88
|
+
def columns=(array)
|
89
|
+
#$log.debug "tabular got columns #{array.count} #{array.inspect} " if $log
|
90
|
+
@columns = array
|
91
|
+
@columns.each_with_index { |e,i|
|
92
|
+
#@chash[i] = ColumnInfo.new(c, c.to_s.length)
|
93
|
+
c = get_column(i)
|
94
|
+
c.name = e
|
95
|
+
c.width = e.to_s.length
|
96
|
+
#@chash[i] = c
|
97
|
+
#@cw[i] ||= c.to_s.length
|
98
|
+
#@calign[i] ||= :left # 2011-09-27 prevent setting later on
|
99
|
+
}
|
100
|
+
end
|
101
|
+
alias :headings= :columns=
|
102
|
+
#
|
103
|
+
# set data as an array of arrays
|
104
|
+
# @param [Array<Array>] data as array of arrays
|
105
|
+
def data=(list)
|
106
|
+
#puts "got data: #{list.size} " if !$log
|
107
|
+
#puts list if !$log
|
108
|
+
@list = list
|
109
|
+
end
|
110
|
+
|
111
|
+
# add a row of data
|
112
|
+
# @param [Array] an array containing entries for each column
|
113
|
+
def add array
|
114
|
+
#$log.debug "tabular got add #{array.count} #{array.inspect} " if $log
|
115
|
+
@list ||= []
|
116
|
+
@list << array
|
117
|
+
end
|
118
|
+
alias :<< :add
|
119
|
+
alias :add_row :add
|
120
|
+
|
121
|
+
# retrieve the column info structure for the given offset. The offset
|
122
|
+
# pertains to the visible offset not actual offset in data model.
|
123
|
+
# These two differ when we move a column.
|
124
|
+
# @return ColumnInfo object containing width align color bgcolor attrib hidden
|
125
|
+
def get_column index
|
126
|
+
return @chash[index] if @chash[index]
|
127
|
+
# create a new entry since none present
|
128
|
+
c = ColumnInfo.new
|
129
|
+
c.index = index
|
130
|
+
@chash[index] = c
|
131
|
+
return c
|
132
|
+
end
|
133
|
+
# set width of a given column, any data beyond this will be truncated at display time.
|
134
|
+
# @param [Number] column offset, starting 0
|
135
|
+
# @param [Number] width
|
136
|
+
def column_width colindex, width=:NONE
|
137
|
+
if width == :NONE
|
138
|
+
#return @cw[colindex]
|
139
|
+
return get_column(colindex).width
|
140
|
+
end
|
141
|
+
@_skip_columns[colindex] = true ## don't calculate col width for this.
|
142
|
+
get_column(colindex).width = width
|
143
|
+
self
|
144
|
+
end
|
145
|
+
|
146
|
+
def column_hidden colindex, flag=:NONE
|
147
|
+
if flag == :NONE
|
148
|
+
return get_column(colindex).hidden
|
149
|
+
#return @chide[colindex]
|
150
|
+
end
|
151
|
+
@_hidden_columns_flag = true if flag
|
152
|
+
#@chide[colindex] = flag
|
153
|
+
get_column(colindex).hidden = flag
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
157
|
+
# set alignment of given column offset
|
158
|
+
# @param [Number] column offset, starting 0
|
159
|
+
# @param [Symbol] :left, :right
|
160
|
+
def column_align colindex, lrc=:NONE
|
161
|
+
if lrc == :NONE
|
162
|
+
return get_column(colindex).align
|
163
|
+
#return @calign[colindex]
|
164
|
+
end
|
165
|
+
raise ArgumentError, "wrong alignment value sent" if ![:right, :left, :center].include? lrc
|
166
|
+
get_column(colindex).align = lrc
|
167
|
+
self
|
168
|
+
end
|
169
|
+
|
170
|
+
## return an array of visible columns names
|
171
|
+
def visible_column_names
|
172
|
+
visible = []
|
173
|
+
@chash.each_with_index do |c, ix|
|
174
|
+
if !c.hidden
|
175
|
+
if block_given?
|
176
|
+
yield c.name, ix
|
177
|
+
else
|
178
|
+
visible << c.name
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
return visible unless block_given?
|
183
|
+
end
|
184
|
+
|
185
|
+
## returns the count of visible columns based on column names.
|
186
|
+
## NOTE: what if no column names gives ???
|
187
|
+
def column_count
|
188
|
+
visible_column_names().count
|
189
|
+
end
|
190
|
+
|
191
|
+
# yields non-hidden columns (ColumnInfo) and the offset/index
|
192
|
+
# This is the order in which columns are to be printed
|
193
|
+
def each_column
|
194
|
+
@chash.each_with_index { |c, i|
|
195
|
+
next if c.hidden
|
196
|
+
yield c,i if block_given?
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
## for the given row, return visible columns as an array
|
201
|
+
## @yield column and index
|
202
|
+
def visible_columns(row)
|
203
|
+
visible = []
|
204
|
+
row.each_with_index do |e, ix|
|
205
|
+
hid = @chash[ix].hidden
|
206
|
+
if !hid
|
207
|
+
if block_given?
|
208
|
+
yield e, ix
|
209
|
+
else
|
210
|
+
visible << e
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
return visible if !block_given?
|
215
|
+
end
|
216
|
+
|
217
|
+
#
|
218
|
+
# Now returns an array with formatted data
|
219
|
+
# @return [Array<String>] array of formatted data
|
220
|
+
def render
|
221
|
+
raise "tabular:: list is nil " unless @list
|
222
|
+
$log.debug " render list:: #{@list.size} "
|
223
|
+
#$log.debug " render list:1: #{@list} "
|
224
|
+
raise "tabular:: columns is nil " unless @columns
|
225
|
+
buffer = []
|
226
|
+
@separ = nil
|
227
|
+
_guess_col_widths
|
228
|
+
rows = @list.size.to_s.length
|
229
|
+
#@rows = rows
|
230
|
+
fmstr = _prepare_format
|
231
|
+
$log.debug "tabular: fmstr:: #{fmstr}"
|
232
|
+
$log.debug "tabular: cols: #{@columns}"
|
233
|
+
#$log.debug "tabular: data: #{@list}"
|
234
|
+
|
235
|
+
str = ""
|
236
|
+
if @numbering
|
237
|
+
str = " "*(rows+1)+@y
|
238
|
+
end
|
239
|
+
#str << fmstr % visible_column_names()
|
240
|
+
str << convert_heading_to_text(visible_column_names(), fmstr)
|
241
|
+
buffer << str
|
242
|
+
#puts "-" * str.length
|
243
|
+
buffer << separator if @use_separator
|
244
|
+
if @list ## XXX why wasn't this done in _prepare_format ???? FIXME
|
245
|
+
if @numbering
|
246
|
+
fmstr = "%#{rows}d "+ @y + fmstr
|
247
|
+
end
|
248
|
+
#@list.each { |e| puts e.join(@y) }
|
249
|
+
count = 0
|
250
|
+
@list.each_with_index { |r,i|
|
251
|
+
if r == :separator
|
252
|
+
buffer << separator
|
253
|
+
next
|
254
|
+
end
|
255
|
+
if @_hidden_columns_flag
|
256
|
+
r = visible_columns(r)
|
257
|
+
end
|
258
|
+
if @numbering
|
259
|
+
r.insert 0, count+1
|
260
|
+
end
|
261
|
+
#value = convert_value_to_text r, count
|
262
|
+
value = convert_value_to_text r, fmstr, i
|
263
|
+
buffer << value
|
264
|
+
count += 1
|
265
|
+
}
|
266
|
+
end
|
267
|
+
buffer
|
268
|
+
end
|
269
|
+
|
270
|
+
## render_row
|
271
|
+
def convert_value_to_text r, fmstr, index
|
272
|
+
return fmstr % r;
|
273
|
+
end
|
274
|
+
def convert_heading_to_text r, fmstr
|
275
|
+
return fmstr % r;
|
276
|
+
end
|
277
|
+
# use this for printing out on terminal
|
278
|
+
# NOTE: Do not name this to_s as it will print the entire content in many places in debug statements
|
279
|
+
# @example
|
280
|
+
# puts t.to_s
|
281
|
+
def to_string
|
282
|
+
render().join "\n"
|
283
|
+
end
|
284
|
+
|
285
|
+
def value_at x,y, value=:NONE
|
286
|
+
if value == :NONE
|
287
|
+
return @list[x, y]
|
288
|
+
end
|
289
|
+
@list[x, y] = value
|
290
|
+
end
|
291
|
+
def delete_at ix
|
292
|
+
return unless @list
|
293
|
+
raise ArgumentError, "Argument must be within 0 and #{@list.length}" if ix < 0 or ix >= @list.length
|
294
|
+
#fire_dimension_changed
|
295
|
+
#@list.delete_at(ix + @_header_adjustment)
|
296
|
+
@list.delete_at(ix)
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
def add_separator
|
303
|
+
@list << :separator
|
304
|
+
end
|
305
|
+
|
306
|
+
## This refers to a separator line after the heading and not a field separator.
|
307
|
+
## Badly named !
|
308
|
+
def separator
|
309
|
+
return @separ if @separ
|
310
|
+
str = ""
|
311
|
+
if @numbering
|
312
|
+
str = "-"*(rows+1)+@x
|
313
|
+
end
|
314
|
+
each_column { | c, ix|
|
315
|
+
v = c.width
|
316
|
+
next if v == 0 ## hidden column
|
317
|
+
str << "-" * (v+1) + @x
|
318
|
+
}
|
319
|
+
@separ = str.chop
|
320
|
+
end
|
321
|
+
|
322
|
+
## This calculates and stores the offset at which each column starts.
|
323
|
+
## Used when going to next column or doing a find for a string in the table.
|
324
|
+
def _calculate_column_offsets
|
325
|
+
total = 0
|
326
|
+
coffsets = []
|
327
|
+
ctr = 0
|
328
|
+
## ix will have gaps in between for hidden fields
|
329
|
+
each_column { | c, ix|
|
330
|
+
v = c.width
|
331
|
+
coffsets[ctr] = total
|
332
|
+
ctr += 1
|
333
|
+
total += v + 2 ## blank space plus separator
|
334
|
+
}
|
335
|
+
return coffsets
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
private
|
340
|
+
def _guess_col_widths #:nodoc:
|
341
|
+
@list.each_with_index { |r, i|
|
342
|
+
break if i > GUESSCOLUMNS
|
343
|
+
next if r == :separator
|
344
|
+
r.each_with_index { |c, j|
|
345
|
+
## we need to skip those columns which user has specified
|
346
|
+
next if @_skip_columns[j] == true
|
347
|
+
#next if @chide[j]
|
348
|
+
next if @chash[j].hidden
|
349
|
+
x = c.to_s.length
|
350
|
+
w = @chash[j].width
|
351
|
+
if !w
|
352
|
+
@chash[j].width = x
|
353
|
+
else
|
354
|
+
@chash[j].width = x if x > w
|
355
|
+
end
|
356
|
+
}
|
357
|
+
}
|
358
|
+
end
|
359
|
+
|
360
|
+
## prepare formatstring.
|
361
|
+
## NOTE: this is not the final value.
|
362
|
+
## render adds numbering to this, if user has set numbering option.!!!!
|
363
|
+
def _prepare_format #:nodoc:
|
364
|
+
fmstr = nil
|
365
|
+
fmt = []
|
366
|
+
each_column { |c, i|
|
367
|
+
## trying a zero for hidden columns
|
368
|
+
## worked but an extra space is added below and the sep
|
369
|
+
w = c.width
|
370
|
+
case c.align
|
371
|
+
when :right
|
372
|
+
#fmt << "%.#{w}s "
|
373
|
+
fmt << "%#{w}.#{w}s "
|
374
|
+
else
|
375
|
+
fmt << "%-#{w}.#{w}s "
|
376
|
+
end
|
377
|
+
}
|
378
|
+
## the next line will put a separator after hidden columns also
|
379
|
+
fmstr = fmt.join(@y)
|
380
|
+
#puts "format: #{fmstr} " # 2011-12-09 23:09:57
|
381
|
+
return fmstr
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
if __FILE__ == $PROGRAM_NAME
|
387
|
+
include Umbra
|
388
|
+
$log = nil
|
389
|
+
t = Tabular.new(['a', 'b'], [1, 2], [3, 4])
|
390
|
+
puts t.to_string
|
391
|
+
puts
|
392
|
+
t = Tabular.new([" Name ", " Number ", " Email "])
|
393
|
+
t.add %w{ rahul 32 r@ruby.org }
|
394
|
+
t << %w{ _why 133 j@gnu.org }
|
395
|
+
t << %w{ Jane 1331 jane@gnu.org }
|
396
|
+
t.column_width 1, 10
|
397
|
+
t.column_align 1, :right
|
398
|
+
puts t.to_string
|
399
|
+
puts
|
400
|
+
|
401
|
+
s = Tabular.new do |b|
|
402
|
+
b.columns = %w{ country continent text }
|
403
|
+
b << ["india","asia","a warm country" ]
|
404
|
+
b << ["japan","asia","a cool country" ]
|
405
|
+
b << ["russia","europe","a hot country" ]
|
406
|
+
b.column_width 2, 30
|
407
|
+
end
|
408
|
+
puts s.to_string
|
409
|
+
puts
|
410
|
+
puts "::::"
|
411
|
+
puts
|
412
|
+
s = Tabular.new do |b|
|
413
|
+
b.columns = %w{ place continent text }
|
414
|
+
b << ["india","asia","a warm country" ]
|
415
|
+
b << ["japan","asia","a cool country" ]
|
416
|
+
b << ["russia","europe","a hot country" ]
|
417
|
+
b << ["sydney","australia","a dry country" ]
|
418
|
+
b << ["canberra","australia","a dry country" ]
|
419
|
+
b << ["ross island","antarctica","a dry country" ]
|
420
|
+
b << ["mount terror","antarctica","a windy country" ]
|
421
|
+
b << ["mt erebus","antarctica","a cold place" ]
|
422
|
+
b << ["siberia","russia","an icy city" ]
|
423
|
+
b << ["new york","USA","a fun place" ]
|
424
|
+
b.column_width 0, 12
|
425
|
+
b.column_width 1, 12
|
426
|
+
b.numbering = true
|
427
|
+
end
|
428
|
+
puts s.to_string
|
429
|
+
end
|
430
|
+
|
431
|
+
# vim: comments=sr\:##,mb\:##,el\:#/,\:## :
|