rkumar-rbcurse 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. data/CHANGELOG +1577 -0
  2. data/README.txt +310 -0
  3. data/examples/qdfilechooser.rb +68 -0
  4. data/examples/rfe.rb +853 -0
  5. data/examples/rfe_renderer.rb +69 -0
  6. data/examples/test1.rb +242 -0
  7. data/examples/test2.rb +498 -0
  8. data/examples/testcombo.rb +95 -0
  9. data/examples/testkeypress.rb +61 -0
  10. data/examples/testmenu.rb +105 -0
  11. data/examples/testtable.rb +266 -0
  12. data/examples/testtabp.rb +106 -0
  13. data/examples/testtodo.rb +532 -0
  14. data/examples/viewtodo.rb +512 -0
  15. data/lib/rbcurse.rb +7 -0
  16. data/lib/rbcurse/action.rb +31 -0
  17. data/lib/rbcurse/applicationheader.rb +57 -0
  18. data/lib/rbcurse/celleditor.rb +120 -0
  19. data/lib/rbcurse/checkboxcellrenderer.rb +69 -0
  20. data/lib/rbcurse/colormap.rb +133 -0
  21. data/lib/rbcurse/comboboxcellrenderer.rb +45 -0
  22. data/lib/rbcurse/defaultlistselectionmodel.rb +49 -0
  23. data/lib/rbcurse/keylabelprinter.rb +143 -0
  24. data/lib/rbcurse/listcellrenderer.rb +99 -0
  25. data/lib/rbcurse/listkeys.rb +33 -0
  26. data/lib/rbcurse/listscrollable.rb +216 -0
  27. data/lib/rbcurse/listselectable.rb +67 -0
  28. data/lib/rbcurse/mapper.rb +108 -0
  29. data/lib/rbcurse/orderedhash.rb +77 -0
  30. data/lib/rbcurse/rcombo.rb +243 -0
  31. data/lib/rbcurse/rdialogs.rb +183 -0
  32. data/lib/rbcurse/rform.rb +845 -0
  33. data/lib/rbcurse/rinputdataevent.rb +36 -0
  34. data/lib/rbcurse/rlistbox.rb +804 -0
  35. data/lib/rbcurse/rmenu.rb +666 -0
  36. data/lib/rbcurse/rmessagebox.rb +325 -0
  37. data/lib/rbcurse/rpopupmenu.rb +754 -0
  38. data/lib/rbcurse/rtabbedpane.rb +259 -0
  39. data/lib/rbcurse/rtable.rb +1296 -0
  40. data/lib/rbcurse/rtextarea.rb +673 -0
  41. data/lib/rbcurse/rtextview.rb +335 -0
  42. data/lib/rbcurse/rwidget.rb +1731 -0
  43. data/lib/rbcurse/scrollable.rb +301 -0
  44. data/lib/rbcurse/selectable.rb +94 -0
  45. data/lib/rbcurse/table/tablecellrenderer.rb +85 -0
  46. data/lib/rbcurse/table/tabledatecellrenderer.rb +102 -0
  47. data/lib/ver/keyboard.rb +150 -0
  48. data/lib/ver/keyboard2.rb +170 -0
  49. data/lib/ver/ncurses.rb +102 -0
  50. data/lib/ver/window.rb +369 -0
  51. data/test/test_rbcurse.rb +0 -0
  52. metadata +117 -0
@@ -0,0 +1,325 @@
1
+ =begin
2
+ * Name: rmessagebox: creates messageboxes
3
+ * Description
4
+ * Author: rkumar (arunachalesha)
5
+ * Date: 2008-11-19 12:49
6
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ * file separated on 2009-01-13 22:39
8
+
9
+ =end
10
+ require 'rbcurse/rwidget'
11
+ require 'rbcurse/rlistbox'
12
+
13
+ module RubyCurses
14
+ ##
15
+ # dimensions of window should be derived on content
16
+ #
17
+ class MessageBox
18
+ include DSL
19
+ include RubyCurses::Utils
20
+ dsl_accessor :title
21
+ dsl_accessor :message
22
+ dsl_accessor :type # :ok, :ok_cancel :yes_no :yes_no_cancel :custom
23
+ dsl_accessor :default_button # TODO - currently first
24
+ dsl_accessor :layout
25
+ dsl_accessor :buttons # used if type :custom
26
+ dsl_accessor :underlines # offsets of each button to underline
27
+ attr_reader :config
28
+ attr_reader :selected_index # button index selected by user
29
+ attr_reader :window # required for keyboard
30
+ dsl_accessor :list_selection_mode # true or false allow multiple selection
31
+ dsl_accessor :list # 2009-01-05 23:59
32
+ dsl_accessor :button_type # ok, ok_cancel, yes_no
33
+ dsl_accessor :default_value #
34
+ dsl_accessor :default_values # # 2009-01-06 00:05 after removing meth missing
35
+ dsl_accessor :height, :width, :top, :left # 2009-01-06 00:05 after removing meth missing
36
+
37
+ dsl_accessor :message_height
38
+
39
+
40
+ def initialize form=nil, aconfig={}, &block
41
+ @form = form
42
+ @config = aconfig
43
+ @buttons = []
44
+ #@keys = {}
45
+ @bcol = 5
46
+ @selected_index = -1
47
+ @config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
48
+ instance_eval &block if block_given?
49
+ if @layout.nil?
50
+ case @type.to_s
51
+ when "input"
52
+ layout(10,60, 10, 20)
53
+ when "list"
54
+ height = [5, @list.length].min
55
+ layout(10+height, 60, 5, 20)
56
+ when "field_list"
57
+ height = @field_list.length
58
+ layout(10+height, 60, 5, 20)
59
+ when "override"
60
+ $log.debug " override: #{@height},#{@width}, #{@top}, #{@left} "
61
+ layout(@height,@width, @top, @left)
62
+ $log.debug " override: #{@layout.inspect}"
63
+ else
64
+ height = @form && @form.widgets.length ## quick fix. FIXME
65
+ height ||= 0
66
+ layout(10+height,60, 10, 20)
67
+ end
68
+ end
69
+ @window = VER::Window.new(@layout)
70
+ if @form.nil?
71
+ @form = RubyCurses::Form.new @window
72
+ else
73
+ @form.window = @window
74
+ end
75
+ acolor = get_color $reversecolor
76
+ $log.debug " MESSAGE BOX #{@bgcolor} , #{@color} , #{acolor}"
77
+ @window.bkgd(Ncurses.COLOR_PAIR(acolor));
78
+ @window.wrefresh
79
+ @panel = @window.panel
80
+ Ncurses::Panel.update_panels
81
+ process_field_list
82
+ print_borders
83
+ print_title
84
+ print_message unless @message.nil?
85
+ print_input
86
+ create_buttons
87
+ @form.repaint
88
+ @window.wrefresh
89
+ handle_keys
90
+ end
91
+ ##
92
+ # takes care of a field list sent in
93
+ def process_field_list
94
+ return if @field_list.nil? or @field_list.length == 0
95
+ @field_list.each do |f|
96
+ f.set_form @form
97
+ end
98
+ end
99
+ def default_button offset0
100
+ @selected_index = offset0
101
+ end
102
+ ##
103
+ # value entered by user if type = input
104
+ def input_value
105
+ return @input.buffer if !@input.nil?
106
+ return @listbox.getvalue if !@listbox.nil?
107
+ end
108
+ def create_buttons
109
+ case @button_type.to_s.downcase
110
+ when "ok"
111
+ make_buttons ["&OK"]
112
+ when "ok_cancel" #, "input", "list", "field_list"
113
+ make_buttons %w[&OK &Cancel]
114
+ when "yes_no"
115
+ make_buttons %w[&Yes &No]
116
+ when "yes_no_cancel"
117
+ make_buttons ["&Yes", "&No", "&Cancel"]
118
+ when "custom"
119
+ raise "Blank list of buttons passed to custom" if @buttons.nil? or @buttons.size == 0
120
+ make_buttons @buttons
121
+ else
122
+ $log.debug "No type passed for creating messagebox. Using default (OK)"
123
+ make_buttons ["&OK"]
124
+ end
125
+ end
126
+ def make_buttons names
127
+ total = names.inject(0) {|total, item| total + item.length + 4}
128
+ bcol = center_column total
129
+
130
+ brow = @layout[:height]-3
131
+ button_ct=0
132
+ names.each_with_index do |bname, ix|
133
+ text = bname
134
+ #underline = @underlines[ix] if !@underlines.nil?
135
+
136
+ button = Button.new @form do
137
+ text text
138
+ name bname
139
+ row brow
140
+ col bcol
141
+ #underline underline
142
+ highlight_background $datacolor
143
+ color $reversecolor
144
+ bgcolor $reversecolor
145
+ end
146
+ index = button_ct
147
+ button.command { |form| @selected_index = index; @stop = true; $log.debug "Pressed Button #{bname}";}
148
+ button_ct += 1
149
+ bcol += text.length+6
150
+ end
151
+ end
152
+ ## message box
153
+ def stopping?
154
+ @stop
155
+ end
156
+ def handle_keys
157
+ begin
158
+ while((ch = @window.getchar()) != 999 )
159
+ case ch
160
+ when -1
161
+ next
162
+ else
163
+ press ch
164
+ break if @stop
165
+ end
166
+ end
167
+ ensure
168
+ destroy
169
+ end
170
+ return @selected_index
171
+ end
172
+ def press ch
173
+ #$log.debug "message box handle_keys : #{ch}" if ch != -1
174
+ case ch
175
+ when -1
176
+ return
177
+ when KEY_F1, 27, ?\C-q
178
+ @selected_index = -1
179
+ @stop = true
180
+ return
181
+ when KEY_ENTER, 10, 13
182
+ field = @form.get_current_field
183
+ if field.respond_to? :fire
184
+ field.fire
185
+ end
186
+ $log.debug "popup ENTER : #{@selected_index} "
187
+ $log.debug "popup ENTER : #{field.name}" if !field.nil?
188
+ @stop = true
189
+ return
190
+ when 9
191
+ @form.select_next_field
192
+ else
193
+ # fields must return unhandled else we will miss hotkeys.
194
+ # On messageboxes, often if no edit field, then O and C are hot.
195
+ field = @form.get_current_field
196
+ handled = field.handle_key ch
197
+
198
+ if handled == :UNHANDLED
199
+ ret = @form.process_key ch, self ## trying out trigger button
200
+ end
201
+ end
202
+ @form.repaint
203
+ Ncurses::Panel.update_panels();
204
+ Ncurses.doupdate();
205
+ @window.wrefresh
206
+ end
207
+ def print_borders
208
+ width = @layout[:width]
209
+ height = @layout[:height]
210
+ @window.print_border_mb 1,2, height, width, $normalcolor, A_REVERSE
211
+ =begin
212
+ start = 2
213
+ hline = "+%s+" % [ "-"*(width-((start+1)*2)) ]
214
+ hline2 = "|%s|" % [ " "*(width-((start+1)*2)) ]
215
+ @window.printstring(row=1, col=start, hline, color=$reversecolor)
216
+ (start).upto(height-2) do |row|
217
+ @window.printstring row, col=start, hline2, color=$normalcolor, A_REVERSE
218
+ end
219
+ @window.printstring(height-2, col=start, hline, color=$reversecolor)
220
+ =end
221
+ end
222
+ def print_title title=@title
223
+ width = @layout[:width]
224
+ title = " "+title+" "
225
+ @window.printstring(row=1,col=(width-title.length)/2,title, color=$normalcolor)
226
+ end
227
+ def center_column textlen
228
+ width = @layout[:width]
229
+ return (width-textlen)/2
230
+ end
231
+ def print_message message=@message, row=nil
232
+ @message_row = @message_col = 2
233
+ display_length = @layout[:width]-8
234
+ # XXX this needs to go up and decide height of window
235
+ if @message_height.nil?
236
+ @message_height = (message.length/display_length)+1
237
+ $log.debug " print_message: mh:#{@message_height}"
238
+ end
239
+ @message_height ||= 1
240
+ width = @layout[:width]
241
+ return if message.nil?
242
+ case @type.to_s
243
+ when "input"
244
+ row=(@layout[:height]/3) if row.nil?
245
+ @message_col = 4
246
+ when "list"
247
+ row=3
248
+ @message_col = 4
249
+ else
250
+ row=(@layout[:height]/3) if row.nil?
251
+ @message_col = (width-message.length)/2
252
+ end
253
+ @message_row = row
254
+ #@window.printstring( row, @message_col , message, color=$reversecolor)
255
+ # 2008-12-30 19:45 experimenting with label so we can get justify and wrapping.
256
+ #@window.printstring( row, @message_col , message, color=$reversecolor)
257
+ message_label = RubyCurses::Label.new @form, {'text' => message, "name"=>"message_label","row" => row, "col" => @message_col, "display_length" => display_length, "height" => @message_height, "attr"=>"reverse"}
258
+
259
+ end
260
+ def print_input
261
+ #return if @type.to_s != "input"
262
+ @message_height ||= 0
263
+ @message_row ||= 2
264
+ @message_col ||= 2
265
+ r = @message_row + @message_height + 1
266
+ c = @message_col
267
+ defaultvalue = @default_value || ""
268
+ input_config = @config["input_config"] || {}
269
+ case @type.to_s
270
+ when "input"
271
+ @input = RubyCurses::Field.new @form, input_config do
272
+ name "input"
273
+ row r
274
+ col c
275
+ display_length 30
276
+ set_buffer defaultvalue
277
+ end
278
+ when "list"
279
+ list = @list
280
+ selection_mode = @list_selection_mode
281
+ default_values = @default_values
282
+ $log.debug " value of select_mode #{selection_mode}"
283
+ @listbox = RubyCurses::Listbox.new @form do
284
+ name "input"
285
+ row r
286
+ col c
287
+ # attr 'reverse'
288
+ color 'black'
289
+ bgcolor 'white'
290
+ width 30
291
+ height 6
292
+ list list
293
+ # ?? display_length 30
294
+ #set_buffer defaultvalue
295
+ selection_mode selection_mode
296
+ default_values default_values
297
+ is_popup false
298
+ end
299
+ end
300
+ end
301
+ def configure(*val , &block)
302
+ case val.size
303
+ when 1
304
+ return @config[val[0]]
305
+ when 2
306
+ @config[val[0]] = val[1]
307
+ instance_variable_set("@#{val[0]}", val[1])
308
+ end
309
+ instance_eval &block if block_given?
310
+ end
311
+ def cget param
312
+ @config[param]
313
+ end
314
+
315
+ def layout(height=0, width=0, top=0, left=0)
316
+ @layout = { :height => height, :width => width, :top => top, :left => left }
317
+ end
318
+ def destroy
319
+ $log.debug "DESTROY : widget"
320
+ panel = @window.panel
321
+ Ncurses::Panel.del_panel(panel) if !panel.nil?
322
+ @window.delwin if !@window.nil?
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,754 @@
1
+ =begin
2
+ * Name: rpopupmenu - this is based on the crappy menubar code and needs a rewrite.
3
+ * Description
4
+ * Author: rkumar
5
+ TODO
6
+ - Action: may have to listen to Action property changes so enabled, name etc change can be reflected
7
+ - menu bar : what to do if adding a menu, or option later.
8
+ we dnt show disabld options in a way that user can know its disabled
9
+ - separate file created on 2008-12-24 17:58
10
+
11
+ Mnemonic should highlight the row if its a menu.
12
+ NOTE : this program works but is one of the first programs and is untouched. It needs to be rewritten
13
+ since its quite crappy.
14
+ Also, we should move to Action classes as against just blokcs of code. And action class would have
15
+ a user friendly string to identifiy the action, as well as a disabled option.
16
+
17
+ --------
18
+ * Date: 2008-11-14 23:43
19
+ * License:
20
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
21
+
22
+ =end
23
+ require 'rubygems'
24
+ require 'ncurses'
25
+ require 'logger'
26
+ require 'rbcurse'
27
+ require 'rbcurse/action'
28
+
29
+ include Ncurses
30
+ include RubyCurses
31
+ module RubyCurses
32
+ extend self
33
+
34
+
35
+ class MenuSeparator
36
+ attr_accessor :enabled
37
+ attr_accessor :parent
38
+ attr_accessor :row
39
+ attr_accessor :col
40
+ attr_accessor :width
41
+ def initialize
42
+ @enable = false
43
+ end
44
+ def repaint
45
+ @parent.window.printstring( @row, 0, "|%s|" % ("-"*@width), $reversecolor)
46
+ end
47
+ def destroy
48
+ end
49
+ def on_enter
50
+ end
51
+ def on_leave
52
+ end
53
+ def to_s
54
+ ""
55
+ end
56
+ end
57
+ ##
58
+ class MenuItem
59
+ attr_accessor :parent
60
+ # attr_accessor :window
61
+ attr_accessor :row
62
+ attr_accessor :col
63
+ attr_accessor :width
64
+ attr_accessor :accelerator
65
+ attr_accessor :enabled
66
+ attr_accessor :mnemonic # changed reader to accessor
67
+ def initialize txt, mnemonic=nil, &block
68
+ @mnemonic = mnemonic
69
+ text txt
70
+ @enabled = true
71
+ instance_eval &block if block_given?
72
+ end
73
+ ##
74
+ # changed so ampersand can be mnemonic esp when action comes in
75
+ def text s
76
+ s = s.dup # since actions are being shared
77
+ if (( ix = s.index('&')) != nil)
78
+ s.slice!(ix,1)
79
+ #@underline = ix unless @form.nil? # this setting a fake underline in messageboxes
80
+ @mnemonic = s[ix,1]
81
+ end
82
+ @text = s
83
+ end
84
+ def to_s
85
+ "#{@text} #{@accelerator}"
86
+ end
87
+ def command *args, &block
88
+ $log.debug ">>>command : #{@text} "
89
+ @command = block if block_given?
90
+ @args = args
91
+ end
92
+ def on_enter
93
+ $log.debug ">>>on enter menuitem : #{@text} #{@row} #{@width} "
94
+ highlight
95
+ end
96
+ def on_leave
97
+ $log.debug ">>>on leave menuitem : #{@text} "
98
+ highlight false
99
+ end
100
+ ## XXX it could be a menu again
101
+ def fire
102
+ $log.debug ">>>fire menuitem : #{@text} #{@command} "
103
+ @command.call self, *@args if !@command.nil?
104
+ @parent.clear_menus
105
+ return :CLOSE # added 2009-01-02 00:09 to close only actions, not submenus
106
+ end
107
+ def highlight tf=true
108
+ if tf
109
+ color = $datacolor
110
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
111
+ # above line did not work in vt100, 200 terminals, next works.
112
+ @parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_REVERSE, $reversecolor, nil)
113
+ else
114
+ repaint
115
+ end
116
+ @parent.window.wrefresh
117
+ end
118
+ def repaint # menuitem.repaint
119
+ if @parent.nil? or @parent.window.nil?
120
+ $log.debug " #{self} parent nil"
121
+ return
122
+ end
123
+ r = @row
124
+ acolor = $reversecolor
125
+ acolor = get_color($reversecolor, 'green', 'white') if !@enabled
126
+ @parent.window.printstring( @row, 0, "|%-*s|" % [@width, @text], acolor)
127
+ if !@accelerator.nil?
128
+ @parent.window.printstring( r, (@width+1)-@accelerator.length, @accelerator, acolor)
129
+ else
130
+ #@parent.window.printstring( r, (@width+1)-1, ".", acolor)
131
+ end
132
+ if !@mnemonic.nil?
133
+ m = @mnemonic
134
+ ix = @text.index(m) || @text.index(m.swapcase)
135
+ charm = @text[ix,1]
136
+ #@parent.window.printstring( r, ix+1, charm, $datacolor) if !ix.nil?
137
+ # prev line changed since not working in vt100 and vt200
138
+ #@parent.window.printstring( r, ix+1, charm, $reversecolor, 'reverse') if !ix.nil?
139
+ @parent.window.mvchgat(y=r, x=ix+1, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, acolor, nil)
140
+ end
141
+ end
142
+ def destroy
143
+ $log.debug "DESTRY menuitem #{@text}"
144
+ end
145
+ end
146
+ class Menu < MenuItem
147
+ attr_accessor :parent
148
+ attr_accessor :row
149
+ attr_accessor :col
150
+ attr_accessor :width
151
+ attr_accessor :enabled
152
+ #attr_reader :text
153
+ attr_reader :items
154
+ attr_reader :window
155
+ attr_reader :panel
156
+ attr_reader :current_menu
157
+ attr_reader :row_margin
158
+ @@menus = []
159
+ @@row = 0
160
+ @@col = 0
161
+
162
+ def initialize text, &block
163
+ super text, nil, &block
164
+ @items = []
165
+ @enabled = true
166
+ @current_menu = []
167
+ instance_eval &block if block_given?
168
+ @row ||=10
169
+ @col ||=10
170
+ @@menus ||= []
171
+ end
172
+ def to_s
173
+ @text
174
+ end
175
+ def clear_menus
176
+ @@menus = []
177
+ end
178
+ # create a Menuitem given an Action
179
+ # if menuitem.kind_of? RubyCurses::Action
180
+ def create_action_component action
181
+ m = MenuItem.new(action.name, action.mnemonic)
182
+ m.command { action.call }
183
+ m.accelerator = action.accelerator
184
+ return m
185
+ end
186
+ # item could be menuitem or another menu or a string or a Action
187
+ # support for action added 2009-01-21 18:08
188
+ def add menuitem
189
+ insert menuitem, @items.size
190
+ return self
191
+ end
192
+ ##
193
+ # added 2009-01-20 13:28 NEW
194
+ def insert menuitem, ix
195
+ if menuitem.kind_of? RubyCurses::Action
196
+ menuitem = create_action_component menuitem
197
+ end
198
+ @items.insert ix, menuitem
199
+ return self
200
+ end
201
+ def insert_separator ix
202
+ @items.insert ix, MenuSeparator.new
203
+ end
204
+ def add_separator
205
+ @items << MenuSeparator.new
206
+ end
207
+ def get_item i
208
+ @items[i]
209
+ end
210
+ def remove n
211
+ if n.is_a? Fixnum
212
+ @items.delete_at n
213
+ else
214
+ @items.delete n
215
+ end
216
+ end
217
+ # menu -
218
+ def fire
219
+ $log.debug "menu fire called: #{@text} "
220
+ if @window.nil?
221
+ #repaint
222
+ create_window
223
+ if !@parent.is_a? RubyCurses::MenuBar
224
+ $log.debug " ADDING self to current menu: #{self}"
225
+ # xxx highlight true
226
+ @parent.current_menu << self
227
+ @@menus << self
228
+
229
+ $log.debug "DDD #{@@menus} << #{self}"
230
+ end
231
+ else
232
+ ### shouod this not just show ?
233
+ $log.debug "menu fire called: #{@text} ELSE XXX WHEN IS THIS CALLED ? 658 "
234
+ return @items[@active_index].fire # this should happen if selected. else selected()
235
+ end
236
+ #@action.call if !@action.nil?
237
+ end
238
+ # user has clicked down, we shoud display items
239
+ # DRAW menuitems
240
+ def repaint # menu.repaint
241
+ return if @items.nil? or @items.empty?
242
+ $log.debug "menu repaint: #{@text} row #{@row} col #{@col} "
243
+ if !@parent.is_a? RubyCurses::MenuBar
244
+ @parent.window.printstring( @row, 0, "|%-*s>|" % [@width-1, @text], $reversecolor)
245
+ # added 2009-01-23 00:49
246
+ if !@mnemonic.nil?
247
+ m = @mnemonic
248
+ ix = @text.index(m) || @text.index(m.swapcase)
249
+ charm = @text[ix,1]
250
+ #@parent.window.printstring( r, ix+1, charm, $datacolor) if !ix.nil?
251
+ # prev line changed since not working in vt100 and vt200
252
+ #@parent.window.printstring( @row, ix+1, charm, $reversecolor, 'reverse') if !ix.nil?
253
+ # 2009-01-23 13:03 replaced reverse with ul
254
+ @parent.window.mvchgat(y=@row, x=ix+1, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, $reversecolor, nil)
255
+ end
256
+ @parent.window.refresh
257
+ end
258
+ if @window.nil?
259
+ #create_window
260
+ else
261
+ @window.show
262
+ select_item 0
263
+ @window.refresh
264
+ end
265
+ end
266
+ ##
267
+ # recursive if given one not enabled goes to next enabled
268
+ def select_item ix0
269
+ return if @items.nil? or @items.empty?
270
+ $log.debug "insdie select item : #{ix0}"
271
+ if !@active_index.nil?
272
+ @items[@active_index].on_leave
273
+ end
274
+ previtem = @active_index
275
+ @active_index = ix0
276
+ if @items[ix0].enabled
277
+ @items[ix0].on_enter
278
+ else
279
+ $log.debug "insdie sele nxt item ENABLED FALSE : #{ix0}"
280
+ if @active_index > previtem
281
+ select_next_item
282
+ else
283
+ select_prev_item
284
+ end
285
+ end
286
+ @window.refresh
287
+ end
288
+ def select_next_item
289
+ return if @items.nil? or @items.empty?
290
+ $log.debug "insdie sele nxt item : #{@active_index}"
291
+ @active_index = -1 if @active_index.nil?
292
+ if @active_index < @items.length-1
293
+ select_item @active_index + 1
294
+ else
295
+ # select_item 0
296
+ end
297
+ end
298
+ def select_prev_item
299
+ return if @items.nil? or @items.empty?
300
+ $log.debug "insdie sele prv item : #{@active_index}"
301
+ if @active_index > 0
302
+ select_item @active_index - 1
303
+ else
304
+ #select_item @items.length-1
305
+ end
306
+ end
307
+ def on_enter # menu.on_enter
308
+ $log.debug "menu onenter: #{@text} #{@row} #{@col} "
309
+ # call parent method. XXX
310
+ if @parent.is_a? RubyCurses::MenuBar
311
+ @parent.window.printstring( @row, @col, " %s " % @text, $datacolor)
312
+ else
313
+ highlight
314
+ end
315
+ if !@window.nil? #and @parent.selected
316
+ $log.debug "menu onenter: #{@text} calling window,show"
317
+ @window.show
318
+ select_item 0
319
+ elsif @parent.is_a? RubyCurses::MenuBar and @parent.selected
320
+ # only on the top level do we open a window if a previous one was opened
321
+ $log.debug "menu onenter: #{@text} calling repaint CLASS: #{@parent.class}"
322
+ # repaint
323
+ create_window
324
+ end
325
+ end
326
+ def on_leave # menu.on_leave
327
+ $log.debug "menu onleave: #{@text} #{@row} #{@col} "
328
+ # call parent method. XXX
329
+ if @parent.is_a? RubyCurses::MenuBar
330
+ @parent.window.printstring( @row, @col, " %s " % @text, $reversecolor)
331
+ @window.hide if !@window.nil?
332
+ else
333
+ $log.debug "MENU SUBMEN. menu onleave: #{@text} #{@row} #{@col} will pop !! "
334
+ # parent is a menu
335
+ highlight false
336
+ #@parent.current_menu.pop
337
+ #@@menus.pop
338
+ #destroy
339
+ end
340
+ end
341
+ def highlight tf=true # menu
342
+ $log.debug "MENU SUBMENU menu highlight: #{@text} #{@row} #{@col}, PW #{@parent.width} "
343
+ color = tf ? $datacolor : $reversecolor
344
+ att = tf ? Ncurses::A_REVERSE : Ncurses::A_NORMAL
345
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
346
+ #@parent.window.mvchgat(y=@row, x=1, @parent.width, Ncurses::A_NORMAL, color, nil)
347
+ # above line did not work with vt100/vt200 next does
348
+ @parent.window.mvchgat(y=@row, x=1, @parent.width, att, $reversecolor, nil)
349
+ @parent.window.wrefresh
350
+ end
351
+ def create_window # menu XXX
352
+ margin = 3
353
+ @width = array_width @items
354
+ $log.debug "create window menu #{@text}: #{@row} ,#{@col},parent: #{@parent}, wd #{@width} "
355
+ #$log.debug "create window menu parent: #{@parent.row}, " unless @parent.nil?
356
+ @row_margin = 1 #+ @@row
357
+ @row_margin = @parent.row+@parent.row_margin unless @parent.nil?
358
+ #@row = @parent.row unless @parent.nil?
359
+ @layout = { :height => @items.length+3, :width => @width+margin, :top => @row+@row_margin, :left => @col }
360
+ @win = VER::Window.new(@layout)
361
+ @window = @win
362
+ @win.bkgd(Ncurses.COLOR_PAIR($datacolor));
363
+ @panel = @win.panel
364
+ @window.printstring( 0, 0, "+%s+" % ("-"*@width), $reversecolor)
365
+ r = 1
366
+ @items.each do |item|
367
+ item.row = r
368
+ item.col = 0
369
+ item.col = @col+@width+margin # margins???
370
+ item.width = @width
371
+ #item.window = @window
372
+ item.parent = self
373
+ item.repaint
374
+ #end
375
+ r+=1
376
+ end
377
+ @window.printstring( r, 0, "+%s+" % ("-"*@width), $reversecolor)
378
+ select_item 0
379
+ @window.refresh
380
+ return @window
381
+ end
382
+ # private
383
+ def array_width a
384
+ longest = a.max {|a,b| a.to_s.length <=> b.to_s.length }
385
+ $log.debug "array width #{longest}"
386
+ longest.to_s.length
387
+ end
388
+ def destroy
389
+ $log.debug "DESTRY menu #{@text}"
390
+ return if @window.nil?
391
+ @visible = false
392
+ panel = @window.panel
393
+ Ncurses::Panel.del_panel(panel) if !panel.nil?
394
+ @window.delwin if !@window.nil?
395
+ @items.each do |item|
396
+ #next if item == :SEPARATOR
397
+ item.destroy
398
+ end
399
+ @window = nil
400
+ end
401
+ # menu LEFT, RIGHT, DOWN, UP, ENTER
402
+ # item could be menuitem or another menu
403
+ #
404
+ def handle_key ch
405
+ #if !@current_menu.empty?
406
+ # cmenu = @current_menu.last
407
+ #else
408
+ # cmenu = self
409
+ #end
410
+ if !@@menus.empty?
411
+ cmenu = @@menus.last
412
+ else
413
+ cmenu = self
414
+ end
415
+ $log.debug " CMENU is #{cmenu}: #{@@menus} "
416
+ case ch
417
+ when KEY_DOWN
418
+ cmenu.select_next_item
419
+ when KEY_UP
420
+ cmenu.select_prev_item
421
+ when KEY_ENTER, 10, 13, 32 # added 32 2008-11-28 23:50
422
+ return cmenu.fire
423
+ when KEY_LEFT
424
+ if cmenu.parent.is_a? RubyCurses::Menu
425
+ $log.debug "LEFT IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
426
+ $log.debug "left IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
427
+ end
428
+ if cmenu.parent.is_a? RubyCurses::Menu and !cmenu.parent.current_menu.empty?
429
+ $log.debug " ABOU TO DESTROY DUE TO LEFT"
430
+ cmenu.parent.current_menu.pop
431
+ @@menus.pop
432
+ cmenu.destroy
433
+ else
434
+ $log.debug " returning UNHANDLED 370"
435
+ return :UNHANDLED
436
+ end
437
+ when KEY_RIGHT
438
+ $log.debug "RIGHTIN MENU : "
439
+ if cmenu.parent.is_a? RubyCurses::Menu
440
+ $log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
441
+ $log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
442
+ end
443
+ if cmenu.parent.is_a? RubyCurses::Menu and !cmenu.parent.current_menu.empty?
444
+ $log.debug " ABOU TO DESTROY DUE TO RIGHT"
445
+ cmenu.parent.current_menu.pop
446
+ @@menus.pop
447
+ cmenu.destroy
448
+ end
449
+ return :UNHANDLED
450
+ else
451
+ ret = check_mnemonics cmenu, ch
452
+ return ret
453
+ end
454
+ end
455
+ ##
456
+ # checks given key against current menu's items and fires key if
457
+ # added on 2008-11-27 12:07
458
+ def check_mnemonics cmenu, ch
459
+ # $log.debug "inside check_mnemonics #{ch}"
460
+ key = ch.chr.downcase rescue ""
461
+ cmenu.items.each_with_index do |item, ix|
462
+ next if !item.respond_to? :mnemonic or item.mnemonic.nil?
463
+ # $log.debug "inside check_mnemonics #{item.mnemonic}"
464
+ if key == item.mnemonic.downcase
465
+ cmenu.select_item ix # 2009-01-23 13:32 so focus moves to menu
466
+ ret = item.fire
467
+ return ret # 0 # 2009-01-23 00:43 menuitem returns CLOSE, menu 0
468
+ end
469
+ end
470
+ return :UNHANDLED
471
+ end
472
+ ## menu
473
+ def show # menu.show
474
+ $log.debug "show (menu) : #{@text} "
475
+ if @window.nil?
476
+ create_window
477
+ end
478
+ @window.show
479
+ select_item 0
480
+ end
481
+ end
482
+ class PopupMenu < Menu
483
+ def initialize text, &block
484
+ @row_margin = 0
485
+ @@row = 0
486
+ @@col = 0
487
+ super
488
+ instance_eval &block if block_given?
489
+ end
490
+ def show component, x, y
491
+ @component = component
492
+ @@row = component.row
493
+ @@col = component.col
494
+ create_window
495
+ handle_keys
496
+ end
497
+ def handle_keys # popup
498
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
499
+ begin
500
+ catch(:menubarclose) do
501
+ while((ch = @window.getchar()) != @toggle_key )
502
+ case ch
503
+ when -1
504
+ next
505
+ else
506
+ ret = handle_key ch
507
+ $log.debug " POPUP got #{ret} added 2009-01-21 18:18 "
508
+ break if ret == :CLOSE
509
+ end
510
+ Ncurses::Panel.update_panels();
511
+ Ncurses.doupdate();
512
+
513
+ @window.wrefresh
514
+ end
515
+ end # catch
516
+ ensure
517
+ #ensure is required becos one can throw a :close
518
+ @@menus = [] # added 2009-01-23 13:21
519
+ destroy # Note that we destroy the menu bar upon exit
520
+ end
521
+ end
522
+ def destroy
523
+ $log.debug "DESTRY popup "
524
+ @visible = false
525
+ panel = @window.panel
526
+ Ncurses::Panel.del_panel(panel) if !panel.nil?
527
+ @window.delwin if !@window.nil?
528
+ @items.each do |item|
529
+ item.destroy
530
+ end
531
+ @window = nil
532
+ end
533
+ end # class
534
+ ##
535
+ # An application related menubar.
536
+ # Currently, I am adding this to a form. But should this not be application specific ?
537
+ # It should popup no matter which window you are on ?? XXX
538
+ class MenuBar
539
+ attr_reader :items
540
+ attr_reader :window
541
+ attr_reader :panel
542
+ attr_reader :selected
543
+ attr_accessor :visible
544
+ attr_accessor :active_index
545
+ attr_accessor :state # normal, selected, highlighted
546
+ attr_accessor :toggle_key # key used to popup, should be set prior to attaching to form
547
+ def initialize &block
548
+ @window = nil
549
+ @active_index = 0
550
+ @items = []
551
+ @visible = false
552
+ @cols = Ncurses.COLS-1
553
+ instance_eval &block if block_given?
554
+ end
555
+ def focusable
556
+ false
557
+ end
558
+ def add menu
559
+ @items << menu
560
+ return self
561
+ end
562
+ def next_menu
563
+ $log.debug "next meu: #{@active_index} "
564
+ if @active_index < @items.length-1
565
+ set_menu @active_index + 1
566
+ else
567
+ set_menu 0
568
+ end
569
+ end
570
+ def prev_menu
571
+ $log.debug "prev meu: #{@active_index} "
572
+ if @active_index > 0
573
+ set_menu @active_index-1
574
+ else
575
+ set_menu @items.length-1
576
+ end
577
+ end
578
+ def set_menu index
579
+ $log.debug "set meu: #{@active_index} #{index}"
580
+ menu = @items[@active_index]
581
+ menu.on_leave # hide its window, if open
582
+ @active_index = index
583
+ menu = @items[@active_index]
584
+ menu.on_enter #display window, if previous was displayed
585
+ @window.wmove menu.row, menu.col
586
+ # menu.show
587
+ # menu.window.wrefresh # XXX we need this
588
+ end
589
+ # menubar LEFT, RIGHT, DOWN
590
+ def handle_keys
591
+ @selected = false
592
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
593
+ set_menu 0
594
+ begin
595
+ catch(:popupclose) do
596
+ while((ch = @window.getchar()) != @toggle_key )
597
+ $log.debug "menuubar inside handle_keys : #{ch}" if ch != -1
598
+ case ch
599
+ when -1
600
+ next
601
+ when KEY_DOWN
602
+ $log.debug "insdie keyDOWN : #{ch}"
603
+ if !@selected
604
+ current_menu.fire
605
+ else
606
+ current_menu.handle_key ch
607
+ end
608
+
609
+ @selected = true
610
+ when KEY_ENTER, 10, 13, 32
611
+ @selected = true
612
+ $log.debug " mb insdie ENTER : #{current_menu}"
613
+ ret = current_menu.handle_key ch
614
+ $log.debug "ret = #{ret} mb insdie ENTER : #{current_menu}"
615
+ #break; ## 2008-12-29 18:00 This will close after firing
616
+ #anything
617
+ break if ret == :CLOSE
618
+ when KEY_UP
619
+ $log.debug " mb insdie keyUPP : #{ch}"
620
+ current_menu.handle_key ch
621
+ when KEY_LEFT
622
+ $log.debug " mb insdie KEYLEFT : #{ch}"
623
+ ret = current_menu.handle_key ch
624
+ prev_menu if ret == :UNHANDLED
625
+ #display_items if @selected
626
+ when KEY_RIGHT
627
+ $log.debug " mb insdie KEYRIGHT : #{ch}"
628
+ ret = current_menu.handle_key ch
629
+ next_menu if ret == :UNHANDLED
630
+ else
631
+ $log.debug " mb insdie ELSE : #{ch}"
632
+ ret = current_menu.handle_key ch
633
+ if ret == :UNHANDLED
634
+ Ncurses.beep
635
+ else
636
+ break # we handled a menu action, close menubar (THIS WORKS FOR MNEMONICS ONLY and always)
637
+ end
638
+ end
639
+ Ncurses::Panel.update_panels();
640
+ Ncurses.doupdate();
641
+
642
+ @window.wrefresh
643
+ end
644
+ end # catch
645
+ ensure
646
+ #ensure is required becos one can throw a :close
647
+ destroy # Note that we destroy the menu bar upon exit
648
+ end
649
+ end
650
+ def current_menu
651
+ @items[@active_index]
652
+ end
653
+ def toggle
654
+ @visible = !@visible
655
+ if !@visible
656
+ hide
657
+ else
658
+ show
659
+ end
660
+ end
661
+ def hide
662
+ @visible = false
663
+ @window.hide if !@window.nil?
664
+ end
665
+ def show
666
+ @visible = true
667
+ if @window.nil?
668
+ repaint # XXX FIXME
669
+ else
670
+ @window.show
671
+ end
672
+ end
673
+ ## menubar
674
+ def repaint
675
+ return if !@visible
676
+ @window ||= create_window
677
+ @window.printstring( 0, 0, "%-*s" % [@cols," "], $reversecolor)
678
+ c = 1; r = 0;
679
+ @items.each do |item|
680
+ item.row = r; item.col = c; item.parent = self
681
+ @window.printstring( r, c, " %s " % item.text, $reversecolor)
682
+ c += (item.text.length + 2)
683
+ end
684
+ @window.wrefresh
685
+ end
686
+ def create_window
687
+ @layout = { :height => 1, :width => 0, :top => 0, :left => 0 }
688
+ @win = VER::Window.new(@layout)
689
+ @window = @win
690
+ @win.bkgd(Ncurses.COLOR_PAIR(5));
691
+ @panel = @win.panel
692
+ return @window
693
+ end
694
+ def destroy
695
+ $log.debug "DESTRY menubar "
696
+ @visible = false
697
+ panel = @window.panel
698
+ Ncurses::Panel.del_panel(panel) if !panel.nil?
699
+ @window.delwin if !@window.nil?
700
+ @items.each do |item|
701
+ item.destroy
702
+ end
703
+ @window = nil
704
+ end
705
+ end # menubar
706
+
707
+ class CheckBoxMenuItem < MenuItem
708
+ include DSL
709
+ attr_reader :checkbox
710
+ def initialize text, mnemonic=nil, &block
711
+ @checkbox = CheckBox.new nil
712
+ @checkbox.text text
713
+ super
714
+ end
715
+ def onvalue
716
+ @checkbox.onvalue onvalue
717
+ end
718
+ def offvalue
719
+ @checkbox.onvalue offvalue
720
+ end
721
+ def text=(t) # stack level too deep if no = .????
722
+ @checkbox.text t
723
+ end
724
+ ## added @ with text, else crashing on testmenu.rb
725
+ def to_s
726
+ " #{@text} "
727
+ end
728
+ def getvalue
729
+ checkbox.getvalue
730
+ end
731
+ def getvalue_for_paint
732
+ "|%-*s|" % [@width, checkbox.getvalue_for_paint]
733
+ end
734
+ def fire
735
+ checkbox.toggle
736
+ super
737
+ repaint
738
+ highlight true
739
+ end
740
+ def repaint
741
+ @parent.window.printstring( row, 0, getvalue_for_paint, $reversecolor)
742
+ parent.window.wrefresh
743
+ end
744
+ def method_missing(sym, *args)
745
+ if checkbox.respond_to? sym
746
+ $log.debug("calling CHECKBOXMENU #{sym} called #{args[0]}")
747
+ checkbox.send(sym, args)
748
+ else
749
+ $log.error("ERROR CHECKBOXMENU #{sym} called")
750
+ end
751
+ end
752
+
753
+ end
754
+ end # modul