rbcurse 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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