rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,958 @@
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 'rbhex'
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
+ @repaint_required = true
689
+ end
690
+ def focusable
691
+ false
692
+ end
693
+ # add a precreated menu
694
+ def add menu
695
+ #$log.debug "YYYY inside MB: add #{menu.text} "
696
+ @items << menu
697
+ return self
698
+ end
699
+ alias :<< :add
700
+
701
+ # add a menu through the block, this would happen through instance eval
702
+ # 2010-09-10 12:07 added while simplifying the interface
703
+ # this calls add so you get the MB back, not a ref to the menu created NOTE
704
+ def menu text, &block
705
+ #$log.debug "YYYY inside MB: menu text #{text} "
706
+ m = Menu.new text, &block
707
+ m.color = @color
708
+ m.bgcolor = @bgcolor
709
+ add m
710
+ return m
711
+ end
712
+ def next_menu
713
+ #$log.debug "next meu: #{@active_index} "
714
+ if @active_index < @items.length-1
715
+ set_menu @active_index + 1
716
+ else
717
+ set_menu 0
718
+ end
719
+ end
720
+ def prev_menu
721
+ #$log.debug "prev meu: #{@active_index} "
722
+ if @active_index > 0
723
+ set_menu @active_index-1
724
+ else
725
+ set_menu @items.length-1
726
+ end
727
+ end
728
+ def set_menu index
729
+ #$log.debug "set meu: #{@active_index} #{index}"
730
+ menu = @items[@active_index]
731
+ menu.on_leave # hide its window, if open
732
+ @active_index = index
733
+ menu = @items[@active_index]
734
+ menu.on_enter #display window, if previous was displayed
735
+ @window.wmove menu.row, menu.col
736
+ # menu.show
737
+ # menu.window.wrefresh # XXX we need this
738
+ end
739
+
740
+ def keep_visible flag=nil
741
+ return @keep_visible unless flag
742
+ @keep_visible = flag
743
+ @visible = flag
744
+ self
745
+ end
746
+ # menubar LEFT, RIGHT, DOWN
747
+ def handle_keys
748
+ @selected = false
749
+ @repaint_required = true # added 2011-12-12 otherwise keeps repainting and you see a flicker
750
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
751
+ set_menu 0
752
+ begin
753
+ catch(:menubarclose) do
754
+ while((ch = @window.getchar()) != @toggle_key )
755
+ #$log.debug "menuubar inside handle_keys : #{ch}" if ch != -1
756
+ case ch
757
+ when -1
758
+ next
759
+ when KEY_DOWN
760
+ #$log.debug "insdie keyDOWN : #{ch}"
761
+ if !@selected
762
+ current_menu.fire
763
+ else
764
+ current_menu.handle_key ch
765
+ end
766
+
767
+ @selected = true
768
+ when KEY_ENTER, 10, 13, 32
769
+ @selected = true
770
+ #$log.debug " mb insdie ENTER : #{current_menu}"
771
+ ret = current_menu.handle_key ch
772
+ #$log.debug "ret = #{ret} mb insdie ENTER : #{current_menu}"
773
+ #break; ## 2008-12-29 18:00 This will close after firing
774
+ #anything
775
+ break if ret == :CLOSE
776
+ when KEY_UP
777
+ #$log.debug " mb insdie keyUPP : #{ch}"
778
+ current_menu.handle_key ch
779
+ when KEY_LEFT
780
+ #$log.debug " mb insdie KEYLEFT : #{ch}"
781
+ ret = current_menu.handle_key ch
782
+ prev_menu if ret == :UNHANDLED
783
+ #display_items if @selected
784
+ when KEY_RIGHT
785
+ #$log.debug " mb insdie KEYRIGHT : #{ch}"
786
+ ret = current_menu.handle_key ch
787
+ next_menu if ret == :UNHANDLED
788
+ when ?\C-g.getbyte(0) # abort
789
+ throw :menubarclose
790
+ else
791
+ #$log.debug " mb insdie ELSE : #{ch}"
792
+ ret = current_menu.handle_key ch
793
+ if ret == :UNHANDLED
794
+ Ncurses.beep
795
+ else
796
+ break # we handled a menu action, close menubar (THIS WORKS FOR MNEMONICS ONLY and always)
797
+ end
798
+ end
799
+ Ncurses::Panel.update_panels();
800
+ Ncurses.doupdate();
801
+
802
+ @window.wrefresh
803
+ end
804
+ end # catch
805
+ ensure
806
+ #ensure is required becos one can throw a :close
807
+ $log.debug " DESTROY IN ENSURE"
808
+ current_menu.clear_menus #@@menus = [] # added 2009-01-23 13:21
809
+ @repaint_required = false
810
+ destroy # Note that we destroy the menu bar upon exit
811
+ end
812
+ end
813
+ def current_menu
814
+ @items[@active_index]
815
+ end
816
+ # called by set_menu_bar in widget.rb (class Form).
817
+ def toggle
818
+ # added keeping it visible, 2011-10-7 being tested in dbdemo
819
+ if @keep_visible
820
+ init_vars
821
+ show
822
+ @items[0].highlight
823
+ @window.ungetch(KEY_DOWN)
824
+ return
825
+ end
826
+ #@items.each { |i| $log.debug " ITEM DDD : #{i.text}" }
827
+ @visible = !@visible
828
+ if !@visible
829
+ hide
830
+ else
831
+ init_vars
832
+ show
833
+ end
834
+ end
835
+ def hide
836
+ @visible = false
837
+ @window.hide if !@window.nil? # seems to cause auto-firing when we resume toggle 2011-09-26
838
+ end
839
+ def show
840
+ @visible = true
841
+ if @window.nil?
842
+ repaint # XXX FIXME
843
+ else
844
+ @window.show
845
+ end
846
+ end
847
+ ## menubar
848
+ # TODO: check for menu to be flush right (only for last one).
849
+ # TODO: repaint only if needed
850
+ def repaint
851
+ return if !@visible
852
+ return unless @repaint_required
853
+ @repaint_required = false
854
+ @color_pair = get_color($reversecolor, @color, @bgcolor)
855
+ @window ||= create_window_menubar
856
+ #@window.printstring( 0, 0, "%-*s" % [@cols," "], @color_pair) # this becomes blank in some terms
857
+ c = 1; r = 0;
858
+ @items.each do |item|
859
+ item.row = r; item.col = c; item.coffset = c; item.parent = self
860
+ item.color = @color
861
+ item.bgcolor = @bgcolor
862
+ @window.printstring( r, c, " %s " % item.text, @color_pair)
863
+ # 2011-09-26 V1.3.1 quick dirty highlighting of first menu on menubar
864
+ # on opening since calling highlight was giving bug in parent.width
865
+ #if c == 1
866
+ #att = Ncurses::A_REVERSE
867
+ #@window.mvchgat(y=r, x=c+1, item.text.length+1, att, @color_pair, nil)
868
+ #end
869
+ c += (item.text.length + 2)
870
+ end
871
+ #@items[0].on_enter # 2011-09-25 V1.3.1 caused issues when toggling, first item fired on DOWN
872
+ @items[0].highlight unless @keep_visible # 2011-09-26 V1.3.1 fixed to take both cases into account
873
+ @window.wrefresh
874
+ end
875
+ def create_window_menubar
876
+ @layout = { :height => 1, :width => 0, :top => 0, :left => 0 }
877
+ @win = VER::Window.new(@layout)
878
+ @window = @win
879
+ att = get_attrib @attr
880
+ @win.bkgd(Ncurses.COLOR_PAIR(5)); # <---- FIXME
881
+ len = @window.width
882
+ len = Ncurses.COLS-0 if len == 0
883
+ # print a bar across the screen , which hopefully will not go blank in some terms
884
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | att)
885
+ @window.mvhline(0, 0, 1, len)
886
+ @window.attroff(Ncurses.COLOR_PAIR(@color_pair) | att)
887
+ @panel = @win.panel
888
+ return @window
889
+ end
890
+ def destroy
891
+ $log.debug "DESTRY menubar "
892
+
893
+ # when we close, but keep visible, we don't want menu to still be hightlighted
894
+ # added on 2011-12-12
895
+ menu = @items[@active_index]
896
+ menu.on_leave # hide its window, if open
897
+
898
+ @items.each do |item|
899
+ item.destroy
900
+ end
901
+ # TODO the current menu should not be highlighted
902
+ return if @keep_visible
903
+ @visible = false
904
+ panel = @window.panel
905
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
906
+ @window.delwin if !@window.nil?
907
+ @window = nil
908
+ end
909
+ end # menubar
910
+
911
+ class CheckBoxMenuItem < MenuItem
912
+ attr_reader :checkbox
913
+ def initialize text, mnemonic=nil, &block
914
+ @checkbox = CheckBox.new nil
915
+ @checkbox.text text
916
+ super
917
+ end
918
+ def onvalue
919
+ @checkbox.onvalue onvalue
920
+ end
921
+ def offvalue
922
+ @checkbox.onvalue offvalue
923
+ end
924
+ def text=(t) # stack level too deep if no = .????
925
+ @checkbox.text t
926
+ end
927
+ def to_s
928
+ " #{text} "
929
+ end
930
+ def getvalue
931
+ checkbox.getvalue
932
+ end
933
+ def getvalue_for_paint
934
+ "|%-*s|" % [@width, checkbox.getvalue_for_paint]
935
+ end
936
+ def fire
937
+ checkbox.toggle
938
+ super
939
+ repaint
940
+ highlight true
941
+ end
942
+ def repaint # checkbox
943
+ # FIXME need @color_pair here
944
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
945
+ @parent.window.printstring( row, 0, getvalue_for_paint, @color_pair)
946
+ parent.window.wrefresh
947
+ end
948
+ def method_missing(sym, *args)
949
+ if checkbox.respond_to? sym
950
+ #$log.debug("calling CHECKBOXMENU #{sym} called #{args[0]}")
951
+ checkbox.send(sym, args)
952
+ else
953
+ $log.error("ERROR CHECKBOXMENU #{sym} called")
954
+ end
955
+ end
956
+ end # class
957
+
958
+ end # modul