rbhex-core 1.0.0
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 +7 -0
- data/.gitignore +20 -0
- data/CHANGELOG +2000 -0
- data/LICENSE +56 -0
- data/README.md +44 -0
- data/examples/abasiclist.rb +179 -0
- data/examples/alpmenu.rb +50 -0
- data/examples/app.sample +19 -0
- data/examples/atree.rb +100 -0
- data/examples/bline.rb +136 -0
- data/examples/common/file.rb +45 -0
- data/examples/data/README.markdown +9 -0
- data/examples/data/brew.txt +38 -0
- data/examples/data/color.2 +37 -0
- data/examples/data/gemlist.txt +60 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/ports.txt +136 -0
- data/examples/data/table.txt +37 -0
- data/examples/data/tasks.csv +88 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/todo.txt +10 -0
- data/examples/data/todo.txt.bak +10 -0
- data/examples/data/todocsv.csv +28 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/data/unix2.txt +11 -0
- data/examples/dbdemo.rb +502 -0
- data/examples/dirtree.rb +94 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +92 -0
- data/examples/tabular.rb +146 -0
- data/examples/tasks.rb +178 -0
- data/examples/term2.rb +84 -0
- data/examples/testbuttons.rb +296 -0
- data/examples/testcombo.rb +102 -0
- data/examples/testfields.rb +195 -0
- data/examples/testkeypress.rb +72 -0
- data/examples/testlistbox.rb +170 -0
- data/examples/testmessagebox.rb +140 -0
- data/examples/testprogress.rb +116 -0
- data/examples/testree.rb +106 -0
- data/examples/testwsshortcuts.rb +66 -0
- data/examples/testwsshortcuts2.rb +128 -0
- data/lib/rbhex.rb +6 -0
- data/lib/rbhex/core/docs/index.txt +73 -0
- data/lib/rbhex/core/include/action.rb +80 -0
- data/lib/rbhex/core/include/actionmanager.rb +49 -0
- data/lib/rbhex/core/include/appmethods.rb +214 -0
- data/lib/rbhex/core/include/bordertitle.rb +48 -0
- data/lib/rbhex/core/include/chunk.rb +203 -0
- data/lib/rbhex/core/include/io.rb +553 -0
- data/lib/rbhex/core/include/listbindings.rb +74 -0
- data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
- data/lib/rbhex/core/include/listeditable.rb +317 -0
- data/lib/rbhex/core/include/listscrollable.rb +663 -0
- data/lib/rbhex/core/include/listselectable.rb +271 -0
- data/lib/rbhex/core/include/multibuffer.rb +83 -0
- data/lib/rbhex/core/include/orderedhash.rb +77 -0
- data/lib/rbhex/core/include/ractionevent.rb +73 -0
- data/lib/rbhex/core/include/rchangeevent.rb +27 -0
- data/lib/rbhex/core/include/rhistory.rb +95 -0
- data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
- data/lib/rbhex/core/include/vieditable.rb +172 -0
- data/lib/rbhex/core/include/widgetmenu.rb +66 -0
- data/lib/rbhex/core/system/colormap.rb +165 -0
- data/lib/rbhex/core/system/keyboard.rb +150 -0
- data/lib/rbhex/core/system/keydefs.rb +30 -0
- data/lib/rbhex/core/system/ncurses.rb +236 -0
- data/lib/rbhex/core/system/panel.rb +162 -0
- data/lib/rbhex/core/system/window.rb +913 -0
- data/lib/rbhex/core/util/ansiparser.rb +119 -0
- data/lib/rbhex/core/util/app.rb +1228 -0
- data/lib/rbhex/core/util/basestack.rb +410 -0
- data/lib/rbhex/core/util/bottomline.rb +1859 -0
- data/lib/rbhex/core/util/colorparser.rb +77 -0
- data/lib/rbhex/core/util/focusmanager.rb +31 -0
- data/lib/rbhex/core/util/padreader.rb +192 -0
- data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
- data/lib/rbhex/core/util/rdialogs.rb +574 -0
- data/lib/rbhex/core/util/viewer.rb +149 -0
- data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
- data/lib/rbhex/core/version.rb +5 -0
- data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
- data/lib/rbhex/core/widgets/box.rb +58 -0
- data/lib/rbhex/core/widgets/divider.rb +310 -0
- data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
- data/lib/rbhex/core/widgets/rcombo.rb +253 -0
- data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
- data/lib/rbhex/core/widgets/rlink.rb +30 -0
- data/lib/rbhex/core/widgets/rlist.rb +696 -0
- data/lib/rbhex/core/widgets/rmenu.rb +958 -0
- data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
- data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
- data/lib/rbhex/core/widgets/rprogress.rb +118 -0
- data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
- data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
- data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
- data/lib/rbhex/core/widgets/rtextview.rb +739 -0
- data/lib/rbhex/core/widgets/rtree.rb +768 -0
- data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
- data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
- data/lib/rbhex/core/widgets/statusline.rb +113 -0
- data/lib/rbhex/core/widgets/tabular.rb +264 -0
- data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
- data/lib/rbhex/core/widgets/textpad.rb +995 -0
- data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
- data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
- data/rbhex-core.gemspec +32 -0
- 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
|