rbcurse-extras 0.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.
- data/README.md +75 -0
- data/VERSION +1 -0
- data/examples/data/list.txt +300 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/table.txt +36 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/inc/qdfilechooser.rb +70 -0
- data/examples/inc/rfe_renderer.rb +121 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/rfe.rb +1236 -0
- data/examples/test2.rb +670 -0
- data/examples/testeditlist.rb +78 -0
- data/examples/testtable.rb +270 -0
- data/examples/testvimsplit.rb +141 -0
- data/lib/rbcurse/extras/include/celleditor.rb +112 -0
- data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
- data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
- data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
- data/lib/rbcurse/extras/include/listkeys.rb +37 -0
- data/lib/rbcurse/extras/include/listselectable.rb +144 -0
- data/lib/rbcurse/extras/include/tableextended.rb +40 -0
- data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
- data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
- data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
- data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
- data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
- data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
- data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
- data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
- data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
- data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
- data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
- data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
- data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
- data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
- data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
- metadata +94 -0
@@ -0,0 +1,1758 @@
|
|
1
|
+
=begin
|
2
|
+
* Name: table widget
|
3
|
+
* Description:
|
4
|
+
* Author: rkumar
|
5
|
+
|
6
|
+
|
7
|
+
TODO: NOTE:
|
8
|
+
A few higher level methods check for no data but lower level ones do not.
|
9
|
+
XXX FIXME if M-tab to exit table then editing_stopped should be called.
|
10
|
+
currenty valus is lost if exiting table using Mtab or M-S-tab 2009-10-06 15:10
|
11
|
+
FIXME if a field is not printed since it is going out, tab still goes there, and celleditor
|
12
|
+
still prints there. - DONE
|
13
|
+
|
14
|
+
FIXME Increasing a column shoud decrease others till min size but not push off.
|
15
|
+
Should we have a method for changing column width online that recomputes others?
|
16
|
+
See testtable.rb - TODO a bit later
|
17
|
+
FIXME - tabbing in a row, should auto scroll to columns not displayed ?
|
18
|
+
currently it moves to next row. (examples/sqlc.rb) - DONE
|
19
|
+
|
20
|
+
* 2010-01-18 19:54 - BUFFERING related changes.
|
21
|
+
* 2011-09-30 - removed all buffer related stuff
|
22
|
+
--------
|
23
|
+
* Date: 2008-12-27 21:33
|
24
|
+
* License:
|
25
|
+
Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
26
|
+
|
27
|
+
=end
|
28
|
+
require 'logger'
|
29
|
+
require 'rbcurse'
|
30
|
+
require 'rbcurse/extras/widgets/table/tablecellrenderer'
|
31
|
+
require 'rbcurse/extras/widgets/table/tabledatecellrenderer'
|
32
|
+
require 'rbcurse/extras/include/checkboxcellrenderer'
|
33
|
+
require 'rbcurse/extras/include/listselectable'
|
34
|
+
require 'rbcurse/extras/include/listkeys'
|
35
|
+
|
36
|
+
#include Ncurses # FFI 2011-09-8
|
37
|
+
include RubyCurses
|
38
|
+
module RubyCurses
|
39
|
+
extend self
|
40
|
+
|
41
|
+
# ------ NOTE ------------------ #
|
42
|
+
# Table contains a TableModel
|
43
|
+
# Table contains a TableColumnModel (which contains TableColumn instances)
|
44
|
+
# TableColumn contains 2 TableCellRenderer: column and header
|
45
|
+
# ------------------------ #
|
46
|
+
#
|
47
|
+
#
|
48
|
+
# Due to not having method overloading, after usig new, use set_data or set_model
|
49
|
+
#
|
50
|
+
# This is a widget that displays tabular data. We will get into editing after this works out.
|
51
|
+
# This uses the MVC architecture and is WIP as of 2009-01-04 18:37
|
52
|
+
# TODO cellrenderers should be able to get parents bgcolor and color (Jtables) if none defined for them.
|
53
|
+
class Table < Widget
|
54
|
+
#include RubyCurses::EventHandler # widget does 2009-01-15 15:38
|
55
|
+
include RubyCurses::ListSelectable
|
56
|
+
include RubyCurses::ListKeys
|
57
|
+
|
58
|
+
dsl_accessor :title
|
59
|
+
dsl_accessor :title_attrib
|
60
|
+
dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
|
61
|
+
attr_accessor :current_index # the row index universally
|
62
|
+
#attr_accessor :current_column # index of column (usually in current row )
|
63
|
+
# a changed event of an editor component can utitlize this if it wishes to know
|
64
|
+
# the row or col that was exited.
|
65
|
+
attr_reader :editing_col, :editing_row # r and col being edited, set to nil on leave
|
66
|
+
attr_accessor :is_editing # boolean is only true if cell_editing_allowed
|
67
|
+
# if set editing starts when you enter a cell, otherwise you would
|
68
|
+
# have to toggle to start editing.
|
69
|
+
dsl_accessor :editing_policy # :EDITING_AUTO
|
70
|
+
dsl_accessor :size_to_fit # boolean, will size columns upon set data just to fit
|
71
|
+
dsl_accessor :estimate_widths # boolean, will size columns upon set data
|
72
|
+
|
73
|
+
# A table should only be editable if this is true regardless of other variables
|
74
|
+
# In addition, A column to be editable must either have editable as nil or true
|
75
|
+
dsl_accessor :cell_editing_allowed # 2009-01-16 22:55
|
76
|
+
|
77
|
+
def initialize form = nil, config={}, &block
|
78
|
+
_data = config.delete :data
|
79
|
+
_columns = config.delete :columns
|
80
|
+
@_column_widths = config.delete :column_widths
|
81
|
+
# if user leaves width blank but gives us col widths, that means calculate total width
|
82
|
+
if @_column_widths && config[:width].nil?
|
83
|
+
total = @_column_widths.inject(0) { |total, w| total+=w }
|
84
|
+
@width = total+2
|
85
|
+
end
|
86
|
+
@suppress_borders = false
|
87
|
+
@col_offset = @row_offset = 1
|
88
|
+
|
89
|
+
super
|
90
|
+
# added LIST event since bombing when selecting a row in table 2011-09-8 FFI
|
91
|
+
@_events.push(*[:TABLE_TRAVERSAL_EVENT,:TABLE_EDITING_EVENT, :LIST_SELECTION_EVENT])
|
92
|
+
init_vars
|
93
|
+
install_list_keys
|
94
|
+
map_keys
|
95
|
+
if _data && _columns
|
96
|
+
set_data _data, _columns
|
97
|
+
end
|
98
|
+
init_actions
|
99
|
+
end
|
100
|
+
|
101
|
+
def init_vars
|
102
|
+
@focusable= true
|
103
|
+
@current_index = 0
|
104
|
+
@current_column = 0
|
105
|
+
@oldrow = @oldcol = 0
|
106
|
+
@current_column_offset ||= 0 # added 2009-01-12 19:06 current_column's offset
|
107
|
+
@toprow = 0
|
108
|
+
@show_grid ||= 1
|
109
|
+
@_first_column_print = 0 # intro for horiz scrolling 2009-02-14 16:20
|
110
|
+
@_last_column_print = 0 # 2009-02-16 23:57 so we don't tab further etc.
|
111
|
+
# table needs to know what columns are being printed.
|
112
|
+
@curpos = 0
|
113
|
+
@inter_column_spacing = 1
|
114
|
+
# @selected_color ||= 'yellow'
|
115
|
+
# @selected_bgcolor ||= 'black'
|
116
|
+
@col_offset = @row_offset = 0 if @suppress_borders
|
117
|
+
@table_changed = true
|
118
|
+
@repaint_required = true
|
119
|
+
end
|
120
|
+
def map_keys
|
121
|
+
|
122
|
+
# alt-tab next column
|
123
|
+
# alt-shift-tab prev column
|
124
|
+
#bind_key(?\M-\C-i) { next_column }
|
125
|
+
#bind_key(481) { previous_column }
|
126
|
+
bind_key(KEY_TAB, :next_column )
|
127
|
+
bind_key(KEY_BTAB, :previous_column )
|
128
|
+
bind_key(KEY_RIGHT, :next_column )
|
129
|
+
bind_key(KEY_LEFT, :previous_column )
|
130
|
+
bind_key(@KEY_ASK_FIND_FORWARD, :ask_search_forward )
|
131
|
+
bind_key(@KEY_ASK_FIND_BACKWARD, :ask_search_backward )
|
132
|
+
bind_key(@KEY_FIND_NEXT, :find_next )
|
133
|
+
bind_key(@KEY_FIND_PREV, :find_prev ) if @KEY_FIND_PREV
|
134
|
+
# added 2010-05-12 21:41 for vim keys, will work if cell editing allowed is false
|
135
|
+
# user should be able to switch to editable and off so he can use keys TODO
|
136
|
+
# TODO vim_editable mode: C dd etc . to repeat change, how about range commands like vim
|
137
|
+
# TODO use numeric to widen, so we can distribute spacing
|
138
|
+
bind_key(?j, :next_row )
|
139
|
+
bind_key(?k, :previous_row )
|
140
|
+
bind_key(?G, :goto_bottom )
|
141
|
+
bind_key([?g,?g], :goto_top )
|
142
|
+
bind_key(?l, :next_column )
|
143
|
+
bind_key(?h, :previous_column )
|
144
|
+
bind_key(KEY_ENTER, :toggle_cell_editing )
|
145
|
+
bind_keys([?\C-n, KEY_DOWN], 'next_row') {
|
146
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
147
|
+
next_row #scroll_forward
|
148
|
+
}
|
149
|
+
bind_keys([KEY_UP, ?\C-p], 'prev_row') {
|
150
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
151
|
+
previous_row #scroll_forward
|
152
|
+
}
|
153
|
+
bind_key(@KEY_SCROLL_FORWARD, 'scroll_down') {
|
154
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
155
|
+
scroll_forward
|
156
|
+
}
|
157
|
+
bind_key(@KEY_SCROLL_BACKWARD, 'scroll_backward') {
|
158
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
159
|
+
scroll_backward
|
160
|
+
}
|
161
|
+
bind_key( @KEY_SCROLL_RIGHT, 'scroll right'){
|
162
|
+
editing_stopped if @is_editing # dts 2009-02-17 00:35
|
163
|
+
scroll_right
|
164
|
+
}
|
165
|
+
bind_key( @KEY_SCROLL_LEFT, 'scroll left') {
|
166
|
+
editing_stopped if @is_editing # dts 2009-02-17 00:35
|
167
|
+
scroll_left
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
def focussed_row
|
172
|
+
#raise "No data in table" if row_count < 1
|
173
|
+
return nil if row_count < 1
|
174
|
+
return @current_index if @current_index < row_count
|
175
|
+
@current_index = row_count-1
|
176
|
+
end
|
177
|
+
def focussed_col
|
178
|
+
return nil if row_count < 1
|
179
|
+
#raise "No data in table" if row_count < 1
|
180
|
+
@current_column
|
181
|
+
end
|
182
|
+
# added 2009-01-07 13:05 so new scrollable can use
|
183
|
+
def row_count
|
184
|
+
return 0 if @table_model.nil?
|
185
|
+
@table_model.row_count
|
186
|
+
end
|
187
|
+
# added 2009-01-07 13:05 so new scrollable can use
|
188
|
+
def scrollatrow
|
189
|
+
if @suppress_borders # NOT TESTED XXX
|
190
|
+
@height - 2 # we forgot to remove 1 from height in border.
|
191
|
+
else
|
192
|
+
@height - 4 # we forgot to remove 1 from height in border.
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# Sets the data in models
|
198
|
+
# Should replace if these are created. TODO FIXME
|
199
|
+
def set_data data, colnames_array
|
200
|
+
# next 2 added in case set_data called again
|
201
|
+
@table_changed = true
|
202
|
+
@repaint_required = true
|
203
|
+
data ||= [[]]
|
204
|
+
colnames_array ||= [""]
|
205
|
+
if data.is_a? Array
|
206
|
+
model = RubyCurses::DefaultTableModel.new data, colnames_array
|
207
|
+
table_model model
|
208
|
+
elsif data.is_a? RubyCurses::TableModel
|
209
|
+
table_model data
|
210
|
+
else
|
211
|
+
raise "set_data: don't know how to handle data: #{data.class.to_s}"
|
212
|
+
end
|
213
|
+
if colnames_array.is_a? Array
|
214
|
+
model = DefaultTableColumnModel.new colnames_array
|
215
|
+
table_column_model model
|
216
|
+
elsif colnames_array.is_a? RubyCurses::TableColumnModel
|
217
|
+
table_column_model colnames_array
|
218
|
+
else
|
219
|
+
raise "set_data: don't know how to handle column data: #{colnames_array.class.to_s}"
|
220
|
+
end
|
221
|
+
create_default_list_selection_model
|
222
|
+
create_table_header
|
223
|
+
# added 2010-09-09 19:57 if user provides col widths in hash, or size_to_fit
|
224
|
+
$log.debug " XXX @size_to_fit: #{@size_to_fit} "
|
225
|
+
if @_column_widths
|
226
|
+
$log.debug "XXXX inside set column widths "
|
227
|
+
set_column_widths @_column_widths
|
228
|
+
elsif @estimate_widths
|
229
|
+
$log.debug "XXXX inside estimate column widths "
|
230
|
+
cw = estimate_column_widths data
|
231
|
+
set_column_widths cw
|
232
|
+
elsif @size_to_fit
|
233
|
+
$log.debug " XXX inside @size_to_fit: #{@size_to_fit} "
|
234
|
+
size_columns_to_fit
|
235
|
+
end
|
236
|
+
end
|
237
|
+
def set_model tm, tcm=nil, lsm=nil
|
238
|
+
table_model tm
|
239
|
+
if tcm.nil?
|
240
|
+
create_default_table_column_model
|
241
|
+
else
|
242
|
+
table_column_model tcm
|
243
|
+
end
|
244
|
+
if lsm.nil?
|
245
|
+
create_default_list_selection_model
|
246
|
+
else
|
247
|
+
list_selection_model lsm
|
248
|
+
end
|
249
|
+
create_table_header
|
250
|
+
end
|
251
|
+
|
252
|
+
# getter and setter for table_model
|
253
|
+
def table_model(*val)
|
254
|
+
if val.empty?
|
255
|
+
@table_model
|
256
|
+
else
|
257
|
+
raise "data error" if !val[0].is_a? RubyCurses::TableModel
|
258
|
+
@table_model = val[0]
|
259
|
+
## table registers as a listener, or rather binds to event
|
260
|
+
@table_model.bind(:TABLE_MODEL_EVENT){|lde| table_data_changed(lde) }
|
261
|
+
end
|
262
|
+
end
|
263
|
+
# updated this so no param will return the tcm 2009-02-14 12:31
|
264
|
+
def table_column_model(*val)
|
265
|
+
if val.empty?
|
266
|
+
return @table_column_model
|
267
|
+
end
|
268
|
+
tcm = val[0]
|
269
|
+
raise "data error: table_column_model wrong class" if !tcm.is_a? RubyCurses::TableColumnModel
|
270
|
+
@table_column_model = tcm
|
271
|
+
@table_column_model.bind(:TABLE_COLUMN_MODEL_EVENT) {|e|
|
272
|
+
table_structure_changed e
|
273
|
+
}
|
274
|
+
@table_column_model.bind(:PROPERTY_CHANGE){|e| column_property_changed(e)}
|
275
|
+
|
276
|
+
#@table_header.column_model(tcm) unless @table_header.nil?
|
277
|
+
@table_header.table_column_model=(tcm) unless @table_header.nil?
|
278
|
+
end
|
279
|
+
# @deprecated, avoid usage
|
280
|
+
def get_table_column_model
|
281
|
+
$log.warn " DEPRECATED. Pls use table_column_model()"
|
282
|
+
@table_column_model
|
283
|
+
end
|
284
|
+
#
|
285
|
+
def create_default_table_column_model
|
286
|
+
table_column_model DefaultTableColumnModel.new
|
287
|
+
end
|
288
|
+
def create_table_header
|
289
|
+
@table_header = TableHeader.new @table_column_model
|
290
|
+
end
|
291
|
+
|
292
|
+
#--- selection methods ---#
|
293
|
+
def is_column_selected col
|
294
|
+
raise "TODO "
|
295
|
+
end
|
296
|
+
def is_cell_selected row, col
|
297
|
+
raise "TODO "
|
298
|
+
end
|
299
|
+
def add_column_selection_interval ix0, ix1
|
300
|
+
raise "TODO "
|
301
|
+
# if column_selection_allowed
|
302
|
+
end
|
303
|
+
def remove_column_selection_interval ix0, ix1
|
304
|
+
raise "TODO "
|
305
|
+
end
|
306
|
+
|
307
|
+
def selected_column
|
308
|
+
@table_column_model.selected_columns[0]
|
309
|
+
end
|
310
|
+
def selected_columns
|
311
|
+
@table_column_model.selected_columns
|
312
|
+
end
|
313
|
+
def selected_column_count
|
314
|
+
@table_column_model.selected_column_count
|
315
|
+
end
|
316
|
+
|
317
|
+
#--- row and column methods ---#
|
318
|
+
|
319
|
+
##
|
320
|
+
# getter and setter for current_column index
|
321
|
+
def current_column(*val)
|
322
|
+
if val.empty?
|
323
|
+
@current_column || 0
|
324
|
+
else
|
325
|
+
@oldcol = @current_column
|
326
|
+
v = val[0]
|
327
|
+
v = 0 if v < 0
|
328
|
+
v = @table_column_model.column_count-1 if v > @table_column_model.column_count-1
|
329
|
+
@current_column = v
|
330
|
+
if @current_column != @oldcol
|
331
|
+
on_leave_column @oldcol
|
332
|
+
on_enter_column @current_column
|
333
|
+
end
|
334
|
+
set_form_col
|
335
|
+
@oldcol = @current_column # added on 2009-01-16 19:40 for next_col
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
|
340
|
+
def add_column tc
|
341
|
+
@table_column_model << tc
|
342
|
+
#table_structure_changed # this should be called by tcm TODO with object
|
343
|
+
end
|
344
|
+
def remove_column tc
|
345
|
+
@table_column_model.remove_column tc
|
346
|
+
#table_structure_changed # this should be called by tcm TODO with object
|
347
|
+
end
|
348
|
+
def get_column identifier
|
349
|
+
ix = @table_column_model.column_index identifier
|
350
|
+
return @table_column_model.column ix
|
351
|
+
end
|
352
|
+
##
|
353
|
+
# returns col by col ix added on 2009-01-16 23:45
|
354
|
+
def column ix
|
355
|
+
@table_column_model.column(ix)
|
356
|
+
end
|
357
|
+
def get_column_name ix
|
358
|
+
@table_column_model.column(ix).identifier
|
359
|
+
end
|
360
|
+
def move_column ix, newix
|
361
|
+
@table_column_model.move_column ix, newix
|
362
|
+
#table_structure_changed # this should be called by tcm TODO with object
|
363
|
+
end
|
364
|
+
|
365
|
+
#--- row and column methods of Table ---#
|
366
|
+
# must not give wrong results when columns switched!
|
367
|
+
def get_value_at row, col
|
368
|
+
return nil if row.nil? || col.nil? # 2011-09-29
|
369
|
+
model_index = @table_column_model.column(col).model_index
|
370
|
+
@table_model.get_value_at row, model_index
|
371
|
+
end
|
372
|
+
# must not give wrong results when columns switched!
|
373
|
+
def set_value_at row, col, value
|
374
|
+
model_index = @table_column_model.column(col).model_index
|
375
|
+
@table_model.set_value_at row, model_index, value
|
376
|
+
end
|
377
|
+
|
378
|
+
#--- event listener support methods (p521) TODO ---#
|
379
|
+
|
380
|
+
def table_data_changed tabmodev
|
381
|
+
#$log.debug " def table_data_changed got #{tabmodev}"
|
382
|
+
@repaint_required = true
|
383
|
+
# next was required otherwise on_enter would bomb if data changed from outside
|
384
|
+
if row_count == 0
|
385
|
+
init_vars
|
386
|
+
set_form_col # added 2009-01-24 14:32 since cursor was still showing on existing col
|
387
|
+
return # added 2009-01-23 15:15
|
388
|
+
end
|
389
|
+
# the next block to be only called if user is inside editing. Often data will be refreshed by
|
390
|
+
# a search field and this gets called.
|
391
|
+
if @is_editing
|
392
|
+
@is_editing = false # 2009-01-19 18:18 testing this out XXX
|
393
|
+
# we need to refresh the editor if you deleted a row while sitting on it
|
394
|
+
# otherwise it shows the old value
|
395
|
+
editing_started
|
396
|
+
end
|
397
|
+
end
|
398
|
+
def table_structure_changed tablecolmodelevent
|
399
|
+
$log.debug " def table_structure_changed #{tablecolmodelevent}"
|
400
|
+
@table_changed = true
|
401
|
+
@repaint_required = true
|
402
|
+
init_vars
|
403
|
+
end
|
404
|
+
def column_property_changed evt
|
405
|
+
$log.debug "JT def column_property_changed #{evt} "
|
406
|
+
@table_changed = true
|
407
|
+
@repaint_required = true
|
408
|
+
end
|
409
|
+
=begin
|
410
|
+
# this could be complicating things. I don't need it in here.
|
411
|
+
def column_added tabcolmodev
|
412
|
+
@repaint_required = true
|
413
|
+
end
|
414
|
+
def column_removed tabcolmodev
|
415
|
+
@repaint_required = true
|
416
|
+
end
|
417
|
+
def column_moved tabcolmodev
|
418
|
+
@repaint_required = true
|
419
|
+
end
|
420
|
+
=end
|
421
|
+
## to do for TrueClass and FalseClass
|
422
|
+
def prepare_renderers
|
423
|
+
@crh = Hash.new
|
424
|
+
@crh['String'] = TableCellRenderer.new "", {"parent" => self }
|
425
|
+
@crh['Fixnum'] = TableCellRenderer.new "", { "justify" => :right, "parent" => self}
|
426
|
+
@crh['Float'] = TableCellRenderer.new "", {"justify" => :right, "parent" => self}
|
427
|
+
@crh['TrueClass'] = CheckBoxCellRenderer.new "", {"parent" => self, "display_length"=>7}
|
428
|
+
@crh['FalseClass'] = CheckBoxCellRenderer.new "", {"parent" => self, "display_length"=>7}
|
429
|
+
@crh['Time'] = TableDateCellRenderer.new "", {"parent" => self, "display_length"=>16}
|
430
|
+
#@crh['String'] = TableCellRenderer.new "", {"bgcolor" => "cyan", "color"=>"white", "parent" => self}
|
431
|
+
#@crh['Fixnum'] = TableCellRenderer.new "", {"display_length" => 6, "justify" => :right, "color"=>"blue","bgcolor"=>"cyan" }
|
432
|
+
#@crh['Float'] = TableCellRenderer.new "", {"display_length" => 6, "justify" => :right, "color"=>"blue", "bgcolor"=>"cyan" }
|
433
|
+
end
|
434
|
+
# this is vry temporary and will change as we begin to use models - i need to pick
|
435
|
+
# columns renderer
|
436
|
+
def get_default_cell_renderer_for_class cname
|
437
|
+
@crh || prepare_renderers
|
438
|
+
@crh[cname] || @crh['String']
|
439
|
+
end
|
440
|
+
def set_default_cell_renderer_for_class cname, rend
|
441
|
+
@crh ||= {}
|
442
|
+
@crh[cname]=rend
|
443
|
+
end
|
444
|
+
## override for cell or row behaviour
|
445
|
+
def get_cell_renderer row, col
|
446
|
+
# get columns renderer else class default
|
447
|
+
column = @table_column_model.column(col)
|
448
|
+
rend = column.cell_renderer
|
449
|
+
return rend # can be nil
|
450
|
+
end
|
451
|
+
#
|
452
|
+
# ------- editing methods---------- #
|
453
|
+
def get_cell_editor row, col
|
454
|
+
$log.debug " def get_cell_editor #{row}, #{col}"
|
455
|
+
column = @table_column_model.column(col)
|
456
|
+
return nil if column.editable == false or (column.editable.nil? and @cell_editing_allowed!=true)
|
457
|
+
editor = column.cell_editor
|
458
|
+
return editor # can be nil
|
459
|
+
end
|
460
|
+
def edit_cell_at row, col
|
461
|
+
acolumn = column(col)
|
462
|
+
if acolumn.editable == false or (acolumn.editable.nil? and @cell_editing_allowed!=true)
|
463
|
+
$log.debug " editing not allowed in #{col}"
|
464
|
+
@is_editing = false
|
465
|
+
return nil
|
466
|
+
end
|
467
|
+
return nil if row >= row_count
|
468
|
+
value = get_value_at row, col
|
469
|
+
editor = get_cell_editor row, col
|
470
|
+
@old_cell_value = value # for event
|
471
|
+
if editor.nil?
|
472
|
+
|
473
|
+
cls = value.nil? ? get_value_at(0,col).class.to_s : value.class.to_s
|
474
|
+
if value.nil?
|
475
|
+
case cls
|
476
|
+
when 'String'
|
477
|
+
value = value.to_s
|
478
|
+
when 'Fixnum'
|
479
|
+
value = value.to_i
|
480
|
+
when 'Float'
|
481
|
+
value = value.to_f
|
482
|
+
else
|
483
|
+
value = value.to_s
|
484
|
+
end
|
485
|
+
end
|
486
|
+
editor = get_default_cell_editor_for_class cls
|
487
|
+
#$log.debug "EDIT_CELL_AT:1 #{cls} #{editor.component.display_length} = #{@table_column_model.column(col).width}i maxlen #{editor.component.maxlen}"
|
488
|
+
editor.component.display_length = @table_column_model.column(col).width
|
489
|
+
# maxlen won't be nil ! This used to work earlier
|
490
|
+
#editor.component.maxlen = editor.component.display_length if editor.component.respond_to? :maxlen and editor.component.maxlen.nil? # 2009-01-18 00:59 XXX don't overwrite if user has set
|
491
|
+
if editor.component.respond_to? :maxlen
|
492
|
+
editor.component.maxlen = @table_column_model.column(col).edit_length || editor.component.display_length
|
493
|
+
end
|
494
|
+
#$log.debug "EDIT_CELL_AT: #{cls} #{editor.component.display_length} = #{@table_column_model.column(col).width}i maxlen #{editor.component.maxlen}"
|
495
|
+
end
|
496
|
+
#$log.debug " got an EDITOR #{editor} :: #{editor.component} "
|
497
|
+
# by now we should have something to edit with. We just need to prepare the widgey.
|
498
|
+
prepare_editor editor, row, col, value
|
499
|
+
|
500
|
+
end
|
501
|
+
def prepare_editor editor, row, col, value
|
502
|
+
r,c = rowcol
|
503
|
+
row = r + (row - @toprow) +1 # @form.row , 1 added for header row!
|
504
|
+
col = c+get_column_offset()
|
505
|
+
editor.prepare_editor self, row, col, value
|
506
|
+
# added on 2009-02-16 23:49
|
507
|
+
# if data is longer than can be displayed then update editors disp len too
|
508
|
+
if (col+editor.component.display_length)>= @col+@width
|
509
|
+
editor.component.display_length = @width-1-col
|
510
|
+
$log.debug "DDDXXX #{editor.component.display_length} = @width-1-col"
|
511
|
+
else
|
512
|
+
$log.debug "EEE if (#{col+editor.component.display_length})> #{@col+@width}"
|
513
|
+
end
|
514
|
+
@cell_editor = editor
|
515
|
+
@repaint_required = true
|
516
|
+
# copied from rlistbox, so that editors write on parent's graphic, otherwise
|
517
|
+
# their screen updates get overwritten by parent. 2010-01-19 20:17
|
518
|
+
set_form_col
|
519
|
+
end
|
520
|
+
## Its too late to call components on_leave here
|
521
|
+
# since cursor would have moved elsewhere.
|
522
|
+
# Prior to moving out of a field, the on_leave should be called and exceptions caught FIXME
|
523
|
+
def cancel_editor
|
524
|
+
# not really required, the refresh was required. Ok, now i call components on_leave inside
|
525
|
+
#@cell_editor.cancel_editor
|
526
|
+
@editing_row, @editing_col = nil, nil
|
527
|
+
@is_editing = false
|
528
|
+
@repaint_required = true
|
529
|
+
end
|
530
|
+
def get_default_cell_editor_for_class cname
|
531
|
+
@ceh ||= {}
|
532
|
+
cname = 'Boolean' if cname == 'TrueClass' or cname == 'FalseClass'
|
533
|
+
if @ceh.include? cname
|
534
|
+
return @ceh[cname]
|
535
|
+
else
|
536
|
+
case cname
|
537
|
+
when 'String'
|
538
|
+
# I do not know cell width here, you will have toset display_length NOTE
|
539
|
+
ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 8, :name => "tb_field_str"}
|
540
|
+
@ceh['String'] = ce
|
541
|
+
return ce
|
542
|
+
when 'Fixnum'
|
543
|
+
ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 5, :name => "tb_field_num"}
|
544
|
+
@ceh[cname] = ce
|
545
|
+
return ce
|
546
|
+
when 'Float'
|
547
|
+
ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 5, :name => "tb_field_flt"}
|
548
|
+
@ceh[cname] = ce
|
549
|
+
return ce
|
550
|
+
when "Boolean" #'TrueClass', 'FalseClass'
|
551
|
+
ce = RubyCurses::CellEditor.new(RubyCurses::CheckBox.new nil, {"display_length"=> 0})
|
552
|
+
@ceh[cname] = ce
|
553
|
+
return ce
|
554
|
+
else
|
555
|
+
$log.debug " get_default_cell_editor_for_class UNKNOWN #{cname}"
|
556
|
+
ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 6, :name => "tb_field_unk"}
|
557
|
+
@ceh[cname] = ce
|
558
|
+
return ce
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|
562
|
+
# returns true if editing is occurring
|
563
|
+
#def is_editing?
|
564
|
+
# @editing
|
565
|
+
#end
|
566
|
+
|
567
|
+
# ----------------- #
|
568
|
+
|
569
|
+
##
|
570
|
+
# key handling
|
571
|
+
# make separate methods so callable programmatically
|
572
|
+
def handle_key(ch)
|
573
|
+
return :UNHANDLED if @table_model.nil?
|
574
|
+
@current_index ||= 0
|
575
|
+
@toprow ||= 0
|
576
|
+
h = scrollatrow()
|
577
|
+
rc = @table_model.row_count
|
578
|
+
# if we are editing let the component handle the key
|
579
|
+
# - unless its ESC, ENTER or C-c in which case we do our own
|
580
|
+
# processing, or coming out of edit mode
|
581
|
+
if @is_editing && (ch != 27 && ch != ?\C-c && ch != 13)
|
582
|
+
$log.debug " sending ch #{ch} to cell editor"
|
583
|
+
ret = @cell_editor.component.handle_key(ch)
|
584
|
+
@repaint_required = true
|
585
|
+
$log.debug "RET #{ret} got from to cell editor"
|
586
|
+
#set_form_col if ret != :UNHANDLED # added 2010-01-30 20:17 CURSOR POS TABBEDPANE
|
587
|
+
return if ret != :UNHANDLED
|
588
|
+
end
|
589
|
+
case ch
|
590
|
+
when ?\C-c, ?\C-g # earlier ESC was here, but in vim ESC completes edit
|
591
|
+
editing_canceled
|
592
|
+
#when KEY_ENTER, 10, 13
|
593
|
+
## actually it should fall through to the else
|
594
|
+
#return :UNHANDLED unless @cell_editing_allowed
|
595
|
+
#toggle_cell_editing
|
596
|
+
|
597
|
+
when @KEY_ROW_SELECTOR # ?\C-x #32
|
598
|
+
#add_row_selection_interval @current_index, @current_index
|
599
|
+
toggle_row_selection @current_index #, @current_index
|
600
|
+
@repaint_required = true
|
601
|
+
#when ?\C-n.getbyte(0)
|
602
|
+
#editing_stopped if @is_editing # 2009-01-16 16:06
|
603
|
+
#scroll_forward
|
604
|
+
#when ?\C-p.getbyte(0)
|
605
|
+
#editing_stopped if @is_editing # 2009-01-16 16:06
|
606
|
+
#scroll_backward
|
607
|
+
when @KEY_GOTO_TOP # removed 48 (0) from here so we can trap numbers
|
608
|
+
# please note that C-[ gives 27, same as esc so will respond after ages
|
609
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
610
|
+
goto_top
|
611
|
+
when @KEY_GOTO_BOTTOM
|
612
|
+
editing_stopped if @is_editing # 2009-01-16 16:06
|
613
|
+
goto_bottom
|
614
|
+
#when @KEY_SCROLL_RIGHT
|
615
|
+
#editing_stopped if @is_editing # dts 2009-02-17 00:35
|
616
|
+
#scroll_right
|
617
|
+
#when @KEY_SCROLL_LEFT
|
618
|
+
#editing_stopped if @is_editing # dts 2009-02-17 00:35
|
619
|
+
#scroll_left
|
620
|
+
when ?0.getbyte(0)..?9.getbyte(0)
|
621
|
+
$multiplier *= 10 ; $multiplier += (ch-48)
|
622
|
+
#$log.debug " setting mult to #{$multiplier} in list "
|
623
|
+
return 0
|
624
|
+
else
|
625
|
+
# there could be a case of editing here too!
|
626
|
+
ret = process_key ch, self
|
627
|
+
$multiplier = 0
|
628
|
+
return :UNHANDLED if ret == :UNHANDLED
|
629
|
+
end
|
630
|
+
return 0 # added 2010-03-14 13:27
|
631
|
+
end
|
632
|
+
def editing_canceled
|
633
|
+
return unless @cell_editing_allowed
|
634
|
+
@is_editing = false if @is_editing
|
635
|
+
cancel_editor
|
636
|
+
end
|
637
|
+
def toggle_cell_editing
|
638
|
+
return unless @cell_editing_allowed
|
639
|
+
@is_editing = !@is_editing
|
640
|
+
if @is_editing
|
641
|
+
editing_started
|
642
|
+
else
|
643
|
+
editing_stopped
|
644
|
+
end
|
645
|
+
end
|
646
|
+
def editing_started
|
647
|
+
return if !@cell_editing_allowed or row_count < 1
|
648
|
+
@is_editing = true # 2009-01-16 16:14
|
649
|
+
$log.debug " turning on editing cell at #{focussed_row}, #{focussed_col}"
|
650
|
+
# on deleting last row, we need to go back 2009-01-19 18:31
|
651
|
+
if focussed_row >= row_count
|
652
|
+
bounds_check
|
653
|
+
end
|
654
|
+
@editing_row, @editing_col = focussed_row(), focussed_col()
|
655
|
+
edit_cell_at focussed_row(), focussed_col()
|
656
|
+
end
|
657
|
+
# EDST
|
658
|
+
# the defualt values are useful when user is ON the field and pressed ENTER
|
659
|
+
# when leaving a cell, this should have oldrow and oldcol, not default values
|
660
|
+
# this throws an exception if validation on field fails NOTE
|
661
|
+
def editing_stopped row=focussed_row(), col=focussed_col()
|
662
|
+
return unless @cell_editing_allowed or @is_editing == false or column(col).editable == false
|
663
|
+
return if row_count < 1
|
664
|
+
$log.debug "editing_stopped set_value_at(#{row}, #{col}: #{@cell_editor.getvalue}"
|
665
|
+
# next line should be in on_leave_cell but that's not being called FIXME from everywhere
|
666
|
+
@cell_editor.on_leave row,col # added here since this is called whenever a cell is exited
|
667
|
+
|
668
|
+
value = @cell_editor.getvalue
|
669
|
+
if value != @old_cell_value
|
670
|
+
set_value_at(row, col, @cell_editor.getvalue) #.dup 2009-01-10 21:42 boolean can't duplicate
|
671
|
+
if @table_editing_event.nil?
|
672
|
+
@table_editing_event ||= TableEditingEvent.new row, col, self, @old_cell_value, value, :EDITING_STOPPED
|
673
|
+
else
|
674
|
+
@table_editing_event.set row, col, self, @old_cell_value, value, :EDITING_STOPPED
|
675
|
+
end
|
676
|
+
fire_handler :TABLE_EDITING_EVENT, @table_editing_event
|
677
|
+
end
|
678
|
+
cancel_editor
|
679
|
+
end
|
680
|
+
##
|
681
|
+
#def previous_row
|
682
|
+
def previous_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
683
|
+
@oldrow = @current_index
|
684
|
+
# @current_index -= 1 if @current_index > 0
|
685
|
+
num.times {
|
686
|
+
@current_index -= 1 if @current_index > 0
|
687
|
+
}
|
688
|
+
$multiplier = 0
|
689
|
+
bounds_check
|
690
|
+
end
|
691
|
+
# goto next row
|
692
|
+
# added multipler 2010-05-12 20:51
|
693
|
+
def next_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
694
|
+
rc = row_count
|
695
|
+
@oldrow = @current_index
|
696
|
+
# don't go on if rc 2009-01-16 19:55 XXX
|
697
|
+
if @current_index < rc
|
698
|
+
@current_index += 1*num if @current_index < rc
|
699
|
+
bounds_check
|
700
|
+
end
|
701
|
+
$multiplier = 0
|
702
|
+
end
|
703
|
+
# move focus to next column
|
704
|
+
# 2009-10-07 12:47 behavior change. earlier this would move to next row
|
705
|
+
# if focus was on last visible field. Now it scrolls so that first invisible
|
706
|
+
# field becomes the first column.
|
707
|
+
# # 2010-05-13 12:42 added multiplier
|
708
|
+
#def next_column
|
709
|
+
def next_column num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
710
|
+
$multiplier = 0 # so not used again in next row
|
711
|
+
#v = @current_column+1
|
712
|
+
v = @current_column + num
|
713
|
+
# normal situation, there is a next column go to
|
714
|
+
if v < @table_column_model.column_count
|
715
|
+
if v <= @_last_column_print
|
716
|
+
$log.debug " if v < #{@table_column_model.column_count} nd lastcolprint "
|
717
|
+
current_column v
|
718
|
+
else
|
719
|
+
# there is a col but its not visible
|
720
|
+
# XXX inefficient but i scroll completely to next column (putting it at start)
|
721
|
+
# otherwise sometimes it was still not visible if last column
|
722
|
+
(v-@_first_column_print).times(){scroll_right}
|
723
|
+
current_column v
|
724
|
+
set_form_col
|
725
|
+
end
|
726
|
+
|
727
|
+
else
|
728
|
+
if @current_index < row_count()-1
|
729
|
+
$log.debug " GOING TO NEXT ROW FROM NEXT COL : #{@current_index} : #{row_count}"
|
730
|
+
@current_column = 0
|
731
|
+
#@current_column = @_first_column_print # added 2009-02-17 00:01
|
732
|
+
@_first_column_print = 0 # added 2009-10-07 11:25
|
733
|
+
next_row 1
|
734
|
+
set_form_col
|
735
|
+
@repaint_required = true
|
736
|
+
@table_changed = true # so columns are modified by print_header
|
737
|
+
else
|
738
|
+
return :UNHANDLED
|
739
|
+
end
|
740
|
+
end
|
741
|
+
end
|
742
|
+
# move focus to previous column
|
743
|
+
# if you are on first column, check if scrolling required, else move up to
|
744
|
+
# last *visible* column of prev row
|
745
|
+
#def previous_column
|
746
|
+
def previous_column num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
747
|
+
v = @current_column - num # 2010-05-13 12:44 1 to num
|
748
|
+
# returning unhandled so focus can go to prev field auto
|
749
|
+
if v < @_first_column_print and @current_index <= 0
|
750
|
+
return :UNHANDLED
|
751
|
+
end
|
752
|
+
if v < @_first_column_print
|
753
|
+
if v > 0
|
754
|
+
scroll_left
|
755
|
+
current_column v
|
756
|
+
elsif @current_index > 0
|
757
|
+
@current_column = @table_column_model.column_count-1
|
758
|
+
@current_column = @_last_column_print # added 2009-02-17 00:01
|
759
|
+
$log.debug " XXXXXX prev col #{@current_column}, las #{@_last_column_print}, fi: #{@_first_column_print}"
|
760
|
+
set_form_col
|
761
|
+
previous_row 1
|
762
|
+
end
|
763
|
+
else
|
764
|
+
current_column v
|
765
|
+
end
|
766
|
+
end
|
767
|
+
def goto_bottom
|
768
|
+
@oldrow = @current_index
|
769
|
+
rc = row_count
|
770
|
+
@current_index = rc -1
|
771
|
+
bounds_check
|
772
|
+
end
|
773
|
+
def goto_top
|
774
|
+
@oldrow = @current_index
|
775
|
+
# actually goto_top should onl;y goto top but I am making a change
|
776
|
+
# for vim's gg behaviour which will take you to line number if
|
777
|
+
# given, actually this should only happen on gg not some othey key
|
778
|
+
# that may specifically be for goto_top. 2012-01-2
|
779
|
+
@current_index = $multiplier || 0
|
780
|
+
#@current_index = 0
|
781
|
+
bounds_check
|
782
|
+
end
|
783
|
+
def scroll_backward
|
784
|
+
@oldrow = @current_index
|
785
|
+
h = scrollatrow()
|
786
|
+
@current_index -= h
|
787
|
+
bounds_check
|
788
|
+
end
|
789
|
+
def scroll_forward
|
790
|
+
@oldrow = @current_index
|
791
|
+
h = scrollatrow()
|
792
|
+
rc = row_count
|
793
|
+
# more rows than box
|
794
|
+
if h < rc
|
795
|
+
@toprow += h+1 #if @current_index+h < rc
|
796
|
+
@current_index = @toprow
|
797
|
+
else
|
798
|
+
# fewer rows than box
|
799
|
+
@current_index = rc -1
|
800
|
+
end
|
801
|
+
#@current_index += h+1 #if @current_index+h < rc
|
802
|
+
bounds_check
|
803
|
+
end
|
804
|
+
|
805
|
+
def bounds_check
|
806
|
+
h = scrollatrow()
|
807
|
+
rc = row_count
|
808
|
+
|
809
|
+
@current_index = 0 if @current_index < 0 # not lt 0
|
810
|
+
@current_index = rc-1 if @current_index >= rc # not gt rowcount
|
811
|
+
@toprow = rc-h-1 if rc > h and @toprow > rc - h - 1 # toprow shows full page if possible
|
812
|
+
# curr has gone below table, move toprow forward
|
813
|
+
if @current_index - @toprow > h
|
814
|
+
@toprow = @current_index - h
|
815
|
+
elsif @current_index < @toprow
|
816
|
+
# curr has gone above table, move toprow up
|
817
|
+
@toprow = @current_index
|
818
|
+
end
|
819
|
+
|
820
|
+
if @oldrow != @current_index
|
821
|
+
#$log.debug "going to call on leave and on enter"
|
822
|
+
on_leave_row @oldrow #if respond_to? :on_leave_row # to be defined by widget that has included this
|
823
|
+
on_enter_row @current_index #if respond_to? :on_enter_row # to be defined by widget that has included this
|
824
|
+
end
|
825
|
+
set_form_row
|
826
|
+
@oldrow = @current_index
|
827
|
+
@repaint_required = true
|
828
|
+
end
|
829
|
+
def on_leave_row arow
|
830
|
+
#$log.debug " def on_leave_row #{arow}"
|
831
|
+
#on_leave_cell arow, @current_column
|
832
|
+
on_leave_cell arow, @oldcol # 2009-01-16 19:41 XXX trying outt
|
833
|
+
end
|
834
|
+
def on_leave_column acol
|
835
|
+
#$log.debug " def on_leave_column #{acol}"
|
836
|
+
#on_leave_cell @current_index, acol
|
837
|
+
on_leave_cell @oldrow, acol
|
838
|
+
end
|
839
|
+
def on_enter_row arow
|
840
|
+
#$log.debug " def on_enter_row #{arow}"
|
841
|
+
on_enter_cell arow, @current_column
|
842
|
+
end
|
843
|
+
def on_enter_column acol
|
844
|
+
#$log.debug " def on_enter_column #{acol}"
|
845
|
+
on_enter_cell @current_index, acol
|
846
|
+
end
|
847
|
+
## OLCE
|
848
|
+
def on_leave_cell arow, acol
|
849
|
+
$log.debug " def on_leave_cell #{arow}, #{acol}"
|
850
|
+
#if @editing_policy == :EDITING_AUTO # actually this should happen in all cases
|
851
|
+
if @is_editing # 2009-01-17 00:49
|
852
|
+
editing_stopped arow, acol
|
853
|
+
end
|
854
|
+
end
|
855
|
+
## OECE
|
856
|
+
def on_enter_cell arow, acol
|
857
|
+
$log.debug " def on_enter_cell #{arow}, #{acol}"
|
858
|
+
if @table_traversal_event.nil?
|
859
|
+
@table_traversal_event ||= TableTraversalEvent.new @oldrow, @oldcol, arow, acol, self
|
860
|
+
else
|
861
|
+
@table_traversal_event.set(@oldrow, @oldcol, arow, acol, self)
|
862
|
+
end
|
863
|
+
fire_handler :TABLE_TRAVERSAL_EVENT, @table_traversal_event
|
864
|
+
if @editing_policy == :EDITING_AUTO
|
865
|
+
editing_started
|
866
|
+
end
|
867
|
+
end
|
868
|
+
# on enter of widget
|
869
|
+
# the cursor should be appropriately positioned
|
870
|
+
def on_enter
|
871
|
+
super
|
872
|
+
set_form_row
|
873
|
+
set_form_col # 2009-01-17 01:35
|
874
|
+
on_enter_cell focussed_row(), focussed_col() unless focussed_row().nil? or focussed_col().nil?
|
875
|
+
end
|
876
|
+
def on_leave
|
877
|
+
super
|
878
|
+
$log.debug " on leave of table 2009-01-16 21:58 "
|
879
|
+
editing_stopped if @is_editing # 2009-01-16 21:58
|
880
|
+
end
|
881
|
+
def set_form_row
|
882
|
+
r,c = rowcol
|
883
|
+
@rows_panned ||= 0 # RFED16 2010-02-19 10:00
|
884
|
+
win_row = 0
|
885
|
+
#win_row=@form.window.top # 2010-01-18 20:28 added
|
886
|
+
# +1 is due to header
|
887
|
+
#@form.row = r + (@current_index-@toprow) + 1
|
888
|
+
frow = r + (@current_index-@toprow) + 1 + win_row + @rows_panned
|
889
|
+
setrowcol(frow, nil) # 2010-01-18 20:04
|
890
|
+
end
|
891
|
+
# set cursor on correct column, widget
|
892
|
+
def set_form_col col=@curpos
|
893
|
+
@curpos = col
|
894
|
+
@cols_panned ||= 0 # RFED16 2010-02-19 10:00
|
895
|
+
@current_column_offset = get_column_offset
|
896
|
+
#@form.col = @col + @col_offset + @curpos + @current_column_offset
|
897
|
+
#win_col=@form.window.left
|
898
|
+
win_col = 0 # RFED16 2010-02-19 10:00
|
899
|
+
fcol = @col + @col_offset + @curpos + @current_column_offset + @cols_panned + win_col
|
900
|
+
setrowcol(nil, fcol) # 2010-01-18 20:04
|
901
|
+
end
|
902
|
+
# protected
|
903
|
+
def get_column_offset columnid=@current_column
|
904
|
+
return 0 if @table_column_model.nil?
|
905
|
+
return @table_column_model.column(columnid).column_offset || 0
|
906
|
+
end
|
907
|
+
|
908
|
+
|
909
|
+
def repaint
|
910
|
+
return unless @repaint_required
|
911
|
+
my_win = @form ? @form.window : @target_window
|
912
|
+
@graphic = my_win unless @graphic
|
913
|
+
#$log.warn "neither form not target window given!!! TV paint 368" unless my_win
|
914
|
+
raise " #{@name} neither form, nor target window given TV paint " unless my_win
|
915
|
+
raise " #{@name} NO GRAPHIC set as yet TV paint " unless @graphic
|
916
|
+
@win_left = my_win.left # unused remove TODO
|
917
|
+
@win_top = my_win.top
|
918
|
+
|
919
|
+
print_border @graphic if !@suppress_borders # do this once only, unless everything changes
|
920
|
+
return if @table_model.nil? # added 2009-02-17 12:45
|
921
|
+
@_first_column_print ||= 0
|
922
|
+
cc = @table_model.column_count
|
923
|
+
rc = @table_model.row_count
|
924
|
+
inter_column_padding = " " * @inter_column_spacing
|
925
|
+
@_last_column_print = cc-1
|
926
|
+
tcm = @table_column_model
|
927
|
+
tm = @table_model
|
928
|
+
tr = @toprow
|
929
|
+
_column_scrolling = false
|
930
|
+
acolor = get_color $datacolor
|
931
|
+
h = scrollatrow()
|
932
|
+
r,c = rowcol
|
933
|
+
# each cell should print itself, however there is a width issue.
|
934
|
+
# Then thee
|
935
|
+
print_header # do this once, unless columns changed
|
936
|
+
# TCM should give modelindex of col which is used to fetch data from TM
|
937
|
+
r += 1 # save for header
|
938
|
+
0.upto(h) do |hh|
|
939
|
+
crow = tr+hh # crow is the row
|
940
|
+
if crow < rc
|
941
|
+
offset = 0 # offset of column
|
942
|
+
# 0.upto(cc-1) do |colix|
|
943
|
+
focussed = @current_index == crow ? true : false
|
944
|
+
selected = is_row_selected crow
|
945
|
+
# we loop through column_model and fetch data based on model index
|
946
|
+
# FIXED better to call table.get_value_at since we may now
|
947
|
+
# introduce a view - 2009-01-18 18:21
|
948
|
+
tcm.each_with_index do |acolumn, colix|
|
949
|
+
next if colix < @_first_column_print
|
950
|
+
#acolumn = tcm.column(colix)
|
951
|
+
#model_index = acolumn.model_index
|
952
|
+
content = get_value_at(crow, colix) # tables
|
953
|
+
renderer = get_cell_renderer(crow, colix)
|
954
|
+
if renderer.nil?
|
955
|
+
renderer = get_default_cell_renderer_for_class(content.class.to_s) if renderer.nil?
|
956
|
+
renderer.display_length acolumn.width unless acolumn.nil?
|
957
|
+
end
|
958
|
+
width = renderer.display_length + @inter_column_spacing
|
959
|
+
acolumn.column_offset = offset
|
960
|
+
# trying to ensure that no overprinting
|
961
|
+
if c+offset+width > @col+@width
|
962
|
+
_column_scrolling = true
|
963
|
+
@_last_column_print = colix
|
964
|
+
# experimental to print subset of last
|
965
|
+
space_left = (@width-3)-(offset) # 3 due to boundaries
|
966
|
+
space_left = 0 if space_left < 0
|
967
|
+
# length bombed for trueclass 2009-10-05 19:34
|
968
|
+
contentlen = content.length rescue content.to_s.length
|
969
|
+
#if content.length > space_left
|
970
|
+
if contentlen > space_left
|
971
|
+
clen = space_left
|
972
|
+
renderer.display_length clen
|
973
|
+
else
|
974
|
+
clen = -1
|
975
|
+
renderer.display_length space_left # content.length
|
976
|
+
end
|
977
|
+
# added 2009-10-05 20:29 since non strings were bombing
|
978
|
+
# in other cases should be just pass the content as-is. XXX
|
979
|
+
contenttrim = content[0..clen] rescue content # .to_s[0..clen]
|
980
|
+
# print the inter cell padding just in case things mess up while scrolling
|
981
|
+
@graphic.mvprintw r+hh, c+offset-@inter_column_spacing, inter_column_padding
|
982
|
+
#renderer.repaint @graphic, r+hh, c+offset, crow, content[0..clen], focussed, selected
|
983
|
+
#renderer.repaint @graphic, r+hh, c+offset, crow, contenttrim, focussed, selected
|
984
|
+
# 2009-10-05 20:35 XXX passing self so we check it doesn't print outside
|
985
|
+
renderer.repaint self, r+hh, c+offset, crow, contenttrim, focussed, selected
|
986
|
+
break
|
987
|
+
end
|
988
|
+
# added crow on 2009-02-11 22:46
|
989
|
+
#renderer.repaint @graphic, r+hh, c+(offset), crow, content, focussed, selected
|
990
|
+
# 2009-10-05 20:35 XXX
|
991
|
+
renderer.repaint self, r+hh, c+(offset), crow, content, focussed, selected
|
992
|
+
offset += width
|
993
|
+
end
|
994
|
+
else
|
995
|
+
#@graphic.printstring r+hh, c, " " * (@width-2), acolor,@attr
|
996
|
+
printstring r+hh, c, " " * (@width-2), acolor,@attr
|
997
|
+
# clear rows
|
998
|
+
end
|
999
|
+
end
|
1000
|
+
if @is_editing
|
1001
|
+
@cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
|
1002
|
+
end
|
1003
|
+
_print_more_columns_marker _column_scrolling
|
1004
|
+
_print_more_data_marker(rc-1 > tr + h)
|
1005
|
+
$log.debug " _print_more_data_marker(#{rc} >= #{tr} + #{h})"
|
1006
|
+
@table_changed = false
|
1007
|
+
@repaint_required = false
|
1008
|
+
#@buffer_modified = true # 2011-09-30 CLEANUP
|
1009
|
+
end
|
1010
|
+
# NEW to correct overflow
|
1011
|
+
# 2009-10-05 21:34
|
1012
|
+
# when resizing columns a renderer can go outside the table bounds
|
1013
|
+
# so printing should be done by parent not window
|
1014
|
+
def printstring(r,c,string, color, att)
|
1015
|
+
# 3 is table borders
|
1016
|
+
# if renderer trying to print outside don't let it
|
1017
|
+
if c > @col+@width-3
|
1018
|
+
return
|
1019
|
+
end
|
1020
|
+
# if date exceeds boundary truncate
|
1021
|
+
if c+string.length > (@col+@width)-3
|
1022
|
+
len = string.length-((c+string.length)-(@col+@width-3))
|
1023
|
+
@graphic.printstring(r,c,string[0..len], color,att)
|
1024
|
+
else
|
1025
|
+
@graphic.printstring(r,c,string, color,att)
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
def print_border g
|
1029
|
+
return unless @table_changed
|
1030
|
+
g.print_border @row, @col, @height-1, @width, $datacolor
|
1031
|
+
return if @table_model.nil?
|
1032
|
+
rc = @table_model.row_count
|
1033
|
+
h = scrollatrow()
|
1034
|
+
_print_more_data_marker (rc>h)
|
1035
|
+
end
|
1036
|
+
# private
|
1037
|
+
def _print_more_data_marker tf
|
1038
|
+
marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_VLINE
|
1039
|
+
@graphic.mvwaddch @row+@height-2, @col+@width-1, marker
|
1040
|
+
marker = @toprow > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_VLINE
|
1041
|
+
@graphic.mvwaddch @row+1, @col+@width-1, marker
|
1042
|
+
end
|
1043
|
+
def _print_more_columns_marker tf
|
1044
|
+
marker = tf ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
|
1045
|
+
@graphic.mvwaddch @row+@height-1, @col+@width-2, marker
|
1046
|
+
# show if columns to left or not
|
1047
|
+
marker = @_first_column_print > 0 ? Ncurses::ACS_CKBOARD : Ncurses::ACS_HLINE
|
1048
|
+
@graphic.mvwaddch @row+@height-1, @col+@_first_column_print+1, marker
|
1049
|
+
end
|
1050
|
+
# print table header
|
1051
|
+
# 2011-09-17 added repaint all check so that external components can triger this
|
1052
|
+
# e.g. multi-container when it changes tables.
|
1053
|
+
def print_header
|
1054
|
+
return unless @table_changed || @repaint_all
|
1055
|
+
$log.debug " TABLE: inside printheader 2009-10-07 11:51 DDD "
|
1056
|
+
|
1057
|
+
r,c = rowcol
|
1058
|
+
header_model = @table_header.table_column_model
|
1059
|
+
tcm = @table_column_model ## could have been overridden, should we use this at all
|
1060
|
+
offset = 0
|
1061
|
+
header_model.each_with_index do |tc, colix|
|
1062
|
+
next if colix < @_first_column_print # added for scrolling rt and left 2009-02-14 17:49
|
1063
|
+
acolumn = tcm.column colix
|
1064
|
+
renderer = tc.cell_renderer
|
1065
|
+
renderer = @table_header.default_renderer if renderer.nil?
|
1066
|
+
renderer.display_length acolumn.width unless acolumn.nil?
|
1067
|
+
width = renderer.display_length + 1
|
1068
|
+
content = tc.header_value
|
1069
|
+
if c+offset+width > @col+@width
|
1070
|
+
#$log.debug " TABLE: experimental code to NOT print if chance of exceeding table width"
|
1071
|
+
# 2009-02-14 14:24 now printing, but truncating data for last column
|
1072
|
+
space_left = (@width-3)-(offset)
|
1073
|
+
space_left = 0 if space_left < 0
|
1074
|
+
if content.length > space_left
|
1075
|
+
clen = space_left
|
1076
|
+
renderer.display_length clen
|
1077
|
+
else
|
1078
|
+
clen = -1
|
1079
|
+
renderer.display_length space_left
|
1080
|
+
end
|
1081
|
+
#$log.debug " TABLE BREAKING SINCE sl: #{space_left},#{crow},#{colix}: #{clen} "
|
1082
|
+
# passing self so can prevent renderer from printing outside 2009-10-05 22:56
|
1083
|
+
#renderer.repaint @graphic, r, c+(offset), 0, content[0..clen], false, false
|
1084
|
+
renderer.repaint self, r, c+(offset), 0, content[0..clen], false, false
|
1085
|
+
break
|
1086
|
+
end
|
1087
|
+
# passing self so can prevent renderer from printing outside 2009-10-05 22:56
|
1088
|
+
#renderer.repaint @graphic, r, c+(offset),0, content, false, false
|
1089
|
+
renderer.repaint self, r, c+(offset),0, content, false, false
|
1090
|
+
offset += width
|
1091
|
+
end
|
1092
|
+
end
|
1093
|
+
# 2009-01-17 13:25
|
1094
|
+
def set_focus_on arow
|
1095
|
+
@oldrow = @current_index
|
1096
|
+
@current_index = arow
|
1097
|
+
bounds_check if @oldrow != @current_index
|
1098
|
+
end
|
1099
|
+
attr_accessor :toprow # top visible
|
1100
|
+
def ask_search_backward
|
1101
|
+
regex = get_string("Enter regex to search (backward)")
|
1102
|
+
ix = @table_model.find_prev regex, @current_index
|
1103
|
+
if ix.nil?
|
1104
|
+
alert("No matching data for: #{regex}")
|
1105
|
+
else
|
1106
|
+
set_focus_on(ix)
|
1107
|
+
end
|
1108
|
+
end
|
1109
|
+
def find_prev
|
1110
|
+
ix = @table_model.find_prev
|
1111
|
+
regex = @table_model.last_regex
|
1112
|
+
if ix.nil?
|
1113
|
+
alert("No previous matching data for: #{regex}")
|
1114
|
+
else
|
1115
|
+
set_focus_on(ix)
|
1116
|
+
end
|
1117
|
+
end
|
1118
|
+
def ask_search_forward
|
1119
|
+
regex = get_string("Enter regex to search (forward)")
|
1120
|
+
#ix = @table_model.find_next regex, @current_index
|
1121
|
+
ix = @table_model.find_match regex, @current_index
|
1122
|
+
if ix.nil?
|
1123
|
+
alert("No matching data for: #{regex}")
|
1124
|
+
else
|
1125
|
+
set_focus_on(ix)
|
1126
|
+
end
|
1127
|
+
end
|
1128
|
+
# table find_next
|
1129
|
+
def find_next
|
1130
|
+
ix = @table_model.find_next
|
1131
|
+
regex = @table_model.last_regex
|
1132
|
+
if ix.nil?
|
1133
|
+
alert("No more matching data for: #{regex}")
|
1134
|
+
else
|
1135
|
+
set_focus_on(ix)
|
1136
|
+
end
|
1137
|
+
end
|
1138
|
+
def scroll_right
|
1139
|
+
cc = @table_model.column_count
|
1140
|
+
if @_first_column_print < cc-1
|
1141
|
+
@_first_column_print += 1
|
1142
|
+
@_last_column_print += 1 if @_last_column_print < cc-1
|
1143
|
+
@current_column = @_first_column_print
|
1144
|
+
set_form_col # FIXME not looking too good till key press
|
1145
|
+
@repaint_required = true
|
1146
|
+
@table_changed = true # so columns are modified
|
1147
|
+
end
|
1148
|
+
end
|
1149
|
+
def scroll_left
|
1150
|
+
if @_first_column_print > 0
|
1151
|
+
@_first_column_print -= 1
|
1152
|
+
@current_column = @_first_column_print
|
1153
|
+
set_form_col
|
1154
|
+
@repaint_required = true
|
1155
|
+
@table_changed = true
|
1156
|
+
end
|
1157
|
+
end
|
1158
|
+
##
|
1159
|
+
# Makes an estimate of columns sizes, returning a hash, and storing it as @column_widths
|
1160
|
+
# based on checking first 20 rows of data.
|
1161
|
+
# This does not try to fit all columns into table, but gives best width, so you
|
1162
|
+
# can scroll right to see other columns.
|
1163
|
+
# @params - columns is columns returned by database
|
1164
|
+
# using the command: @columns, *rows = @db.execute2(command)
|
1165
|
+
# @param - datatypes is an array returned by following command to DB
|
1166
|
+
# @datatypes = @content[0].types
|
1167
|
+
def estimate_column_widths columns, datatypes=nil
|
1168
|
+
tablewidth = @width-3
|
1169
|
+
colwidths = {}
|
1170
|
+
unless datatypes
|
1171
|
+
datatypes = []
|
1172
|
+
row = columns[0]
|
1173
|
+
$log.debug " XXX row: #{row} "
|
1174
|
+
|
1175
|
+
row.each do |c|
|
1176
|
+
$log.debug " XXX c: #{c} "
|
1177
|
+
case c
|
1178
|
+
when Fixnum, Integer
|
1179
|
+
datatypes << "int"
|
1180
|
+
when Date, Time
|
1181
|
+
datatypes << "date"
|
1182
|
+
else
|
1183
|
+
datatypes << "varchar"
|
1184
|
+
end
|
1185
|
+
end
|
1186
|
+
end
|
1187
|
+
min_column_width = (tablewidth/columns.length) -1
|
1188
|
+
$log.debug("min: #{min_column_width}, #{tablewidth}")
|
1189
|
+
0.upto(20) do |rowix|
|
1190
|
+
break if rowix >= row_count
|
1191
|
+
#@content.each_with_index do |row, cix|
|
1192
|
+
# break if cix >= 20
|
1193
|
+
@table_column_model.each_with_index do |acolumn, ix|
|
1194
|
+
col = get_value_at(rowix, ix)
|
1195
|
+
colwidths[ix] ||= 0
|
1196
|
+
colwidths[ix] = [colwidths[ix], col.to_s.length].max
|
1197
|
+
end
|
1198
|
+
end
|
1199
|
+
total = 0
|
1200
|
+
# crashing in 1.9.2 due to hash key no insert in iteration 2010-08-22 20:09
|
1201
|
+
#colwidths.each_pair do |k,v|
|
1202
|
+
tkeys = colwidths.keys
|
1203
|
+
tkeys.each do |k|
|
1204
|
+
name = columns[k.to_i]
|
1205
|
+
v = colwidths[k]
|
1206
|
+
colwidths[name] = v
|
1207
|
+
total += v
|
1208
|
+
end
|
1209
|
+
colwidths["__TOTAL__"] = total
|
1210
|
+
column_widths = colwidths
|
1211
|
+
@max_data_widths = column_widths.dup
|
1212
|
+
|
1213
|
+
$log.debug "XXXX datatypes #{datatypes} "
|
1214
|
+
columns.each_with_index do | col, i|
|
1215
|
+
break if datatypes[i].nil?
|
1216
|
+
if datatypes[i].match(/(real|int)/) != nil
|
1217
|
+
wid = column_widths[i]
|
1218
|
+
# cw = [column_widths[i], [8,min_column_width].min].max
|
1219
|
+
$log.debug("XXX #{wid}. #{columns[i].length}")
|
1220
|
+
cw = [wid, columns[i].length].max
|
1221
|
+
$log.debug("int #{col} #{column_widths[i]}, #{cw}")
|
1222
|
+
elsif datatypes[i].match(/(date)/) != nil
|
1223
|
+
cw = [column_widths[i], [12,min_column_width].min].max
|
1224
|
+
#cw = [12,min_column_width].min
|
1225
|
+
$log.debug("date #{col} #{column_widths[i]}, #{cw}")
|
1226
|
+
else
|
1227
|
+
cw = [column_widths[i], min_column_width].max
|
1228
|
+
if column_widths[i] <= col.length and col.length <= min_column_width
|
1229
|
+
cw = col.length
|
1230
|
+
end
|
1231
|
+
$log.debug("else #{col} #{column_widths[i]}, #{col.length} #{cw}")
|
1232
|
+
end
|
1233
|
+
column_widths[i] = cw
|
1234
|
+
total += cw
|
1235
|
+
end
|
1236
|
+
column_widths["__TOTAL__"] = total
|
1237
|
+
$log.debug("Estimated col widths: #{column_widths.inspect}")
|
1238
|
+
@column_widths = column_widths
|
1239
|
+
return column_widths
|
1240
|
+
end
|
1241
|
+
##
|
1242
|
+
# convenience method
|
1243
|
+
# sets column widths given an array of ints
|
1244
|
+
# You may get such an array from estimate_column_widths
|
1245
|
+
def set_column_widths cw
|
1246
|
+
raise "Cannot call set_column_widths till table set" unless @table_column_model
|
1247
|
+
tcm = @table_column_model
|
1248
|
+
tcm.each_with_index do |col, ix|
|
1249
|
+
col.width cw[ix]
|
1250
|
+
end
|
1251
|
+
table_structure_changed(nil)
|
1252
|
+
end
|
1253
|
+
def size_columns_to_fit
|
1254
|
+
delta = @width - table_column_model().get_total_column_width()
|
1255
|
+
tcw = table_column_model().get_total_column_width()
|
1256
|
+
|
1257
|
+
$log.debug "size_columns_to_fit D #{delta}, W #{@width}, TCW #{tcw}"
|
1258
|
+
accomodate_delta(delta) if delta != 0
|
1259
|
+
#set_width_from_preferred_widths
|
1260
|
+
end
|
1261
|
+
private
|
1262
|
+
def accomodate_delta delta
|
1263
|
+
tcm = @table_column_model
|
1264
|
+
cc = tcm.column_count
|
1265
|
+
return if cc == 0
|
1266
|
+
average = (delta/cc).ceil
|
1267
|
+
total = 0
|
1268
|
+
tcm.each do |col|
|
1269
|
+
oldcw = col.width + average
|
1270
|
+
next if oldcw < col.min_width or oldcw > col.max_width
|
1271
|
+
if delta >0
|
1272
|
+
break if total > delta
|
1273
|
+
else
|
1274
|
+
break if total < delta
|
1275
|
+
end
|
1276
|
+
col.width oldcw
|
1277
|
+
total += average
|
1278
|
+
end
|
1279
|
+
$log.debug "accomodate_delta: #{average}. #{total}"
|
1280
|
+
table_structure_changed(nil)
|
1281
|
+
end
|
1282
|
+
public
|
1283
|
+
def init_actions
|
1284
|
+
return if @actions_added
|
1285
|
+
@actions_added = true
|
1286
|
+
am = action_manager()
|
1287
|
+
am.add_action( Action.new("&Auto Edit On ") { @editing_policy = :EDITING_AUTO })
|
1288
|
+
am.add_action( Action.new("Auto Edit O&ff ") { @editing_policy = nil })
|
1289
|
+
|
1290
|
+
end
|
1291
|
+
|
1292
|
+
|
1293
|
+
# ADD METHODS HERE
|
1294
|
+
end # class Table
|
1295
|
+
|
1296
|
+
## TC
|
1297
|
+
# All column changes take place in ColumnModel not in data. TC keeps pointer to col in data via
|
1298
|
+
# TODO - can't change width beyond min and max if set
|
1299
|
+
# resizable - user can't resize but programatically can
|
1300
|
+
# model_index
|
1301
|
+
# XXX Seems we are not using min_width and max_width.
|
1302
|
+
# min should be used for when resizing,, max should not be used. we are using width which is
|
1303
|
+
# updated as changed
|
1304
|
+
class TableColumn
|
1305
|
+
include RubyCurses::EventHandler # 2009-01-15 22:49
|
1306
|
+
attr_reader :identifier
|
1307
|
+
attr_accessor :min_width, :max_width, :is_resizable
|
1308
|
+
attr_accessor :cell_renderer
|
1309
|
+
attr_accessor :model_index # index inside TableModel
|
1310
|
+
# user may override or set for this column, else headers default will be used
|
1311
|
+
attr_accessor :header_renderer
|
1312
|
+
dsl_property :header_value
|
1313
|
+
dsl_property :width # XXX don;t let user set width later, should be readonly
|
1314
|
+
dsl_property :preferred_width # user should use this when requesting a change
|
1315
|
+
# some columns may not be editable. e.g in a Finder, file size or time not editable
|
1316
|
+
# whereas name is.
|
1317
|
+
|
1318
|
+
# is this column editable. Set to false to disable a column from editing IF the table
|
1319
|
+
# allows editing. Setting to true not required.
|
1320
|
+
dsl_accessor :editable # if not set takes tables value 2009-01-16 22:49
|
1321
|
+
## added column_offset on 2009-01-12 19:01
|
1322
|
+
attr_accessor :column_offset # where we've place this guy. in case we need to position cursor
|
1323
|
+
attr_accessor :cell_editor
|
1324
|
+
dsl_accessor :edit_length # corresponds to maxlen, if not set, col width will be useda 2009-02-16 21:55
|
1325
|
+
|
1326
|
+
|
1327
|
+
# width is used as initial and preferred width. It has actual value at any time
|
1328
|
+
# width must never be directly set, use preferred width later
|
1329
|
+
def initialize model_index, identifier, header_value, width, config={}, &block
|
1330
|
+
@width = width
|
1331
|
+
@preferred_width = width
|
1332
|
+
@min_width = 4
|
1333
|
+
@max_width = 1000
|
1334
|
+
@model_index = model_index
|
1335
|
+
@identifier = identifier
|
1336
|
+
@header_value = header_value
|
1337
|
+
@config={}
|
1338
|
+
instance_eval &block if block_given?
|
1339
|
+
@_events = [:PROPERTY_CHANGE]
|
1340
|
+
end
|
1341
|
+
def fire_property_change(text, oldval, newval)
|
1342
|
+
#$log.debug "TC: def fire_property_change(#{text}, #{oldval}, #{newval})"
|
1343
|
+
# need to send changeevent FIXME XXX maybe dsl_prop should do this.
|
1344
|
+
fire_handler :PROPERTY_CHANGE, self
|
1345
|
+
end
|
1346
|
+
end # class tc
|
1347
|
+
|
1348
|
+
## TCM
|
1349
|
+
#
|
1350
|
+
class TableColumnModel
|
1351
|
+
def column ix
|
1352
|
+
nil
|
1353
|
+
end
|
1354
|
+
def columns
|
1355
|
+
nil
|
1356
|
+
end
|
1357
|
+
def column_count
|
1358
|
+
0
|
1359
|
+
end
|
1360
|
+
def column_selection_allowed
|
1361
|
+
false
|
1362
|
+
end
|
1363
|
+
def selected_column_count
|
1364
|
+
0
|
1365
|
+
end
|
1366
|
+
def selected_columns
|
1367
|
+
nil
|
1368
|
+
end
|
1369
|
+
def total_column_width
|
1370
|
+
-1
|
1371
|
+
end
|
1372
|
+
def get_selection_model
|
1373
|
+
nil
|
1374
|
+
end
|
1375
|
+
def set_selection_model lsm
|
1376
|
+
end
|
1377
|
+
def add_column tc
|
1378
|
+
end
|
1379
|
+
def remove_column tc
|
1380
|
+
end
|
1381
|
+
def move_column ix, newix
|
1382
|
+
end
|
1383
|
+
def column_index identifier
|
1384
|
+
nil
|
1385
|
+
end
|
1386
|
+
# add tcm listener
|
1387
|
+
end
|
1388
|
+
## DTCM DCM
|
1389
|
+
class DefaultTableColumnModel < TableColumnModel
|
1390
|
+
include Enumerable
|
1391
|
+
include RubyCurses::EventHandler # widget does 2009-01-15 15:38
|
1392
|
+
attr_accessor :column_selection_allowed
|
1393
|
+
|
1394
|
+
##
|
1395
|
+
# takes a column names array
|
1396
|
+
def initialize cols=[]
|
1397
|
+
@columns = []
|
1398
|
+
@total_column_width= -1
|
1399
|
+
##cols.each_with_index {|c, index| @columns << TableColumn.new(index, c, c, 10) }
|
1400
|
+
cols.each_with_index {|c, index| add_column(TableColumn.new(index, c, c, 10)) }
|
1401
|
+
@selected_columns = []
|
1402
|
+
@_events = [:TABLE_COLUMN_MODEL_EVENT, :PROPERTY_CHANGE]
|
1403
|
+
end
|
1404
|
+
def column ix
|
1405
|
+
raise "Invalid arg #{ix}" if ix < 0 or ix > (@columns.length() -1)
|
1406
|
+
@columns[ix]
|
1407
|
+
end
|
1408
|
+
def columns; @columns; end
|
1409
|
+
##
|
1410
|
+
# yields a table column
|
1411
|
+
def each
|
1412
|
+
@columns.each { |c|
|
1413
|
+
yield c
|
1414
|
+
}
|
1415
|
+
end
|
1416
|
+
def column_count
|
1417
|
+
@columns.length
|
1418
|
+
end
|
1419
|
+
def selected_column_count
|
1420
|
+
@selected_columns.length
|
1421
|
+
end
|
1422
|
+
def selected_columns
|
1423
|
+
@selected_columns
|
1424
|
+
end
|
1425
|
+
def clear_selection
|
1426
|
+
@selected_columns = []
|
1427
|
+
end
|
1428
|
+
##
|
1429
|
+
# added 2009-10-07 23:04
|
1430
|
+
def get_total_column_width
|
1431
|
+
@total_column_width = -1 # XXX
|
1432
|
+
if @total_column_width == -1
|
1433
|
+
total = 0
|
1434
|
+
each { |c| total += c.width ; $log.debug "get_total_column_width: #{c.width}"}
|
1435
|
+
@total_column_width = total
|
1436
|
+
end
|
1437
|
+
return @total_column_width
|
1438
|
+
end
|
1439
|
+
def set_selection_model lsm
|
1440
|
+
@column_selection_model = lsm
|
1441
|
+
end
|
1442
|
+
def add_column tc
|
1443
|
+
@columns << tc
|
1444
|
+
tc.bind(:PROPERTY_CHANGE){|e| column_property_changed(e)}
|
1445
|
+
tmce = TableColumnModelEvent.new(nil, @columns.length-1, self, :INSERT)
|
1446
|
+
fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
|
1447
|
+
end
|
1448
|
+
def column_property_changed evt
|
1449
|
+
$log.debug "DTCM def column_property_changed #{evt} "
|
1450
|
+
# need to send changeevent FIXME XXX
|
1451
|
+
fire_handler :PROPERTY_CHANGE, self
|
1452
|
+
end
|
1453
|
+
def remove_column tc
|
1454
|
+
ix = @columns.index tc
|
1455
|
+
@columns.delete tc
|
1456
|
+
tmce = TableColumnModelEvent.new(ix, nil, self, :DELETE)
|
1457
|
+
fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
|
1458
|
+
end
|
1459
|
+
def move_column ix, newix
|
1460
|
+
# acol = remove_column column(ix)
|
1461
|
+
acol = @columns.delete_at ix
|
1462
|
+
@columns.insert newix, acol
|
1463
|
+
tmce = TableColumnModelEvent.new(ix, newix, self, :MOVE)
|
1464
|
+
fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
|
1465
|
+
end
|
1466
|
+
##
|
1467
|
+
# return index of column identified with identifier
|
1468
|
+
def column_index identifier
|
1469
|
+
@columns.each_with_index {|c, i| return i if c.identifier == identifier }
|
1470
|
+
return nil
|
1471
|
+
end
|
1472
|
+
## TODO - if we get into column selection somewhen
|
1473
|
+
def get_selection_model
|
1474
|
+
@lsm
|
1475
|
+
end
|
1476
|
+
def set_selection_model lsm
|
1477
|
+
@lsm = lsm
|
1478
|
+
end
|
1479
|
+
# add tcm listener
|
1480
|
+
end
|
1481
|
+
|
1482
|
+
## TM
|
1483
|
+
class TableModel
|
1484
|
+
def column_count
|
1485
|
+
end
|
1486
|
+
def row_count
|
1487
|
+
end
|
1488
|
+
def set_value_at row, col, val
|
1489
|
+
end
|
1490
|
+
def get_value_at row, col
|
1491
|
+
end
|
1492
|
+
def get_total_column_width
|
1493
|
+
end
|
1494
|
+
=begin
|
1495
|
+
def << obj
|
1496
|
+
end
|
1497
|
+
def insert row, obj
|
1498
|
+
end
|
1499
|
+
def delete obj
|
1500
|
+
end
|
1501
|
+
def delete_at row
|
1502
|
+
end
|
1503
|
+
|
1504
|
+
=end
|
1505
|
+
end # class
|
1506
|
+
|
1507
|
+
##
|
1508
|
+
# DTM
|
1509
|
+
class DefaultTableModel < TableModel
|
1510
|
+
attr_reader :last_regex
|
1511
|
+
include RubyCurses::EventHandler # 2009-01-15 15:38
|
1512
|
+
def initialize data, colnames_array
|
1513
|
+
@data = data
|
1514
|
+
@column_identifiers = colnames_array
|
1515
|
+
@_events = [:TABLE_MODEL_EVENT, :PROPERTY_CHANGE]
|
1516
|
+
end
|
1517
|
+
def column_count
|
1518
|
+
# 2010-01-12 19:35 changed count to size since size is supported in 1.8.6 also
|
1519
|
+
#@column_identifiers.count
|
1520
|
+
@column_identifiers.size
|
1521
|
+
end
|
1522
|
+
def row_count
|
1523
|
+
@data.length
|
1524
|
+
end
|
1525
|
+
#
|
1526
|
+
# please avoid directly hitting this. Suggested to use get_value_at of jtable
|
1527
|
+
# since columns could have been switched.
|
1528
|
+
def set_value_at row, col, val
|
1529
|
+
# $log.debug " def set_value_at #{row}, #{col}, #{val} "
|
1530
|
+
# if editing allowed
|
1531
|
+
@data[row][col] = val
|
1532
|
+
tme = TableModelEvent.new(row, row, col, self, :UPDATE)
|
1533
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1534
|
+
end
|
1535
|
+
##
|
1536
|
+
# please avoid directly hitting this. Suggested to use get_value_at of jtable
|
1537
|
+
# since columns could have been switched.
|
1538
|
+
def get_value_at row, col
|
1539
|
+
#$log.debug " def get_value_at #{row}, #{col} "
|
1540
|
+
|
1541
|
+
raise "IndexError get_value_at #{row}, #{col}" if @data.nil? or row >= @data.size
|
1542
|
+
return @data[row][ col]
|
1543
|
+
end
|
1544
|
+
def << obj
|
1545
|
+
@data << obj
|
1546
|
+
tme = TableModelEvent.new(@data.length-1,@data.length-1, :ALL_COLUMNS, self, :INSERT)
|
1547
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1548
|
+
# create tablemodelevent and fire_table_changed for all listeners
|
1549
|
+
end
|
1550
|
+
def insert row, obj
|
1551
|
+
@data.insert row, obj
|
1552
|
+
tme = TableModelEvent.new(row, row,:ALL_COLUMNS, self, :INSERT)
|
1553
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1554
|
+
# create tablemodelevent and fire_table_changed for all listeners
|
1555
|
+
end
|
1556
|
+
def delete obj
|
1557
|
+
row = @data.index obj
|
1558
|
+
return if row.nil?
|
1559
|
+
ret = @data.delete obj
|
1560
|
+
tme = TableModelEvent.new(row, row,:ALL_COLUMNS, self, :DELETE)
|
1561
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1562
|
+
# create tablemodelevent and fire_table_changed for all listeners
|
1563
|
+
return ret
|
1564
|
+
end
|
1565
|
+
def delete_at row
|
1566
|
+
if !$multiplier or $multiplier == 0
|
1567
|
+
@delete_buffer = @data.delete_at row
|
1568
|
+
else
|
1569
|
+
@delete_buffer = @data.slice!(row, $multiplier)
|
1570
|
+
end
|
1571
|
+
$multiplier = 0
|
1572
|
+
#ret = @data.delete_at row
|
1573
|
+
# create tablemodelevent and fire_table_changed for all listeners
|
1574
|
+
# we don;t pass buffer to event as in listeditable. how to undo later?
|
1575
|
+
tme = TableModelEvent.new(row, row+@delete_buffer.length,:ALL_COLUMNS, self, :DELETE)
|
1576
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1577
|
+
return @delete_buffer
|
1578
|
+
end
|
1579
|
+
# a quick method to undo deletes onto given row. More like paste
|
1580
|
+
def undo where
|
1581
|
+
return unless @delete_buffer
|
1582
|
+
case @delete_buffer[0]
|
1583
|
+
when Array
|
1584
|
+
@delete_buffer.each do |r|
|
1585
|
+
insert where, r
|
1586
|
+
end
|
1587
|
+
else
|
1588
|
+
insert where, @delete_buffer
|
1589
|
+
end
|
1590
|
+
end
|
1591
|
+
##
|
1592
|
+
# added 2009-01-17 21:36
|
1593
|
+
# Use with caution, does not call events per row
|
1594
|
+
def delete_all
|
1595
|
+
len = @data.length-1
|
1596
|
+
@data=[]
|
1597
|
+
tme = TableModelEvent.new(0, len,:ALL_COLUMNS, self, :DELETE)
|
1598
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1599
|
+
end
|
1600
|
+
##
|
1601
|
+
# for those quick cases when you wish to replace all the data
|
1602
|
+
# and not have an event per row being generated
|
1603
|
+
def data=(data)
|
1604
|
+
raise "Data nil or invalid" if data.nil? or data.size == 0
|
1605
|
+
delete_all
|
1606
|
+
@data = data
|
1607
|
+
tme = TableModelEvent.new(0, @data.length-1,:ALL_COLUMNS, self, :INSERT)
|
1608
|
+
fire_handler :TABLE_MODEL_EVENT, tme
|
1609
|
+
end
|
1610
|
+
def ask_search_forward
|
1611
|
+
regex = get_string "Enter regex to search for:"
|
1612
|
+
ix = get_list_data_model.find_match regex
|
1613
|
+
if ix.nil?
|
1614
|
+
alert("No matching data for: #{regex}")
|
1615
|
+
else
|
1616
|
+
set_focus_on(ix)
|
1617
|
+
end
|
1618
|
+
end
|
1619
|
+
# continues previous search
|
1620
|
+
##
|
1621
|
+
def find_match regex, ix0=0, ix1=row_count()
|
1622
|
+
$log.debug " find_match got #{regex} #{ix0} #{ix1}"
|
1623
|
+
@last_regex = regex
|
1624
|
+
@search_start_ix = ix0
|
1625
|
+
@search_end_ix = ix1
|
1626
|
+
@data.each_with_index do |row, ix|
|
1627
|
+
next if ix < ix0
|
1628
|
+
break if ix > ix1
|
1629
|
+
if row.grep(/#{regex}/) != []
|
1630
|
+
#if !row.match(regex).nil?
|
1631
|
+
@search_found_ix = ix
|
1632
|
+
return ix
|
1633
|
+
end
|
1634
|
+
end
|
1635
|
+
return nil
|
1636
|
+
end
|
1637
|
+
def find_prev regex=@last_regex, start = @search_found_ix
|
1638
|
+
raise "No previous search" if @last_regex.nil?
|
1639
|
+
$log.debug " find_prev #{@search_found_ix} : #{@current_index}"
|
1640
|
+
start -= 1 unless start == 0
|
1641
|
+
@last_regex = regex
|
1642
|
+
@search_start_ix = start
|
1643
|
+
start.downto(0) do |ix|
|
1644
|
+
row = @data[ix]
|
1645
|
+
if row.grep(/#{regex}/) != []
|
1646
|
+
@search_found_ix = ix
|
1647
|
+
return ix
|
1648
|
+
end
|
1649
|
+
end
|
1650
|
+
return nil
|
1651
|
+
#return find_match @last_regex, start, @search_end_ix
|
1652
|
+
end
|
1653
|
+
## dtm findnext
|
1654
|
+
def find_next
|
1655
|
+
raise "No more search" if @last_regex.nil?
|
1656
|
+
start = @search_found_ix && @search_found_ix+1 || 0
|
1657
|
+
return find_match @last_regex, start, @search_end_ix
|
1658
|
+
end
|
1659
|
+
end # class DTC
|
1660
|
+
|
1661
|
+
##
|
1662
|
+
##
|
1663
|
+
# Class that manages Table's Header
|
1664
|
+
# are we not taking events such as column added, removed ?
|
1665
|
+
class TableHeader
|
1666
|
+
attr_accessor :default_renderer
|
1667
|
+
attr_accessor :table_column_model
|
1668
|
+
def initialize table_column_model
|
1669
|
+
@table_column_model = table_column_model
|
1670
|
+
create_default_renderer
|
1671
|
+
end
|
1672
|
+
def create_default_renderer
|
1673
|
+
#@default_renderer = TableCellRenderer.new "", {"display_length" => 10, "justify" => :center}
|
1674
|
+
@default_renderer = TableCellRenderer.new "", {"display_length" => 10, "justify" => :center, "color"=>"white", "bgcolor"=>"blue"}
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
# added 2009-10-07 14:03
|
1678
|
+
# returns the column being resized
|
1679
|
+
# @returns TableColumn
|
1680
|
+
# @protected
|
1681
|
+
def get_resizing_column
|
1682
|
+
end
|
1683
|
+
|
1684
|
+
end # class TableHeader
|
1685
|
+
##
|
1686
|
+
# When an event is fired by TableModel, contents are changed, then this object will be passed
|
1687
|
+
# to trigger
|
1688
|
+
# type is :INSERT :UPDATE :DELETE :HEADER_ROW
|
1689
|
+
# columns: number or :ALL_COLUMNS
|
1690
|
+
class TableModelEvent
|
1691
|
+
attr_accessor :firstrow, :lastrow, :column, :source, :type
|
1692
|
+
def initialize firstrow, lastrow, column, source, type
|
1693
|
+
@firstrow = firstrow
|
1694
|
+
@lastrow = lastrow
|
1695
|
+
@column = column
|
1696
|
+
@source = source
|
1697
|
+
@type = type
|
1698
|
+
end
|
1699
|
+
def to_s
|
1700
|
+
"#{@type.to_s}, firstrow: #{@firstrow}, lastrow: #{@lastrow}, column: #{@column}, source: #{@source}"
|
1701
|
+
end
|
1702
|
+
def inspect
|
1703
|
+
to_s
|
1704
|
+
end
|
1705
|
+
end
|
1706
|
+
##
|
1707
|
+
# event sent when a column is added, removed or moved
|
1708
|
+
# type :INSERT :DELETE :MOVE
|
1709
|
+
# in the case of add query first col, for removed query second
|
1710
|
+
class TableColumnModelEvent
|
1711
|
+
attr_accessor :from_col, :to_col, :source, :type
|
1712
|
+
def initialize from_col, to_col, source, type
|
1713
|
+
@from_col = from_col
|
1714
|
+
@to_col = to_col
|
1715
|
+
@source = source
|
1716
|
+
@type = type
|
1717
|
+
end
|
1718
|
+
def to_s
|
1719
|
+
"#{@type.to_s}, from_col: #{@from_col}, to_col: #{@to_col}, source: #{@source}"
|
1720
|
+
end
|
1721
|
+
def inspect
|
1722
|
+
to_s
|
1723
|
+
end
|
1724
|
+
end
|
1725
|
+
## caller can create one and reuse NOTE TODO
|
1726
|
+
class TableTraversalEvent
|
1727
|
+
attr_accessor :oldrow, :oldcol, :newrow, :newcol, :source
|
1728
|
+
def initialize oldrow, oldcol, newrow, newcol, source
|
1729
|
+
@oldrow, @oldcol, @newrow, @newcol, @source = oldrow, oldcol, newrow, newcol, source
|
1730
|
+
end
|
1731
|
+
def set oldrow, oldcol, newrow, newcol, source
|
1732
|
+
@oldrow, @oldcol, @newrow, @newcol, @source = oldrow, oldcol, newrow, newcol, source
|
1733
|
+
end
|
1734
|
+
def to_s
|
1735
|
+
"TRAVERSAL oldrow: #{@oldrow}, oldcol: #{@oldcol}, newrow: #{@newrow}, newcol: #{@newcol}, source: #{@source}"
|
1736
|
+
end
|
1737
|
+
def inspect
|
1738
|
+
to_s
|
1739
|
+
end
|
1740
|
+
end
|
1741
|
+
## caller can create one and reuse NOTE TODO
|
1742
|
+
class TableEditingEvent
|
1743
|
+
attr_accessor :row, :col, :source, :oldvalue, :newvalue, :type
|
1744
|
+
def initialize row, col, source, oldvalue, newvalue, type
|
1745
|
+
set row, col, source, oldvalue, newvalue, type
|
1746
|
+
end
|
1747
|
+
def set row, col, source, oldvalue, newvalue, type
|
1748
|
+
@row, @col, @source, @oldvalue, @newvalue, @type = row, col, source, oldvalue, newvalue, type
|
1749
|
+
end
|
1750
|
+
def to_s
|
1751
|
+
"TABLEDITING #{@type} row: #{@row}, col: #{@col}, oldval: #{@oldvalue}, newvalue: #{@newvalue}, source: #{@source}"
|
1752
|
+
end
|
1753
|
+
def inspect
|
1754
|
+
to_s
|
1755
|
+
end
|
1756
|
+
end
|
1757
|
+
|
1758
|
+
end # module
|