rbcurse-core 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/README.md +69 -0
  2. data/VERSION +1 -0
  3. data/examples/abasiclist.rb +151 -0
  4. data/examples/alpmenu.rb +46 -0
  5. data/examples/app.sample +17 -0
  6. data/examples/atree.rb +100 -0
  7. data/examples/common/file.rb +45 -0
  8. data/examples/data/README.markdown +9 -0
  9. data/examples/data/brew.txt +38 -0
  10. data/examples/data/color.2 +37 -0
  11. data/examples/data/gemlist.txt +60 -0
  12. data/examples/data/lotr.txt +12 -0
  13. data/examples/data/ports.txt +136 -0
  14. data/examples/data/table.txt +37 -0
  15. data/examples/data/tasks.csv +88 -0
  16. data/examples/data/tasks.txt +27 -0
  17. data/examples/data/todo.txt +10 -0
  18. data/examples/data/todocsv.csv +28 -0
  19. data/examples/data/unix1.txt +21 -0
  20. data/examples/data/unix2.txt +11 -0
  21. data/examples/dbdemo.rb +487 -0
  22. data/examples/dirtree.rb +90 -0
  23. data/examples/newtabbedwindow.rb +100 -0
  24. data/examples/newtesttabp.rb +92 -0
  25. data/examples/tabular.rb +132 -0
  26. data/examples/tasks.rb +167 -0
  27. data/examples/term2.rb +83 -0
  28. data/examples/testkeypress.rb +72 -0
  29. data/examples/testlistbox.rb +158 -0
  30. data/examples/testmessagebox.rb +140 -0
  31. data/examples/testree.rb +106 -0
  32. data/examples/testwsshortcuts.rb +66 -0
  33. data/examples/testwsshortcuts2.rb +127 -0
  34. data/lib/rbcurse.rb +8 -0
  35. data/lib/rbcurse/core/docs/index.txt +73 -0
  36. data/lib/rbcurse/core/include/action.rb +40 -0
  37. data/lib/rbcurse/core/include/appmethods.rb +112 -0
  38. data/lib/rbcurse/core/include/bordertitle.rb +41 -0
  39. data/lib/rbcurse/core/include/chunk.rb +182 -0
  40. data/lib/rbcurse/core/include/io.rb +953 -0
  41. data/lib/rbcurse/core/include/listcellrenderer.rb +140 -0
  42. data/lib/rbcurse/core/include/listeditable.rb +317 -0
  43. data/lib/rbcurse/core/include/listscrollable.rb +590 -0
  44. data/lib/rbcurse/core/include/listselectable.rb +264 -0
  45. data/lib/rbcurse/core/include/multibuffer.rb +83 -0
  46. data/lib/rbcurse/core/include/orderedhash.rb +77 -0
  47. data/lib/rbcurse/core/include/ractionevent.rb +67 -0
  48. data/lib/rbcurse/core/include/rchangeevent.rb +27 -0
  49. data/lib/rbcurse/core/include/rhistory.rb +62 -0
  50. data/lib/rbcurse/core/include/rinputdataevent.rb +47 -0
  51. data/lib/rbcurse/core/include/vieditable.rb +170 -0
  52. data/lib/rbcurse/core/system/colormap.rb +163 -0
  53. data/lib/rbcurse/core/system/keyboard.rb +150 -0
  54. data/lib/rbcurse/core/system/keydefs.rb +30 -0
  55. data/lib/rbcurse/core/system/ncurses.rb +218 -0
  56. data/lib/rbcurse/core/system/panel.rb +162 -0
  57. data/lib/rbcurse/core/system/window.rb +901 -0
  58. data/lib/rbcurse/core/util/ansiparser.rb +117 -0
  59. data/lib/rbcurse/core/util/app.rb +1235 -0
  60. data/lib/rbcurse/core/util/basestack.rb +407 -0
  61. data/lib/rbcurse/core/util/bottomline.rb +1850 -0
  62. data/lib/rbcurse/core/util/colorparser.rb +71 -0
  63. data/lib/rbcurse/core/util/focusmanager.rb +31 -0
  64. data/lib/rbcurse/core/util/padreader.rb +189 -0
  65. data/lib/rbcurse/core/util/rcommandwindow.rb +587 -0
  66. data/lib/rbcurse/core/util/rdialogs.rb +619 -0
  67. data/lib/rbcurse/core/util/viewer.rb +149 -0
  68. data/lib/rbcurse/core/util/widgetshortcuts.rb +505 -0
  69. data/lib/rbcurse/core/widgets/applicationheader.rb +102 -0
  70. data/lib/rbcurse/core/widgets/box.rb +58 -0
  71. data/lib/rbcurse/core/widgets/divider.rb +310 -0
  72. data/lib/rbcurse/core/widgets/keylabelprinter.rb +178 -0
  73. data/lib/rbcurse/core/widgets/rcombo.rb +238 -0
  74. data/lib/rbcurse/core/widgets/rcontainer.rb +415 -0
  75. data/lib/rbcurse/core/widgets/rlink.rb +30 -0
  76. data/lib/rbcurse/core/widgets/rlist.rb +723 -0
  77. data/lib/rbcurse/core/widgets/rmenu.rb +939 -0
  78. data/lib/rbcurse/core/widgets/rmenulink.rb +22 -0
  79. data/lib/rbcurse/core/widgets/rmessagebox.rb +373 -0
  80. data/lib/rbcurse/core/widgets/rprogress.rb +118 -0
  81. data/lib/rbcurse/core/widgets/rtabbedpane.rb +615 -0
  82. data/lib/rbcurse/core/widgets/rtabbedwindow.rb +68 -0
  83. data/lib/rbcurse/core/widgets/rtextarea.rb +920 -0
  84. data/lib/rbcurse/core/widgets/rtextview.rb +780 -0
  85. data/lib/rbcurse/core/widgets/rtree.rb +787 -0
  86. data/lib/rbcurse/core/widgets/rwidget.rb +3040 -0
  87. data/lib/rbcurse/core/widgets/scrollbar.rb +143 -0
  88. data/lib/rbcurse/core/widgets/statusline.rb +94 -0
  89. data/lib/rbcurse/core/widgets/tabular.rb +264 -0
  90. data/lib/rbcurse/core/widgets/tabularwidget.rb +1211 -0
  91. data/lib/rbcurse/core/widgets/textpad.rb +516 -0
  92. data/lib/rbcurse/core/widgets/tree/treecellrenderer.rb +150 -0
  93. data/lib/rbcurse/core/widgets/tree/treemodel.rb +428 -0
  94. metadata +156 -0
@@ -0,0 +1,939 @@
1
+ =begin
2
+ * Name: menu and related classes
3
+ * Description
4
+ * Author: rkumar
5
+ * I am redoing this totally, since this was one my first ruby programs and needs
6
+ * simplification. It was hard to maintain.
7
+ TODO
8
+ -- cursor to be on current menuitem if possible ... UNABLE TO !!
9
+ -- Number and letter indexing for item_list
10
+ -- Use Box characters and hline for separator
11
+ -- MenuSeparator and MenuItem should be common to popups and menus, so we don't need
12
+ 2 separate names, there was clobbering the same namespace.
13
+
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: 2011-09-23 (old 2008-11-14 23:43 )
19
+ == Major changes v1.3.1
20
+ 2011-09-24 V1.3.1 added item_list for dynamic menuitem generation, see examples/menu1.rb
21
+ 2011-09-24 V1.3.1 added multicolumn outputs
22
+ 2011-09-24 V1.3.1 left and right keys on menua, C-g to abort
23
+
24
+ * License:
25
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
26
+
27
+ =end
28
+ #require 'logger'
29
+ require 'rbcurse'
30
+
31
+ include RubyCurses
32
+ module RubyCurses
33
+ extend self
34
+
35
+
36
+ # The separator that separates menuitems, helping to group them.
37
+ class MenuSeparator
38
+ attr_accessor :enabled
39
+ attr_accessor :parent
40
+ attr_accessor :row
41
+ attr_accessor :col
42
+ attr_accessor :coffset
43
+ attr_accessor :width
44
+ attr_accessor :color, :bgcolor # 2011-09-25 V1.3.1
45
+ def initialize
46
+ @enable = false
47
+ end
48
+ def repaint
49
+ acolor = get_color($reversecolor, @color, @bgcolor)
50
+ @parent.window.printstring( @row, 0, "|%s|" % ("-"*@width), acolor)
51
+ end
52
+ def destroy
53
+ end
54
+ def on_enter
55
+ end
56
+ def on_leave
57
+ end
58
+ def to_s
59
+ ""
60
+ end
61
+ end
62
+ ##
63
+ # Items in menus. These will usually result in an action which closes the entire
64
+ # menubar.
65
+ class MenuItem
66
+ attr_accessor :parent
67
+ # attr_accessor :window
68
+ attr_accessor :row
69
+ attr_accessor :col
70
+ attr_accessor :coffset
71
+ attr_accessor :width
72
+ attr_writer :accelerator
73
+ attr_accessor :enabled
74
+ attr_accessor :color, :bgcolor # 2011-09-25 V1.3.1
75
+ attr_accessor :color_pair # 2011-09-25 V1.3.1
76
+ attr_reader :active_index # 2011-09-24 V1.3.1 trying to do a right
77
+ attr_accessor :text, :mnemonic # changed reader to accessor
78
+ def initialize text, mnemonic=nil, &block
79
+ @text = text
80
+ @enabled = true
81
+ # check for mnem that is not one char, could be an accelerator
82
+ if mnemonic
83
+ if mnemonic.length != 1
84
+ $log.error "MenuItem #{text} mnemonic #{mnemonic} should be one character. Maybe you meant accelerator? "
85
+ mnemonic = nil
86
+ end
87
+ end
88
+ @mnemonic = mnemonic
89
+ instance_eval &block if block_given?
90
+ end
91
+ def to_s
92
+ "#{@text} #{@accelerator}"
93
+ end
94
+ def command *args, &block
95
+ $log.debug ">>>command : #{@text} "
96
+ @command = block if block_given?
97
+ alert "Command nil or some error! #{text} " unless @command
98
+ @args = args
99
+ end
100
+ # add accelerator for a menu item
101
+ # NOTE: accelerator means that the application has tied this string to some action, outside
102
+ # of the menu bar. It does not mean that the menu bar will trigger the action. So the app still has to
103
+ # define the action and bind a key to that accelerator. This is only informative.
104
+ # Had to do this since dsl_accessor was throwing some nilclass does not have []= nomethod error
105
+ # This allows user to put accelerator inside dsl block
106
+ # @example
107
+ # accelerator "Ctrl-X"
108
+ def accelerator(*val)
109
+ if val.empty?
110
+ return @accelerator
111
+ else
112
+ @accelerator = val[0]
113
+ end
114
+ end
115
+ def on_enter #item
116
+ highlight
117
+ #@parent.window.wmove @row, @col+1 # 2011-09-25 V1.3.1 NO EFFECT
118
+ end
119
+ def on_leave
120
+ highlight false
121
+ end
122
+ ## XXX it could be a menu again
123
+ # We should not be firing a :NO_MENUITEMS
124
+ def fire
125
+ $log.debug ">>>fire menuitem : #{@text} #{@command} "
126
+ @command.call self, *@args if !@command.nil?
127
+ @parent.clear_menus
128
+ return :CLOSE # added 2009-01-02 00:09 to close only actions, not submenus
129
+ end
130
+ def highlight tf=true
131
+ if @parent.nil? or @parent.window.nil?
132
+ #$log.debug "HL XXX #{self} parent nil"
133
+ #$log.debug "HL XXX #{self} - > #{@parent} parent nil"
134
+ end
135
+ if tf
136
+ #color = $datacolor
137
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
138
+ # above line did not work in vt100, 200 terminals, next works.
139
+ # @parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_REVERSE, $reversecolor, nil) # changed 2011 dts 2011-09-24 multicolumn, 1 skips the border
140
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
141
+ @parent.window.mvchgat(y=@row, x=@col+1, @width, Ncurses::A_REVERSE, @color_pair, nil)
142
+ #@parent.window.mvaddch @row, @col, "*".ord
143
+ #@parent.window.wmove @row, @col # 2011-09-25 V1.3.1 NO EFFECT
144
+ else
145
+ repaint
146
+ end
147
+ @parent.window.wrefresh unless @parent.window.nil? ## XXX 2009-01-21 22:00
148
+ end
149
+ def repaint # menuitem.repaint
150
+ if @parent.nil? or @parent.window.nil?
151
+ $log.debug "repaint #{self} parent nil"
152
+ # return
153
+ end
154
+ r = @row
155
+ c = @col
156
+ ltext = text
157
+ ltext = "* No Items *" if text == :NO_MENUITEMS
158
+ @color_pair = get_color($reversecolor, @color, @bgcolor)
159
+ #acolor = $reversecolor
160
+ acolor = @color_pair
161
+ acolor = get_color($reversecolor, 'green', @bgcolor) if !@enabled
162
+ # @parent.window.printstring( @row, 0, "|%-*s|" % [@width, ltext], acolor) # changed 2011 2011-09-24
163
+ @parent.window.printstring( @row, c, "|%-*s|" % [@width, ltext], acolor)
164
+ if @enabled # 2010-09-10 23:56
165
+ if !@accelerator.nil?
166
+ # FIXME add c earlier 0 was offset
167
+ @parent.window.printstring( r, (@width+1)-@accelerator.length, @accelerator, acolor)
168
+ elsif !@mnemonic.nil?
169
+ m = @mnemonic
170
+ ix = text.index(m) || text.index(m.swapcase)
171
+ charm = text[ix,1]
172
+ #@parent.window.printstring( r, ix+1, charm, $datacolor) if !ix.nil?
173
+ # prev line changed since not working in vt100 and vt200
174
+ @parent.window.printstring( r, ix+1, charm, $reversecolor, 'reverse') if !ix.nil?
175
+ end
176
+ #@parent.window.wmove r, c # NO EFFECT
177
+ end
178
+ end
179
+ def destroy
180
+ $log.debug "DESTROY menuitem #{@text}"
181
+ end
182
+ end
183
+ ## class Menu. Contains menuitems, and can be a menuitem itself.
184
+ # Opens out another list of menuitems.
185
+ class Menu < MenuItem
186
+ attr_accessor :parent
187
+ attr_accessor :row
188
+ attr_accessor :col
189
+ attr_accessor :coffset
190
+ attr_accessor :width
191
+ attr_accessor :enabled
192
+ attr_reader :text
193
+ attr_reader :items
194
+ attr_reader :window
195
+ attr_reader :panel
196
+ attr_reader :current_menu
197
+ attr_reader :row_margin ## 2009-01-21 12:06 NEW
198
+ ## this keeps a stack of menus. if we coud somehow put this in
199
+ # menubar would be great.
200
+ @@menus = []
201
+ @@row = 0
202
+ @@col = 0
203
+
204
+ def initialize text, &block
205
+ @text = text
206
+ @items = []
207
+ @enabled = true
208
+ @current_menu = []
209
+ super text, nil, &block
210
+ @row ||=10
211
+ @col ||=10
212
+ @coffset = 0
213
+ @@menus ||= []
214
+ @active_index = nil # 2011-09-25 V1.3.1 otherwise crashing in select_right
215
+ end
216
+ ## called upon firing so when we next show menubar there are not any left overs in here.
217
+ def clear_menus
218
+ @@menus = []
219
+ end
220
+ def to_s
221
+ @text
222
+ end
223
+ # item could be menuitem or another menu (precreated)
224
+ def add menuitem
225
+ #$log.debug " YYYY inside add menuitem #{menuitem.text} "
226
+ @items << menuitem
227
+ return self
228
+ end
229
+ alias :<< :add
230
+
231
+ # add item method which could be used from blocks
232
+ # add 2010-09-10 12:20 simplifying
233
+ def item text, mnem=nil, &block
234
+ #$log.debug "YYYY inside M: menuitem text #{text} "
235
+ m = MenuItem.new text, mnem, &block
236
+ add m
237
+ return m
238
+ end
239
+ # create a menu within a menu
240
+ # add menu method which could be used from blocks
241
+ # add 2010-09-10 12:20 simplifying
242
+ def menu text, &block
243
+ #$log.debug "YYYY inside M: menu text #{text} "
244
+ m = Menu.new text, &block
245
+ add m
246
+ return m
247
+ end
248
+ def insert_separator ix
249
+ @items.insert ix, MenuSeparator.new
250
+ end
251
+ def add_separator
252
+ @items << MenuSeparator.new
253
+ end
254
+ alias :separator :add_separator
255
+
256
+ ## added 2009-01-21 12:09 NEW
257
+ def get_item i
258
+ @items[i]
259
+ end
260
+ ## added 2009-01-21 12:09 NEW
261
+ def remove n
262
+ if n.is_a? Fixnum
263
+ @items.delete_at n
264
+ else
265
+ @items.delete n
266
+ end
267
+ end
268
+ # generate an item list at runtime for this menu
269
+ def item_list *args, &block
270
+ $log.debug ">>>item_list : #{@text} "
271
+ @item_list = block if block_given?
272
+ @item_list_args = args
273
+ end
274
+ # menu -
275
+ def fire
276
+ $log.debug "menu fire called: #{text} "
277
+ if @window.nil?
278
+ #repaint
279
+ # added 2011-09-24 adding ability to generate list of items
280
+ if @item_list
281
+ # generate a list, but we need to know what to do with that list.
282
+ @items = []
283
+ l = @item_list.call self, *@item_list_args if !@item_list.nil?
284
+ if l.nil? || l.size == 0
285
+ item(:NO_MENUITEMS)
286
+ else
287
+ # for each element returned create a menuitem, and attach the command to it.
288
+ l.each { |e| it = item(e);
289
+ if @command # there should be a command otherwise what's the point
290
+ it.command(@args) do @command.call(it, it.text) end;
291
+ else
292
+ it.command(@args) do alert("No command attached to #{it.text} ") end;
293
+ $log.warn "No command attached to item_list "
294
+ end
295
+ }
296
+ end
297
+ $log.debug "menu got items #{@items.count} "
298
+ end
299
+ if @items.empty? # user did not specify any items
300
+ item(:NO_MENUITEMS)
301
+ end
302
+ create_window
303
+ if !@parent.is_a? RubyCurses::MenuBar
304
+ @parent.current_menu << self
305
+ @@menus << self # NEW
306
+ end
307
+ else
308
+ ### shouod this not just show ?
309
+ $log.debug "menu fire called: #{text} ELSE XXX WHEN IS THIS CALLED ? 658 #{@items[@active_index].text} "
310
+ if @active_index # sometimes no menu item specified 2011-09-24 NEWMENU
311
+ return @items[@active_index].fire # this should happen if selected. else selected()
312
+ end
313
+ end
314
+ #@action.call if !@action.nil?
315
+ end
316
+ # user has clicked down, we shoud display items
317
+ # DRAW menuitems
318
+ def repaint # menu.repaint
319
+ # OMG will not print anything if no items !
320
+ # When we do item generation this list will be empty
321
+ #return if @items.nil? or @items.empty? # commented 2011-09-24 NEWMENU
322
+ #$log.debug "menu repaint: #{text} row #{@row} col #{@col} "
323
+ @color_pair = get_color($reversecolor, @color, @bgcolor)
324
+ if !@parent.is_a? RubyCurses::MenuBar
325
+ @parent.window.printstring( @row, 0, "|%-*s>|" % [@width-1, text], @color_pair)
326
+ @parent.window.refresh
327
+ end
328
+ if @window.nil?
329
+ #create_window
330
+ else
331
+ @window.show
332
+ select_item 0
333
+ @window.refresh
334
+ end
335
+ end
336
+ ##
337
+ # recursive if given one not enabled goes to next enabled
338
+ def select_item ix0
339
+ return if @items.nil? or @items.empty?
340
+ #$log.debug "insdie select item : #{ix0} active: #{@active_index}"
341
+ if !@active_index.nil?
342
+ @items[@active_index].on_leave
343
+ end
344
+ previtem = @active_index
345
+ @active_index = ix0
346
+ if @items[ix0].enabled
347
+ @items[ix0].on_enter
348
+ else
349
+ #$log.debug "insdie sele nxt item ENABLED FALSE : #{ix0}"
350
+ if @active_index > previtem
351
+ select_next_item
352
+ else
353
+ select_prev_item
354
+ end
355
+ end
356
+ @window.refresh
357
+ end
358
+ def select_next_item
359
+ return if @items.nil? or @items.empty?
360
+ #$log.debug "insdie sele nxt item : #{@active_index}"
361
+ @active_index = -1 if @active_index.nil?
362
+ if @active_index < @items.length-1
363
+ select_item @active_index + 1
364
+ else
365
+ # select_item 0
366
+ end
367
+ end
368
+ def select_prev_item
369
+ return if @items.nil? or @items.empty?
370
+ #$log.debug "insdie sele prv item : #{@active_index}"
371
+ if @active_index > 0
372
+ select_item @active_index - 1
373
+ else
374
+ #select_item @items.length-1
375
+ end
376
+ end
377
+ #
378
+ # If multi-column menuitems then try going to a left item (prev column same row)
379
+ # NOTE It should only come here if items are open, otherwise row and col will be blank.
380
+ # NOTE active_index nil means no items open
381
+ #
382
+ def select_left_item
383
+ return :UNHANDLED if @items.nil? or @items.empty? or @active_index.nil?
384
+ index = nil
385
+ crow = @items[@active_index].row
386
+ ccol = @items[@active_index].col
387
+ @items.each_with_index { |e, i| index = i if e.row == crow && e.col < ccol }
388
+ if index
389
+ select_item index
390
+ else
391
+ return :UNHANDLED
392
+ end
393
+ end
394
+ # @since 1.3.1 2011-09-24
395
+ # If multi-column menuitems then try going to a right item (next column same row)
396
+ # Only if items are open, not from a menubar menu
397
+ def select_right_item
398
+ return :UNHANDLED if @items.nil? or @items.empty? or @active_index.nil?
399
+ crow = @items[@active_index].row
400
+ ccol = @items[@active_index].col
401
+ #alert "inside select right with #{@items.size} #{@items[@active_index].text}: items. r #{crow} col #{ccol} "
402
+ index = nil
403
+ @items.each_with_index { |e, i|
404
+ $log.debug " select_right #{e.row} == #{crow} , #{e.col} > #{ccol} " if $log.debug?
405
+ if e.row == crow && e.col > ccol
406
+ index = i
407
+ $log.debug "YYY select_right #{e.row} == #{crow} , #{e.col} > #{ccol} FOUND #{i} " if $log.debug?
408
+ break
409
+ end
410
+ }
411
+ if index
412
+ select_item index
413
+ else
414
+ return :UNHANDLED
415
+ end
416
+ end
417
+ def on_enter # menu.on_enter
418
+ #$log.debug "menu onenter: #{text} #{@row} #{@col} "
419
+ # call parent method. XXX
420
+ #if @parent.is_a? RubyCurses::MenuBar
421
+ #acolor = get_color($datacolor, @bgcolor, @color)
422
+ #@parent.window.printstring( @row, @col, " %s " % text, acolor)
423
+ #else
424
+ highlight
425
+ #end
426
+ if !@window.nil? #and @parent.selected
427
+ #$log.debug "menu onenter: #{text} calling window,show"
428
+ @window.show
429
+ select_item 0
430
+ elsif @parent.is_a? RubyCurses::MenuBar and @parent.selected
431
+ # only on the top level do we open a window if a previous one was opened
432
+ #$log.debug "menu onenter: #{text} calling repaint CLASS: #{@parent.class}"
433
+ # repaint
434
+ create_window
435
+ end
436
+ end
437
+ def on_leave # menu.on_leave
438
+ #$log.debug "menu onleave: #{text} #{@row} #{@col} "
439
+ # call parent method. XXX
440
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
441
+ if @parent.is_a? RubyCurses::MenuBar
442
+ # @parent.window.printstring( @row, @col, " %s " % text, $reversecolor) # changed 2011 2011-09-24
443
+ @parent.window.printstring( @row, @col, " %s " % text, @color_pair)
444
+ @window.hide if !@window.nil?
445
+ else
446
+ #$log.debug "MENU SUBMEN. menu onleave: #{text} #{@row} #{@col} "
447
+ # parent is a menu
448
+ highlight false
449
+ #@parent.current_menu.pop
450
+ #@@menus.pop
451
+ #destroy
452
+ end
453
+ end
454
+ def highlight tf=true # menu
455
+ if @parent.is_a? RubyCurses::MenuBar # top level menu
456
+ #acolor = get_color($datacolor, @bgcolor, @color)
457
+ #@parent.window.printstring( @row, @col, " %s " % text, acolor)
458
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
459
+ att = Ncurses::A_REVERSE
460
+ @parent.window.mvchgat(y=@row, x=@col+1, text.length+1, att, @color_pair, nil)
461
+ else
462
+ #$log.debug "MENU SUBMENU menu highlight: #{text} #{@row} #{@col}, PW #{@parent.width} "
463
+ acolor = tf ? $datacolor : $reversecolor
464
+ att = tf ? Ncurses::A_REVERSE : Ncurses::A_NORMAL
465
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
466
+ #@parent.window.mvchgat(y=@row, x=1, @parent.width, Ncurses::A_NORMAL, color, nil)
467
+ # above line did not work with vt100/vt200 next does
468
+ # @parent.window.mvchgat(y=@row, x=1, @parent.width, att, $reversecolor, nil) # changed 2011 2011-09-24
469
+ @parent.window.mvchgat(y=@row, x=1, @parent.width, att, @color_pair, nil)
470
+ @parent.window.wrefresh
471
+ end
472
+ end
473
+ def create_window # menu
474
+ margin = 2 # flush against parent
475
+ @width = array_width(@items) + 1 # adding 1 since menus append a ">" 2011-09-24
476
+ $log.debug "create window menu #{@text}: r #{@row} ,col #{@col}, wd #{@width} "
477
+ t = @row+1
478
+ h = @items.length+3
479
+ ww = @width+margin
480
+ ww1 = @width
481
+ max = Ncurses.LINES-1
482
+ if t + h > max
483
+ t = 2 # one below menubar, not touching
484
+ if h > max
485
+ i = ((h*1.0)/max).ceil
486
+ h = max - 1
487
+ ww = ww * i # FIXME we need to calculate
488
+ end
489
+ end # t + 1
490
+ $log.debug "create window menu #{@text}: t #{t} ,h #{h}, w: #{ww} , col #{@col} max #{max} "
491
+
492
+ #@layout = { :height => @items.length+3, :width => ww, :top => @row+1, :left => @col }
493
+ # earlier col had the offset to start the next level, I was not using it to print
494
+ # but with mulitple cols i am using it. So, this col will overwrite existing menu.
495
+ @layout = { :height => h-1, :width => ww, :top => t, :left => @coffset }
496
+ @win = VER::Window.new(@layout)
497
+ @window = @win
498
+ @color_pair ||= get_color($datacolor, @color, @bgcolor)
499
+ @rev_color_pair ||= get_color($reversecolor, @color, @bgcolor)
500
+ @win.bkgd(Ncurses.COLOR_PAIR(@color_pair));
501
+ @panel = @win.panel
502
+ #@window.printstring( 0, 0, "+%s+" % ("-"*@width), $reversecolor)
503
+ @window.printstring( 0, 0, "+%s+" % ("-"*(ww1)), @rev_color_pair)
504
+ saved_r = 1
505
+ r = 1
506
+ #saved_c = @col+@width+margin # margins???
507
+ saved_c = 0 ; # actual program uses 0 in repain for col
508
+ c = saved_c
509
+ $log.debug "create window menu #{@text}: first col r #{r} ,c #{c}"
510
+ @items.each do |item|
511
+ #break if r > h # added 2011-09-24 for large number of items - causes error
512
+ if r >= h-2
513
+ @window.printstring( h-2, c, "+%s+" % ("-"*(ww1)), @rev_color_pair)
514
+ r = saved_r
515
+ c += (@width + 2)
516
+ @window.printstring( 0, c, "+%s+" % ("-"*(ww1)), @rev_color_pair)
517
+ $log.debug "create window menu #{@text}: new col r #{r} ,c #{c}, #{item.text} "
518
+ end
519
+ item.row = r
520
+ item.col = c
521
+ item.coffset = @coffset+@width+margin # margins???
522
+
523
+
524
+ item.width = @width
525
+ #item.window = @window
526
+ item.parent = self
527
+ item.color = @color; item.bgcolor = @bgcolor
528
+ item.repaint
529
+ r+=1
530
+ end
531
+ # @window.printstring( r, 0, "+%s+" % ("-"*@width), $reversecolor) # changed 2011 2011-09-24
532
+ @window.printstring( h-2, 0, "+%s+" % ("-"*(ww1)), @rev_color_pair)
533
+ # in case of multiple rows
534
+ @window.printstring( r, c, "+%s+" % ("-"*(ww1)), @rev_color_pair)
535
+ select_item 0
536
+ @window.refresh
537
+ return @window
538
+ end
539
+ # private
540
+ def array_width a
541
+ longest = a.max {|a,b| a.to_s.length <=> b.to_s.length }
542
+ #$log.debug "array width #{longest}"
543
+ longest.to_s.length
544
+ end
545
+ def destroy
546
+ $log.debug "DESTRY menu #{@text}"
547
+ return if @window.nil?
548
+ @visible = false
549
+ panel = @window.panel
550
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
551
+ @window.delwin if !@window.nil?
552
+ @items.each do |item|
553
+ #next if item == :SEPARATOR
554
+ item.destroy
555
+ end
556
+ @window = nil
557
+ end
558
+ # menu LEFT, RIGHT, DOWN, UP, ENTER
559
+ # item could be menuitem or another menu
560
+ #
561
+ def handle_key ch
562
+ if !@current_menu.empty?
563
+ cmenu = @current_menu.last
564
+ else
565
+ cmenu = self
566
+ end
567
+ if !@@menus.empty?
568
+ cmenu = @@menus.last
569
+ else
570
+ cmenu = self
571
+ end
572
+ case ch
573
+ when KEY_DOWN
574
+ cmenu.select_next_item
575
+ #return cmenu.fire # XXX 2010-10-16 21:39 trying out
576
+ if cmenu.is_a? RubyCurses::Menu
577
+ #alert "is a menu" # this gets triggered even when we are on items
578
+ end
579
+ when KEY_UP
580
+ cmenu.select_prev_item
581
+ when KEY_ENTER, 10, 13, 32 # added 32 2008-11-27 23:50
582
+ return cmenu.fire
583
+ when KEY_LEFT
584
+ if cmenu.parent.is_a? RubyCurses::Menu
585
+ #$log.debug "LEFT IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
586
+ #$log.debug "left IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
587
+ end
588
+ ret = cmenu.select_left_item # 2011-09-24 V1.3.1 attempt to goto left item if columns
589
+ if ret == :UNHANDLED
590
+ if cmenu.parent.is_a? RubyCurses::MenuBar #and !cmenu.parent.current_menu.empty?
591
+ #$log.debug " ABOU TO DESTROY DUE TO LEFT"
592
+ cmenu.current_menu.pop
593
+ @@menus.pop ## NEW
594
+ cmenu.destroy
595
+ return :UNHANDLED
596
+ end
597
+ # LEFT on a menu list allows me to close and return to higher level
598
+ if cmenu.parent.is_a? RubyCurses::Menu #and !cmenu.parent.current_menu.empty?
599
+ #$log.debug " ABOU TO DESTROY DUE TO LEFT"
600
+ cmenu.current_menu.pop
601
+ @@menus.pop ## NEW
602
+ cmenu.destroy
603
+ #return :UNHANDLED
604
+ end
605
+ end
606
+ when KEY_RIGHT
607
+ $log.debug "RIGHTIN MENU : #{text} "
608
+ if cmenu.active_index
609
+ if cmenu.items[cmenu.active_index].is_a? RubyCurses::Menu
610
+ #alert "could fire here cmenu: #{cmenu.text}, par: #{cmenu.parent.text} "
611
+ cmenu.fire
612
+ return
613
+ #$log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
614
+ #$log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
615
+ end
616
+ end
617
+ # This introduces a bug if no open items
618
+ ret = cmenu.select_right_item # 2011-09-24 V1.3.1 attempt to goto right item if columns
619
+ #alert "attempting to select right #{ret} "
620
+ if ret == :UNHANDLED
621
+ #if cmenu.parent.is_a? RubyCurses::Menu and !cmenu.parent.current_menu.empty?
622
+ if cmenu.parent.is_a? RubyCurses::MenuBar #and !cmenu.current_menu.empty?
623
+ $log.debug " ABOU TO DESTROY DUE TO RIGHT"
624
+ cmenu.current_menu.pop
625
+ @@menus.pop
626
+ cmenu.destroy
627
+ return :UNHANDLED
628
+ end
629
+ end
630
+ else
631
+ ret = check_mnemonics cmenu, ch
632
+ return ret
633
+ end
634
+ end
635
+ ##
636
+ # checks given key against current menu's items and fires key if
637
+ # added on 2008-11-27 12:07
638
+ def check_mnemonics cmenu, ch
639
+ # $log.debug "inside check_mnemonics #{ch}"
640
+ key = ch.chr.downcase rescue ""
641
+ cmenu.items.each do |item|
642
+ next if !item.respond_to? :mnemonic or item.mnemonic.nil?
643
+ # $log.debug "inside check_mnemonics #{item.mnemonic}"
644
+ if key == item.mnemonic.downcase && item.enabled # 2010-09-11 00:03 enabled
645
+ ret = item.fire
646
+ return ret #0 2009-01-23 00:45
647
+ end
648
+ end
649
+ return :UNHANDLED
650
+ end
651
+ ## menu
652
+ def show # menu.show
653
+ #$log.debug "show (menu) : #{@text} "
654
+ if @window.nil?
655
+ create_window #@col+@width
656
+ end
657
+ @window.show
658
+ select_item 0
659
+ end
660
+ end
661
+ ##
662
+ # An application related menubar.
663
+ # Currently, I am adding this to a form. But should this not be application specific ?
664
+ # It should popup no matter which window you are on ?? XXX
665
+ class MenuBar
666
+ attr_reader :items
667
+ attr_reader :window
668
+ attr_reader :panel
669
+ attr_reader :selected
670
+ attr_reader :text # temp 2011-09-24 V1.3.1
671
+ attr_accessor :visible
672
+ attr_accessor :active_index
673
+ attr_accessor :state # normal, selected, highlighted
674
+ attr_accessor :toggle_key # key used to popup, should be set prior to attaching to form
675
+ attr_accessor :color, :bgcolor # 2011-09-25 V1.3.1
676
+ attr_accessor :_object_created # 2011-10-7 if visible then Form will call this
677
+ def initialize &block
678
+ @window = nil
679
+ @text = "menubar"
680
+ @items = []
681
+ init_vars
682
+ @visible = false
683
+ @cols = Ncurses.COLS-1
684
+ instance_eval &block if block_given?
685
+ end
686
+ def init_vars
687
+ @active_index = 0
688
+ end
689
+ def focusable
690
+ false
691
+ end
692
+ # add a precreated menu
693
+ def add menu
694
+ #$log.debug "YYYY inside MB: add #{menu.text} "
695
+ @items << menu
696
+ return self
697
+ end
698
+ alias :<< :add
699
+
700
+ # add a menu through the block, this would happen through instance eval
701
+ # 2010-09-10 12:07 added while simplifying the interface
702
+ # this calls add so you get the MB back, not a ref to the menu created NOTE
703
+ def menu text, &block
704
+ #$log.debug "YYYY inside MB: menu text #{text} "
705
+ m = Menu.new text, &block
706
+ m.color = @color
707
+ m.bgcolor = @bgcolor
708
+ add m
709
+ return m
710
+ end
711
+ def next_menu
712
+ #$log.debug "next meu: #{@active_index} "
713
+ if @active_index < @items.length-1
714
+ set_menu @active_index + 1
715
+ else
716
+ set_menu 0
717
+ end
718
+ end
719
+ def prev_menu
720
+ #$log.debug "prev meu: #{@active_index} "
721
+ if @active_index > 0
722
+ set_menu @active_index-1
723
+ else
724
+ set_menu @items.length-1
725
+ end
726
+ end
727
+ def set_menu index
728
+ #$log.debug "set meu: #{@active_index} #{index}"
729
+ menu = @items[@active_index]
730
+ menu.on_leave # hide its window, if open
731
+ @active_index = index
732
+ menu = @items[@active_index]
733
+ menu.on_enter #display window, if previous was displayed
734
+ @window.wmove menu.row, menu.col
735
+ # menu.show
736
+ # menu.window.wrefresh # XXX we need this
737
+ end
738
+
739
+ def keep_visible flag=nil
740
+ return @keep_visible unless flag
741
+ @keep_visible = flag
742
+ @visible = flag
743
+ self
744
+ end
745
+ # menubar LEFT, RIGHT, DOWN
746
+ def handle_keys
747
+ @selected = false
748
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
749
+ set_menu 0
750
+ begin
751
+ catch(:menubarclose) do
752
+ while((ch = @window.getchar()) != @toggle_key )
753
+ #$log.debug "menuubar inside handle_keys : #{ch}" if ch != -1
754
+ case ch
755
+ when -1
756
+ next
757
+ when KEY_DOWN
758
+ #$log.debug "insdie keyDOWN : #{ch}"
759
+ if !@selected
760
+ current_menu.fire
761
+ else
762
+ current_menu.handle_key ch
763
+ end
764
+
765
+ @selected = true
766
+ when KEY_ENTER, 10, 13, 32
767
+ @selected = true
768
+ #$log.debug " mb insdie ENTER : #{current_menu}"
769
+ ret = current_menu.handle_key ch
770
+ #$log.debug "ret = #{ret} mb insdie ENTER : #{current_menu}"
771
+ #break; ## 2008-12-29 18:00 This will close after firing
772
+ #anything
773
+ break if ret == :CLOSE
774
+ when KEY_UP
775
+ #$log.debug " mb insdie keyUPP : #{ch}"
776
+ current_menu.handle_key ch
777
+ when KEY_LEFT
778
+ #$log.debug " mb insdie KEYLEFT : #{ch}"
779
+ ret = current_menu.handle_key ch
780
+ prev_menu if ret == :UNHANDLED
781
+ #display_items if @selected
782
+ when KEY_RIGHT
783
+ #$log.debug " mb insdie KEYRIGHT : #{ch}"
784
+ ret = current_menu.handle_key ch
785
+ next_menu if ret == :UNHANDLED
786
+ when ?\C-g.getbyte(0) # abort
787
+ throw :menubarclose
788
+ else
789
+ #$log.debug " mb insdie ELSE : #{ch}"
790
+ ret = current_menu.handle_key ch
791
+ if ret == :UNHANDLED
792
+ Ncurses.beep
793
+ else
794
+ break # we handled a menu action, close menubar (THIS WORKS FOR MNEMONICS ONLY and always)
795
+ end
796
+ end
797
+ Ncurses::Panel.update_panels();
798
+ Ncurses.doupdate();
799
+
800
+ @window.wrefresh
801
+ end
802
+ end # catch
803
+ ensure
804
+ #ensure is required becos one can throw a :close
805
+ $log.debug " DESTROY IN ENSURE"
806
+ current_menu.clear_menus #@@menus = [] # added 2009-01-23 13:21
807
+ destroy # Note that we destroy the menu bar upon exit
808
+ end
809
+ end
810
+ def current_menu
811
+ @items[@active_index]
812
+ end
813
+ # called by set_menu_bar in widget.rb (class Form).
814
+ def toggle
815
+ # added keeping it visible, 2011-10-7 being tested in dbdemo
816
+ if @keep_visible
817
+ init_vars
818
+ show
819
+ @items[0].highlight
820
+ @window.ungetch(KEY_DOWN)
821
+ return
822
+ end
823
+ #@items.each { |i| $log.debug " ITEM DDD : #{i.text}" }
824
+ @visible = !@visible
825
+ if !@visible
826
+ hide
827
+ else
828
+ init_vars
829
+ show
830
+ end
831
+ end
832
+ def hide
833
+ @visible = false
834
+ @window.hide if !@window.nil? # seems to cause auto-firing when we resume toggle 2011-09-26
835
+ end
836
+ def show
837
+ @visible = true
838
+ if @window.nil?
839
+ repaint # XXX FIXME
840
+ else
841
+ @window.show
842
+ end
843
+ end
844
+ ## menubar
845
+ # TODO: check for menu to be flush right (only for last one).
846
+ def repaint
847
+ return if !@visible
848
+ @color_pair = get_color($reversecolor, @color, @bgcolor)
849
+ @window ||= create_window_menubar
850
+ # @window.printstring( 0, 0, "%-*s" % [@cols," "], $reversecolor) # changed 2011 2011-09-24
851
+ @window.printstring( 0, 0, "%-*s" % [@cols," "], @color_pair)
852
+ c = 1; r = 0;
853
+ @items.each do |item|
854
+ item.row = r; item.col = c; item.coffset = c; item.parent = self
855
+ item.color = @color
856
+ item.bgcolor = @bgcolor
857
+ @window.printstring( r, c, " %s " % item.text, @color_pair)
858
+ # 2011-09-26 V1.3.1 quick dirty highlighting of first menu on menubar
859
+ # on opening since calling highlight was giving bug in parent.width
860
+ #if c == 1
861
+ #att = Ncurses::A_REVERSE
862
+ #@window.mvchgat(y=r, x=c+1, item.text.length+1, att, @color_pair, nil)
863
+ #end
864
+ c += (item.text.length + 2)
865
+ end
866
+ #@items[0].on_enter # 2011-09-25 V1.3.1 caused issues when toggling, first item fired on DOWN
867
+ @items[0].highlight unless @keep_visible # 2011-09-26 V1.3.1 fixed to take both cases into account
868
+ @window.wrefresh
869
+ end
870
+ def create_window_menubar
871
+ @layout = { :height => 1, :width => 0, :top => 0, :left => 0 }
872
+ @win = VER::Window.new(@layout)
873
+ @window = @win
874
+ @win.bkgd(Ncurses.COLOR_PAIR(5)); # <---- FIXME
875
+ @panel = @win.panel
876
+ return @window
877
+ end
878
+ def destroy
879
+ $log.debug "DESTRY menubar "
880
+ @items.each do |item|
881
+ item.destroy
882
+ end
883
+ return if @keep_visible
884
+ @visible = false
885
+ panel = @window.panel
886
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
887
+ @window.delwin if !@window.nil?
888
+ @window = nil
889
+ end
890
+ end # menubar
891
+
892
+ class CheckBoxMenuItem < MenuItem
893
+ attr_reader :checkbox
894
+ def initialize text, mnemonic=nil, &block
895
+ @checkbox = CheckBox.new nil
896
+ @checkbox.text text
897
+ super
898
+ end
899
+ def onvalue
900
+ @checkbox.onvalue onvalue
901
+ end
902
+ def offvalue
903
+ @checkbox.onvalue offvalue
904
+ end
905
+ def text=(t) # stack level too deep if no = .????
906
+ @checkbox.text t
907
+ end
908
+ def to_s
909
+ " #{text} "
910
+ end
911
+ def getvalue
912
+ checkbox.getvalue
913
+ end
914
+ def getvalue_for_paint
915
+ "|%-*s|" % [@width, checkbox.getvalue_for_paint]
916
+ end
917
+ def fire
918
+ checkbox.toggle
919
+ super
920
+ repaint
921
+ highlight true
922
+ end
923
+ def repaint # checkbox
924
+ # FIXME need @color_pair here
925
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
926
+ @parent.window.printstring( row, 0, getvalue_for_paint, @color_pair)
927
+ parent.window.wrefresh
928
+ end
929
+ def method_missing(sym, *args)
930
+ if checkbox.respond_to? sym
931
+ #$log.debug("calling CHECKBOXMENU #{sym} called #{args[0]}")
932
+ checkbox.send(sym, args)
933
+ else
934
+ $log.error("ERROR CHECKBOXMENU #{sym} called")
935
+ end
936
+ end
937
+ end # class
938
+
939
+ end # modul