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.
Files changed (38) hide show
  1. data/README.md +75 -0
  2. data/VERSION +1 -0
  3. data/examples/data/list.txt +300 -0
  4. data/examples/data/lotr.txt +12 -0
  5. data/examples/data/table.txt +36 -0
  6. data/examples/data/tasks.txt +27 -0
  7. data/examples/data/unix1.txt +21 -0
  8. data/examples/inc/qdfilechooser.rb +70 -0
  9. data/examples/inc/rfe_renderer.rb +121 -0
  10. data/examples/newtabbedwindow.rb +100 -0
  11. data/examples/rfe.rb +1236 -0
  12. data/examples/test2.rb +670 -0
  13. data/examples/testeditlist.rb +78 -0
  14. data/examples/testtable.rb +270 -0
  15. data/examples/testvimsplit.rb +141 -0
  16. data/lib/rbcurse/extras/include/celleditor.rb +112 -0
  17. data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
  18. data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
  19. data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
  20. data/lib/rbcurse/extras/include/listkeys.rb +37 -0
  21. data/lib/rbcurse/extras/include/listselectable.rb +144 -0
  22. data/lib/rbcurse/extras/include/tableextended.rb +40 -0
  23. data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
  24. data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
  25. data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
  26. data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
  27. data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
  28. data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
  29. data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
  30. data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
  31. data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
  32. data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
  33. data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
  34. data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
  35. data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
  36. data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
  37. data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
  38. 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