ppcurses 0.0.25 → 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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/README.rdoc +50 -11
  3. data/lib/ppcurses/Screen.rb +128 -15
  4. data/lib/ppcurses/actions/BaseAction.rb +1 -2
  5. data/lib/ppcurses/actions/GetBooleanAction.rb +6 -6
  6. data/lib/ppcurses/actions/GetDataAction.rb +4 -4
  7. data/lib/ppcurses/actions/GetEnumeratedStringAction.rb +6 -6
  8. data/lib/ppcurses/actions/GetIntegerAction.rb +2 -2
  9. data/lib/ppcurses/actions/GetStringAction.rb +2 -0
  10. data/lib/ppcurses/actions/PromptAction.rb +2 -2
  11. data/lib/ppcurses/application.rb +219 -0
  12. data/lib/ppcurses/date/meta_month.rb +139 -0
  13. data/lib/ppcurses/form/button.rb +120 -0
  14. data/lib/ppcurses/form/combo_box.rb +93 -0
  15. data/lib/ppcurses/form/date_picker.rb +86 -0
  16. data/lib/ppcurses/form/form.rb +96 -0
  17. data/lib/ppcurses/form/input_element.rb +189 -0
  18. data/lib/ppcurses/form/radio_button_group.rb +56 -0
  19. data/lib/ppcurses/geometry.rb +23 -0
  20. data/lib/ppcurses/menu/BaseMenu.rb +8 -14
  21. data/lib/ppcurses/menu/Menu.rb +5 -8
  22. data/lib/ppcurses/menu/RadioMenu.rb +2 -5
  23. data/lib/ppcurses/menu/choice_menu.rb +33 -0
  24. data/lib/ppcurses/menu/date_menu.rb +97 -0
  25. data/lib/ppcurses/menu_bar.rb +102 -0
  26. data/lib/ppcurses/table_view.rb +58 -0
  27. data/lib/ppcurses/view.rb +45 -0
  28. data/lib/ppcurses/window/pp_window.rb +42 -0
  29. data/lib/ppcurses.rb +57 -3
  30. data/test/application/create_application.rb +23 -0
  31. data/test/date/printMetaMonth.rb +30 -0
  32. data/test/form/menu_opens_form.rb +15 -0
  33. data/test/form/simple_form.rb +48 -0
  34. data/test/form/test_combo.rb +20 -0
  35. data/test/form/test_date_picker.rb +19 -0
  36. data/test/getBooleanAction.rb +1 -1
  37. data/test/getDataAction.rb +6 -6
  38. data/test/getEnumStringAction.rb +4 -4
  39. data/test/getIntegerAction.rb +4 -4
  40. data/test/menu/changeMenuBorder.rb +1 -1
  41. data/test/menu/compositeMenu.rb +2 -2
  42. data/test/menu/displayMenu.rb +1 -1
  43. data/test/raw_screen/display_colours.rb +69 -0
  44. data/test/raw_screen/press_a_key.rb +18 -0
  45. data/test/table_view/testTableView.rb +16 -0
  46. data/test/threads/block_test.rb +63 -0
  47. data/test/threads/handle_resize.rb +43 -0
  48. metadata +37 -12
  49. data/lib/ppcurses/Constants.rb +0 -8
@@ -0,0 +1,56 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module PPCurses
4
+
5
+ RADIO_NOT_SELECTED = '◎'
6
+ RADIO_SELECTED = '◉'
7
+
8
+ class RadioButtonGroup < View
9
+
10
+ attr_accessor :selected
11
+ attr_reader :current_option
12
+
13
+ #
14
+ # label : a label for the radio button group
15
+ # options: an array of strings
16
+ #
17
+ def initialize( label, options )
18
+
19
+ @label = label
20
+ @options = options
21
+ @current_option = 0
22
+ end
23
+
24
+ def show(screen)
25
+ screen.attron(Curses::A_REVERSE) if @selected
26
+ screen.addstr(" #{@label}:")
27
+ screen.attroff(Curses::A_REVERSE) if @selected
28
+ @options.each_with_index do |option, index|
29
+ if index == @current_option
30
+ screen.addstr(" #{option} #{RADIO_SELECTED}")
31
+ else
32
+ screen.addstr(" #{option} #{RADIO_NOT_SELECTED}")
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ def key_down( key )
39
+ if key == KEY_LEFT then @current_option = @current_option-1 end
40
+ if key == KEY_RIGHT then @current_option = @current_option+1 end
41
+
42
+ if @current_option < 0 then @current_option = @options.length-1 end
43
+ if @current_option > @options.length-1 then @current_option = 0 end
44
+ end
45
+
46
+ def set_curs_pos(screen)
47
+ Curses.curs_set(INVISIBLE)
48
+ end
49
+
50
+ def height
51
+ 1
52
+ end
53
+
54
+ end
55
+
56
+ end
@@ -0,0 +1,23 @@
1
+ module PPCurses
2
+
3
+
4
+ class Point
5
+
6
+ attr_accessor :x, :y
7
+
8
+ def initialize( x, y )
9
+ @x = x
10
+ @y = y
11
+ end
12
+
13
+ def to_s
14
+ "x=#{@x} y=#{@y}"
15
+ end
16
+
17
+ end
18
+
19
+ class Rect
20
+
21
+ end
22
+
23
+ end
@@ -1,5 +1,3 @@
1
- require 'curses'
2
-
3
1
  module PPCurses
4
2
 
5
3
  #noinspection RubyResolve
@@ -11,20 +9,15 @@ module PPCurses
11
9
  attr_accessor :side_wall_char
12
10
  attr_accessor :top_bot_wall_char
13
11
 
14
- #
15
- # Current base ruby is 1.9.2. action_items could be an optional parameter
16
- # if the base was moved up to 2.0
17
- #
18
- def initialize(menu_items, action_items )
12
+
13
+ def initialize(menu_items, action_items=nil)
19
14
  @selection=0
20
15
  @max_menu_width = 0
21
16
 
22
17
  @side_wall_char = '|'
23
18
  @top_bot_wall_char = '-'
24
19
 
25
- sample = menu_items[0]
26
-
27
- case sample
20
+ case menu_items[0]
28
21
  when String
29
22
  # Case 1: menu_items is a list of strings, with an associated action list
30
23
  build_menu_items(menu_items, action_items)
@@ -61,14 +54,15 @@ module PPCurses
61
54
  }
62
55
  end
63
56
 
57
+ def set_origin( point )
58
+ @win.move_to_point( point )
59
+ end
60
+
64
61
  def create_window
65
62
  w_height = @menu_items.length + 4
66
63
  w_width = @max_menu_width + 4
67
- @win = Window.new(w_height,w_width,(lines-w_height) / 2, (cols-w_width)/2)
68
-
64
+ @win = PPCurses::Window.new(w_height,w_width,(Curses.lines-w_height) / 2, (Curses.cols-w_width)/2)
69
65
  @win.timeout=-1
70
- # Enables reading arrow keys in getch
71
- @win.keypad(true)
72
66
  end
73
67
 
74
68
  def set_sub_menu(menu)
@@ -1,13 +1,9 @@
1
- # Curses reference:
2
- # http://www.ruby-doc.org/stdlib-1.9.3/libdoc/curses/rdoc/Curses.html
3
-
4
- require_relative 'BaseMenu.rb'
5
- require 'curses'
6
-
7
1
  module PPCurses
8
2
  #noinspection RubyResolve
9
3
  class Menu < BaseMenu
10
4
 
5
+ attr_accessor :selection
6
+
11
7
  def show
12
8
  @win.box(self.side_wall_char, self.top_bot_wall_char)
13
9
  y = 2
@@ -15,9 +11,9 @@ module PPCurses
15
11
 
16
12
  (0...@menu_items.length).each { |i|
17
13
  @win.setpos(y, x)
18
- @win.attron(A_REVERSE) if @selection == i
14
+ @win.attron(Curses::A_REVERSE) if @selection == i
19
15
  @win.addstr(@menu_items[i].display_string)
20
- @win.attroff(A_REVERSE) if @selection == i
16
+ @win.attroff(Curses::A_REVERSE) if @selection == i
21
17
  y += 1
22
18
  }
23
19
 
@@ -27,6 +23,7 @@ module PPCurses
27
23
  end
28
24
 
29
25
  def set_global_action(action)
26
+ PPCurses.implements_protocol(action, %w(execute))
30
27
  @global_action = action
31
28
  end
32
29
 
@@ -1,12 +1,9 @@
1
-
2
- require_relative 'BaseMenu.rb'
3
-
4
1
  module PPCurses
5
2
  #noinspection RubyResolve
6
3
  class RadioMenu < BaseMenu
7
4
 
8
5
  # TODO - duplicate code from Menu ...
9
- def initialize( menu_items, action_items )
6
+ def initialize( menu_items, action_items=nil )
10
7
  @items = Array.new
11
8
  @actions = Array.new
12
9
 
@@ -28,7 +25,7 @@ module PPCurses
28
25
 
29
26
  w_width = @menu_length + 4
30
27
 
31
- @win = Window.new(3, w_width ,0, (cols - w_width) / 2)
28
+ @win = Window.new(3, w_width ,0, (Curses.cols - w_width) / 2)
32
29
 
33
30
  @win.timeout=-1
34
31
  # Enables reading arrow keys in getch
@@ -0,0 +1,33 @@
1
+ module PPCurses
2
+
3
+
4
+ class ChoiceMenu < Menu
5
+
6
+ attr_reader :selection
7
+ attr_reader :pressed_enter
8
+
9
+ def menu_selection
10
+ while 1
11
+ c = @win.getch
12
+
13
+ if c == ESCAPE
14
+ @pressed_enter = false
15
+ self.hide
16
+ break
17
+ end
18
+
19
+ if c == ENTER
20
+ @pressed_enter = true
21
+ self.hide
22
+ break
23
+ end
24
+
25
+ self.handle_menu_selection(c)
26
+
27
+ end
28
+ end
29
+
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,97 @@
1
+ module PPCurses
2
+
3
+
4
+ class DateMenu < ChoiceMenu
5
+
6
+ def initialize(day)
7
+ @meta_info = MetaMonth.new(day)
8
+ find_max_menu_width
9
+ create_window
10
+ end
11
+
12
+ def day
13
+ @meta_info.day
14
+ end
15
+
16
+
17
+ def find_max_menu_width
18
+ @max_menu_width = 0
19
+
20
+ str_array = @meta_info.month_str_array
21
+
22
+ (0...str_array.length).each { |i|
23
+ display = str_array[i]
24
+ @max_menu_width = display.length if display.length > @max_menu_width
25
+ }
26
+ end
27
+
28
+
29
+ def create_window
30
+ w_height = @meta_info.month_str_array.length + 4
31
+ w_width = @max_menu_width + 4
32
+ @win = PPCurses::Window.new(w_height,w_width,(Curses.lines-w_height) / 2, (Curses.cols-w_width)/2)
33
+ @win.timeout=-1
34
+ end
35
+
36
+
37
+ def show
38
+ y = 2
39
+ x = 2
40
+
41
+ str_array = @meta_info.month_str_array
42
+
43
+ str_array.each_with_index { |val, i|
44
+ @win.setpos(y, x)
45
+
46
+ if i != @meta_info.day_row
47
+ @win.addstr(val)
48
+ else
49
+ num_of_digits = @meta_info.day.day > 9 ? 2 : 1
50
+ @win.addstr(val[0, @meta_info.day_col])
51
+ @win.attron(Curses::A_REVERSE)
52
+ @win.addstr(val[@meta_info.day_col, num_of_digits])
53
+ @win.attroff(Curses::A_REVERSE)
54
+ @win.addstr(val[@meta_info.day_col + num_of_digits, val.length])
55
+ end
56
+
57
+ y += 1
58
+ }
59
+
60
+ @win.refresh
61
+
62
+ @sub_menu.show if @sub_menu
63
+ end
64
+
65
+ def handle_menu_selection(c)
66
+
67
+ curr_day = @meta_info.day
68
+ day_change = 0
69
+
70
+ if c == KEY_UP then day_change = -7 end
71
+ if c == KEY_DOWN then day_change = 7 end
72
+ if c == KEY_LEFT then day_change = -1 end
73
+ if c == KEY_RIGHT then day_change = 1 end
74
+
75
+ # Use vi key bindings for months and year
76
+ # browsing.
77
+ if c == 'l' then day_change = 30 end
78
+ if c == 'h' then day_change = -30 end
79
+ if c == 'j' then day_change = 365 end
80
+ if c == 'k' then day_change = -365 end
81
+
82
+ if day_change != 0
83
+ curr_day = Date.jd(curr_day.jd + day_change)
84
+ @meta_info.day = curr_day
85
+ self.show
86
+ return true
87
+ end
88
+
89
+ false
90
+ end
91
+
92
+
93
+
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,102 @@
1
+ module PPCurses
2
+
3
+
4
+
5
+ # The menubar is activated and deactivated by the ESCAPE key.
6
+ #
7
+ # Any responders further down the responder chain will never receive ESCAPE key events.
8
+ #
9
+ class MenuBar < Responder
10
+
11
+ def initialize
12
+ @menu_items = []
13
+ @selected = false
14
+ end
15
+
16
+ # Expects screen to be a PPCurses::Screen object
17
+ # Need to convert to work with a window or a view.
18
+ def show(screen)
19
+
20
+ screen.set_pos_by_point(ZERO_POINT)
21
+
22
+ if @selected
23
+ screen.attron(A_REVERSE)
24
+ else
25
+ screen.attron(A_UNDERLINE)
26
+ end
27
+
28
+ @menu_items.each do |menu_item|
29
+ screen.addstr( "#{menu_item} ")
30
+ end
31
+
32
+ p = screen.cur_point
33
+
34
+ screen.addstr( ' ' * (screen.width - p.x) )
35
+
36
+ if @selected
37
+ screen.attroff(A_REVERSE)
38
+ else
39
+ screen.attroff(A_UNDERLINE)
40
+ end
41
+
42
+ end
43
+
44
+
45
+ def add_menu_item(menu_item)
46
+ @menu_items.push(menu_item)
47
+ end
48
+
49
+
50
+ def key_down( key )
51
+
52
+ if key == ESCAPE
53
+ @selected = !@selected
54
+ return
55
+ end
56
+
57
+ if @selected
58
+ @menu_items.each do |menu_item|
59
+ if key == menu_item.key
60
+ menu_item.action.call
61
+ return
62
+ end
63
+ end
64
+ return
65
+ end
66
+
67
+ @next_responder.key_down(key) unless @next_responder.nil?
68
+
69
+ end
70
+
71
+ end
72
+
73
+ # Based on Cocoa NSMenuItem
74
+ #
75
+ # Current link, which probably won't be valid in the future ...
76
+ #
77
+ # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSMenuItem_Class/index.html#//apple_ref/occ/instm/NSMenuItem/action
78
+ # ----------------------------------------------------------
79
+ # MenuBarItems ...
80
+ #
81
+ # q:Quit d:Del u:Undel
82
+ #
83
+ class MenuBarItem
84
+
85
+ attr_accessor :key
86
+ attr_accessor :label
87
+
88
+ attr_accessor :action
89
+
90
+
91
+ def initialize( key, label )
92
+ @key = key
93
+ @label = label
94
+ end
95
+
96
+ def to_s
97
+ "#{@key}:#{@label}"
98
+ end
99
+
100
+ end
101
+
102
+ end
@@ -0,0 +1,58 @@
1
+ module PPCurses
2
+ # Based loosely on ...
3
+ #
4
+ # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSTableView_Class/index.html#//apple_ref/occ/cl/NSTableView
5
+ #
6
+ #
7
+ class TableView < View
8
+
9
+ attr_accessor :data_source
10
+
11
+
12
+ def selected_row
13
+
14
+ end
15
+
16
+ # A data source must implement a formal protocol
17
+ #
18
+ # - def number_of_rows_in_table(tableView)
19
+ # - def object_value_for(tableView, tableColumn, rowIndex)
20
+ #
21
+ def data_source=(val)
22
+ PPCurses.implements_protocol( val, %w(number_of_rows_in_table object_value_for ))
23
+ @data_source = val
24
+ end
25
+
26
+ end
27
+
28
+
29
+ # Based loosely on ...
30
+ #
31
+ # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Protocols/NSTableDataSource_Protocol/index.html#//apple_ref/occ/intf/NSTableViewDataSource
32
+
33
+ class SingleColumnDataSource
34
+
35
+ def initialize(values)
36
+ @values = values
37
+ end
38
+
39
+ def number_of_rows_in_table
40
+ @values.length
41
+ end
42
+
43
+ def object_value_for(column, row_index)
44
+ @values[row_index]
45
+ end
46
+ end
47
+
48
+ class TableViewDataSource
49
+
50
+ def TableViewDataSource.from_string_array(values)
51
+ SingleColumnDataSource.new(values)
52
+ end
53
+
54
+ end
55
+
56
+
57
+
58
+ end
@@ -0,0 +1,45 @@
1
+ module PPCurses
2
+
3
+ # View Hierarchy information:
4
+ #
5
+ # https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CocoaViewsGuide/WorkingWithAViewHierarchy/WorkingWithAViewHierarchy.html
6
+
7
+ # Based loosely on ...
8
+ #
9
+ # https://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/index.html#//apple_ref/occ/instp/NSView/subviews
10
+
11
+ class View < ResponderManager
12
+
13
+ ## Managing the View Hierarchy
14
+
15
+ # superview
16
+
17
+ # add_subview
18
+
19
+ # subviews
20
+
21
+ # set_subviews
22
+
23
+
24
+ ## Drawing
25
+
26
+ # Expects screen to be a PPCurses::Screen object
27
+ # Need to convert to work with a window or a view.
28
+ #
29
+ # The default implementation does nothing
30
+ def display(screen)
31
+
32
+ end
33
+
34
+
35
+ ## Key-view Loop Management
36
+
37
+ # canBecomeKeyView
38
+
39
+ # nextKeyView
40
+
41
+ #
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,42 @@
1
+ module PPCurses
2
+
3
+ class Window < Curses::Window
4
+
5
+ # TODO - use optional parameters. A rect or a Curses window to wrap.
6
+ def initialize(height, width, top, left)
7
+ super(height,width,top,left)
8
+
9
+ # Enables reading arrow keys in getch
10
+ keypad(true)
11
+
12
+ box('|', '-')
13
+ end
14
+
15
+
16
+
17
+ # EXPERIMENTAL/HACK
18
+ #
19
+ # The following could be used to wrap all getch calls
20
+ # and support window resizes when the getch is blocking
21
+ # all threads.
22
+ #
23
+ def get_ch_handle_signals
24
+ got_input = false
25
+ until got_input
26
+ begin
27
+ c = getch
28
+ got_input = true
29
+ rescue NoMethodError
30
+ # Assuming a SIGWINCH occurred -- reposition..
31
+ c = ''
32
+ end
33
+ end
34
+
35
+ c
36
+ end
37
+
38
+ end
39
+
40
+
41
+
42
+ end
data/lib/ppcurses.rb CHANGED
@@ -1,13 +1,52 @@
1
+ gem 'curses', '=1.0.1'
2
+ require 'curses'
3
+ require 'date'
1
4
 
2
5
  module PPCurses
6
+
7
+ TAB = 9
8
+ ENTER = 10
9
+ ESCAPE = 27
10
+ DELETE = 127
11
+ SPACE_BAR = ' '
12
+ KEY_RIGHT = Curses::KEY_RIGHT
13
+ KEY_LEFT = Curses::KEY_LEFT
14
+ KEY_UP = Curses::KEY_UP
15
+ KEY_DOWN = Curses::KEY_DOWN
16
+
17
+
18
+ A_REVERSE = Curses::A_REVERSE
19
+ A_UNDERLINE = Curses::A_UNDERLINE
20
+
21
+ # To be used in conjunction with curs_set for more readable code e.g. Curses.curs_set(INVISIBLE)
22
+ INVISIBLE = 0
23
+ VISIBLE = 1
24
+
25
+ def PPCurses.implements_protocol( element, methods )
26
+ methods.each { |method|
27
+ unless element.respond_to?(method); raise TypeError, "** Method #{method} MUST be defined **" end
28
+ }
29
+ end
30
+
31
+
32
+ require_relative 'ppcurses/application.rb'
33
+ require_relative 'ppcurses/view.rb'
34
+ require_relative 'ppcurses/menu_bar.rb'
3
35
  require_relative 'ppcurses/Screen.rb'
4
- require_relative 'ppcurses/Constants.rb'
5
- # Menus
36
+ require_relative 'ppcurses/geometry.rb'
37
+ require_relative 'ppcurses/table_view.rb'
38
+
39
+ # Dates ---------------------------------------------------------------------------------------------------------------
40
+ require_relative 'ppcurses/date/meta_month.rb'
41
+ # Menus ---------------------------------------------------------------------------------------------------------------
42
+ require_relative 'ppcurses/menu/BaseMenu.rb'
6
43
  require_relative 'ppcurses/menu/Menu.rb'
7
44
  require_relative 'ppcurses/menu/CompositeMenu.rb'
8
45
  require_relative 'ppcurses/menu/RadioMenu.rb'
9
46
  require_relative 'ppcurses/menu/menu_item.rb'
10
- # Actions
47
+ require_relative 'ppcurses/menu/choice_menu.rb'
48
+ require_relative 'ppcurses/menu/date_menu.rb'
49
+ # Actions -------------------------------------------------------------------------------------------------------------
11
50
  require_relative 'ppcurses/actions/ShowMenuAction.rb'
12
51
  require_relative 'ppcurses/actions/GetStringAction.rb'
13
52
  require_relative 'ppcurses/actions/GetBooleanAction.rb'
@@ -16,6 +55,21 @@ module PPCurses
16
55
  require_relative 'ppcurses/actions/GetDataAction.rb'
17
56
  require_relative 'ppcurses/actions/NulAction.rb'
18
57
  require_relative 'ppcurses/actions/InsertSQLDataAction.rb'
58
+ # Forms ---------------------------------------------------------------------------------------------------------------
59
+ require_relative 'ppcurses/form/form.rb'
60
+ require_relative 'ppcurses/form/button.rb'
61
+ require_relative 'ppcurses/form/input_element.rb'
62
+ require_relative 'ppcurses/form/radio_button_group.rb'
63
+ require_relative 'ppcurses/form/combo_box.rb'
64
+ require_relative 'ppcurses/form/date_picker.rb'
65
+ # Windows ------------------------------------------------------------------------------------------------------------
66
+ require_relative 'ppcurses/window/pp_window.rb'
67
+
68
+
69
+ ZERO_POINT = Point.new(0,0)
70
+
71
+ NO = false
72
+ YES = true
19
73
  end
20
74
 
21
75
 
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../../lib/ppcurses.rb'
5
+
6
+
7
+ class MyDelegate
8
+
9
+ def applicationDidFinishLaunching( app )
10
+
11
+ end
12
+
13
+ end
14
+
15
+
16
+
17
+ delegate = MyDelegate.new
18
+
19
+ app = PPCurses::Application.new
20
+ app.set_delegate(delegate)
21
+ app.launch
22
+
23
+
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_relative '../../lib/ppcurses.rb'
5
+
6
+
7
+ class String
8
+ def reverse_color; "\033[7m#{self}\033[27m" end
9
+ end
10
+
11
+ cal = PPCurses::MetaMonth.new
12
+
13
+ cal.month_str_array.each_with_index { |val, i|
14
+
15
+ if i != cal.day_row
16
+ puts val
17
+ else
18
+ num_of_digits = cal.day.day > 9 ? 2 : 1
19
+
20
+ print val[0, cal.day_col]
21
+ print val[cal.day_col, num_of_digits].reverse_color
22
+ puts val[cal.day_col + num_of_digits, val.length]
23
+ end
24
+
25
+ }
26
+
27
+
28
+
29
+
30
+