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,139 @@
1
+
2
+ def group(xs, n)
3
+ (0..xs.size / n - 1).collect { |i| xs[i * n, n] } # / This comment fixes a Visual Studio Code highlighting bug
4
+ end
5
+
6
+ # --------------------------------------------------------------------------
7
+ # This algorithm is based on ref/cal.rb by Tadayoshi Funaba
8
+ #
9
+ # Differences:
10
+ # - Only supports a monthly calendar type; no yearly calendar.
11
+ #
12
+ # - Returns a secondary day position array that specifies the row
13
+ # and column for each day of the month. Useful for highlighting a
14
+ # specific day.
15
+ #
16
+ # Example day position array for May 2015. The 1st of February
17
+ # occurs in the second row and the 16th column of the returned
18
+ # string calendar array
19
+ #
20
+ # day_pos = [ nil, [2,16], [2,19],
21
+ # [3,1], [3,4], [3,7], [3,10], [3,13], [3,16], [3,19],
22
+ # [4,0], [4,3], [4,6], [4,9], [4,12], [4,15], [4,18],
23
+ # [5,0], [5,3], [5,6], [5,9], [5,12], [5,15], [5,18],
24
+ # [6,0], [6,3], [6,6], [6,9], [6,12], [6,15], [6,18],
25
+ # [7,0] ]
26
+ #
27
+ #
28
+ # - Takes a Date object as initial parameter
29
+ #
30
+ # - K variable eliminated
31
+ #
32
+ # - Does not support the transformation option
33
+ #
34
+ #
35
+ def pict(day)
36
+
37
+ year = day.year
38
+ month = day.month
39
+
40
+ dw = 2 # Day width
41
+ mw = (dw + 1) * 7 - 1 # Month width
42
+ day_pos = [ nil ] # Create an array indexed by 1 by putting nil in slot zero.
43
+
44
+ # First day of the month e.g. 2015-05-01
45
+ fi = Date.new(year, month, 1, Date::GREGORIAN)
46
+ # Go backwards to the earliest closest day divisible by 7 date, e.g. 2015-04-26,
47
+ # using the Julian day number and modulus.
48
+ fi -= (fi.jd + 1) % 7
49
+
50
+ ve = (fi..fi + 6).collect{ |cu|
51
+ %w(S M Tu W Th F S)[cu.wday]
52
+ }
53
+
54
+ row = 1
55
+ pos = -1
56
+ # Creates an array of values ["S", " M", ... "31"]
57
+ ve += (fi..fi + 41).collect{ |cu| # of type Date
58
+
59
+ pos += 1 # Needed for meta info
60
+ if (cu.jd + 1) % 7 == 0 then
61
+ row += 1
62
+ pos = 0
63
+ end
64
+
65
+ if cu.mon == month then
66
+
67
+ # -------- Save meta information in day_pos array
68
+ i = pos * 3
69
+ if cu.mday < 10 then i += 1 end
70
+
71
+ day_pos.push [row, i]
72
+ # --------- End Meta info ------------------
73
+
74
+ cu.mday
75
+ end.to_s
76
+
77
+ }
78
+
79
+ # adds padding based on day width "1" --> " 1"
80
+ ve = ve.collect{|e| e.rjust(dw)}
81
+
82
+ # Creates an array of arrays. 7 for the days in the week
83
+ gr = group(ve, 7)
84
+
85
+ # Converts inner arrays to strings
86
+ ta = gr.collect{|xs| xs.join(' ')}
87
+
88
+ # Adds month/year header to top
89
+ ca = %w(January February March April May June July
90
+ August September October November December)[month - 1]
91
+ ca = ca + ' ' + year.to_s
92
+ ca = ca.center(mw)
93
+ ta.unshift(ca)
94
+
95
+ return ta, day_pos
96
+ end
97
+
98
+
99
+
100
+ module PPCurses
101
+
102
+
103
+ # A container object that holds:
104
+ #
105
+ # - A date object
106
+ # - The month the day belongs to in string array format
107
+ # - The row and column position of the date in the string array
108
+ #
109
+ # This container is used for displaying dates in the datepicker.
110
+ # The row and column position are necessary so that the day
111
+ # selected can be highlighted.
112
+ #
113
+ class MetaMonth
114
+
115
+ attr_accessor :day
116
+ attr_accessor :month_str_array
117
+ attr_accessor :day_pos
118
+
119
+ def initialize(day=Date.today)
120
+ self.day = day
121
+ end
122
+
123
+ def day=(new_day)
124
+ @day = new_day
125
+ @month_str_array, @day_pos = pict(@day)
126
+ end
127
+
128
+ def day_row
129
+ @day_pos[ @day.day ][0]
130
+ end
131
+
132
+ def day_col
133
+ @day_pos[ @day.day ][1]
134
+ end
135
+
136
+ end
137
+
138
+
139
+ end
@@ -0,0 +1,120 @@
1
+ module PPCurses
2
+
3
+ class Button < View
4
+
5
+ attr_accessor :label
6
+ attr_accessor :selected
7
+ attr_accessor :pushed
8
+
9
+ attr_accessor :action
10
+
11
+ def initialize(label)
12
+ @label = label
13
+ @selected = false
14
+ @pushed = false
15
+ end
16
+
17
+ #
18
+ # Screen should be of type Curses::Window
19
+ #
20
+ def show(screen)
21
+ screen.attron(Curses::A_REVERSE) if @selected
22
+ screen.addstr("< #{@label} >")
23
+ screen.attroff(Curses::A_REVERSE) if @selected
24
+ end
25
+
26
+ def width
27
+ @label.length + 4
28
+ end
29
+
30
+ def height
31
+ 1
32
+ end
33
+
34
+ def set_curs_pos(screen)
35
+ Curses.curs_set(INVISIBLE)
36
+ end
37
+
38
+ def key_down( key )
39
+
40
+ if key == ENTER
41
+ @action.call unless action.nil?
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+
48
+
49
+
50
+
51
+ # For grouping two buttons together, i.e. SUBMIT/CANCEL
52
+ class ButtonPair < View
53
+
54
+ attr_accessor :selected
55
+ attr_accessor :selected_element
56
+
57
+ attr_reader :button1
58
+ attr_reader :button2
59
+
60
+ def initialize(button1_label, button2_label)
61
+ @button1 = Button.new(button1_label)
62
+ @button2 = Button.new(button2_label)
63
+
64
+ @selected_button = @button1
65
+
66
+ @selected = false
67
+ end
68
+
69
+ def selected=(val)
70
+ @selected_button.selected = val
71
+ @selected=val
72
+ end
73
+
74
+ def height
75
+ 1
76
+ end
77
+
78
+ def set_curs_pos(screen)
79
+ Curses.curs_set(INVISIBLE)
80
+ end
81
+
82
+ def key_down( key )
83
+ if key == KEY_RIGHT or key == KEY_LEFT
84
+ @selected_button.selected=false
85
+
86
+ if @selected_button == @button1
87
+ @selected_button = @button2
88
+ else
89
+ @selected_button = @button1
90
+ end
91
+
92
+ @selected_button.selected=true
93
+ return
94
+ end
95
+
96
+ @selected_button.key_down(key)
97
+
98
+ end
99
+
100
+ def show(screen)
101
+
102
+ p = screen.cur_point
103
+ p.y+=1
104
+ screen.set_pos_by_point(p)
105
+
106
+
107
+ @button1.show(screen)
108
+
109
+ p.x += @button1.width + 2
110
+ screen.set_pos_by_point(p)
111
+
112
+ @button2.show(screen)
113
+
114
+ end
115
+
116
+ end
117
+
118
+
119
+
120
+ end
@@ -0,0 +1,93 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module PPCurses
4
+
5
+ class ComboBox < View
6
+
7
+ DOWN_ARROW = '∇'
8
+
9
+ # Does the element have focus in the form?
10
+ attr_accessor :selected
11
+
12
+
13
+ def initialize(label, options)
14
+ @label = label
15
+ @options = options
16
+
17
+ @display_width = 13
18
+ @choice_made = false
19
+
20
+ @options.each do |option|
21
+ if option.length > @display_width
22
+ @display_width = option.length
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ def show(screen)
29
+ screen.attron(A_REVERSE) if @selected
30
+ screen.addstr("#{@label}:")
31
+
32
+ @combo_display_point = screen.cur_point
33
+
34
+ screen.attroff(A_REVERSE) if @selected
35
+
36
+ screen.addstr(display_string)
37
+ end
38
+
39
+ def height
40
+ 1
41
+ end
42
+
43
+ def set_curs_pos(screen)
44
+ screen.curs_set(INVISIBLE)
45
+ end
46
+
47
+ # Return Value
48
+ # The object in the receiver's internal item list corresponding to the last item selected
49
+ # from the pop-up list, or nil if no item is selected.
50
+ def object_value_of_selected_item
51
+ if @options_menu.nil?
52
+ return nil
53
+ end
54
+
55
+ @options[@options_menu.selection]
56
+ end
57
+
58
+ def key_down(key)
59
+
60
+ if key == ENTER
61
+
62
+ if @options_menu.nil?
63
+ @options_menu = PPCurses::ChoiceMenu.new( @options )
64
+ end
65
+
66
+ @options_menu.set_origin(@combo_display_point)
67
+
68
+ @options_menu.show
69
+ @options_menu.menu_selection
70
+
71
+ if @options_menu.pressed_enter
72
+ @choice_made = true
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ #------------------------------------------------
80
+ protected
81
+ def display_string
82
+ if @choice_made
83
+ return ' '+ "#{@options[@options_menu.selection].center(@display_width,'-')} #{DOWN_ARROW}"
84
+ end
85
+
86
+ ' ' + ' select '.center(@display_width,'-') + " #{DOWN_ARROW}"
87
+ end
88
+
89
+
90
+
91
+ end
92
+
93
+ end
@@ -0,0 +1,86 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module PPCurses
4
+
5
+
6
+ # Ruby date objects are immutable
7
+ # ---------------------------------
8
+ # 09-15-1950
9
+ # 05-04-2015 = May 4th 2015
10
+ class DatePicker < View
11
+
12
+ DOWN_ARROW = '∇'
13
+
14
+ # Does the element have focus in the form?
15
+ attr_accessor :selected
16
+
17
+
18
+ def initialize(label, initial_date = Date.today)
19
+ @label = label
20
+ @display_width = 13
21
+ @date = initial_date
22
+ end
23
+
24
+
25
+ def show(screen)
26
+ screen.attron(A_REVERSE) if @selected
27
+ screen.addstr("#{@label}:")
28
+
29
+ @combo_display_point = screen.cur_point
30
+
31
+ screen.attroff(A_REVERSE) if @selected
32
+
33
+ screen.addstr(display_string)
34
+ end
35
+
36
+ def height
37
+ 1
38
+ end
39
+
40
+ def set_curs_pos(screen)
41
+ screen.curs_set(INVISIBLE)
42
+ end
43
+
44
+ # Return Value
45
+ # The object in the receiver's internal item list corresponding to the last item selected
46
+ # from the pop-up list, or nil if no item is selected.
47
+ def object_value_of_selected_item
48
+ if @options_menu.nil?
49
+ return nil
50
+ end
51
+
52
+ @options[@options_menu.selection]
53
+ end
54
+
55
+ def key_down(key)
56
+
57
+ if key == ENTER
58
+
59
+ if @date_menu.nil?
60
+ @date_menu = PPCurses::DateMenu.new(@date)
61
+ end
62
+
63
+ @date_menu.set_origin(@combo_display_point)
64
+
65
+ @date_menu.show
66
+ @date_menu.menu_selection
67
+
68
+ if @date_menu.pressed_enter
69
+ @date = @date_menu.day
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ #------------------------------------------------
77
+ protected
78
+ def display_string
79
+ " #{@date.month}-#{@date.day}-#{@date.year} #{DOWN_ARROW}"
80
+ end
81
+
82
+
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,96 @@
1
+ module PPCurses
2
+
3
+ class Form < View
4
+
5
+ attr_accessor :selected_element
6
+
7
+ #
8
+ # Screen should be of type Curses::Window
9
+ #
10
+ def initialize
11
+ @elements = []
12
+ end
13
+
14
+ # Formal protocol required
15
+ # Adds an element to the form. Elements are rendered in the order they are added.
16
+ #
17
+ # An element must implement the following protocol:
18
+ #
19
+ # - def show(screen)
20
+ # - def height
21
+ # - def set_curs_pos(screen)
22
+ # - def key_down(key)
23
+ # - def selected=
24
+ #
25
+ def add (element)
26
+ PPCurses.implements_protocol( element, %w(show height set_curs_pos key_down selected=))
27
+ @elements.push(element)
28
+
29
+ if @selected_element.nil?
30
+ set_selected_element(@elements[0])
31
+ end
32
+
33
+ end
34
+
35
+
36
+ def submitted?
37
+ @button_pair.button1.pushed
38
+ end
39
+
40
+
41
+ def key_down( key )
42
+
43
+ if key == KEY_UP or key == KEY_DOWN or key == TAB
44
+ selected_index = @elements.index(@selected_element)
45
+ n_choices = @elements.length
46
+
47
+ if key == KEY_DOWN or key == TAB
48
+ (selected_index == n_choices-1) ? next_selection = 0 : next_selection = selected_index + 1
49
+ else
50
+ (selected_index == 0) ? next_selection = n_choices - 1 : next_selection = selected_index - 1
51
+ end
52
+
53
+ set_selected_element(@elements[next_selection])
54
+ return
55
+ end
56
+
57
+ @selected_element.key_down(key)
58
+
59
+ end
60
+
61
+
62
+ # TODO -- call display of subview???
63
+ def display(screen)
64
+ y = 1
65
+ x = 1
66
+
67
+ for i in 0..@elements.length - 1
68
+ element = @elements[i]
69
+ screen.setpos(y, x)
70
+ element.show(screen)
71
+ y += element.height
72
+ end
73
+
74
+
75
+ @selected_element.set_curs_pos(screen) unless @selected_element.nil?
76
+ end
77
+
78
+ # --------------------------------------------------------------------------------
79
+ protected
80
+
81
+
82
+
83
+ def set_selected_element(new_element)
84
+
85
+ unless @selected_element.nil?
86
+ @selected_element.selected=false
87
+ end
88
+
89
+ @selected_element = new_element
90
+ @selected_element.selected=true
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
@@ -0,0 +1,189 @@
1
+ module PPCurses
2
+
3
+ class InputElement < View
4
+
5
+ attr_accessor :label
6
+ attr_accessor :value
7
+ attr_accessor :size
8
+ attr_accessor :selected
9
+
10
+ attr_accessor :filter
11
+
12
+ attr_accessor :value_start_point
13
+
14
+
15
+ # Stores the X location of the cursor relative to the
16
+ # value being displayed. If the cursor is in
17
+ # the middle of the string then subsequent keys
18
+ # will be added from this location, etc.
19
+ attr_accessor :cursor_location
20
+
21
+
22
+ def initialize(label, size )
23
+ @label = label
24
+ @size = size
25
+ @selected = false
26
+ @value = ''
27
+ @cursor_location = 0
28
+ @filter = nil
29
+ end
30
+
31
+ # Creates an InputElement that only allows integer input
32
+ #
33
+ def InputElement.new_integer_only( label, size)
34
+ i_only = PPCurses::InputElement.new(label, size)
35
+ i_only.filter =PPCurses::NumberFilter.new
36
+ i_only
37
+ end
38
+
39
+
40
+ def show(screen)
41
+ print_label( screen )
42
+
43
+ @value_start_point = screen.cur_point
44
+
45
+ print_value( screen )
46
+ end
47
+
48
+
49
+
50
+ def key_down( key )
51
+ if key == DELETE
52
+ handle_delete
53
+ return
54
+ end
55
+
56
+ if key == KEY_LEFT
57
+ @cursor_location -= 1 unless @cursor_location == 0
58
+ return
59
+ end
60
+
61
+ if key == KEY_RIGHT
62
+ @cursor_location += 1 unless @cursor_location == @value.length
63
+ return
64
+ end
65
+
66
+
67
+ # Ignore control characters
68
+ if key.is_a?(Fixnum)
69
+ return
70
+ end
71
+
72
+ # Adding new characters to the string
73
+
74
+ # Check size of string before adding another character
75
+ if @value.length >= @size
76
+ # Ignore input
77
+ return
78
+ end
79
+
80
+ unless passes_filter(key)
81
+ return
82
+ end
83
+
84
+ add_character(key)
85
+
86
+ end
87
+
88
+
89
+ def passes_filter( key )
90
+ if @filter.nil?
91
+ return true
92
+ end
93
+
94
+ @filter.passes_filter( key )
95
+ end
96
+
97
+
98
+ def set_curs_pos(screen)
99
+ Curses.curs_set(VISIBLE)
100
+ x = @value_start_point.x + @cursor_location
101
+
102
+ screen.setpos( @value_start_point.y, x )
103
+ end
104
+
105
+
106
+ def height
107
+ 1
108
+ end
109
+
110
+ # --------------------------------------------------------------------------------
111
+ protected
112
+
113
+
114
+ def print_label( screen )
115
+ screen.attron(Curses::A_REVERSE) if @selected
116
+ screen.addstr("#{@label}:")
117
+ screen.attroff(Curses::A_REVERSE) if @selected
118
+ screen.addstr(' ')
119
+ end
120
+
121
+
122
+ def print_value( screen )
123
+ screen.attron(Curses::A_UNDERLINE)
124
+ screen.addstr(@value.ljust(@size))
125
+ screen.attroff(Curses::A_UNDERLINE)
126
+ end
127
+
128
+
129
+ def handle_delete
130
+ # Cursor is at the front of the string, nothing in
131
+ # front of it to delete, or there is nothing to delete
132
+ if @cursor_location == 0 or @value.length == 0
133
+ return
134
+ end
135
+
136
+ if @value.length == 1
137
+ @value = ''
138
+ @cursor_location = 0
139
+ return
140
+ end
141
+
142
+ # Cursor is at the end of the string, remove the last character
143
+ if @cursor_location == @value.length
144
+ @value = @value.slice(0..@cursor_location-2)
145
+ elsif @cursor_location == 1
146
+ # cursor is right after the first character
147
+ @value = @value.slice(1..@value.length-1)
148
+ else
149
+ # Cursor is in the middle of the string, remove the character at the cursor location
150
+ # Example:
151
+ #
152
+ # abcdefg
153
+ # 1234567
154
+ # ^ -> cursor-location = 3
155
+ #
156
+ @value = @value.slice(0..@cursor_location-2) + @value.slice(@cursor_location..@value.length-1)
157
+ end
158
+
159
+ @cursor_location -= 1
160
+ end
161
+
162
+
163
+ def add_character ( char )
164
+ if @cursor_location == @value.length
165
+ @value += char
166
+ else
167
+ @value = @value.slice(0..@cursor_location-1) + char + @value.slice(@cursor_location..@value.length-1)
168
+ end
169
+
170
+ @cursor_location += 1
171
+ end
172
+
173
+ end
174
+
175
+
176
+
177
+ class NumberFilter
178
+
179
+ def passes_filter( key )
180
+ if key =~ /^\d+$/
181
+ return true
182
+ end
183
+
184
+ false
185
+ end
186
+
187
+ end
188
+
189
+ end