rbcurse-core 0.0.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 (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