rbcurse 0.1.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 (55) hide show
  1. data/CHANGELOG +1570 -0
  2. data/History.txt +6 -0
  3. data/Manifest.txt +54 -0
  4. data/README.txt +304 -0
  5. data/Rakefile +28 -0
  6. data/examples/qdfilechooser.rb +68 -0
  7. data/examples/rfe.rb +853 -0
  8. data/examples/rfe_renderer.rb +69 -0
  9. data/examples/test1.rb +242 -0
  10. data/examples/test2.rb +498 -0
  11. data/examples/testcombo.rb +95 -0
  12. data/examples/testkeypress.rb +61 -0
  13. data/examples/testmenu.rb +105 -0
  14. data/examples/testtable.rb +266 -0
  15. data/examples/testtabp.rb +106 -0
  16. data/examples/testtodo.rb +532 -0
  17. data/examples/viewtodo.rb +512 -0
  18. data/lib/rbcurse/action.rb +31 -0
  19. data/lib/rbcurse/applicationheader.rb +57 -0
  20. data/lib/rbcurse/celleditor.rb +120 -0
  21. data/lib/rbcurse/checkboxcellrenderer.rb +69 -0
  22. data/lib/rbcurse/colormap.rb +133 -0
  23. data/lib/rbcurse/comboboxcellrenderer.rb +45 -0
  24. data/lib/rbcurse/defaultlistselectionmodel.rb +49 -0
  25. data/lib/rbcurse/keylabelprinter.rb +143 -0
  26. data/lib/rbcurse/listcellrenderer.rb +99 -0
  27. data/lib/rbcurse/listkeys.rb +33 -0
  28. data/lib/rbcurse/listscrollable.rb +216 -0
  29. data/lib/rbcurse/listselectable.rb +67 -0
  30. data/lib/rbcurse/mapper.rb +108 -0
  31. data/lib/rbcurse/orderedhash.rb +77 -0
  32. data/lib/rbcurse/rcombo.rb +243 -0
  33. data/lib/rbcurse/rdialogs.rb +183 -0
  34. data/lib/rbcurse/rform.rb +845 -0
  35. data/lib/rbcurse/rinputdataevent.rb +36 -0
  36. data/lib/rbcurse/rlistbox.rb +804 -0
  37. data/lib/rbcurse/rmenu.rb +666 -0
  38. data/lib/rbcurse/rmessagebox.rb +325 -0
  39. data/lib/rbcurse/rpopupmenu.rb +754 -0
  40. data/lib/rbcurse/rtabbedpane.rb +259 -0
  41. data/lib/rbcurse/rtable.rb +1296 -0
  42. data/lib/rbcurse/rtextarea.rb +673 -0
  43. data/lib/rbcurse/rtextview.rb +335 -0
  44. data/lib/rbcurse/rwidget.rb +1731 -0
  45. data/lib/rbcurse/scrollable.rb +301 -0
  46. data/lib/rbcurse/selectable.rb +94 -0
  47. data/lib/rbcurse/table/tablecellrenderer.rb +85 -0
  48. data/lib/rbcurse/table/tabledatecellrenderer.rb +102 -0
  49. data/lib/rbcurse.rb +7 -0
  50. data/lib/ver/keyboard.rb +150 -0
  51. data/lib/ver/keyboard2.rb +170 -0
  52. data/lib/ver/ncurses.rb +102 -0
  53. data/lib/ver/window.rb +369 -0
  54. data/test/test_rbcurse.rb +0 -0
  55. metadata +118 -0
@@ -0,0 +1,1296 @@
1
+ =begin
2
+ * Name: table widget
3
+ * Description:
4
+ * Author: rkumar (arunachalesha)
5
+
6
+
7
+ TODO: NOTE: Needs to be tested under conditions such as no data, deleting all data
8
+ A few higher level methods check for no data but lower level ones do not.
9
+
10
+ --------
11
+ * Date: 2008-12-27 21:33
12
+ * License:
13
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
14
+
15
+ =end
16
+ require 'rubygems'
17
+ require 'ncurses'
18
+ require 'logger'
19
+ require 'rbcurse'
20
+ require 'rbcurse/table/tablecellrenderer'
21
+ require 'rbcurse/table/tabledatecellrenderer'
22
+ require 'rbcurse/checkboxcellrenderer'
23
+ require 'rbcurse/listselectable'
24
+ require 'rbcurse/listkeys'
25
+
26
+ include Ncurses
27
+ include RubyCurses
28
+ module RubyCurses
29
+ extend self
30
+
31
+ # ------ NOTE ------------------ #
32
+ # Table contains a TableModel
33
+ # Table contains a TableColumnModel (which contains TableColumn instances)
34
+ # TableColumn contains 2 TableCellRenderer: column and header
35
+ # ------------------------ #
36
+ #
37
+ #
38
+ # Due to not having method overloading, after usig new, use set_data or set_model
39
+ #
40
+ # This is a widget that displays tabular data. We will get into editing after this works out.
41
+ # This uses the MVC architecture and is WIP as of 2009-01-04 18:37
42
+ # TODO cellrenderers should be able to get parents bgcolor and color (Jtables) if none defined for them.
43
+ class Table < Widget
44
+ #include RubyCurses::EventHandler # widget does 2009-01-15 15:38
45
+ include RubyCurses::ListSelectable
46
+ include RubyCurses::ListKeys
47
+
48
+ dsl_accessor :height
49
+ dsl_accessor :title
50
+ dsl_accessor :title_attrib
51
+ dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
52
+ attr_accessor :current_index # the row index universally
53
+ #attr_accessor :current_column # index of column (usually in current row )
54
+ # a changed event of an editor component can utitlize this if it wishes to know
55
+ # the row or col that was exited.
56
+ attr_reader :editing_col, :editing_row # r and col being edited, set to nil on leave
57
+ attr_accessor :is_editing # boolean is only true if cell_editing_allowed
58
+ dsl_accessor :editing_policy # :EDITING_AUTO
59
+
60
+ # A table should only be editable if this is true regardless of other variables
61
+ # In addition, A column to be editable must either have editable as nil or true
62
+ dsl_accessor :cell_editing_allowed # 2009-01-16 22:55
63
+
64
+ def initialize form, config={}, &block
65
+ super
66
+ init_vars
67
+ install_list_keys
68
+ install_keys_bindings
69
+ end
70
+
71
+ def init_vars
72
+ @col_offset = @row_offset = 1
73
+ @focusable= true
74
+ @current_index = 0
75
+ @current_column = 0
76
+ @oldrow = @oldcol = 0
77
+ @current_column_offset ||= 0 # added 2009-01-12 19:06 current_column's offset
78
+ @toprow = 0
79
+ @to_print_borders ||= 1
80
+ @show_grid ||= 1
81
+ @curpos = 0
82
+ # @selected_color ||= 'yellow'
83
+ # @selected_bgcolor ||= 'black'
84
+ @table_changed = true
85
+ @repaint_required = true
86
+ end
87
+ def install_keys_bindings
88
+
89
+ # alt-tab next column
90
+ # alt-shift-tab prev column
91
+ #bind_key(?\M-\C-i) { next_column }
92
+ #bind_key(481) { previous_column }
93
+ bind_key(9) { next_column }
94
+ bind_key(353) { previous_column }
95
+ bind_key(KEY_RIGHT) { next_column }
96
+ bind_key(KEY_LEFT) { previous_column }
97
+ bind_key(@KEY_ASK_FIND_FORWARD) { ask_search_forward }
98
+ bind_key(@KEY_ASK_FIND_BACKWARD) { ask_search_backward }
99
+ bind_key(@KEY_FIND_NEXT) { find_next }
100
+ bind_key(@KEY_FIND_PREV) { find_prev }
101
+ end
102
+
103
+ def focussed_row
104
+ #raise "No data in table" if row_count < 1
105
+ return nil if row_count < 1
106
+ @current_index
107
+ end
108
+ def focussed_col
109
+ return nil if row_count < 1
110
+ #raise "No data in table" if row_count < 1
111
+ @current_column
112
+ end
113
+ # added 2009-01-07 13:05 so new scrollable can use
114
+ def row_count
115
+ @table_model.row_count
116
+ end
117
+ # added 2009-01-07 13:05 so new scrollable can use
118
+ def scrollatrow
119
+ @height -3
120
+ end
121
+
122
+ #
123
+ # Sets the data in models
124
+ # Should replace if these are created. TODO FIXME
125
+ def set_data data, colnames_array
126
+ if data.is_a? Array
127
+ model = RubyCurses::DefaultTableModel.new data, colnames_array
128
+ table_model model
129
+ elsif data.is_a? RubyCurses::TableModel
130
+ table_model data
131
+ end
132
+ if colnames_array.is_a? Array
133
+ model = DefaultTableColumnModel.new colnames_array
134
+ table_column_model model
135
+ elsif colnames_array.is_a? RubyCurses::TableColumnModel
136
+ table_column_model colnames_array
137
+ end
138
+ create_default_list_selection_model
139
+ create_table_header
140
+ end
141
+ def set_model tm, tcm=nil, lsm=nil
142
+ table_model tm
143
+ if tcm.nil?
144
+ create_default_table_column_model
145
+ else
146
+ table_column_model tcm
147
+ end
148
+ if lsm.nil?
149
+ create_default_list_selection_model
150
+ else
151
+ list_selection_model lsm
152
+ end
153
+ create_table_header
154
+ end
155
+
156
+ # getter and setter for table_model
157
+ def table_model(*val)
158
+ if val.empty?
159
+ @table_model
160
+ else
161
+ raise "data error" if !val[0].is_a? RubyCurses::TableModel
162
+ @table_model = val[0]
163
+ ## table registers as a listener, or rather binds to event
164
+ @table_model.bind(:TABLE_MODEL_EVENT){|lde| table_data_changed(lde) }
165
+ end
166
+ end
167
+ def table_column_model tcm
168
+ raise "data error" if !tcm.is_a? RubyCurses::TableColumnModel
169
+ @table_column_model = tcm
170
+ @table_column_model.bind(:TABLE_COLUMN_MODEL_EVENT) {|e|
171
+ table_structure_changed e
172
+ }
173
+ @table_column_model.bind(:PROPERTY_CHANGE){|e| column_property_changed(e)}
174
+
175
+ #@table_header.column_model(tcm) unless @table_header.nil?
176
+ @table_header.table_column_model=(tcm) unless @table_header.nil?
177
+ end
178
+ # FIX THIS SO NO GET_ XXX
179
+ def get_table_column_model
180
+ @table_column_model
181
+ end
182
+ #
183
+ def create_default_table_column_model
184
+ table_column_model DefaultTableColumnModel.new
185
+ end
186
+ def create_table_header
187
+ @table_header = TableHeader.new @table_column_model
188
+ end
189
+
190
+ #--- selection methods ---#
191
+ def is_column_selected col
192
+ raise "TODO "
193
+ end
194
+ def is_cell_selected row, col
195
+ raise "TODO "
196
+ end
197
+ def add_column_selection_interval ix0, ix1
198
+ raise "TODO "
199
+ # if column_selection_allowed
200
+ end
201
+ def remove_column_selection_interval ix0, ix1
202
+ raise "TODO "
203
+ end
204
+
205
+ def selected_column
206
+ @table_column_model.selected_columns[0]
207
+ end
208
+ def selected_columns
209
+ @table_column_model.selected_columns
210
+ end
211
+ def selected_column_count
212
+ @table_column_model.selected_column_count
213
+ end
214
+
215
+ #--- row and column methods ---#
216
+
217
+ ##
218
+ # getter and setter for current_column index
219
+ def current_column(*val)
220
+ if val.empty?
221
+ @current_column || 0
222
+ else
223
+ @oldcol = @current_column
224
+ v = val[0]
225
+ v = 0 if v < 0
226
+ v = @table_column_model.column_count-1 if v > @table_column_model.column_count-1
227
+ @current_column = v
228
+ if @current_column != @oldcol
229
+ on_leave_column @oldcol
230
+ on_enter_column @current_column
231
+ end
232
+ set_form_col
233
+ @oldcol = @current_column # added on 2009-01-16 19:40 for next_col
234
+ end
235
+ end
236
+
237
+
238
+ def add_column tc
239
+ @table_column_model << tc
240
+ #table_structure_changed # this should be called by tcm TODO with object
241
+ end
242
+ def remove_column tc
243
+ @table_column_model.remove_column tc
244
+ #table_structure_changed # this should be called by tcm TODO with object
245
+ end
246
+ def get_column identifier
247
+ ix = @table_column_model.column_index identifier
248
+ return @table_column_model.column ix
249
+ end
250
+ ##
251
+ # returns col by col ix added on 2009-01-16 23:45
252
+ def column ix
253
+ @table_column_model.column(ix)
254
+ end
255
+ def get_column_name ix
256
+ @table_column_model.column(ix).identifier
257
+ end
258
+ def move_column ix, newix
259
+ @table_column_model.move_column ix, newix
260
+ #table_structure_changed # this should be called by tcm TODO with object
261
+ end
262
+
263
+ #--- row and column methods of Table ---#
264
+ # must not give wrong results when columns switched!
265
+ def get_value_at row, col
266
+ model_index = @table_column_model.column(col).model_index
267
+ @table_model.get_value_at row, model_index
268
+ end
269
+ # must not give wrong results when columns switched!
270
+ def set_value_at row, col, value
271
+ model_index = @table_column_model.column(col).model_index
272
+ @table_model.set_value_at row, model_index, value
273
+ end
274
+
275
+ #--- event listener support methods (p521) TODO ---#
276
+
277
+ def table_data_changed tabmodev
278
+ #$log.debug " def table_data_changed got #{tabmodev}"
279
+ @repaint_required = true
280
+ # next was required otherwise on_enter would bomb if data changed from outside
281
+ if row_count == 0
282
+ init_vars
283
+ set_form_col # added 2009-01-24 14:32 since cursor was still showing on existing col
284
+ return # added 2009-01-23 15:15
285
+ end
286
+ # the next block to be only called if user is inside editing. Often data will be refreshed by
287
+ # a search field and this gets called.
288
+ if @is_editing
289
+ @is_editing = false # 2009-01-19 18:18 testing this out XXX
290
+ # we need to refresh the editor if you deleted a row while sitting on it
291
+ # otherwise it shows the old value
292
+ editing_started
293
+ end
294
+ end
295
+ def table_structure_changed tablecolmodelevent
296
+ $log.debug " def table_structure_changed #{tablecolmodelevent}"
297
+ @table_changed = true
298
+ @repaint_required = true
299
+ init_vars
300
+ end
301
+ def column_property_changed evt
302
+ $log.debug "JT def column_property_changed #{evt} "
303
+ @table_changed = true
304
+ @repaint_required = true
305
+ end
306
+ =begin
307
+ # this could be complicating things. I don't need it in here.
308
+ def column_added tabcolmodev
309
+ @repaint_required = true
310
+ end
311
+ def column_removed tabcolmodev
312
+ @repaint_required = true
313
+ end
314
+ def column_moved tabcolmodev
315
+ @repaint_required = true
316
+ end
317
+ =end
318
+ ## to do for TrueClass and FalseClass
319
+ def prepare_renderers
320
+ @crh = Hash.new
321
+ @crh['String'] = TableCellRenderer.new "", {"parent" => self }
322
+ @crh['Fixnum'] = TableCellRenderer.new "", { "justify" => :right, "parent" => self}
323
+ @crh['Float'] = TableCellRenderer.new "", {"justify" => :right, "parent" => self}
324
+ @crh['TrueClass'] = CheckBoxCellRenderer.new "", {"parent" => self, "display_length"=>7}
325
+ @crh['FalseClass'] = CheckBoxCellRenderer.new "", {"parent" => self, "display_length"=>7}
326
+ @crh['Time'] = TableDateCellRenderer.new "", {"parent" => self, "display_length"=>16}
327
+ #@crh['String'] = TableCellRenderer.new "", {"bgcolor" => "cyan", "color"=>"white", "parent" => self}
328
+ #@crh['Fixnum'] = TableCellRenderer.new "", {"display_length" => 6, "justify" => :right, "color"=>"blue","bgcolor"=>"cyan" }
329
+ #@crh['Float'] = TableCellRenderer.new "", {"display_length" => 6, "justify" => :right, "color"=>"blue", "bgcolor"=>"cyan" }
330
+ end
331
+ # this is vry temporary and will change as we begin to use models - i need to pick
332
+ # columns renderer
333
+ def get_default_cell_renderer_for_class cname
334
+ @crh || prepare_renderers
335
+ @crh[cname] || @crh['String']
336
+ end
337
+ def set_default_cell_renderer_for_class cname, rend
338
+ @crh ||= {}
339
+ @crh[cname]=rend
340
+ end
341
+ ## override for cell or row behaviour
342
+ def get_cell_renderer row, col
343
+ # get columns renderer else class default
344
+ column = @table_column_model.column(col)
345
+ rend = column.cell_renderer
346
+ return rend # can be nil
347
+ end
348
+ #
349
+ # ------- editing methods---------- #
350
+ def get_cell_editor row, col
351
+ $log.debug " def get_cell_editor #{row}, #{col}"
352
+ column = @table_column_model.column(col)
353
+ return nil if column.editable == false or (column.editable.nil? and @cell_editing_allowed!=true)
354
+ editor = column.cell_editor
355
+ return editor # can be nil
356
+ end
357
+ def edit_cell_at row, col
358
+ acolumn = column(col)
359
+ if acolumn.editable == false or (acolumn.editable.nil? and @cell_editing_allowed!=true)
360
+ $log.debug " editing not allowed in #{col}"
361
+ @is_editing = false
362
+ return nil
363
+ end
364
+ return nil if row >= row_count
365
+ value = get_value_at row, col
366
+ editor = get_cell_editor row, col
367
+ @old_cell_value = value # for event
368
+ if editor.nil?
369
+
370
+ cls = value.nil? ? get_value_at(0,col).class.to_s : value.class.to_s
371
+ if value.nil?
372
+ case cls
373
+ when 'String'
374
+ value = value.to_s
375
+ when 'Fixnum'
376
+ value = value.to_i
377
+ when 'Float'
378
+ value = value.to_f
379
+ else
380
+ value = value.to_s
381
+ end
382
+ end
383
+ editor = get_default_cell_editor_for_class cls
384
+ editor.component.display_length = @table_column_model.column(col).width
385
+ 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
386
+ #$log.debug "EDIT_CELL_AT: #{cls} #{editor.component.display_length} = #{@table_column_model.column(col).width}i maxlen #{editor.component.maxlen}"
387
+ end
388
+ $log.debug " got an EDITOR #{editor} :: #{editor.component} "
389
+ # by now we should have something to edit with. We just need to prepare the widgey.
390
+ prepare_editor editor, row, col, value
391
+
392
+ end
393
+ def prepare_editor editor, row, col, value
394
+ r,c = rowcol
395
+ row = r + (row - @toprow) +1 # @form.row , 1 added for header row!
396
+ col = c+get_column_offset()
397
+ editor.prepare_editor self, row, col, value
398
+ @cell_editor = editor
399
+ @repaint_required = true
400
+ set_form_col
401
+ end
402
+ ## Its too late to call components on_leave here
403
+ # since cursor would have moved elsewhere.
404
+ # Prior to moving out of a field, the on_leave should be called and exceptions caught FIXME
405
+ def cancel_editor
406
+ # not really required, the refresh was required. Ok, now i call components on_leave inside
407
+ #@cell_editor.cancel_editor
408
+ @editing_row, @editing_col = nil, nil
409
+ @is_editing = false
410
+ @repaint_required = true
411
+ end
412
+ def get_default_cell_editor_for_class cname
413
+ @ceh ||= {}
414
+ cname = 'Boolean' if cname == 'TrueClass' or cname == 'FalseClass'
415
+ if @ceh.include? cname
416
+ return @ceh[cname]
417
+ else
418
+ case cname
419
+ when 'String'
420
+ # I do not know cell width here, you will have toset display_length NOTE
421
+ ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 8}
422
+ @ceh['String'] = ce
423
+ return ce
424
+ when 'Fixnum'
425
+ ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 5}
426
+ @ceh[cname] = ce
427
+ return ce
428
+ when 'Float'
429
+ ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 5}
430
+ @ceh[cname] = ce
431
+ return ce
432
+ when "Boolean" #'TrueClass', 'FalseClass'
433
+ ce = RubyCurses::CellEditor.new(RubyCurses::CheckBox.new nil, {"display_length"=> 0})
434
+ @ceh[cname] = ce
435
+ return ce
436
+ else
437
+ $log.debug " get_default_cell_editor_for_class UNKNOWN #{cname}"
438
+ ce = RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> 6}
439
+ @ceh[cname] = ce
440
+ return ce
441
+ end
442
+ end
443
+ end
444
+ # returns true if editing is occurring
445
+ #def is_editing?
446
+ # @editing
447
+ #end
448
+
449
+ # ----------------- #
450
+
451
+ ##
452
+ # key handling
453
+ # make separate methods so callable programmatically
454
+ def handle_key(ch)
455
+ @current_index ||= 0
456
+ @toprow ||= 0
457
+ h = scrollatrow()
458
+ rc = @table_model.row_count
459
+ if @is_editing and (ch != 27 and ch != ?\C-c and ch != 13)
460
+ $log.debug " sending ch #{ch} to cell editor"
461
+ ret = @cell_editor.component.handle_key(ch)
462
+ @repaint_required = true
463
+ $log.debug "RET #{ret} got from to cell editor"
464
+ return if ret != :UNHANDLED
465
+ end
466
+ case ch
467
+ when KEY_UP # show previous value
468
+ editing_stopped if @is_editing # 2009-01-16 16:06
469
+ previous_row
470
+ when KEY_DOWN # show previous value
471
+ editing_stopped if @is_editing # 2009-01-16 16:06
472
+ next_row
473
+ when 27, ?\C-c:
474
+ editing_canceled
475
+ when KEY_ENTER, 10, 13:
476
+ # actually it should fall through to the else
477
+ return :UNHANDLED unless @cell_editing_allowed
478
+ toggle_cell_editing
479
+
480
+ when @KEY_ROW_SELECTOR # ?\C-x #32:
481
+ #add_row_selection_interval @current_index, @current_index
482
+ toggle_row_selection @current_index #, @current_index
483
+ @repaint_required = true
484
+ when ?\C-n:
485
+ editing_stopped if @is_editing # 2009-01-16 16:06
486
+ scroll_forward
487
+ when ?\C-p:
488
+ editing_stopped if @is_editing # 2009-01-16 16:06
489
+ scroll_backward
490
+ when 48, @KEY_GOTO_TOP
491
+ # please note that C-[ gives 27, same as esc so will respond after ages
492
+ editing_stopped if @is_editing # 2009-01-16 16:06
493
+ goto_top
494
+ when @KEY_GOTO_BOTTOM
495
+ editing_stopped if @is_editing # 2009-01-16 16:06
496
+ goto_bottom
497
+ else
498
+ # there could be a case of editing here too!
499
+ ret = process_key ch, self
500
+ return :UNHANDLED if ret == :UNHANDLED
501
+ end
502
+ end
503
+ def editing_canceled
504
+ return unless @cell_editing_allowed
505
+ @is_editing = false if @is_editing
506
+ cancel_editor
507
+ end
508
+ def toggle_cell_editing
509
+ return unless @cell_editing_allowed
510
+ @is_editing = !@is_editing
511
+ if @is_editing
512
+ editing_started
513
+ else
514
+ editing_stopped
515
+ end
516
+ end
517
+ def editing_started
518
+ return if !@cell_editing_allowed or row_count < 1
519
+ @is_editing = true # 2009-01-16 16:14
520
+ $log.debug " turning on editing cell at #{focussed_row}, #{focussed_col}"
521
+ # on deleting last row, we need to go back 2009-01-19 18:31
522
+ if focussed_row >= row_count
523
+ bounds_check
524
+ end
525
+ @editing_row, @editing_col = focussed_row(), focussed_col()
526
+ edit_cell_at focussed_row(), focussed_col()
527
+ end
528
+ # EDST
529
+ # the defualt values are useful when user is ON the field and pressed ENTER
530
+ # when leaving a cell, this should have oldrow and oldcol, not default values
531
+ # this throws an exception if validation on field fails NOTE
532
+ def editing_stopped row=focussed_row(), col=focussed_col()
533
+ return unless @cell_editing_allowed or @is_editing == false or column(col).editable == false
534
+ return if row_count < 1
535
+ $log.debug "editing_stopped set_value_at(#{row}, #{col}: #{@cell_editor.getvalue}"
536
+ # next line should be in on_leave_cell but that's not being called FIXME from everywhere
537
+ @cell_editor.on_leave row,col # added here since this is called whenever a cell is exited
538
+
539
+ value = @cell_editor.getvalue
540
+ if value != @old_cell_value
541
+ set_value_at(row, col, @cell_editor.getvalue) #.dup 2009-01-10 21:42 boolean can't duplicate
542
+ if @table_editing_event.nil?
543
+ @table_editing_event ||= TableEditingEvent.new row, col, self, @old_cell_value, value, :EDITING_STOPPED
544
+ else
545
+ @table_editing_event.set row, col, self, @old_cell_value, value, :EDITING_STOPPED
546
+ end
547
+ fire_handler :TABLE_EDITING_EVENT, @table_editing_event
548
+ end
549
+ cancel_editor
550
+ end
551
+ ##
552
+ def previous_row
553
+ @oldrow = @current_index
554
+ @current_index -= 1 if @current_index > 0
555
+ bounds_check
556
+ end
557
+ def next_row
558
+ rc = row_count
559
+ @oldrow = @current_index
560
+ # don't go on if rc 2009-01-16 19:55 XXX
561
+ if @current_index < rc
562
+ @current_index += 1
563
+ bounds_check
564
+ end
565
+ end
566
+ def next_column
567
+ v = @current_column+1
568
+ if v < @table_column_model.column_count
569
+ $log.debug " if v < #{@table_column_model.column_count} "
570
+ current_column v
571
+ else
572
+ if @current_index < row_count()-1
573
+ $log.debug " GOING TO NEXT ROW FROM NEXT COL : #{@current_index} : #{row_count}"
574
+ @current_column = 0
575
+ next_row
576
+ else
577
+ return :UNHANDLED
578
+ end
579
+ end
580
+ end
581
+ def previous_column
582
+ v = @current_column-1
583
+ # returning unhandled so focus can go to prev field auto
584
+ if v < 0 and @current_index <= 0
585
+ return :UNHANDLED
586
+ end
587
+ if v < 0 and @current_index > 0
588
+ @current_column = @table_column_model.column_count-1
589
+ previous_row
590
+ else
591
+ current_column @current_column-1
592
+ end
593
+ end
594
+ def goto_bottom
595
+ @oldrow = @current_index
596
+ rc = row_count
597
+ @current_index = rc -1
598
+ bounds_check
599
+ end
600
+ def goto_top
601
+ @oldrow = @current_index
602
+ @current_index = 0
603
+ bounds_check
604
+ end
605
+ def scroll_backward
606
+ @oldrow = @current_index
607
+ h = scrollatrow()
608
+ @current_index -= h
609
+ bounds_check
610
+ end
611
+ def scroll_forward
612
+ @oldrow = @current_index
613
+ h = scrollatrow()
614
+ rc = row_count
615
+ # more rows than box
616
+ if h < rc
617
+ @toprow += h+1 #if @current_index+h < rc
618
+ @current_index = @toprow
619
+ else
620
+ # fewer rows than box
621
+ @current_index = rc -1
622
+ end
623
+ #@current_index += h+1 #if @current_index+h < rc
624
+ bounds_check
625
+ end
626
+
627
+ def bounds_check
628
+ h = scrollatrow()
629
+ rc = row_count
630
+ #$log.debug " PRE CURR:#{@current_index}, TR: #{@toprow} RC: #{rc} H:#{h}"
631
+ @current_index = 0 if @current_index < 0 # not lt 0
632
+ @current_index = rc-1 if @current_index >= rc # not gt rowcount
633
+ @toprow = rc-h-1 if rc > h and @toprow > rc - h - 1 # toprow shows full page if possible
634
+ # curr has gone below table, move toprow forward
635
+ if @current_index - @toprow > h
636
+ @toprow = @current_index - h
637
+ elsif @current_index < @toprow
638
+ # curr has gone above table, move toprow up
639
+ @toprow = @current_index
640
+ end
641
+ #$log.debug " POST CURR:#{@current_index}, TR: #{@toprow} RC: #{rc} H:#{h}"
642
+ if @oldrow != @current_index
643
+ #$log.debug "going to call on leave and on enter"
644
+ on_leave_row @oldrow #if respond_to? :on_leave_row # to be defined by widget that has included this
645
+ on_enter_row @current_index #if respond_to? :on_enter_row # to be defined by widget that has included this
646
+ end
647
+ set_form_row
648
+ @oldrow = @current_index # added 2009-01-16 19:43 XXX
649
+ @repaint_required = true
650
+ end
651
+ def on_leave_row arow
652
+ #$log.debug " def on_leave_row #{arow}"
653
+ #on_leave_cell arow, @current_column
654
+ on_leave_cell arow, @oldcol # 2009-01-16 19:41 XXX trying outt
655
+ end
656
+ def on_leave_column acol
657
+ #$log.debug " def on_leave_column #{acol}"
658
+ #on_leave_cell @current_index, acol
659
+ on_leave_cell @oldrow, acol
660
+ end
661
+ def on_enter_row arow
662
+ #$log.debug " def on_enter_row #{arow}"
663
+ on_enter_cell arow, @current_column
664
+ end
665
+ def on_enter_column acol
666
+ #$log.debug " def on_enter_column #{acol}"
667
+ on_enter_cell @current_index, acol
668
+ end
669
+ ## OLCE
670
+ def on_leave_cell arow, acol
671
+ $log.debug " def on_leave_cell #{arow}, #{acol}"
672
+ #if @editing_policy == :EDITING_AUTO # actually this should happen in all cases
673
+ if @is_editing # 2009-01-17 00:49
674
+ editing_stopped arow, acol
675
+ end
676
+ end
677
+ ## OECE
678
+ def on_enter_cell arow, acol
679
+ $log.debug " def on_enter_cell #{arow}, #{acol}"
680
+ if @table_traversal_event.nil?
681
+ @table_traversal_event ||= TableTraversalEvent.new @oldrow, @oldcol, arow, acol, self
682
+ else
683
+ @table_traversal_event.set(@oldrow, @oldcol, arow, acol, self)
684
+ end
685
+ fire_handler :TABLE_TRAVERSAL_EVENT, @table_traversal_event
686
+ if @editing_policy == :EDITING_AUTO
687
+ editing_started
688
+ end
689
+ end
690
+ # on enter of widget
691
+ # the cursor should be appropriately positioned
692
+ def on_enter
693
+ super
694
+ set_form_row
695
+ set_form_col # 2009-01-17 01:35
696
+ on_enter_cell focussed_row(), focussed_col()
697
+ end
698
+ def on_leave
699
+ super
700
+ $log.debug " on leave of table 2009-01-16 21:58 "
701
+ editing_stopped if @is_editing # 2009-01-16 21:58
702
+ end
703
+ def set_form_row
704
+ r,c = rowcol
705
+ # +1 is due to header
706
+ @form.row = r + (@current_index-@toprow) + 1
707
+ end
708
+ # set cursor on correct column, widget
709
+ def set_form_col col=@curpos
710
+ @curpos = col
711
+ @current_column_offset = get_column_offset
712
+ @form.col = @col + @col_offset + @curpos + @current_column_offset
713
+ end
714
+ # protected
715
+ def get_column_offset columnid=@current_column
716
+ return @table_column_model.column(columnid).column_offset || 0
717
+ end
718
+
719
+
720
+ def repaint
721
+ return unless @repaint_required
722
+ print_border @form.window if @to_print_borders == 1 # do this once only, unless everything changes
723
+ cc = @table_model.column_count
724
+ rc = @table_model.row_count
725
+ tcm = @table_column_model
726
+ tm = @table_model
727
+ tr = @toprow
728
+ acolor = get_color $datacolor
729
+ h = scrollatrow()
730
+ r,c = rowcol
731
+ # each cell should print itself, however there is a width issue.
732
+ # Then thee
733
+ print_header # do this once, unless columns changed
734
+ # TCM should give modelindex of col which is used to fetch data from TM
735
+ r += 1 # save for header
736
+ 0.upto(h) do |hh|
737
+ crow = tr+hh
738
+ if crow < rc
739
+ offset = 0
740
+ # 0.upto(cc-1) do |colix|
741
+ # we loop through column_model and fetch data based on model index
742
+ # FIXED better to call table.get_value_at since we may now
743
+ # introduce a view - 2009-01-18 18:21
744
+ tcm.each_with_index do |acolumn, colix|
745
+ #acolumn = tcm.column(colix)
746
+ #model_index = acolumn.model_index
747
+ focussed = @current_index == crow ? true : false
748
+ selected = is_row_selected crow
749
+ content = get_value_at(crow, colix) # tables
750
+ #renderer = get_default_cell_renderer_for_class content.class.to_s
751
+ renderer = get_cell_renderer(crow, colix)
752
+ if renderer.nil?
753
+ renderer = get_default_cell_renderer_for_class(content.class.to_s) if renderer.nil?
754
+ renderer.display_length acolumn.width unless acolumn.nil?
755
+ end
756
+ width = renderer.display_length + 1
757
+ #renderer.repaint @form.window, r+hh, c+(colix*11), content, focussed, selected
758
+ acolumn.column_offset = offset
759
+ renderer.repaint @form.window, r+hh, c+(offset), content, focussed, selected
760
+ offset += width
761
+ end
762
+ else
763
+ @form.window.printstring r+hh, c, " " * (@width-2), acolor,@attr
764
+ # clear rows
765
+ end
766
+ end
767
+ if @is_editing
768
+ @cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
769
+ end
770
+ @table_changed = false
771
+ @repaint_required = false
772
+ end
773
+ def print_border g
774
+ return unless @table_changed
775
+ g.print_border @row, @col, @height, @width, $datacolor
776
+ end
777
+ def print_header
778
+ return unless @table_changed
779
+ r,c = rowcol
780
+ header_model = @table_header.table_column_model
781
+ tcm = @table_column_model ## could have been overridden, should we use this at all
782
+ offset = 0
783
+ header_model.each_with_index do |tc, colix|
784
+ acolumn = tcm.column colix
785
+ renderer = tc.cell_renderer
786
+ renderer = @table_header.default_renderer if renderer.nil?
787
+ renderer.display_length acolumn.width unless acolumn.nil?
788
+ width = renderer.display_length + 1
789
+ content = tc.header_value
790
+ renderer.repaint @form.window, r, c+(offset), content, false, false
791
+ offset += width
792
+ end
793
+ end
794
+ # 2009-01-17 13:25
795
+ def set_focus_on arow
796
+ @oldrow = @current_index
797
+ @current_index = arow
798
+ bounds_check if @oldrow != @current_index
799
+ end
800
+ attr_accessor :toprow # top visible
801
+ def ask_search_backward
802
+ regex = get_string("Enter regex to search (backward)")
803
+ ix = @table_model.find_prev regex, @current_index
804
+ if ix.nil?
805
+ alert("No matching data for: #{regex}")
806
+ else
807
+ set_focus_on(ix)
808
+ end
809
+ end
810
+ def find_prev
811
+ ix = @table_model.find_prev
812
+ regex = @table_model.last_regex
813
+ if ix.nil?
814
+ alert("No previous matching data for: #{regex}")
815
+ else
816
+ set_focus_on(ix)
817
+ end
818
+ end
819
+ def ask_search_forward
820
+ regex = get_string("Enter regex to search (forward)")
821
+ #ix = @table_model.find_next regex, @current_index
822
+ ix = @table_model.find_match regex, @current_index
823
+ if ix.nil?
824
+ alert("No matching data for: #{regex}")
825
+ else
826
+ set_focus_on(ix)
827
+ end
828
+ end
829
+ # table find_next
830
+ def find_next
831
+ ix = @table_model.find_next
832
+ regex = @table_model.last_regex
833
+ if ix.nil?
834
+ alert("No more matching data for: #{regex}")
835
+ else
836
+ set_focus_on(ix)
837
+ end
838
+ end
839
+ end # class Table
840
+
841
+ ## TC
842
+ # All column changes take place in ColumnModel not in data. TC keeps pointer to col in data via
843
+ # TODO - can't change width beyond min and max if set
844
+ # resizable - user can't resize but programatically can
845
+ # model_index
846
+ class TableColumn
847
+ include RubyCurses::EventHandler # 2009-01-15 22:49
848
+ attr_reader :identifier
849
+ attr_accessor :min_width, :max_width, :is_resizable
850
+ attr_accessor :cell_renderer
851
+ attr_accessor :model_index # index inside TableModel
852
+ # user may override or set for this column, else headers default will be used
853
+ attr_accessor :header_renderer
854
+ dsl_property :header_value
855
+ dsl_property :width
856
+ # some columns may not be editable. e.g in a Finder, file size or time not editable
857
+ # whereas name is.
858
+
859
+ # is this column editable. Set to false to disable a column from editing IF the table
860
+ # allows editing. Setting to true not required.
861
+ dsl_accessor :editable # if not set takes tables value 2009-01-16 22:49
862
+ ## added column_offset on 2009-01-12 19:01
863
+ attr_accessor :column_offset # where we've place this guy. in case we need to position cursor
864
+ attr_accessor :cell_editor
865
+
866
+
867
+ def initialize model_index, identifier, header_value, width, config={}, &block
868
+ @width = width
869
+ @model_index = model_index
870
+ @identifier = identifier
871
+ @header_value = header_value
872
+ @config={}
873
+ instance_eval &block if block_given?
874
+ end
875
+ def fire_property_change(text, oldval, newval)
876
+ #$log.debug "TC: def fire_property_change(#{text}, #{oldval}, #{newval})"
877
+ # need to send changeevent FIXME XXX maybe dsl_prop should do this.
878
+ fire_handler :PROPERTY_CHANGE, self
879
+ end
880
+ end # class tc
881
+
882
+ ## TCM
883
+ #
884
+ class TableColumnModel
885
+ def column ix
886
+ nil
887
+ end
888
+ def columns
889
+ nil
890
+ end
891
+ def column_count
892
+ 0
893
+ end
894
+ def column_selection_allowed
895
+ false
896
+ end
897
+ def selected_column_count
898
+ 0
899
+ end
900
+ def selected_columns
901
+ nil
902
+ end
903
+ def total_column_width
904
+ 0
905
+ end
906
+ def get_selection_model
907
+ nil
908
+ end
909
+ def set_selection_model lsm
910
+ end
911
+ def add_column tc
912
+ end
913
+ def remove_column tc
914
+ end
915
+ def move_column ix, newix
916
+ end
917
+ def column_index identifier
918
+ nil
919
+ end
920
+ # add tcm listener
921
+ end
922
+ ## DTCM DCM
923
+ class DefaultTableColumnModel < TableColumnModel
924
+ include Enumerable
925
+ include RubyCurses::EventHandler # widget does 2009-01-15 15:38
926
+ attr_accessor :column_selection_allowed
927
+
928
+ ##
929
+ # takes a column names array
930
+ def initialize cols=[]
931
+ @columns = []
932
+ ##cols.each_with_index {|c, index| @columns << TableColumn.new(index, c, c, 10) }
933
+ cols.each_with_index {|c, index| add_column(TableColumn.new(index, c, c, 10)) }
934
+ @selected_columns = []
935
+ end
936
+ def column ix
937
+ raise "Invalid arg #{ix}" if ix < 0 or ix > (@columns.length() -1)
938
+ @columns[ix]
939
+ end
940
+ def columns; @columns; end
941
+ ##
942
+ # yields a table column
943
+ def each
944
+ @columns.each { |c|
945
+ yield c
946
+ }
947
+ end
948
+ def column_count
949
+ @columns.length
950
+ end
951
+ def selected_column_count
952
+ @selected_columns.length
953
+ end
954
+ def selected_columns
955
+ @selected_columns
956
+ end
957
+ def clear_selection
958
+ @selected_columns = []
959
+ end
960
+ def total_column_width
961
+ 0
962
+ end
963
+ def set_selection_model lsm
964
+ @column_selection_model = lsm
965
+ end
966
+ def add_column tc
967
+ @columns << tc
968
+ tc.bind(:PROPERTY_CHANGE){|e| column_property_changed(e)}
969
+ tmce = TableColumnModelEvent.new(nil, @columns.length-1, self, :INSERT)
970
+ fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
971
+ end
972
+ def column_property_changed evt
973
+ $log.debug "DTCM def column_property_changed #{evt} "
974
+ # need to send changeevent FIXME XXX
975
+ fire_handler :PROPERTY_CHANGE, self
976
+ end
977
+ def remove_column tc
978
+ ix = @columns.index tc
979
+ @columns.delete tc
980
+ tmce = TableColumnModelEvent.new(ix, nil, self, :DELETE)
981
+ fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
982
+ end
983
+ def move_column ix, newix
984
+ # acol = remove_column column(ix)
985
+ acol = @columns.delete_at ix
986
+ @columns.insert newix, acol
987
+ tmce = TableColumnModelEvent.new(ix, newix, self, :MOVE)
988
+ fire_handler :TABLE_COLUMN_MODEL_EVENT, tmce
989
+ end
990
+ ##
991
+ # return index of column identified with identifier
992
+ def column_index identifier
993
+ @columns.each_with_index {|c, i| return i if c.identifier == identifier }
994
+ return nil
995
+ end
996
+ ## TODO - if we get into column selection somewhen
997
+ def get_selection_model
998
+ @lsm
999
+ end
1000
+ def set_selection_model lsm
1001
+ @lsm = lsm
1002
+ end
1003
+ # add tcm listener
1004
+ end
1005
+
1006
+ ## TM
1007
+ class TableModel
1008
+ def column_count
1009
+ end
1010
+ def row_count
1011
+ end
1012
+ def set_value_at row, col, val
1013
+ end
1014
+ def get_value_at row, col
1015
+ end
1016
+ =begin
1017
+ def << obj
1018
+ end
1019
+ def insert row, obj
1020
+ end
1021
+ def delete obj
1022
+ end
1023
+ def delete_at row
1024
+ end
1025
+
1026
+ =end
1027
+ end # class
1028
+
1029
+ ##
1030
+ # DTM
1031
+ class DefaultTableModel < TableModel
1032
+ attr_reader :last_regex
1033
+ include RubyCurses::EventHandler # 2009-01-15 15:38
1034
+ def initialize data, colnames_array
1035
+ @data = data
1036
+ @column_identifiers = colnames_array
1037
+ end
1038
+ def column_count
1039
+ @column_identifiers.count
1040
+ end
1041
+ def row_count
1042
+ @data.length
1043
+ end
1044
+ #
1045
+ # please avoid directly hitting this. Suggested to use get_value_at of jtable
1046
+ # since columns could have been switched.
1047
+ def set_value_at row, col, val
1048
+ # $log.debug " def set_value_at #{row}, #{col}, #{val} "
1049
+ # if editing allowed
1050
+ @data[row][col] = val
1051
+ tme = TableModelEvent.new(row, row, col, self, :UPDATE)
1052
+ fire_handler :TABLE_MODEL_EVENT, tme
1053
+ end
1054
+ ##
1055
+ # please avoid directly hitting this. Suggested to use get_value_at of jtable
1056
+ # since columns could have been switched.
1057
+ def get_value_at row, col
1058
+ #$log.debug " def get_value_at #{row}, #{col} "
1059
+
1060
+ raise "IndexError get_value_at #{row}, #{col}" if @data.nil? or row >= @data.size
1061
+ return @data[row][ col]
1062
+ end
1063
+ def << obj
1064
+ @data << obj
1065
+ tme = TableModelEvent.new(@data.length-1,@data.length-1, :ALL_COLUMNS, self, :INSERT)
1066
+ fire_handler :TABLE_MODEL_EVENT, tme
1067
+ # create tablemodelevent and fire_table_changed for all listeners
1068
+ end
1069
+ def insert row, obj
1070
+ @data.insert row, obj
1071
+ tme = TableModelEvent.new(row, row,:ALL_COLUMNS, self, :INSERT)
1072
+ fire_handler :TABLE_MODEL_EVENT, tme
1073
+ # create tablemodelevent and fire_table_changed for all listeners
1074
+ end
1075
+ def delete obj
1076
+ row = @data.index obj
1077
+ return if row.nil?
1078
+ ret = @data.delete obj
1079
+ tme = TableModelEvent.new(row, row,:ALL_COLUMNS, self, :DELETE)
1080
+ fire_handler :TABLE_MODEL_EVENT, tme
1081
+ # create tablemodelevent and fire_table_changed for all listeners
1082
+ return ret
1083
+ end
1084
+ def delete_at row
1085
+ ret = @data.delete_at row
1086
+ # create tablemodelevent and fire_table_changed for all listeners
1087
+ tme = TableModelEvent.new(row, row,:ALL_COLUMNS, self, :DELETE)
1088
+ fire_handler :TABLE_MODEL_EVENT, tme
1089
+ return ret
1090
+ end
1091
+ ##
1092
+ # added 2009-01-17 21:36
1093
+ # Use with caution, does not call events per row
1094
+ def delete_all
1095
+ len = @data.length-1
1096
+ @data=[]
1097
+ tme = TableModelEvent.new(0, len,:ALL_COLUMNS, self, :DELETE)
1098
+ fire_handler :TABLE_MODEL_EVENT, tme
1099
+ end
1100
+ ##
1101
+ # for those quick cases when you wish to replace all the data
1102
+ # and not have an event per row being generated
1103
+ def data=(data)
1104
+ raise "Data nil or invalid" if data.nil? or data.size == 0
1105
+ delete_all
1106
+ @data = data
1107
+ tme = TableModelEvent.new(0, @data.length-1,:ALL_COLUMNS, self, :INSERT)
1108
+ fire_handler :TABLE_MODEL_EVENT, tme
1109
+ end
1110
+ def ask_search_forward
1111
+ regex = get_string "Enter regex to search for:"
1112
+ ix = get_list_data_model.find_match regex
1113
+ if ix.nil?
1114
+ alert("No matching data for: #{regex}")
1115
+ else
1116
+ set_focus_on(ix)
1117
+ end
1118
+ end
1119
+ # continues previous search
1120
+ ##
1121
+ def find_match regex, ix0=0, ix1=row_count()
1122
+ $log.debug " find_match got #{regex} #{ix0} #{ix1}"
1123
+ @last_regex = regex
1124
+ @search_start_ix = ix0
1125
+ @search_end_ix = ix1
1126
+ @data.each_with_index do |row, ix|
1127
+ next if ix < ix0
1128
+ break if ix > ix1
1129
+ if row.grep(/#{regex}/) != []
1130
+ #if !row.match(regex).nil?
1131
+ @search_found_ix = ix
1132
+ return ix
1133
+ end
1134
+ end
1135
+ return nil
1136
+ end
1137
+ def find_prev regex=@last_regex, start = @search_found_ix
1138
+ raise "No previous search" if @last_regex.nil?
1139
+ $log.debug " find_prev #{@search_found_ix} : #{@current_index}"
1140
+ start -= 1 unless start == 0
1141
+ @last_regex = regex
1142
+ @search_start_ix = start
1143
+ start.downto(0) do |ix|
1144
+ row = @data[ix]
1145
+ if row.grep(/#{regex}/) != []
1146
+ @search_found_ix = ix
1147
+ return ix
1148
+ end
1149
+ end
1150
+ return nil
1151
+ #return find_match @last_regex, start, @search_end_ix
1152
+ end
1153
+ ## dtm findnext
1154
+ def find_next
1155
+ raise "No more search" if @last_regex.nil?
1156
+ start = @search_found_ix && @search_found_ix+1 || 0
1157
+ return find_match @last_regex, start, @search_end_ix
1158
+ end
1159
+ end # class DTC
1160
+
1161
+ ##
1162
+ # LSM
1163
+ #
1164
+ class DefaultListSelectionModel
1165
+ include RubyCurses::EventHandler
1166
+ attr_accessor :selection_mode
1167
+ attr_reader :anchor_selection_index
1168
+ attr_reader :lead_selection_index
1169
+ def initialize
1170
+ @selected_indices=[]
1171
+ @anchor_selection_index = -1
1172
+ @lead_selection_index = -1
1173
+ @selection_mode = :MULTIPLE
1174
+ end
1175
+
1176
+ def clear_selection
1177
+ @selected_indices=[]
1178
+ end
1179
+ def is_selected_index ix
1180
+ @selected_indices.include? ix
1181
+ end
1182
+ def get_max_selection_index
1183
+ @selected_indices[-1]
1184
+ end
1185
+ def get_min_selection_index
1186
+ @selected_indices[0]
1187
+ end
1188
+ def get_selected_rows
1189
+ @selected_indices
1190
+ end
1191
+ ## TODO should go in sorted, and no dupes
1192
+ def add_selection_interval ix0, ix1
1193
+ @anchor_selection_index = ix0
1194
+ @lead_selection_index = ix1
1195
+ ix0.upto(ix1) {|i| @selected_indices << i unless @selected_indices.include? i }
1196
+ end
1197
+ def remove_selection_interval ix0, ix1
1198
+ @anchor_selection_index = ix0
1199
+ @lead_selection_index = ix1
1200
+ @selected_indices.delete_if {|x| x >= ix0 and x <= ix1}
1201
+ end
1202
+ def insert_index_interval ix0, len
1203
+ @anchor_selection_index = ix0
1204
+ @lead_selection_index = ix0+len
1205
+ add_selection_interval @anchor_selection_index, @lead_selection_index
1206
+ end
1207
+ end # class DefaultListSelectionModel
1208
+ ##
1209
+ #
1210
+ class TableHeader
1211
+ attr_accessor :default_renderer
1212
+ attr_accessor :table_column_model
1213
+ def initialize table_column_model
1214
+ @table_column_model = table_column_model
1215
+ create_default_renderer
1216
+ end
1217
+ def create_default_renderer
1218
+ #@default_renderer = TableCellRenderer.new "", {"display_length" => 10, "justify" => :center}
1219
+ @default_renderer = TableCellRenderer.new "", {"display_length" => 10, "justify" => :center, "color"=>"white", "bgcolor"=>"blue"}
1220
+ end
1221
+
1222
+ end
1223
+ ##
1224
+ # When an event is fired by TableModel, contents are changed, then this object will be passed
1225
+ # to trigger
1226
+ # type is :INSERT :UPDATE :DELETE :HEADER_ROW
1227
+ # columns: number or :ALL_COLUMNS
1228
+ class TableModelEvent
1229
+ attr_accessor :firstrow, :lastrow, :column, :source, :type
1230
+ def initialize firstrow, lastrow, column, source, type
1231
+ @firstrow = firstrow
1232
+ @lastrow = lastrow
1233
+ @column = column
1234
+ @source = source
1235
+ @type = type
1236
+ end
1237
+ def to_s
1238
+ "#{@type.to_s}, firstrow: #{@firstrow}, lastrow: #{@lastrow}, column: #{@column}, source: #{@source}"
1239
+ end
1240
+ def inspect
1241
+ to_s
1242
+ end
1243
+ end
1244
+ ##
1245
+ # event sent when a column is added, removed or moved
1246
+ # type :INSERT :DELETE :MOVE
1247
+ # in the case of add query first col, for removed query second
1248
+ class TableColumnModelEvent
1249
+ attr_accessor :from_col, :to_col, :source, :type
1250
+ def initialize from_col, to_col, source, type
1251
+ @from_col = from_col
1252
+ @to_col = to_col
1253
+ @source = source
1254
+ @type = type
1255
+ end
1256
+ def to_s
1257
+ "#{@type.to_s}, from_col: #{@from_col}, to_col: #{@to_col}, source: #{@source}"
1258
+ end
1259
+ def inspect
1260
+ to_s
1261
+ end
1262
+ end
1263
+ ## caller can create one and reuse NOTE TODO
1264
+ class TableTraversalEvent
1265
+ attr_accessor :oldrow, :oldcol, :newrow, :newcol, :source
1266
+ def initialize oldrow, oldcol, newrow, newcol, source
1267
+ @oldrow, @oldcol, @newrow, @newcol, @source = oldrow, oldcol, newrow, newcol, source
1268
+ end
1269
+ def set oldrow, oldcol, newrow, newcol, source
1270
+ @oldrow, @oldcol, @newrow, @newcol, @source = oldrow, oldcol, newrow, newcol, source
1271
+ end
1272
+ def to_s
1273
+ "TRAVERSAL oldrow: #{@oldrow}, oldcol: #{@oldcol}, newrow: #{@newrow}, newcol: #{@newcol}, source: #{@source}"
1274
+ end
1275
+ def inspect
1276
+ to_s
1277
+ end
1278
+ end
1279
+ ## caller can create one and reuse NOTE TODO
1280
+ class TableEditingEvent
1281
+ attr_accessor :row, :col, :source, :oldvalue, :newvalue, :type
1282
+ def initialize row, col, source, oldvalue, newvalue, type
1283
+ set row, col, source, oldvalue, newvalue, type
1284
+ end
1285
+ def set row, col, source, oldvalue, newvalue, type
1286
+ @row, @col, @source, @oldvalue, @newvalue, @type = row, col, source, oldvalue, newvalue, type
1287
+ end
1288
+ def to_s
1289
+ "TABLEDITING #{@type} row: #{@row}, col: #{@col}, oldval: #{@oldvalue}, newvalue: #{@newvalue}, source: #{@source}"
1290
+ end
1291
+ def inspect
1292
+ to_s
1293
+ end
1294
+ end
1295
+
1296
+ end # module