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