canis 0.0.4

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/CHANGES +52 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +24 -0
  7. data/Rakefile +2 -0
  8. data/canis.gemspec +25 -0
  9. data/examples/alpmenu.rb +46 -0
  10. data/examples/app.sample +19 -0
  11. data/examples/appemail.rb +191 -0
  12. data/examples/atree.rb +105 -0
  13. data/examples/bline.rb +181 -0
  14. data/examples/common/devel.rb +319 -0
  15. data/examples/common/file.rb +93 -0
  16. data/examples/data/README.markdown +9 -0
  17. data/examples/data/brew.txt +38 -0
  18. data/examples/data/color.2 +37 -0
  19. data/examples/data/gemlist.txt +59 -0
  20. data/examples/data/lotr.txt +12 -0
  21. data/examples/data/ports.txt +136 -0
  22. data/examples/data/table.txt +37 -0
  23. data/examples/data/tasks.csv +88 -0
  24. data/examples/data/tasks.txt +27 -0
  25. data/examples/data/todo.txt +16 -0
  26. data/examples/data/todocsv.csv +28 -0
  27. data/examples/data/unix1.txt +21 -0
  28. data/examples/data/unix2.txt +11 -0
  29. data/examples/dbdemo.rb +506 -0
  30. data/examples/dirtree.rb +177 -0
  31. data/examples/newtabbedwindow.rb +100 -0
  32. data/examples/newtesttabp.rb +92 -0
  33. data/examples/tabular.rb +212 -0
  34. data/examples/tasks.rb +179 -0
  35. data/examples/term2.rb +88 -0
  36. data/examples/testbuttons.rb +307 -0
  37. data/examples/testcombo.rb +102 -0
  38. data/examples/testdb.rb +182 -0
  39. data/examples/testfields.rb +208 -0
  40. data/examples/testflowlayout.rb +43 -0
  41. data/examples/testkeypress.rb +98 -0
  42. data/examples/testlistbox.rb +187 -0
  43. data/examples/testlistbox1.rb +199 -0
  44. data/examples/testmessagebox.rb +144 -0
  45. data/examples/testprogress.rb +116 -0
  46. data/examples/testree.rb +107 -0
  47. data/examples/testsplitlayout.rb +53 -0
  48. data/examples/testsplitlayout1.rb +49 -0
  49. data/examples/teststacklayout.rb +48 -0
  50. data/examples/testwsshortcuts.rb +68 -0
  51. data/examples/testwsshortcuts2.rb +129 -0
  52. data/lib/canis.rb +16 -0
  53. data/lib/canis/core/docs/index.txt +104 -0
  54. data/lib/canis/core/docs/list.txt +16 -0
  55. data/lib/canis/core/docs/style_help.yml +34 -0
  56. data/lib/canis/core/docs/tabbedpane.txt +15 -0
  57. data/lib/canis/core/docs/table.txt +31 -0
  58. data/lib/canis/core/docs/textpad.txt +48 -0
  59. data/lib/canis/core/docs/tree.txt +23 -0
  60. data/lib/canis/core/include/.DS_Store +0 -0
  61. data/lib/canis/core/include/action.rb +83 -0
  62. data/lib/canis/core/include/actionmanager.rb +49 -0
  63. data/lib/canis/core/include/appmethods.rb +179 -0
  64. data/lib/canis/core/include/bordertitle.rb +49 -0
  65. data/lib/canis/core/include/canisparser.rb +100 -0
  66. data/lib/canis/core/include/colorparser.rb +437 -0
  67. data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
  68. data/lib/canis/core/include/io.rb +320 -0
  69. data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
  70. data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
  71. data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
  72. data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
  73. data/lib/canis/core/include/listbindings.rb +89 -0
  74. data/lib/canis/core/include/listeditable.rb +319 -0
  75. data/lib/canis/core/include/listoperations.rb +61 -0
  76. data/lib/canis/core/include/listselectionmodel.rb +388 -0
  77. data/lib/canis/core/include/multibuffer.rb +173 -0
  78. data/lib/canis/core/include/ractionevent.rb +73 -0
  79. data/lib/canis/core/include/rchangeevent.rb +27 -0
  80. data/lib/canis/core/include/rhistory.rb +95 -0
  81. data/lib/canis/core/include/rinputdataevent.rb +47 -0
  82. data/lib/canis/core/include/textdocument.rb +111 -0
  83. data/lib/canis/core/include/vieditable.rb +175 -0
  84. data/lib/canis/core/include/widgetmenu.rb +66 -0
  85. data/lib/canis/core/system/colormap.rb +165 -0
  86. data/lib/canis/core/system/keydefs.rb +32 -0
  87. data/lib/canis/core/system/ncurses.rb +237 -0
  88. data/lib/canis/core/system/panel.rb +129 -0
  89. data/lib/canis/core/system/window.rb +1081 -0
  90. data/lib/canis/core/util/ansiparser.rb +119 -0
  91. data/lib/canis/core/util/app.rb +696 -0
  92. data/lib/canis/core/util/basestack.rb +412 -0
  93. data/lib/canis/core/util/defaultcolorparser.rb +84 -0
  94. data/lib/canis/core/util/extras/README +5 -0
  95. data/lib/canis/core/util/extras/bottomline.rb +1815 -0
  96. data/lib/canis/core/util/extras/padreader.rb +192 -0
  97. data/lib/canis/core/util/focusmanager.rb +31 -0
  98. data/lib/canis/core/util/helpmanager.rb +160 -0
  99. data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
  100. data/lib/canis/core/util/promptmenu.rb +235 -0
  101. data/lib/canis/core/util/rcommandwindow.rb +933 -0
  102. data/lib/canis/core/util/rdialogs.rb +520 -0
  103. data/lib/canis/core/util/textutils.rb +74 -0
  104. data/lib/canis/core/util/viewer.rb +238 -0
  105. data/lib/canis/core/util/widgetshortcuts.rb +508 -0
  106. data/lib/canis/core/widgets/applicationheader.rb +103 -0
  107. data/lib/canis/core/widgets/box.rb +58 -0
  108. data/lib/canis/core/widgets/divider.rb +310 -0
  109. data/lib/canis/core/widgets/extras/README.md +12 -0
  110. data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
  111. data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
  112. data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
  113. data/lib/canis/core/widgets/listbox.rb +326 -0
  114. data/lib/canis/core/widgets/listfooter.rb +86 -0
  115. data/lib/canis/core/widgets/rcombo.rb +210 -0
  116. data/lib/canis/core/widgets/rcontainer.rb +415 -0
  117. data/lib/canis/core/widgets/rlink.rb +30 -0
  118. data/lib/canis/core/widgets/rmenu.rb +970 -0
  119. data/lib/canis/core/widgets/rmenulink.rb +30 -0
  120. data/lib/canis/core/widgets/rmessagebox.rb +400 -0
  121. data/lib/canis/core/widgets/rprogress.rb +118 -0
  122. data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
  123. data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
  124. data/lib/canis/core/widgets/rwidget.rb +3634 -0
  125. data/lib/canis/core/widgets/scrollbar.rb +147 -0
  126. data/lib/canis/core/widgets/statusline.rb +113 -0
  127. data/lib/canis/core/widgets/table.rb +1072 -0
  128. data/lib/canis/core/widgets/tabular.rb +264 -0
  129. data/lib/canis/core/widgets/textpad.rb +1674 -0
  130. data/lib/canis/core/widgets/tree.rb +690 -0
  131. data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
  132. data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
  133. data/lib/canis/version.rb +3 -0
  134. metadata +229 -0
@@ -0,0 +1,30 @@
1
+ require 'canis'
2
+ ##
3
+ module Canis
4
+ class Link < Button
5
+ dsl_property :description
6
+
7
+
8
+ def initialize form, config={}, &block
9
+ super
10
+ @text_offset = 0
11
+ # haha we've never done this, pin the cursor up on 0,0
12
+ @col_offset = -1
13
+ # this won't be triggered since the shortcut does not set menmo
14
+ # unless form is there.
15
+ # Sometimes the mnemonic is not in text, such as '?'
16
+ if @mnemonic
17
+ form.bind_key(@mnemonic.downcase, self){ self.fire }
18
+ end
19
+ @width = config[:width]
20
+ end
21
+ def fire
22
+ super
23
+ self.focus
24
+ end
25
+ def getvalue_for_paint
26
+ getvalue()
27
+ end
28
+ ##
29
+ end # class
30
+ end # module
@@ -0,0 +1,970 @@
1
+ =begin
2
+ * Name: menu and related classes
3
+ * Description
4
+ * Author: jkepler
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 'canis'
30
+
31
+ include Canis
32
+ module Canis
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? Canis::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? Canis::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? Canis::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? Canis::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? Canis::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? Canis::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 = Canis::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
+ #2014-05-12 - 20:53 next 3 replaced with destroy since destroy refreshes root window.
550
+ #panel = @window.panel
551
+ #Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
552
+ #@window.delwin if !@window.nil?
553
+ @window.destroy
554
+ @items.each do |item|
555
+ #next if item == :SEPARATOR
556
+ item.destroy
557
+ end
558
+ @window = nil
559
+ end
560
+ # menu LEFT, RIGHT, DOWN, UP, ENTER
561
+ # item could be menuitem or another menu
562
+ #
563
+ def handle_key ch
564
+ if !@current_menu.empty?
565
+ cmenu = @current_menu.last
566
+ else
567
+ cmenu = self
568
+ end
569
+ if !@@menus.empty?
570
+ cmenu = @@menus.last
571
+ else
572
+ cmenu = self
573
+ end
574
+ case ch
575
+ when KEY_DOWN
576
+ cmenu.select_next_item
577
+ #return cmenu.fire # XXX 2010-10-16 21:39 trying out
578
+ if cmenu.is_a? Canis::Menu
579
+ #alert "is a menu" # this gets triggered even when we are on items
580
+ end
581
+ when KEY_UP
582
+ cmenu.select_prev_item
583
+ when KEY_ENTER, 10, 13, 32 # added 32 2008-11-27 23:50
584
+ return cmenu.fire
585
+ when KEY_LEFT
586
+ if cmenu.parent.is_a? Canis::Menu
587
+ #$log.debug "LEFT IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
588
+ #$log.debug "left IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
589
+ end
590
+ ret = cmenu.select_left_item # 2011-09-24 V1.3.1 attempt to goto left item if columns
591
+ if ret == :UNHANDLED
592
+ if cmenu.parent.is_a? Canis::MenuBar #and !cmenu.parent.current_menu.empty?
593
+ #$log.debug " ABOU TO DESTROY DUE TO LEFT"
594
+ cmenu.current_menu.pop
595
+ @@menus.pop ## NEW
596
+ cmenu.destroy
597
+ return :UNHANDLED
598
+ end
599
+ # LEFT on a menu list allows me to close and return to higher level
600
+ if cmenu.parent.is_a? Canis::Menu #and !cmenu.parent.current_menu.empty?
601
+ #$log.debug " ABOU TO DESTROY DUE TO LEFT"
602
+ cmenu.current_menu.pop
603
+ @@menus.pop ## NEW
604
+ cmenu.destroy
605
+ #return :UNHANDLED
606
+ end
607
+ end
608
+ when KEY_RIGHT
609
+ $log.debug "RIGHTIN MENU : #{text} "
610
+ if cmenu.active_index
611
+ if cmenu.items[cmenu.active_index].is_a? Canis::Menu
612
+ #alert "could fire here cmenu: #{cmenu.text}, par: #{cmenu.parent.text} "
613
+ cmenu.fire
614
+ return
615
+ #$log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
616
+ #$log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
617
+ end
618
+ end
619
+ # This introduces a bug if no open items
620
+ ret = cmenu.select_right_item # 2011-09-24 V1.3.1 attempt to goto right item if columns
621
+ #alert "attempting to select right #{ret} "
622
+ if ret == :UNHANDLED
623
+ #if cmenu.parent.is_a? Canis::Menu and !cmenu.parent.current_menu.empty?
624
+ if cmenu.parent.is_a? Canis::MenuBar #and !cmenu.current_menu.empty?
625
+ $log.debug " ABOU TO DESTROY DUE TO RIGHT"
626
+ cmenu.current_menu.pop
627
+ @@menus.pop
628
+ cmenu.destroy
629
+ return :UNHANDLED
630
+ end
631
+ end
632
+ else
633
+ ret = check_mnemonics cmenu, ch
634
+ return ret
635
+ end
636
+ end
637
+ ##
638
+ # checks given key against current menu's items and fires key if
639
+ # added on 2008-11-27 12:07
640
+ def check_mnemonics cmenu, ch
641
+ # $log.debug "inside check_mnemonics #{ch}"
642
+ key = ch.chr.downcase rescue ""
643
+ cmenu.items.each do |item|
644
+ next if !item.respond_to? :mnemonic or item.mnemonic.nil?
645
+ # $log.debug "inside check_mnemonics #{item.mnemonic}"
646
+ if key == item.mnemonic.downcase && item.enabled # 2010-09-11 00:03 enabled
647
+ ret = item.fire
648
+ return ret #0 2009-01-23 00:45
649
+ end
650
+ end
651
+ return :UNHANDLED
652
+ end
653
+ ## menu
654
+ def show # menu.show
655
+ #$log.debug "show (menu) : #{@text} "
656
+ if @window.nil?
657
+ create_window #@col+@width
658
+ end
659
+ @window.show
660
+ select_item 0
661
+ end
662
+ end
663
+ ##
664
+ # An application related menubar.
665
+ # Currently, I am adding this to a form. But should this not be application specific ?
666
+ # It should popup no matter which window you are on ?? XXX
667
+ class MenuBar
668
+ attr_reader :items
669
+ attr_reader :window
670
+ attr_reader :panel
671
+ attr_reader :selected
672
+ attr_reader :text # temp 2011-09-24 V1.3.1
673
+ attr_accessor :visible
674
+ attr_accessor :active_index
675
+ attr_accessor :state # normal, selected, highlighted
676
+ attr_accessor :toggle_key # key used to popup, should be set prior to attaching to form
677
+ attr_accessor :color, :bgcolor # 2011-09-25 V1.3.1
678
+ attr_accessor :_object_created # 2011-10-7 if visible then Form will call this
679
+ def initialize &block
680
+ @window = nil
681
+ @text = "menubar"
682
+ @items = []
683
+ init_vars
684
+ @visible = false
685
+ @cols = Ncurses.COLS-1
686
+ instance_eval &block if block_given?
687
+ end
688
+ def init_vars
689
+ @active_index = 0
690
+ @repaint_required = true
691
+ end
692
+ def focusable
693
+ false
694
+ end
695
+ alias :focusable? focusable
696
+ # add a precreated menu
697
+ def add menu
698
+ #$log.debug "YYYY inside MB: add #{menu.text} "
699
+ @items << menu
700
+ return self
701
+ end
702
+ alias :<< :add
703
+
704
+ # add a menu through the block, this would happen through instance eval
705
+ # 2010-09-10 12:07 added while simplifying the interface
706
+ # this calls add so you get the MB back, not a ref to the menu created NOTE
707
+ def menu text, &block
708
+ #$log.debug "YYYY inside MB: menu text #{text} "
709
+ m = Menu.new text, &block
710
+ m.color = @color
711
+ m.bgcolor = @bgcolor
712
+ add m
713
+ return m
714
+ end
715
+ def next_menu
716
+ #$log.debug "next meu: #{@active_index} "
717
+ if @active_index < @items.length-1
718
+ set_menu @active_index + 1
719
+ else
720
+ set_menu 0
721
+ end
722
+ end
723
+ def prev_menu
724
+ #$log.debug "prev meu: #{@active_index} "
725
+ if @active_index > 0
726
+ set_menu @active_index-1
727
+ else
728
+ set_menu @items.length-1
729
+ end
730
+ end
731
+ def set_menu index
732
+ #$log.debug "set meu: #{@active_index} #{index}"
733
+ menu = @items[@active_index]
734
+ menu.on_leave # hide its window, if open
735
+ @active_index = index
736
+ menu = @items[@active_index]
737
+ menu.on_enter #display window, if previous was displayed
738
+ @window.wmove menu.row, menu.col
739
+ # menu.show
740
+ # menu.window.wrefresh # XXX we need this
741
+ end
742
+
743
+ def keep_visible flag=nil
744
+ return @keep_visible unless flag
745
+ @keep_visible = flag
746
+ @visible = flag
747
+ self
748
+ end
749
+ # menubar LEFT, RIGHT, DOWN
750
+ def handle_keys
751
+ @selected = false
752
+ @repaint_required = true # added 2011-12-12 otherwise keeps repainting and you see a flicker
753
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
754
+ set_menu 0
755
+ begin
756
+ catch(:menubarclose) do
757
+ while((ch = @window.getchar()) != @toggle_key )
758
+ #$log.debug "menuubar inside handle_keys : #{ch}" if ch != -1
759
+ case ch
760
+ when -1
761
+ next
762
+ when KEY_DOWN
763
+ #$log.debug "insdie keyDOWN : #{ch}"
764
+ if !@selected
765
+ current_menu.fire
766
+ else
767
+ current_menu.handle_key ch
768
+ end
769
+
770
+ @selected = true
771
+ when KEY_ENTER, 10, 13, 32
772
+ @selected = true
773
+ #$log.debug " mb insdie ENTER : #{current_menu}"
774
+ ret = current_menu.handle_key ch
775
+ #$log.debug "ret = #{ret} mb insdie ENTER : #{current_menu}"
776
+ #break; ## 2008-12-29 18:00 This will close after firing
777
+ #anything
778
+ break if ret == :CLOSE
779
+ when KEY_UP
780
+ #$log.debug " mb insdie keyUPP : #{ch}"
781
+ current_menu.handle_key ch
782
+ when KEY_LEFT
783
+ #$log.debug " mb insdie KEYLEFT : #{ch}"
784
+ ret = current_menu.handle_key ch
785
+ prev_menu if ret == :UNHANDLED
786
+ #display_items if @selected
787
+ when KEY_RIGHT
788
+ #$log.debug " mb insdie KEYRIGHT : #{ch}"
789
+ ret = current_menu.handle_key ch
790
+ next_menu if ret == :UNHANDLED
791
+ when ?\C-g.getbyte(0) # abort
792
+ throw :menubarclose
793
+ else
794
+ #$log.debug " mb insdie ELSE : #{ch}"
795
+ ret = current_menu.handle_key ch
796
+ if ret == :UNHANDLED
797
+ Ncurses.beep
798
+ else
799
+ break # we handled a menu action, close menubar (THIS WORKS FOR MNEMONICS ONLY and always)
800
+ end
801
+ end
802
+ Ncurses::Panel.update_panels();
803
+ Ncurses.doupdate();
804
+
805
+ @window.wrefresh
806
+ end
807
+ end # catch
808
+ ensure
809
+ #ensure is required becos one can throw a :close
810
+ $log.debug " DESTROY IN ENSURE"
811
+ current_menu.clear_menus #@@menus = [] # added 2009-01-23 13:21
812
+ @repaint_required = false
813
+ destroy # Note that we destroy the menu bar upon exit
814
+ end
815
+ end
816
+ def current_menu
817
+ @items[@active_index]
818
+ end
819
+ # called by set_menu_bar in widget.rb (class Form).
820
+ def toggle
821
+ # added keeping it visible, 2011-10-7 being tested in dbdemo
822
+ if @keep_visible
823
+ init_vars
824
+ show
825
+ @items[0].highlight
826
+ @window.ungetch(KEY_DOWN)
827
+ return
828
+ end
829
+ #@items.each { |i| $log.debug " ITEM DDD : #{i.text}" }
830
+ @visible = !@visible
831
+ if !@visible
832
+ hide
833
+ else
834
+ init_vars
835
+ show
836
+ end
837
+ end
838
+ def hide
839
+ @visible = false
840
+ @window.hide if !@window.nil? # seems to cause auto-firing when we resume toggle 2011-09-26
841
+ end
842
+ def show
843
+ @visible = true
844
+ if @window.nil?
845
+ repaint # XXX FIXME
846
+ else
847
+ @window.show
848
+ end
849
+ end
850
+ ## menubar
851
+ # TODO: check for menu to be flush right (only for last one).
852
+ # TODO: repaint only if needed
853
+ def repaint
854
+ return if !@visible
855
+ return unless @repaint_required
856
+ @repaint_required = false
857
+ @color_pair = get_color($reversecolor, @color, @bgcolor)
858
+ @window ||= create_window_menubar
859
+ #@window.printstring( 0, 0, "%-*s" % [@cols," "], @color_pair) # this becomes blank in some terms
860
+ c = 1; r = 0;
861
+ @items.each do |item|
862
+ item.row = r; item.col = c; item.coffset = c; item.parent = self
863
+ item.color = @color
864
+ item.bgcolor = @bgcolor
865
+ @window.printstring( r, c, " %s " % item.text, @color_pair)
866
+ # 2011-09-26 V1.3.1 quick dirty highlighting of first menu on menubar
867
+ # on opening since calling highlight was giving bug in parent.width
868
+ #if c == 1
869
+ #att = Ncurses::A_REVERSE
870
+ #@window.mvchgat(y=r, x=c+1, item.text.length+1, att, @color_pair, nil)
871
+ #end
872
+ c += (item.text.length + 2)
873
+ end
874
+ #@items[0].on_enter # 2011-09-25 V1.3.1 caused issues when toggling, first item fired on DOWN
875
+ @items[0].highlight unless @keep_visible # 2011-09-26 V1.3.1 fixed to take both cases into account
876
+ @window.wrefresh
877
+ end
878
+ def create_window_menubar
879
+ @layout = { :height => 1, :width => 0, :top => 0, :left => 0 }
880
+ @win = Canis::Window.new(@layout)
881
+ @window = @win
882
+ att = get_attrib @attr
883
+ @win.bkgd(Ncurses.COLOR_PAIR(5)); # <---- FIXME
884
+ len = @window.width
885
+ len = Ncurses.COLS-0 if len == 0
886
+ # print a bar across the screen , which hopefully will not go blank in some terms
887
+ @window.attron(Ncurses.COLOR_PAIR(@color_pair) | att)
888
+ @window.mvhline(0, 0, 1, len)
889
+ @window.attroff(Ncurses.COLOR_PAIR(@color_pair) | att)
890
+ @panel = @win.panel
891
+ return @window
892
+ end
893
+ def destroy
894
+ $log.debug "DESTRY menubar #{@keep_visible} "
895
+
896
+ # when we close, but keep visible, we don't want menu to still be hightlighted
897
+ # added on 2011-12-12
898
+ menu = @items[@active_index]
899
+ menu.on_leave # hide its window, if open
900
+
901
+ @items.each do |item|
902
+ item.destroy
903
+ end
904
+ $log.debug " DESTROY finished with items "
905
+ # TODO the current menu should not be highlighted
906
+ # FIXME even here i think underlying windows need to be repainted.
907
+ #return if @keep_visible
908
+ if @keep_visible
909
+ Window.refresh_all
910
+ return
911
+ end
912
+ @visible = false
913
+ # replacing next 3 lines with destroy. 2014-05-12 - 17:07 CANIS
914
+ #panel = @window.panel
915
+ #Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
916
+ #@window.delwin if !@window.nil?
917
+ $log.debug " CALLING WINDOW DESTROY from menubar"
918
+ @window.destroy
919
+ @window = nil
920
+ end
921
+ end # menubar
922
+
923
+ class CheckBoxMenuItem < MenuItem
924
+ attr_reader :checkbox
925
+ def initialize text, mnemonic=nil, &block
926
+ @checkbox = CheckBox.new nil
927
+ @checkbox.text text
928
+ super
929
+ end
930
+ def onvalue
931
+ @checkbox.onvalue onvalue
932
+ end
933
+ def offvalue
934
+ @checkbox.onvalue offvalue
935
+ end
936
+ def text=(t) # stack level too deep if no = .????
937
+ @checkbox.text t
938
+ end
939
+ def to_s
940
+ " #{text} "
941
+ end
942
+ def getvalue
943
+ checkbox.getvalue
944
+ end
945
+ def getvalue_for_paint
946
+ "|%-*s|" % [@width, checkbox.getvalue_for_paint]
947
+ end
948
+ def fire
949
+ checkbox.toggle
950
+ super
951
+ repaint
952
+ highlight true
953
+ end
954
+ def repaint # checkbox
955
+ # FIXME need @color_pair here
956
+ @color_pair ||= get_color($reversecolor, @color, @bgcolor)
957
+ @parent.window.printstring( row, 0, getvalue_for_paint, @color_pair)
958
+ parent.window.wrefresh
959
+ end
960
+ def method_missing(sym, *args)
961
+ if checkbox.respond_to? sym
962
+ #$log.debug("calling CHECKBOXMENU #{sym} called #{args[0]}")
963
+ checkbox.send(sym, args)
964
+ else
965
+ $log.error("ERROR CHECKBOXMENU #{sym} called")
966
+ end
967
+ end
968
+ end # class
969
+
970
+ end # modul