ncumbra 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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\:#/,\:## :
|