rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,194 @@
1
+ require 'rbhex/core/widgets/rwidget'
2
+ #include Ncurses # FFI 2011-09-8
3
+ include RubyCurses
4
+ module RubyCurses
5
+ #
6
+ # This paints labels for various keys at the bottom of the screen, in 2 rows.
7
+ # This is based on alpines last 2 rows. Modes are supported so that the
8
+ # labels change as you enter a widget.
9
+ # For an example, see dbdemo.rb or rfe.rb
10
+ # NOTE: applications using 'App' use a shortcut "dock" to create this.
11
+ #
12
+ # The most minimal keylabel to print one label in first row, and none in second is:
13
+ # [["F1", "Help"], nil]
14
+ # To print 2 labels, one over the other:
15
+ # [["F1", "Help"], ["F10", "Quit"]]
16
+ #
17
+ class KeyLabelPrinter < Widget
18
+ attr_reader :key_labels
19
+ # the current mode (labels are based on mode, changing the mode, changes the labels
20
+ # displayed)
21
+ dsl_property :mode
22
+ # set the color of the labels, overriding the defaults
23
+ dsl_accessor :footer_color_pair
24
+ # set the color of the mnemonic, overriding the defaults
25
+ dsl_accessor :footer_mnemonic_color_pair
26
+ #attr_accessor :row_relative # lets only advertise this when we've tested it out
27
+
28
+ def initialize form, key_labels, config={}, &block
29
+
30
+ @name = "dock"
31
+ case key_labels
32
+ when Hash
33
+ raise "KeyLabelPrinter: KeyLabels cannot be a hash, Array of key labels required. Perhaps you did not pass labels"
34
+ when Array
35
+ else
36
+ raise "KeyLabelPrinter: Array of key labels required. Perhaps you did not pass labels"
37
+ end
38
+ super form, config, &block
39
+ @mode ||= :normal
40
+ #@key_labels = key_labels
41
+ @key_hash = {}
42
+ @key_hash[@mode] = key_labels
43
+ @editable = false
44
+ @focusable = false
45
+ unless @row
46
+ @row_relative = -2
47
+ @row = Ncurses.LINES + @row_relative
48
+ end
49
+ @col ||= 0
50
+ # if negativ row passed we store as relative to bottom, so we can maintain that.
51
+ if @row < 0
52
+ @row_relative = @row
53
+ @row = Ncurses.LINES - @row
54
+ else
55
+ @row_relative = (Ncurses.LINES - @row) * -1
56
+ end
57
+ @cols ||= Ncurses.COLS-1
58
+ @repaint_required = true
59
+ @footer_color_pair ||= $bottomcolor
60
+ @footer_mnemonic_color_pair ||= $reversecolor #2
61
+ end
62
+ def key_labels mode=@mode
63
+ @key_hash[mode]
64
+ end
65
+ # returns the keys as printed. these may or may not help
66
+ # in validation depedign on what you passed as zeroth index
67
+ def get_current_keys
68
+ a = []
69
+ @key_hash[@mode].each do |arr|
70
+ a << arr[0] unless arr.nil?
71
+ end
72
+ return a
73
+ end
74
+ def getvalue
75
+ @key_hash
76
+ end
77
+ def set_key_labels _key_labels, mode=:normal
78
+ @key_hash[mode] = _key_labels
79
+ end
80
+
81
+ ##
82
+ # XXX need to move wrapping etc up and done once.
83
+ def repaint
84
+ return unless @repaint_required
85
+ r,c = rowcol
86
+ # this should only happen if there's a change in window
87
+ if @row_relative
88
+ @row = Ncurses.LINES+@row_relative
89
+ end
90
+ arr = key_labels()
91
+ print_key_labels(arr, mode=@mode)
92
+ @repaint_required = false
93
+ end
94
+ # ?? does not use mode, i think key_labels is unused. a hash is now used 2011-10-11 XXX FIXME
95
+ # WARNING, i have not tested this after changing it.
96
+ def append_key_label key, label, mode=@mode
97
+ #@key_labels << [key, label] if !@key_labels.include? [key, label]
98
+ @key_hash[mode] << [key, label] if !@key_hash[mode].include? [key, label]
99
+ @repaint_required = true
100
+ end
101
+ def print_key_labels(arr = key_labels(), mode=@mode)
102
+ #return if !@show_key_labels # XXX
103
+ @win ||= @form.window
104
+ #$log.debug "XXX: PKL #{arr.length}, #{arr}"
105
+ @padding = @cols / (arr.length/2)
106
+ posx = 0
107
+ even = []
108
+ odd = []
109
+ arr.each_index { |i|
110
+ if i % 2 == 0
111
+ #arr[i+1] = ['',''] if arr[i+1].nil?
112
+ nextarr = arr[i+1] || ['', '']
113
+ keyw = [arr[i][0].length, nextarr[0].length].max
114
+ labelw = [arr[i][1].length, nextarr[1].length].max
115
+
116
+ even << [ sprintf("%*s", keyw, arr[i][0]), sprintf("%-*s", labelw, arr[i][1]) ]
117
+ odd << [ sprintf("%*s", keyw, nextarr[0]), sprintf("%-*s", labelw, nextarr[1]) ]
118
+ #$log.debug("loop even: #{even.inspect}")
119
+ else
120
+ end
121
+ }
122
+ #$log.debug("even: #{even.inspect}")
123
+ #$log.debug("odd : #{odd.inspect}")
124
+ #posy = @barrow-1
125
+ posy = @row
126
+ print_key_labels_row(posy, posx, even)
127
+ posy = @row+1
128
+ print_key_labels_row(posy, posx, odd)
129
+ # uncommented next line after ffi-ncurses else not showing till key press FFI 2011-09-17
130
+ @win.wrefresh # needed else secod row not shown after askchoice XXX
131
+ end
132
+ def print_key_labels_row(posy, posx, arr)
133
+ # FIXME: this logic of padding needs to take into account
134
+ # width of window
135
+ padding = 8
136
+ padding = 4 if arr.length > 5
137
+ padding = 2 if arr.length > 7
138
+ padding = 0 if arr.length > 9
139
+ #padding = @padding # XXX 2008-11-13 23:01
140
+ my_form_win = @win
141
+ @win.printstring(posy,0, "%-*s" % [@cols," "], @footer_color_pair, @attr)
142
+ arr.each do |kl|
143
+ key = kl[0]
144
+ lab = kl[1]
145
+ if key !="" # don't print that white blank space for fillers
146
+ color_pair= @footer_mnemonic_color_pair # $reversecolor #2
147
+ x = posx + (key.length - key.strip.length)
148
+ my_form_win.attron(Ncurses.COLOR_PAIR(color_pair))
149
+ my_form_win.mvprintw(posy, x, "%s" % kl[0].strip );
150
+ my_form_win.attroff(Ncurses.COLOR_PAIR(color_pair))
151
+ end
152
+ color_pair=@footer_color_pair
153
+ posx = posx + kl[0].length
154
+ my_form_win.attron(Ncurses.COLOR_PAIR(color_pair))
155
+
156
+ #lab = sprintf(" %s %*s" , kl[1], padding, " ");
157
+ lab = sprintf(" %s %s" , kl[1], " "*padding);
158
+ my_form_win.mvprintw(posy, posx, lab)
159
+ my_form_win.attroff(Ncurses.COLOR_PAIR(color_pair))
160
+ posx = posx + lab.length
161
+ end
162
+ end
163
+ ##
164
+ # updates existing label with a new one.
165
+ # @return true if updated, else false
166
+ # @example update "C-x", "C-x", "Disable"
167
+ def update_application_key_label(display_code, new_display_code, text)
168
+ @repaint_required = true
169
+ labels = key_labels()
170
+ raise "labels are nil !!!" unless labels
171
+ labels.each_index do |ix|
172
+ lab = labels[ix]
173
+ next if lab.nil?
174
+ if lab[0] == display_code
175
+ labels[ix] = [new_display_code , text]
176
+ $log.debug("updated #{labels[ix]}")
177
+ return true
178
+ end
179
+ end
180
+ return false
181
+ end
182
+ alias :update :update_application_key_label
183
+ ##
184
+ # inserts an application label at given index
185
+ # to add the key, use create_datakeys to add bindings
186
+ # remember to call restore_application_key_labels after updating/inserting
187
+ def insert_application_key_label(index, display_code, text)
188
+ @repaint_required = true
189
+ labels = key_labels()
190
+ labels.insert(index, [display_code , text] )
191
+ end
192
+ # ADD HERE KEYLABEL
193
+ end
194
+ end
@@ -0,0 +1,253 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: rcombo.rb
3
+ # Description: Non-editable combo box.
4
+ # Make it dead-simple to use.
5
+ # This is a simpler version of the original ComboBox which allowed
6
+ # editing and used rlistbox. This simpler class is meant for the rbhex
7
+ # core package and will only depend on a core class if at all.
8
+ # Author: rkumar http://github.com/rkumar/rbcurse/
9
+ # Date: 2011-11-11 - 21:42
10
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
11
+ # Last update: 2014-03-24 16:42
12
+ # ----------------------------------------------------------------------------- #
13
+ #
14
+ require 'rbhex'
15
+
16
+ include RubyCurses
17
+ module RubyCurses
18
+ extend self
19
+
20
+ # the quick approach would be to use field, and just add a popup.
21
+ # Or since we are not editing, we could use a Label and a popup
22
+ # Or just display a label and a popup without using anything else.
23
+ # Thre is an undocumented variable +display_length+ which is the size of the label
24
+ # This is used to position the combo symbol and the popup. This can be calculated
25
+ # based on the label. 2014-03-24 - 16:42
26
+
27
+ class ComboBox < Field
28
+ include RubyCurses::EventHandler
29
+ dsl_accessor :list_config
30
+
31
+ attr_accessor :current_index
32
+ # the symbol you want to use for combos
33
+ attr_accessor :COMBO_SYMBOL
34
+ attr_accessor :show_symbol # show that funny symbol after a combo to signify its a combo
35
+ dsl_accessor :arrow_key_policy # :IGNORE :NEXT_ROW :POPUP
36
+
37
+ def initialize form, config={}, &block
38
+ @arrow_key_policy = :ignore
39
+ @editable = false
40
+ #@COMBO_SYMBOL = "v".ord # trying this out
41
+ # thanks hramrach for fix
42
+ if RUBY_VERSION < "1.9" then
43
+ @COMBO_SYMBOL = "v"[0] # trying this out
44
+ else
45
+ @COMBO_SYMBOL = "v".ord # trying this out
46
+ end
47
+ @current_index = 0
48
+ super
49
+ ## this was getting overridden this making the combo editable 2014-03-24 - 16:24
50
+ @editable = false
51
+ # added if check since it was overriding set_buffer in creation. 2009-01-18 00:03
52
+ set_buffer @list[@current_index].dup if @buffer.nil? or @buffer.empty?
53
+ init_vars
54
+ @_events.push(*[:CHANGE, :ENTER_ROW, :LEAVE_ROW])
55
+ end
56
+ def init_vars
57
+ super
58
+ @show_symbol = true if @show_symbol.nil? # if set to false don't touch
59
+ #@show_symbol = false if @label # 2011-11-13
60
+ @COMBO_SYMBOL ||= FFI::NCurses::ACS_DARROW #GEQUAL
61
+
62
+ # next 2 lines giving an error in newtabbedwindow.rb example since the methods
63
+ # have been deprecated.
64
+ #bind_key(KEY_UP) { previous_row }
65
+ #bind_key(KEY_DOWN) { next_row }
66
+ end
67
+ def selected_item
68
+ @list[@current_index]
69
+ end
70
+ def selected_index
71
+ @current_index
72
+ end
73
+
74
+ ##
75
+ # convert given list to datamodel
76
+ def list alist=nil
77
+ return @list if alist.nil?
78
+ #@list = RubyCurses::ListDataModel.new(alist)
79
+ @list = alist
80
+ end
81
+ ##
82
+ # combo edit box key handling
83
+ # removed UP and DOWN and bound it, so it can be unbound
84
+ def handle_key(ch)
85
+ @current_index ||= 0
86
+ # added 2009-01-18 22:44 no point moving horiz or passing up to Field if not edit
87
+ if !@editable
88
+ if ch == KEY_LEFT or ch == KEY_RIGHT
89
+ return :UNHANDLED
90
+ end
91
+ end
92
+ case @arrow_key_policy
93
+ when :ignore
94
+ if ch == KEY_DOWN or ch == KEY_UP
95
+ return :UNHANDLED
96
+ end
97
+ when :popup
98
+ if ch == KEY_DOWN or ch == KEY_UP
99
+ popup
100
+ end
101
+ end
102
+ case ch
103
+ #when KEY_UP # show previous value
104
+ # previous_row
105
+ #when KEY_DOWN # show previous value
106
+ # next_row
107
+ # adding spacebar to popup combo, as in microemacs 2010-10-01 13:21
108
+ when 32, KEY_DOWN+ META_KEY # alt down
109
+ popup # pop up the popup
110
+ else
111
+ super
112
+ end
113
+ end
114
+ def DEPprevious_row
115
+ @current_index -= 1 if @current_index > 0
116
+ set_buffer @list[@current_index].dup
117
+ set_modified(true) ## ??? not required
118
+ fire_handler :ENTER_ROW, self
119
+ @list.on_enter_row self
120
+ end
121
+ def DEPnext_row
122
+ @current_index += 1 if @current_index < @list.length()-1
123
+ set_buffer @list[@current_index].dup
124
+ set_modified(true) ## ??? not required
125
+ fire_handler :ENTER_ROW, self
126
+ @list.on_enter_row self
127
+ end
128
+ ##
129
+ # calls a popup list
130
+ # TODO: should not be positioned so that it goes off edge
131
+ # user's customizations of list should be passed in
132
+ # The dup of listconfig is due to a tricky feature/bug.
133
+ # I try to keep the config hash and instance variables in synch. So
134
+ # this config hash is sent to popuplist which updates its row col and
135
+ # next time we pop up the popup row and col are zero.
136
+ #
137
+ #
138
+ # added dup in PRESS since editing edit field mods this
139
+ # on pressing ENTER, value set back and current_index updated
140
+ def popup
141
+ @list_config ||= {}
142
+ @list_config[:row] ||= @row
143
+ #@list_config[:col] ||= @col
144
+ @list_config[:col] ||= @col + @display_length
145
+ @list_config[:relative_to] ||= self
146
+ # this does not allow us to bind to events in the list
147
+ index = popuplist @list, @list_config
148
+ if index
149
+ set_buffer @list[index].dup
150
+ set_modified(true) if @current_index != index
151
+ @current_index = index
152
+ end
153
+ end
154
+ def OLDpopup
155
+ listconfig = (@list_config && @list_config.dup) || {}
156
+ dm = @list
157
+ # current item in edit box will be focussed when list pops up
158
+ #$log.debug "XXX POPUP: #{dm.selected_index} = #{@current_index}, value #{@buffer}"
159
+ # we are having some problms when using this in a list. it retains earlier value
160
+ _index = dm.index @buffer
161
+ dm.selected_index = _index # @current_index
162
+ poprow = @row+0 # one row below the edit box
163
+ popcol = @col
164
+ dlength = @display_length
165
+ f = self
166
+ @popup = RubyCurses::PopupList.new do
167
+ row poprow
168
+ col popcol
169
+ width dlength
170
+ list_data_model dm
171
+ list_selection_mode 'single'
172
+ relative_to f
173
+ list_config listconfig
174
+ bind(:PRESS) do |index|
175
+ f.set_buffer dm[index].dup
176
+ f.set_modified(true) if f.current_index != index
177
+ f.current_index = index
178
+ end
179
+ end
180
+ end
181
+
182
+ # Field putc advances cursor when it gives a char so we override this
183
+ def putc c
184
+ if c >= 0 and c <= 127
185
+ ret = putch c.chr
186
+ if ret == 0
187
+ addcol 1 if @editable
188
+ set_modified
189
+ end
190
+ end
191
+ return -1 # always ??? XXX
192
+ end
193
+ ##
194
+ # field does not give char to non-editable fields so we override
195
+ def putch char
196
+ @current_index ||= 0
197
+ if @editable
198
+ raise "how is it editable here in combo"
199
+ super
200
+ return 0
201
+ else
202
+ match = next_match(char)
203
+ set_buffer match unless match.nil?
204
+ fire_handler :ENTER_ROW, self
205
+ end
206
+ @modified = true
207
+ fire_handler :CHANGE, self # 2008-12-09 14:51 ???
208
+ 0
209
+ end
210
+ ##
211
+ # the sets the next match in the edit field
212
+ def next_match char
213
+ start = @current_index
214
+ start.upto(@list.length-1) do |ix|
215
+ if @list[ix][0,1].casecmp(char) == 0
216
+ return @list[ix] unless @list[ix] == @buffer
217
+ end
218
+ @current_index += 1
219
+ end
220
+ ## could not find, start from zero
221
+ @current_index = 0
222
+ start = [@list.length()-1, start].min
223
+ 0.upto(start) do |ix|
224
+ if @list[ix][0,1].casecmp(char) == 0
225
+ return @list[ix] unless @list[ix] == @buffer
226
+ end
227
+ @current_index += 1
228
+ end
229
+ @current_index = [@list.length()-1, @current_index].min
230
+ return nil
231
+ end
232
+ ##
233
+ # on leaving the listbox, update the combo/datamodel.
234
+ # we are using methods of the datamodel. Updating our list will have
235
+ # no effect on the list, and wont trigger events.
236
+ # Do not override.
237
+ def on_leave
238
+ fire_handler :LEAVE, self
239
+ end
240
+
241
+ def repaint
242
+ super
243
+ c = @col + @display_length
244
+ if @show_symbol # 2009-01-11 18:47
245
+ # i have changed c +1 to c, since we have no right to print beyond display_length
246
+ @form.window.mvwaddch @row, c, @COMBO_SYMBOL # Ncurses::ACS_GEQUAL
247
+ @form.window.mvchgat(y=@row, x=c, max=1, Ncurses::A_REVERSE|Ncurses::A_UNDERLINE, $datacolor, nil)
248
+ end
249
+ end
250
+
251
+ end # class ComboBox
252
+
253
+ end # module
@@ -0,0 +1,415 @@
1
+ =begin
2
+ * Name: A container that manages components placed in it but
3
+ is not a form. Thus it can be safely placed as a widget
4
+ without all the complicatinos of a form embedded inside another.
5
+ NOTE: Still experimental
6
+ * Description
7
+ * Author: rkumar (http://github.com/rkumar/rbcurse/)
8
+ * Date: 21.10.11 - 00:29
9
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+
11
+ * Last update: 23.10.11 - 00:29
12
+ == CHANGES
13
+ Focusables so we don't focus on label
14
+ == TODO
15
+ How to put blank lines in stack - use a blank label
16
+
17
+ - The contaomers and multis need to do their own on_enter and on_leave
18
+ management, they cannot rely on some other container doing it.
19
+ We can only rely on handle_key being called. HK should determine
20
+ whether any set_form row etc needs to be done.
21
+ - Should have its own stack and flow
22
+ =end
23
+
24
+ require 'rbhex'
25
+
26
+ include RubyCurses
27
+ module RubyCurses
28
+ extend self
29
+
30
+ # This is an attempt at having a container which can contain multiple
31
+ # widgets without being a form itself. Having forms within forms
32
+ # complicates code too much, esp cursor positioning. e.g. tabbedpane
33
+
34
+ class Container < Widget
35
+
36
+ dsl_accessor :suppress_borders #to_print_borders
37
+ dsl_accessor :border_attrib, :border_color
38
+ dsl_accessor :title #set this on top
39
+ dsl_accessor :title_attrib #bold, reverse, normal
40
+ # should container stack objects ignoring users row col
41
+ # this is esp needed since App sets row and col which is too early
42
+ # This is now the default value, till i can redo things
43
+ #dsl_accessor :stack
44
+ dsl_accessor :positioning # absolute, relative, stack
45
+ attr_reader :current_component
46
+
47
+ def initialize form=nil, config={}, &block
48
+ @suppress_borders = false
49
+ @row_offset = @col_offset = 1
50
+ @_events ||= []
51
+ @stack = true
52
+ @positioning = :stack
53
+ super
54
+ @focusable = true
55
+ @editable = false
56
+ @components = [] # all components
57
+ @focusables = [] # focusable components, makes checks easier
58
+
59
+ init_vars
60
+ end
61
+ def init_vars
62
+ @repaint_required = true
63
+ @row_offset = @col_offset = 0 if @suppress_borders # FIXME supposed to use this !!
64
+
65
+ @internal_width = 2
66
+ @internal_width = 1 if @suppress_borders
67
+ @name ||= "AContainer"
68
+ @first_time = true
69
+
70
+ end
71
+
72
+ # NOTE: since we are handling the traversal, we delink the object from any
73
+ # form's widgets array that might have been added. Whenever a form is available,
74
+ # we set it (without adding widget to it) so it can print using the form's window.
75
+ #
76
+ # @param [Widget] to add
77
+ def add *items
78
+ items.each do |c|
79
+ raise ArgumentError, "Nil component passed to add" unless c
80
+ if c.is_a? Widget
81
+ if c.form && c.form != @form
82
+ $log.debug " removing widget VIMSPLIT #{c.class} wr: #{c.row} row:#{@row} ht:#{@height} "
83
+ c.form.remove_widget c
84
+ c.form = nil
85
+ # or should i just stack them myself and screw what you've asked for
86
+ end
87
+ # take it out of form's control. We will control it.
88
+ if c.form
89
+ c.form.remove_widget c
90
+ end
91
+ # shoot, what if at this point the container does not have a form
92
+ attach_form c if @form
93
+ end
94
+ # most likely if you have created both container and widgets
95
+ # inside app, it would have given row after container
96
+
97
+ @components << c
98
+ if c.focusable
99
+ @focusables << c
100
+ @current_component ||= c # only the first else cursor falls on last on enter
101
+ end
102
+
103
+ end # items each
104
+ self
105
+ end
106
+
107
+ # When we get a form, we silently attach it to this object, without the form
108
+ # knowing. We don't want form managing this object.
109
+ def attach_form c
110
+ c.form = @form
111
+ c.override_graphic @graphic
112
+ c.parent_component = self
113
+ end
114
+ alias :add_widget :add
115
+ def widgets; @components; end
116
+ # what of by_name
117
+
118
+
119
+ # correct coordinates of comp esp if App has stacked them after this
120
+ # container
121
+ # It is best to use the simple stack feature. The rest could change at any time
122
+ # and is quite arbitrary. Some folks may set absolute locations if container
123
+ # is directly on a form, others may set relative locations if it is inside a
124
+ # tabbed pane or other container. Thus, stacks are best
125
+ def correct_component c
126
+ raise "Form is still not set in Container" unless @form
127
+ attach_form(c) unless c.form
128
+ @last_row ||= @row + 1
129
+ inset = 2
130
+ # 2011-10-20 current default behaviour is to stack
131
+ if @positioning == :stack
132
+ c.row = @last_row
133
+ c.col = @col + inset
134
+
135
+ # do not advance row, save col for next row
136
+ @last_row += 1
137
+ elsif @positioning == :relative # UNTESTED NOTE
138
+ if (c.row || 0) <= 0
139
+ $log.warn "c.row in CONTAINER is #{c.row} "
140
+ c.row = @last_row
141
+ @last_row += 1
142
+ elsif c.row > @row + @height -1
143
+ $log.warn "c.row in CONTAINER exceeds container. #{c.row} "
144
+ c.row -= @height - @row_offset
145
+ else
146
+ # this is where it should come
147
+ c.row += @row + @row_offset
148
+ @last_row = c.row + 1
149
+ end
150
+ if (c.col || 0) <= 0
151
+ c.col = @col + inset + @col_offset
152
+ elsif c.col > @col + @width -1
153
+ c.col -= @width
154
+ elsif c.col == @col
155
+ c.col += @col_offset + inset
156
+ else #f c.col < @col
157
+ c.col += @col+@col_offset
158
+ end
159
+ $log.debug "XXX: CORRECT #{c.name} r:#{c.row} c:#{c.col} "
160
+ end
161
+ @first_time = false
162
+ end
163
+ def check_component c
164
+ raise "row is less than container #{c.row} #{@row} " if c.row <= @row
165
+ raise "col is less than container #{c.col} #{@col} " if c.col <= @col
166
+ end
167
+
168
+ public
169
+ # repaint object
170
+ # called by Form, and sometimes parent component (if not form).
171
+ def repaint
172
+ my_win = @form ? @form.window : @target_window
173
+ @graphic = my_win unless @graphic
174
+ raise " #{@name} NO GRAPHIC set as yet CONTAINER paint " unless @graphic
175
+ @components.each { |e| correct_component e } if @first_time
176
+ #@components.each { |e| check_component e } # seeme one if printing out
177
+
178
+ #return unless @repaint_required
179
+
180
+ # if some major change has happened then repaint everything
181
+ if @repaint_required
182
+ $log.debug " VIM repaint graphic #{@graphic} "
183
+ print_borders unless @suppress_borders # do this once only, unless everything changes
184
+ @components.each { |e| e.repaint_all(true); e.repaint }
185
+ else
186
+ @components.each { |e| e.repaint }
187
+ end # if repaint_required
188
+
189
+ @repaint_required = false
190
+ end
191
+
192
+ private
193
+ def print_borders
194
+ width = @width
195
+ height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
196
+ window = @graphic # 2010-01-04 12:37 BUFFERED
197
+ startcol = @col
198
+ startrow = @row
199
+ @color_pair = get_color($datacolor)
200
+ #$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
201
+ window.print_border startrow, startcol, height, width, @color_pair, @attr
202
+ print_title
203
+ end
204
+ def print_title
205
+ $log.debug "CONTAINER PRINTING TITLE at #{row} #{col} "
206
+ @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
207
+ end
208
+
209
+ public
210
+ # called by parent or form, otherwise its private
211
+ def handle_key ch
212
+ $log.debug " CONTAINER handle_key #{ch} "
213
+ return if @components.empty?
214
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
215
+
216
+ # should this go here 2011-10-19
217
+ unless @_entered
218
+ $log.warn "XXX WARN: calling ON_ENTER since in this situation it was not called"
219
+ on_enter
220
+ end
221
+ if ch == KEY_TAB
222
+ $log.debug "CONTAINER GOTO NEXT TAB"
223
+ return goto_next_component
224
+ elsif ch == KEY_BTAB
225
+ return goto_prev_component
226
+ end
227
+ comp = @current_component
228
+ $log.debug " CONTAINER handle_key #{ch}: #{comp}"
229
+ if comp
230
+ ret = comp.handle_key(ch)
231
+ $log.debug " CONTAINER handle_key#{ch}: #{comp} returned #{ret} "
232
+ if ret != :UNHANDLED
233
+ comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
234
+ # in repaint of this.
235
+ return ret
236
+ end
237
+ $log.debug "XXX CONTAINER key unhandled by comp #{comp.name} "
238
+ else
239
+ $log.warn "XXX CONTAINER key unhandled NULL comp"
240
+ end
241
+ case ch
242
+ when ?\C-c.getbyte(0)
243
+ $multiplier = 0
244
+ return 0
245
+ when ?0.getbyte(0)..?9.getbyte(0)
246
+ $log.debug " VIM coming here to set multiplier #{$multiplier} "
247
+ $multiplier *= 10 ; $multiplier += (ch-48)
248
+ return 0
249
+ end
250
+ ret = process_key ch, self
251
+ # allow user to map left and right if he wants
252
+ if ret == :UNHANDLED
253
+ case ch
254
+ when KEY_UP
255
+ # form will pick this up and do needful
256
+ return goto_prev_component #unless on_first_component?
257
+ when KEY_LEFT
258
+ # if i don't check for first component, key will go back to form,
259
+ # but not be processes. so focussed remain here, but be false.
260
+ # In case of returnign an unhandled TAB, on_leave will happen and cursor will move to
261
+ # previous component outside of this.
262
+ return goto_prev_component unless on_first_component?
263
+ when KEY_RIGHT
264
+ return goto_next_component #unless on_last_component?
265
+ when KEY_DOWN
266
+ return goto_next_component #unless on_last_component?
267
+ else
268
+ @_entered = false
269
+ return :UNHANDLED
270
+ end
271
+ end
272
+
273
+ $multiplier = 0
274
+ return 0
275
+ end
276
+ # Actually we should only go to current component if it accepted
277
+ # a key stroke. if user tabbed thru it, then no point going back to
278
+ # it. Go to first or last depending on TAB or BACKTAB otherwise.
279
+ # NOTE: if user comes in using DOWN or UP, last traversed component will get the focus
280
+ #
281
+ def on_enter
282
+ # if BTAB, the last comp XXX they must be focusable FIXME
283
+ if $current_key == KEY_BTAB || $current_key == KEY_UP
284
+ @current_component = @focusables.last
285
+ else
286
+ @current_component = @focusables.first
287
+ end
288
+ return unless @current_component
289
+ $log.debug " CONTAINER came to ON_ENTER #{@current_component} "
290
+ set_form_row
291
+ @_entered = true
292
+ end
293
+ # we cannot be sure that this will be called especially if this is embedded
294
+ # inside some other component
295
+ def on_leave
296
+ @_entered = false
297
+ super
298
+ end
299
+ def goto_next_component
300
+ if @current_component != nil
301
+ leave_current_component
302
+ if on_last_component?
303
+ #@_entered = false
304
+ return :UNHANDLED
305
+ end
306
+ @current_index = @focusables.index(@current_component)
307
+ index = @current_index + 1
308
+ f = @focusables[index]
309
+ if f
310
+ @current_index = index
311
+ @current_component = f
312
+ return set_form_row
313
+ end
314
+ end
315
+ @_entered = false
316
+ return :UNHANDLED
317
+ end
318
+ def goto_prev_component
319
+ if @current_component != nil
320
+ leave_current_component
321
+ if on_first_component?
322
+ @_entered = false
323
+ return :UNHANDLED
324
+ end
325
+ @current_index = @focusables.index(@current_component)
326
+ index = @current_index -= 1
327
+ f = @focusables[index]
328
+ if f
329
+ @current_index = index
330
+ @current_component = f
331
+ return set_form_row
332
+ end
333
+ end
334
+ return :UNHANDLED
335
+ end
336
+ # private
337
+ # XXX why are we calling 3 methods in a row, why not OE manages these 3
338
+ # There's double calling going on.
339
+ def set_form_row
340
+ return :UNHANDLED if @current_component.nil?
341
+ cc = @current_component
342
+ $log.debug "CONT #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
343
+ $log.debug " CONTAINER on enter sfr #{@current_component.name} #{@current_component} "
344
+
345
+ # bug caught here. we were printing a field before it had been set, so it printed out
346
+ @components.each { |e| correct_component e } if @first_time
347
+ @current_component.on_enter
348
+ @current_component.set_form_row # why was this missing in vimsplit. is it
349
+ $log.debug "CONT2 #{@name} set_form_row calling sfr for #{cc.name}, r #{cc.row} c: #{cc.col} "
350
+ # that on_enter does a set_form_row
351
+ @current_component.set_form_col # XXX
352
+ @current_component.repaint # OMG this could happen before we've set row and col
353
+ # XXX compo should do set_form_row and col if it has that
354
+ end
355
+ #
356
+ def set_form_col
357
+ return if @current_component.nil?
358
+ $log.debug " #{@name} CONTAINER EMPTY set_form_col calling sfc for #{@current_component.name} "
359
+ # already called from above.
360
+ #@current_component.set_form_col
361
+ end
362
+ # leave the component we are on.
363
+ # This should be followed by all containers, so that the on_leave action
364
+ # of earlier comp can be displayed, such as dimming components selections
365
+ def leave_current_component
366
+ @current_component.on_leave
367
+ # NOTE this is required, since repaint will just not happen otherwise
368
+ # Some components are erroneously repainting all, after setting this to true so it is
369
+ # working there.
370
+ @current_component.repaint_required true
371
+ $log.debug " after on_leave RCONT XXX #{@current_component.focussed} #{@current_component.name}"
372
+ @current_component.repaint
373
+ end
374
+
375
+ # is focus on first component FIXME check for focusable
376
+ def on_first_component?
377
+ @current_component == @focusables.first
378
+ end
379
+ # is focus on last component FIXME check for focusable
380
+ def on_last_component?
381
+ @current_component == @focusables.last
382
+ end
383
+ # set focus on given component
384
+ # Sometimes you have the handle to component, and you want to move focus to it
385
+ def goto_component comp
386
+ return if comp == @current_component
387
+ leave_current_component
388
+ @current_component = comp
389
+ set_form_row
390
+ end
391
+
392
+ # ADD HERE ABOVe
393
+ end # class
394
+ end # module
395
+
396
+ if __FILE__ == $PROGRAM_NAME
397
+ require 'rbhex/core/util/app'
398
+ App.new do
399
+ f1 = field "name", :maxlen => 20, :display_length => 20, :bgcolor => :white,
400
+ :color => :black, :text => "abc", :label => " Name: ", :label_color_pair => @datacolor
401
+ f2 = field "email", :display_length => 20, :bgcolor => :white,
402
+ :color => :blue, :text => "me@google.com", :label => "Email: ", :label_color_pair => @datacolor
403
+ f3 = radio :group => :grp, :text => "red", :value => "RED", :color => :red
404
+ f4 = radio :group => :grp, :text => "blue", :value => "BLUE", :color => :blue
405
+ f5 = radio :group => :grp, :text => "green", :value => "GREEN", :color => :green
406
+ stack :margin_top => 2, :margin => 2 do
407
+ r = container :row => 1, :col => 2, :width => 80, :height => 20, :title => "A container"
408
+ r.add(f1)
409
+ r.add(f2)
410
+ r.add(f3,f4,f5)
411
+ sl = status_line
412
+ end # stack
413
+
414
+ end # app
415
+ end # if