rbhex-core 1.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 (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