rkumar-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 (52) hide show
  1. data/CHANGELOG +1577 -0
  2. data/README.txt +310 -0
  3. data/examples/qdfilechooser.rb +68 -0
  4. data/examples/rfe.rb +853 -0
  5. data/examples/rfe_renderer.rb +69 -0
  6. data/examples/test1.rb +242 -0
  7. data/examples/test2.rb +498 -0
  8. data/examples/testcombo.rb +95 -0
  9. data/examples/testkeypress.rb +61 -0
  10. data/examples/testmenu.rb +105 -0
  11. data/examples/testtable.rb +266 -0
  12. data/examples/testtabp.rb +106 -0
  13. data/examples/testtodo.rb +532 -0
  14. data/examples/viewtodo.rb +512 -0
  15. data/lib/rbcurse.rb +7 -0
  16. data/lib/rbcurse/action.rb +31 -0
  17. data/lib/rbcurse/applicationheader.rb +57 -0
  18. data/lib/rbcurse/celleditor.rb +120 -0
  19. data/lib/rbcurse/checkboxcellrenderer.rb +69 -0
  20. data/lib/rbcurse/colormap.rb +133 -0
  21. data/lib/rbcurse/comboboxcellrenderer.rb +45 -0
  22. data/lib/rbcurse/defaultlistselectionmodel.rb +49 -0
  23. data/lib/rbcurse/keylabelprinter.rb +143 -0
  24. data/lib/rbcurse/listcellrenderer.rb +99 -0
  25. data/lib/rbcurse/listkeys.rb +33 -0
  26. data/lib/rbcurse/listscrollable.rb +216 -0
  27. data/lib/rbcurse/listselectable.rb +67 -0
  28. data/lib/rbcurse/mapper.rb +108 -0
  29. data/lib/rbcurse/orderedhash.rb +77 -0
  30. data/lib/rbcurse/rcombo.rb +243 -0
  31. data/lib/rbcurse/rdialogs.rb +183 -0
  32. data/lib/rbcurse/rform.rb +845 -0
  33. data/lib/rbcurse/rinputdataevent.rb +36 -0
  34. data/lib/rbcurse/rlistbox.rb +804 -0
  35. data/lib/rbcurse/rmenu.rb +666 -0
  36. data/lib/rbcurse/rmessagebox.rb +325 -0
  37. data/lib/rbcurse/rpopupmenu.rb +754 -0
  38. data/lib/rbcurse/rtabbedpane.rb +259 -0
  39. data/lib/rbcurse/rtable.rb +1296 -0
  40. data/lib/rbcurse/rtextarea.rb +673 -0
  41. data/lib/rbcurse/rtextview.rb +335 -0
  42. data/lib/rbcurse/rwidget.rb +1731 -0
  43. data/lib/rbcurse/scrollable.rb +301 -0
  44. data/lib/rbcurse/selectable.rb +94 -0
  45. data/lib/rbcurse/table/tablecellrenderer.rb +85 -0
  46. data/lib/rbcurse/table/tabledatecellrenderer.rb +102 -0
  47. data/lib/ver/keyboard.rb +150 -0
  48. data/lib/ver/keyboard2.rb +170 -0
  49. data/lib/ver/ncurses.rb +102 -0
  50. data/lib/ver/window.rb +369 -0
  51. data/test/test_rbcurse.rb +0 -0
  52. metadata +117 -0
@@ -0,0 +1,512 @@
1
+ $LOAD_PATH << "/Users/rahul/work/projects/rbcurse/"
2
+ require 'rubygems'
3
+ require 'ncurses'
4
+ require 'logger'
5
+ #require 'ver/keyboard'
6
+ require 'rbcurse'
7
+ require 'rbcurse/rcombo'
8
+ require 'rbcurse/rtable'
9
+ #require 'rbcurse/table/tablecellrenderer'
10
+ require 'rbcurse/keylabelprinter'
11
+ require 'rbcurse/applicationheader'
12
+ require 'rbcurse/action'
13
+
14
+ class TodoList
15
+ def initialize file
16
+ @file = file
17
+ end
18
+ def load
19
+ @todomap = YAML::load(File.open(@file));
20
+ @records = convert_to_text
21
+ end
22
+ def get_statuses
23
+ @todomap['__STATUSES']
24
+ end
25
+ def get_modules
26
+ @todomap['__MODULES'].sort
27
+ end
28
+ def get_categories
29
+ @todomap.keys.delete_if {|k| k.match(/^__/) }
30
+ end
31
+ def get_tasks_for_category categ
32
+ c = @todomap[categ]
33
+ d = []
34
+ c.each_pair {|k,v|
35
+ v.each do |r|
36
+ row=[]
37
+ row << k
38
+ r.each { |r1| row << r1 }
39
+ d << row
40
+ #$log.debug " ROW = #{row.inspect} "
41
+ end
42
+ }
43
+ return d
44
+ end
45
+ def get_records_for_category categ
46
+ if categ.nil? or categ == ""
47
+ return @records
48
+ else
49
+ return @records.select { |row| row[0] == categ }
50
+ end
51
+ end
52
+ def sort categ, column, descending=false
53
+ d = get_records_for_category categ
54
+ d = d.sort { |y,x|
55
+ if descending
56
+ if x[column].nil?
57
+ $log.debug "sort -1"
58
+ -1
59
+ elsif y[column].nil?
60
+ $log.debug "sort 1"
61
+ 1
62
+ else
63
+ $log.debug "sort <> #{x[column]} <=> #{y[column]} "
64
+ x[column] <=> y[column]
65
+ end
66
+ else
67
+ if x[column].nil?
68
+ 1
69
+ elsif y[column].nil?
70
+ -1
71
+ else
72
+ y[column] <=> x[column]
73
+ end
74
+ end
75
+ }
76
+ return d
77
+ end
78
+ def set_tasks_for_category categ, data
79
+ d = {}
80
+ data.each do |row|
81
+ #key = row.delete_at 0
82
+ key = row.first
83
+ d[key] ||= []
84
+ d[key] << row[1..-1]
85
+ end
86
+ @todomap[categ]=d
87
+ $log.debug " NEW DATA #{categ}: #{data}"
88
+ end
89
+ def convert_to_text
90
+ d = []
91
+ cats = get_categories
92
+ cats.each do |c|
93
+ tasks = get_tasks_for_category c
94
+ tasks.each do |t|
95
+ n = t.dup
96
+ n.insert 0, c
97
+ d << n
98
+ end
99
+ end
100
+ return d
101
+ end
102
+ =begin
103
+ File.open("todo.csv", "w") { |f| YAML.dump( d, f )}
104
+ buf =''
105
+ require 'csv'
106
+ CSV.open('todocsv.csv', 'w') do |writer|
107
+ #writer << [nil, nil]
108
+ d.each do |row|
109
+ #parced_cells = CSV.generate_rows(row, row.size, buf)
110
+ writer << row
111
+ end
112
+ end
113
+ =end
114
+ def dump
115
+ f = "#{@file}"
116
+ #File.open(f, "w") { |f| YAML.dump( @todomap, f )}
117
+ convert_to_text
118
+ end
119
+ end
120
+ def get_key_labels
121
+ key_labels = [
122
+ ['C-q', 'Exit'], nil,
123
+ ['M-c', 'Category'], nil,
124
+ ['M-f', 'Filter Fld'], ['M-p', 'Pattern'],
125
+ ['M-s', 'Sort'], ['M-i', 'Filter']
126
+ ]
127
+ return key_labels
128
+ end
129
+ def get_key_labels_table
130
+ key_labels = [
131
+ ['M-n','NewRow'], ['M-d','DelRow'],
132
+ ['C-x','Select'], nil,
133
+ ['M-0', 'Top'], ['M-9', 'End'],
134
+ ['C-p', 'PgUp'], ['C-n', 'PgDn'],
135
+ ['M-Tab','Nxt Fld'], ['Tab','Nxt Col'],
136
+ ['+','Widen'], ['-','Narrow']
137
+ ]
138
+ return key_labels
139
+ end
140
+ class TodoApp
141
+ def initialize
142
+ @window = VER::Window.root_window
143
+ @form = Form.new @window
144
+ @sort_dir = true
145
+
146
+ @todo = TodoList.new "todo.yml"
147
+ @todo.load
148
+ end
149
+ def run
150
+ todo = @todo
151
+ statuses = todo.get_statuses
152
+ cats = todo.get_categories
153
+ cats.insert 0,""
154
+ modules = todo.get_modules
155
+ title = "TODO APP"
156
+ @header = ApplicationHeader.new @form, title, {:text2=>"Some Text", :text_center=>"Task View"}
157
+ status_row = RubyCurses::Label.new @form, {'text' => "", :row => Ncurses.LINES-4, :col => 0, :display_length=>60}
158
+ @status_row = status_row
159
+ # setting ENTER across all objects on a form
160
+ @form.bind(:ENTER) {|f| status_row.text = f.help_text unless f.help_text.nil? }
161
+ #@window.printstring 0,(Ncurses.COLS-title.length)/2,title, $datacolor
162
+ r = 1; c = 1;
163
+ categ = ComboBox.new @form do
164
+ name "categ"
165
+ row r
166
+ col 15
167
+ display_length 10
168
+ editable false
169
+ list cats
170
+ set_label Label.new @form, {'text' => "Category", 'color'=>'cyan','col'=>1, "mnemonic"=>"C"}
171
+ list_config 'height' => 4
172
+ help_text "Select a category and <TAB> out. KEY_UP, KEY_DOWN, M-Down"
173
+ end
174
+ colnames = %w[ Categ Module Prior Task Status Date]
175
+
176
+ colnames_cbl = colnames.dup
177
+ colnames_cbl.insert 0, ""
178
+ col_combo = ComboBox.new @form do
179
+ name "col_combo"
180
+ row r
181
+ col 45
182
+ display_length 10
183
+ editable false
184
+ list colnames_cbl
185
+ set_label Label.new @form, {'text' => "Filter on:", 'color'=>'cyan',"mnemonic"=>"F"}
186
+ list_config 'height' => 6
187
+ help_text "Select a field to filter on"
188
+ end
189
+ col_value = Field.new @form do
190
+ name "col_value"
191
+ row r+1
192
+ col 45
193
+ bgcolor 'cyan'
194
+ color 'white'
195
+ display_length 10
196
+ set_label Label.new @form, {'text' => "Pattern:", 'color'=>'cyan',:bgcolor => 'black',"mnemonic"=>"P"}
197
+ help_text "Pattern/Regex to filter on"
198
+ end
199
+ data = todo.get_records_for_category 'TODO'
200
+ @data = data
201
+ b_filter = Button.new @form do
202
+ text "Fi&lter"
203
+ row r
204
+ col 65
205
+ help_text "Filter on selected filter column and value"
206
+ #bind(:ENTER) { status_row.text "New button adds a new row below current " }
207
+ end
208
+
209
+
210
+ table_ht = 15
211
+ atable = Table.new @form do
212
+ name "tasktable"
213
+ row r+2
214
+ col c
215
+ width 104
216
+ height table_ht
217
+ #title "A Table"
218
+ #title_attrib (Ncurses::A_REVERSE | Ncurses::A_BOLD)
219
+ cell_editing_allowed false
220
+ set_data data, colnames
221
+ end
222
+ @atable = atable
223
+ categ.bind(:CHANGED) do |fld| $log.debug " COMBO EXIT XXXXXXXX";
224
+ data = todo.get_records_for_category fld.getvalue;
225
+ @data = data
226
+ atable.table_model.data = data
227
+ end
228
+ b_filter.command {
229
+ alert("Data is blank") if data.nil? or data.size == 0
230
+ raise("Data is blank") if data.nil? or data.size == 0
231
+ raise("selected is blank") if col_combo.selected_item.nil?
232
+ raise("col_val is blank") if col_value.getvalue.nil?
233
+
234
+ $log.debug "#{col_combo.selected_index}, .#{col_value.getvalue}"
235
+ d = data.select {|row| row[col_combo.selected_index-1].to_s.match(col_value.getvalue) }
236
+ atable.table_model.data = d unless d.nil? or d.size == 0
237
+ }
238
+
239
+ tcm = atable.get_table_column_model
240
+ #
241
+ ## key bindings fo atable
242
+ # column widths
243
+ tcm.column(0).width 8
244
+ tcm.column(1).width 8
245
+ tcm.column(2).width 5
246
+ tcm.column(3).width 50
247
+ tcm.column(4).width 8
248
+ tcm.column(5).width 16
249
+ app = self
250
+ atable.configure() do
251
+ #bind_key(330) { atable.remove_column(tcm.column(atable.focussed_col)) rescue "" }
252
+ bind_key(?+) {
253
+ acolumn = atable.column atable.focussed_col()
254
+ w = acolumn.width + 1
255
+ acolumn.width w
256
+ #atable.table_structure_changed
257
+ }
258
+ bind_key(?-) {
259
+ acolumn = atable.column atable.focussed_col()
260
+ w = acolumn.width - 1
261
+ if w > 3
262
+ acolumn.width w
263
+ #atable.table_structure_changed
264
+ end
265
+ }
266
+ bind_key(?>) {
267
+ colcount = tcm.column_count-1
268
+ #atable.move_column sel_col.value, sel_col.value+1 unless sel_col.value == colcount
269
+ col = atable.focussed_col
270
+ atable.move_column col, col+1 unless col == colcount
271
+ }
272
+ bind_key(?<) {
273
+ col = atable.focussed_col
274
+ atable.move_column col, col-1 unless col == 0
275
+ #atable.move_column sel_col.value, sel_col.value-1 unless sel_col.value == 0
276
+ }
277
+ bind_key(?\M-h, app) {|tab,td| $log.debug " BIND... #{tab.class}, #{td.class}"; app.make_popup atable}
278
+ end
279
+ #keylabel = RubyCurses::Label.new @form, {'text' => "", "row" => r+table_ht+3, "col" => c, "color" => "yellow", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
280
+ #eventlabel = RubyCurses::Label.new @form, {'text' => "Events:", "row" => r+table_ht+6, "col" => c, "color" => "white", "bgcolor"=>"blue", "display_length"=>60, "height"=>2}
281
+
282
+ # report some events
283
+ #atable.table_model.bind(:TABLE_MODEL_EVENT){|e| #eventlabel.text = "Event: #{e}"}
284
+ #atable.get_table_column_model.bind(:TABLE_COLUMN_MODEL_EVENT){|e| eventlabel.text = "Event: #{e}"}
285
+ atable.bind(:TABLE_TRAVERSAL_EVENT){|e| @header.text_right "Row #{e.newrow+1} of #{atable.row_count}" }
286
+
287
+
288
+ #str_renderer = TableCellRenderer.new ""
289
+ #num_renderer = TableCellRenderer.new "", { "justify" => :right }
290
+ #bool_renderer = CheckBoxCellRenderer.new "", {"parent" => atable, "display_length"=>5}
291
+ #combo_renderer = RubyCurses::ComboBoxCellRenderer.new nil, {"parent" => atable, "display_length"=> 8}
292
+ #combo_editor = RubyCurses::CellEditor.new(RubyCurses::ComboBox.new nil, {"focusable"=>false, "visible"=>false, "list"=>statuses, "display_length"=>8})
293
+ #combo_editor1 = RubyCurses::CellEditor.new(RubyCurses::ComboBox.new nil, {"focusable"=>false, "visible"=>false, "list"=>modules, "display_length"=>8})
294
+ #atable.set_default_cell_renderer_for_class "String", str_renderer
295
+ #atable.set_default_cell_renderer_for_class "Fixnum", num_renderer
296
+ #atable.set_default_cell_renderer_for_class "Float", num_renderer
297
+ #atable.set_default_cell_renderer_for_class "TrueClass", bool_renderer
298
+ #atable.set_default_cell_renderer_for_class "FalseClass", bool_renderer
299
+ #atable.get_table_column_model.column(3).cell_editor = combo_editor
300
+ #atable.get_table_column_model.column(0).cell_editor = combo_editor1
301
+ #ce = atable.get_default_cell_editor_for_class "String"
302
+ # increase the maxlen of task
303
+ #ce.component.maxlen = 80
304
+ # I want up and down to go up and down rows inside the combo box, i can use M-down for changing.
305
+ #combo_editor.component.unbind_key(KEY_UP)
306
+ #combo_editor.component.unbind_key(KEY_DOWN)
307
+ #combo_editor1.component.unbind_key(KEY_UP)
308
+ #combo_editor1.component.unbind_key(KEY_DOWN)
309
+ buttrow = r+table_ht+8 #Ncurses.LINES-4
310
+ buttrow = Ncurses.LINES-5
311
+ create_table_actions atable, todo, data, categ.getvalue
312
+ =begin
313
+ b_newrow = Button.new @form do
314
+ text "&New"
315
+ row buttrow
316
+ col c+10
317
+ bind(:ENTER) { status_row.text "New button adds a new row below current " }
318
+ end
319
+ b_newrow.command { @new_act.call }
320
+ =end
321
+ ## We use Action to create a button: to test out ampersand with MI and Button
322
+ new_act = @new_act
323
+ # using ampersand to set mnemonic
324
+ b_sort = Button.new @form do
325
+ text "&Sort"
326
+ row buttrow
327
+ col c+25
328
+ #bind(:ENTER) { status_row.text "Deletes focussed row" }
329
+ help_text "Sort focussed row"
330
+ end
331
+ b_sort.command {
332
+ if @sorted_key == atable.focussed_col
333
+ @sort_dir = !@sort_dir
334
+ else
335
+ @sort_dir = true
336
+ end
337
+ @sorted_key = atable.focussed_col
338
+
339
+ $log.debug " SORT = #{categ.getvalue}, #{atable.focussed_col}, sort:#{@sort_dir}"
340
+ d = @todo.sort categ.getvalue, atable.focussed_col, @sort_dir
341
+ atable.table_model.data = d
342
+ }
343
+ =begin
344
+ b_change = Button.new @form do
345
+ text "&Lock"
346
+ row buttrow
347
+ col c+35
348
+ command {
349
+ r = atable.focussed_row
350
+ #c = sel_col.value
351
+ #$log.debug " Update gets #{field.getvalue.class}"
352
+ #atable.set_value_at(r, c, field.getvalue)
353
+ toggle = atable.column(atable.focussed_col()).editable
354
+ if toggle.nil? or toggle==true
355
+ toggle = false
356
+ text "Un&lock"
357
+ else
358
+ toggle = true
359
+ text "&Lock "
360
+ end
361
+ #eventlabel.text "Set column #{atable.focussed_col()} editable to #{toggle}"
362
+ atable.column(atable.focussed_col()).editable toggle
363
+ alert("Set column #{atable.focussed_col()} editable to #{toggle}")
364
+ }
365
+ help_text "Toggles editable state of current column "
366
+ end
367
+ =end
368
+ #buttons = [b_save, b_newrow, b_delrow, b_move ]
369
+ #Button.button_layout buttons, buttrow
370
+ @klp = RubyCurses::KeyLabelPrinter.new @form, get_key_labels
371
+ @klp.set_key_labels get_key_labels_table, :table
372
+ atable.bind(:ENTER){ @klp.mode :table ;
373
+ }
374
+ atable.bind(:LEAVE){@klp.mode :normal;
375
+ }
376
+
377
+
378
+ @form.repaint
379
+ @window.wrefresh
380
+ Ncurses::Panel.update_panels
381
+ begin
382
+ while((ch = @window.getchar()) != ?\C-q )
383
+ colcount = tcm.column_count-1
384
+ s = keycode_tos ch
385
+ #status_row.text = "Pressed #{ch} , #{s}"
386
+ @form.handle_key(ch)
387
+
388
+ @form.repaint
389
+ @window.wrefresh
390
+ end
391
+ ensure
392
+ @window.destroy if !@window.nil?
393
+ end
394
+ end
395
+ def create_table_actions atable, todo, data, categ
396
+ #@new_act = Action.new("New Row", "mnemonic"=>"N") {
397
+ @new_act = Action.new("&New Row") {
398
+ cc = atable.get_table_column_model.column_count
399
+ if atable.row_count < 1
400
+ mod = nil
401
+ frow = 0
402
+ else
403
+ frow = atable.focussed_row
404
+ frow += 1
405
+ mod = atable.get_value_at(frow,0)
406
+ end
407
+ tmp = [mod, 5, "", "TODO", Time.now]
408
+ tm = atable.table_model
409
+ tm.insert frow, tmp
410
+ atable.set_focus_on frow
411
+ @status_row.text = "Added a row. Please press Save before changing Category."
412
+ alert("Added a row below current one. Use C-k to clear task.")
413
+ }
414
+ @new_act.accelerator "Alt-N"
415
+ @save_cmd = lambda {
416
+ todo.set_tasks_for_category categ, data
417
+ todo.dump
418
+ alert("Rewritten yaml file")
419
+ }
420
+ @del_cmd = lambda {
421
+ row = atable.focussed_row
422
+ if confirm("Do your really want to delete row #{row+1}?")== :YES
423
+ tm = atable.table_model
424
+ tm.delete_at row
425
+ else
426
+ @status_row.text = "Delete cancelled"
427
+ end
428
+ }
429
+
430
+ end
431
+ def make_popup table
432
+ require 'rbcurse/rpopupmenu'
433
+ tablemenu = RubyCurses::PopupMenu.new "Table"
434
+ #tablemenu.add(item = RubyCurses::MenuItem.new("Open",'O'))
435
+ tablemenu.add(item = RubyCurses::MenuItem.new("&Open"))
436
+
437
+ tablemenu.insert_separator 1
438
+ #tablemenu.add(RubyCurses::MenuItem.new "New",'N')
439
+ tablemenu.add(@new_act)
440
+ tablemenu.add(item = RubyCurses::MenuItem.new("&Save"))
441
+ item.command() { @save_cmd.call }
442
+
443
+ item=RubyCurses::MenuItem.new "Select"
444
+ item.accelerator = "Ctrl-X"
445
+ item.command() { table.toggle_row_selection() }
446
+ #item.enabled = false
447
+ tablemenu.add(item)
448
+
449
+ item=RubyCurses::MenuItem.new "Clr Selection"
450
+ item.accelerator = "Alt-e"
451
+ item.command() { table.clear_selection() }
452
+ item.enabled = table.selected_row_count > 0 ? true : false
453
+ tablemenu.add(item)
454
+
455
+ item=RubyCurses::MenuItem.new "Delete"
456
+ item.accelerator = "Alt-D"
457
+ item.command() { @del_cmd.call }
458
+ tablemenu.add(item)
459
+
460
+ gotomenu = RubyCurses::Menu.new "&Goto"
461
+
462
+ item = RubyCurses::MenuItem.new "Top"
463
+ item.accelerator = "Alt-0"
464
+ item.command() { table.goto_top }
465
+ gotomenu.add(item)
466
+
467
+ item = RubyCurses::MenuItem.new "Bottom"
468
+ item.accelerator = "Alt-9"
469
+ item.command() { table.goto_bottom }
470
+ gotomenu.add(item)
471
+
472
+ item = RubyCurses::MenuItem.new "Next Page"
473
+ item.accelerator = "Ctrl-n"
474
+ item.command() { table.scroll_forward }
475
+ gotomenu.add(item)
476
+
477
+ item = RubyCurses::MenuItem.new "Prev Page"
478
+ item.accelerator = "Ctrl-p"
479
+ item.command() { table.scroll_backward }
480
+ gotomenu.add(item)
481
+
482
+ tablemenu.add(gotomenu)
483
+
484
+
485
+ tablemenu.show @atable, 0,1
486
+ end
487
+ end
488
+ if $0 == __FILE__
489
+ include RubyCurses
490
+ include RubyCurses::Utils
491
+
492
+ begin
493
+ # Initialize curses
494
+ VER::start_ncurses # this is initializing colors via ColorMap.setup
495
+ $log = Logger.new("view.log")
496
+ $log.level = Logger::DEBUG
497
+
498
+ colors = Ncurses.COLORS
499
+
500
+ catch(:close) do
501
+ t = TodoApp.new
502
+ t.run
503
+ end
504
+ rescue => ex
505
+ ensure
506
+ VER::stop_ncurses
507
+ p ex if ex
508
+ p(ex.backtrace.join("\n")) if ex
509
+ $log.debug( ex) if ex
510
+ $log.debug(ex.backtrace.join("\n")) if ex
511
+ end
512
+ end