rbcurse-extras 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/README.md +75 -0
  2. data/VERSION +1 -0
  3. data/examples/data/list.txt +300 -0
  4. data/examples/data/lotr.txt +12 -0
  5. data/examples/data/table.txt +36 -0
  6. data/examples/data/tasks.txt +27 -0
  7. data/examples/data/unix1.txt +21 -0
  8. data/examples/inc/qdfilechooser.rb +70 -0
  9. data/examples/inc/rfe_renderer.rb +121 -0
  10. data/examples/newtabbedwindow.rb +100 -0
  11. data/examples/rfe.rb +1236 -0
  12. data/examples/test2.rb +670 -0
  13. data/examples/testeditlist.rb +78 -0
  14. data/examples/testtable.rb +270 -0
  15. data/examples/testvimsplit.rb +141 -0
  16. data/lib/rbcurse/extras/include/celleditor.rb +112 -0
  17. data/lib/rbcurse/extras/include/checkboxcellrenderer.rb +57 -0
  18. data/lib/rbcurse/extras/include/comboboxcellrenderer.rb +30 -0
  19. data/lib/rbcurse/extras/include/defaultlistselectionmodel.rb +79 -0
  20. data/lib/rbcurse/extras/include/listkeys.rb +37 -0
  21. data/lib/rbcurse/extras/include/listselectable.rb +144 -0
  22. data/lib/rbcurse/extras/include/tableextended.rb +40 -0
  23. data/lib/rbcurse/extras/widgets/horizlist.rb +203 -0
  24. data/lib/rbcurse/extras/widgets/menutree.rb +63 -0
  25. data/lib/rbcurse/extras/widgets/multilinelabel.rb +142 -0
  26. data/lib/rbcurse/extras/widgets/rcomboedit.rb +256 -0
  27. data/lib/rbcurse/extras/widgets/rlink.rb.moved +27 -0
  28. data/lib/rbcurse/extras/widgets/rlistbox.rb +1247 -0
  29. data/lib/rbcurse/extras/widgets/rmenulink.rb.moved +21 -0
  30. data/lib/rbcurse/extras/widgets/rmulticontainer.rb +304 -0
  31. data/lib/rbcurse/extras/widgets/rmultisplit.rb +722 -0
  32. data/lib/rbcurse/extras/widgets/rmultitextview.rb +306 -0
  33. data/lib/rbcurse/extras/widgets/rpopupmenu.rb +755 -0
  34. data/lib/rbcurse/extras/widgets/rtable.rb +1758 -0
  35. data/lib/rbcurse/extras/widgets/rvimsplit.rb +800 -0
  36. data/lib/rbcurse/extras/widgets/table/tablecellrenderer.rb +86 -0
  37. data/lib/rbcurse/extras/widgets/table/tabledatecellrenderer.rb +98 -0
  38. metadata +94 -0
@@ -0,0 +1,306 @@
1
+ =begin
2
+ * Name: MultiTextView
3
+ * Description View text in this widget for multiple files
4
+ * This differs from multicontainer in that since all components are textviews, so they
5
+ * are all gauranteed to be non-editable and thus we can map many more keys.
6
+ * MultiContainer can only map Ctrl and Alt (Meta) keys.
7
+ * Author: rkumar (arunachalesha)
8
+ * file created 2010-03-11 08:05
9
+ --------
10
+ * License:
11
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
12
+
13
+ =end
14
+ require 'logger'
15
+ require 'rbcurse'
16
+ require 'rbcurse/core/widgets/rtextview'
17
+ require 'rbcurse/core/include/listscrollable'
18
+
19
+ #include Ncurses # FFI 2011-09-8
20
+ include RubyCurses
21
+ module RubyCurses
22
+ extend self
23
+
24
+ ##
25
+ # A viewable read only box. Can scroll.
26
+ # Extends TextView with ability to load more than one file or content
27
+ # and switch between files (buffers).
28
+ # NOTE: ideally, i should be able to dynamically add this functionality to either Textview
29
+ # or TextArea or even ListBox or Table someday. Should then be a Module rather than a class.
30
+ class MultiTextView < TextView
31
+ include ListScrollable
32
+
33
+ def initialize form = nil, config={}, &block
34
+
35
+ super
36
+ @bmanager = BufferManager.new self
37
+
38
+ end
39
+ def init_vars
40
+ super
41
+ bind_key(?:, :buffer_menu)
42
+ bind_key(?e, :file_edit)
43
+ bind_key([?\C-x, ?f], :file_edit)
44
+ bind_key([?\C-x, ?k], :buffer_delete)
45
+ bind_key([?\C-x, ?\C-b], :buffers_list)
46
+ # easily cycle using p. n is used for next search.
47
+ bind_key(?p, :buffer_previous)
48
+ end
49
+ ## returns current buffer
50
+ # @return [RBuffer] current buffer
51
+ def current_buffer
52
+ @bmanager.current
53
+ end
54
+ ##
55
+ # send in a list
56
+ # e.g. set_content File.open("README.txt","r").readlines
57
+ # set wrap at time of passing :WRAP_NONE :WRAP_WORD
58
+ # @see add (add takes a title too which is required here)
59
+ def set_content list, wrap = :WRAP_NONE
60
+ ret = super
61
+ # buff = @bmanager.add_content @list
62
+ return ret
63
+ end
64
+ # multi-textview
65
+ def handle_key ch
66
+ # put list as current list and buffer too then super
67
+ #@current_buffer = @bmanager.current
68
+ #@list = @current_buffer.list
69
+ @buffer = @list[@current_index]
70
+ #@buffer = @bmanager.current
71
+ ret = super
72
+ # check for any keys not handled and check our own ones
73
+ return ret #
74
+ end
75
+ ## prompt user for a filename to read in
76
+ def getfilename prompt="Enter filename: ", maxlen=90
77
+ tabc = Proc.new {|str| Dir.glob(str +"*") }
78
+ config={}; config[:tab_completion] = tabc
79
+ #config[:default] = "defaulT"
80
+ $log.debug " inside getstr before call #{$error_message_row} + #{$error_message_col} "
81
+ #ret, str = rbgetstr(@form.window, @row+@height-1, @col+1, prompt, maxlen, config)
82
+ ret, str = rbgetstr(@form.window, $error_message_row, $error_message_col, prompt, maxlen, config)
83
+ $log.debug " rbgetstr returned #{ret} , #{str} "
84
+ return "" if ret != 0
85
+ return str
86
+ end
87
+ # this is just a test of the simple "most" menu
88
+ # can use this for next, prev, first, last, new, delete, overwrite etc
89
+ def buffer_menu
90
+ menu = PromptMenu.new self
91
+ menu.add(menu.create_mitem( 'e', "edit a file", "opened file ", :file_edit ))
92
+ menu.add(menu.create_mitem( 'o', "overwrite file", "opened a file ", :file_overwrite ))
93
+ menu.add(menu.create_mitem( 'l', "list buffers", "list buffers ", :buffers_list ))
94
+ item = menu.create_mitem( 'b', "Buffer Options", "Buffer Options" )
95
+ menu1 = PromptMenu.new( self, "Buffer Options")
96
+ menu1.add(menu1.create_mitem( 'n', "Next", "Switched to next buffer", :buffer_next ))
97
+ menu1.add(menu1.create_mitem( 'p', "Prev", "Switched to previous buffer", :buffer_previous ))
98
+ menu1.add(menu1.create_mitem( 'f', "First", "Switched to first buffer", :buffer_first ))
99
+ menu1.add(menu1.create_mitem( 'l', "Last", "Switched to last buffer", :buffer_last ))
100
+ menu1.add(menu1.create_mitem( 'd', "Delete", "Deleted buffer", :buffer_delete ))
101
+ item.action = menu1
102
+ menu.add(item)
103
+ # how do i know what's available. the application or window should know where to place
104
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor #, menu
105
+ end
106
+
107
+ %w[next previous first last].each do |pos|
108
+ eval(
109
+ "def _buffer_#{pos}
110
+ @current_buffer = @bmanager.#{pos}
111
+ set_current_buffer
112
+ end"
113
+ )
114
+ end
115
+
116
+ def buffer_next
117
+ perror "No other buffer" and return if @bmanager.size < 2
118
+
119
+ @current_buffer = @bmanager.next
120
+ set_current_buffer
121
+ end
122
+ def buffer_previous
123
+ perror "No other buffer" and return if @bmanager.size < 2
124
+
125
+ @current_buffer = @bmanager.previous
126
+ $log.debug " buffer_prev got #{@current_buffer} "
127
+ set_current_buffer
128
+ end
129
+ def buffer_first
130
+ @current_buffer = @bmanager.first
131
+ $log.debug " buffer_first got #{@current_buffer} "
132
+ set_current_buffer
133
+ end
134
+ def buffer_last
135
+ @current_buffer = @bmanager.last
136
+ $log.debug " buffer_last got #{@current_buffer} "
137
+ set_current_buffer
138
+ end
139
+ def buffer_delete
140
+ if @bmanager.size > 1
141
+ @bmanager.delete_at
142
+ @current_buffer = @bmanager.previous
143
+ set_current_buffer
144
+ else
145
+ perror "Only one buffer. Cannot delete."
146
+ end
147
+ end
148
+ def buffers_list
149
+ menu = PromptMenu.new self
150
+ @bmanager.each_with_index{ |b, ix|
151
+ aproc = Proc.new { buffer_at(ix) }
152
+ name = b.title
153
+ num = ix + 1
154
+ menu.add(menu.create_mitem( num.to_s, name, "Switched to buffer #{ix}", aproc ))
155
+ }
156
+ menu.display @form.window, $error_message_row, $error_message_col, $datacolor
157
+ end
158
+ # prompts user for filename and opens in buffer
159
+ # Like vim's :e
160
+ def file_edit
161
+ file = getfilename()
162
+ $log.debug " got file_edit: #{file} "
163
+ return if file == ""
164
+ add file, file
165
+ end
166
+ # load a file into the textview.
167
+ # This is the preferred method since it lets us add a title too
168
+ # Shucks, this misses wrap_style which the other one has
169
+ # @param [String] filename
170
+ def add file, title
171
+ begin
172
+ @current_buffer = @bmanager.add file, title
173
+ $log.debug " file edit got cb : #{@current_buffer} "
174
+ set_current_buffer
175
+ rescue => err
176
+ alert err.to_s
177
+ Ncurses.beep
178
+ return -1
179
+ end
180
+ end
181
+ def buffer_at index
182
+ @current_buffer = @bmanager.element_at index
183
+ $log.debug " buffer_last got #{@current_buffer} "
184
+ set_current_buffer
185
+ end
186
+ def set_current_buffer
187
+ @current_index = @current_buffer.current_index
188
+ @curpos = @current_buffer.curpos
189
+ @title = @current_buffer.title
190
+ @list = @current_buffer.list
191
+ end
192
+ def perror errmess
193
+ #@form.window.print_error_message errmess
194
+ alert errmess
195
+ end
196
+ end # class multitextview
197
+ ##
198
+ # Handles multiple buffers, navigation, maintenance etc
199
+ # Instantiated at startup of MultiTextView
200
+ #
201
+ class BufferManager
202
+ include Enumerable
203
+ def initialize source
204
+ @source = source
205
+ @buffers = [] # contains RBuffer
206
+ @counter = 0
207
+ # for each buffer i need to store data, current_index (row), curpos (col offset) and title (filename).
208
+ end
209
+ def element_at index
210
+ @buffers[index]
211
+ end
212
+ def each
213
+ @buffers.each {|k| yield(k)}
214
+ end
215
+ ##
216
+ # @return [RBuffer] current buffer/file
217
+ ##
218
+ def current
219
+ @buffers[@counter]
220
+ end
221
+ ##
222
+ # Would have liked to just return next buffer and not get lost in details of caller
223
+ #
224
+ # @return [RBuffer] next buffer/file
225
+ ##
226
+ def next
227
+ @counter += 1
228
+ @counter = 0 if @counter >= @buffers.size
229
+ @buffers[@counter]
230
+ end
231
+ ##
232
+ # @return [RBuffer] previous buffer/file
233
+ ##
234
+ def previous
235
+ $log.debug " previous bs: #{@buffers.size}, #{@counter} "
236
+ @counter -= 1
237
+ return last() if @counter < 0
238
+ $log.debug " previous ctr #{@counter} "
239
+ @buffers[@counter]
240
+ end
241
+ def first
242
+ @counter = 0
243
+ @buffers[@counter]
244
+ end
245
+ def last
246
+ @counter = @buffers.size - 1
247
+ @buffers[@counter]
248
+ end
249
+ ##
250
+ def delete_at index=@counter
251
+ @buffers.delete_at index
252
+ end
253
+ def delete_by_name name
254
+ @buffers.delete_if {|b| b.filename == name }
255
+ end
256
+ def insert filename, position, title=nil
257
+ # read up file
258
+ lines = File.open(filename,"r").readlines
259
+ $log.debug "multitextview loaded #{filename}, #{lines.size} lines " if $log.debug?
260
+ list = @source.set_content lines
261
+ # set new RBuffer
262
+ title = filename unless title
263
+ raise "invalid value for list, Should be an array #{list.class} " unless list.is_a? Array
264
+ anew = RBuffer.new(list, 0, 0, filename, title)
265
+ #@buffers << anew
266
+ @buffers.insert position, anew
267
+ @counter = position
268
+ return anew
269
+ end
270
+ def add filename, title=nil
271
+ insert filename, @buffers.size, title
272
+ end
273
+ def insert_content str, position, title="UNTitled"
274
+ case str
275
+ when String
276
+ # put str into list
277
+ @source.set_content str
278
+ list = @list
279
+ when Array
280
+ list = str
281
+ end
282
+ anew = RBuffer.new(list, 0, 0, "", title)
283
+ #@buffers << anew
284
+ @buffers.insert position, anew
285
+ @counter = position
286
+ return anew
287
+ end
288
+ # add content (array or str) to buffer list as a new buffer
289
+ def add_content str, title="UNtitled"
290
+ $log.debug " inside BUFFER MANAGER "
291
+ insert_content str, @buffers.size, title
292
+ end
293
+ def size
294
+ @buffers.size
295
+ end
296
+ alias :count :size
297
+ def index buff
298
+ return @buffers.index
299
+ end
300
+ end
301
+ RBuffer = Struct.new(:list, :current_index, :curpos, :filename, :title) do
302
+ def xxx
303
+ end
304
+ end
305
+
306
+ end # modul
@@ -0,0 +1,755 @@
1
+ =begin
2
+ * Name: rpopupmenu - this is based on the crappy menubar code and needs a rewrite.
3
+ * Description
4
+ * Author: rkumar
5
+ TODO
6
+ - Action: may have to listen to Action property changes so enabled, name etc change can be reflected
7
+ - menu bar : what to do if adding a menu, or option later.
8
+ we dnt show disabld options in a way that user can know its disabled
9
+ - separate file created on 2008-12-24 17:58
10
+
11
+ Mnemonic should highlight the row if its a menu.
12
+ NOTE : this program works but is one of the first programs and is untouched. It needs to be rewritten
13
+ since its quite crappy.
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
+ I am prefixing class names since they are same as those in rmenu and there's
18
+ some crashing happening when i first execcute rmenu in one app then rpopupmenu
19
+ in another.
20
+
21
+ --------
22
+ * Date: 2008-11-14 23:43
23
+ * License:
24
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
25
+
26
+ =end
27
+ require 'logger'
28
+ require 'rbcurse'
29
+ require 'rbcurse/core/include/action'
30
+
31
+ #include Ncurses # FFI 2011-09-8
32
+ include RubyCurses
33
+ module RubyCurses
34
+ extend self
35
+
36
+
37
+ class PMenuSeparator
38
+ attr_accessor :enabled
39
+ attr_accessor :parent
40
+ attr_accessor :row
41
+ attr_accessor :col
42
+ attr_accessor :width
43
+ def initialize
44
+ @enable = false
45
+ end
46
+ def repaint
47
+ @parent.window.printstring( @row, 0, "|%s|" % ("-"*@width), $reversecolor)
48
+ end
49
+ def destroy
50
+ end
51
+ def on_enter
52
+ end
53
+ def on_leave
54
+ end
55
+ def to_s
56
+ ""
57
+ end
58
+ end
59
+ ##
60
+ class PMenuItem
61
+ attr_accessor :parent
62
+ # attr_accessor :window
63
+ attr_accessor :row
64
+ attr_accessor :col
65
+ attr_accessor :width
66
+ attr_accessor :accelerator
67
+ attr_accessor :enabled
68
+ attr_accessor :mnemonic # changed reader to accessor
69
+ def initialize txt, mnemonic=nil, &block
70
+ @mnemonic = mnemonic
71
+ text txt
72
+ @enabled = true
73
+ instance_eval &block if block_given?
74
+ end
75
+ ##
76
+ # changed so ampersand can be mnemonic esp when action comes in
77
+ def text s
78
+ s = s.dup # since actions are being shared
79
+ if (( ix = s.index('&')) != nil)
80
+ s.slice!(ix,1)
81
+ #@underline = ix unless @form.nil? # this setting a fake underline in messageboxes
82
+ @mnemonic = s[ix,1]
83
+ end
84
+ @text = s
85
+ end
86
+ def to_s
87
+ "#{@text} #{@accelerator}"
88
+ end
89
+ def command *args, &block
90
+ $log.debug ">>>command : #{@text} "
91
+ @command = block if block_given?
92
+ @args = args
93
+ end
94
+ def on_enter
95
+ $log.debug ">>>on enter Pmenuitem : #{@text} #{@row} #{@width} "
96
+ highlight
97
+ end
98
+ def on_leave
99
+ $log.debug ">>>on leave Pmenuitem : #{@text} "
100
+ highlight false
101
+ end
102
+ ## XXX it could be a menu again
103
+ def fire
104
+ $log.debug ">>>fire Pmenuitem : #{@text} #{@command} "
105
+ @command.call self, *@args if !@command.nil?
106
+ @parent.clear_menus
107
+ return :CLOSE # added 2009-01-02 00:09 to close only actions, not submenus
108
+ end
109
+ def highlight tf=true
110
+ if tf
111
+ color = $datacolor
112
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
113
+ # above line did not work in vt100, 200 terminals, next works.
114
+ @parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_REVERSE, $reversecolor, nil)
115
+ else
116
+ repaint
117
+ end
118
+ @parent.window.wrefresh
119
+ end
120
+ def repaint # menuitem.repaint
121
+ if @parent.nil? or @parent.window.nil?
122
+ $log.debug " #{self} parent nil"
123
+ return
124
+ end
125
+ r = @row
126
+ acolor = $reversecolor
127
+ acolor = get_color($reversecolor, 'green', 'white') if !@enabled
128
+ @parent.window.printstring( @row, 0, "|%-*s|" % [@width, @text], acolor)
129
+ if !@accelerator.nil?
130
+ @parent.window.printstring( r, (@width+1)-@accelerator.length, @accelerator, acolor)
131
+ else
132
+ #@parent.window.printstring( r, (@width+1)-1, ".", acolor)
133
+ end
134
+ if !@mnemonic.nil?
135
+ m = @mnemonic
136
+ ix = @text.index(m) || @text.index(m.swapcase)
137
+ charm = @text[ix,1]
138
+ #@parent.window.printstring( r, ix+1, charm, $datacolor) if !ix.nil?
139
+ # prev line changed since not working in vt100 and vt200
140
+ #@parent.window.printstring( r, ix+1, charm, $reversecolor, 'reverse') if !ix.nil?
141
+ @parent.window.mvchgat(y=r, x=ix+1, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, acolor, nil)
142
+ end
143
+ end
144
+ def destroy
145
+ $log.debug "DESTRY menuitem #{@text}"
146
+ end
147
+ end
148
+ class PMenu < PMenuItem
149
+ attr_accessor :parent
150
+ attr_accessor :row
151
+ attr_accessor :col
152
+ attr_accessor :width
153
+ attr_accessor :enabled
154
+ #attr_reader :text
155
+ attr_reader :items
156
+ attr_reader :window
157
+ attr_reader :panel
158
+ attr_reader :current_menu
159
+ attr_reader :row_margin
160
+ @@menus = []
161
+ @@row = 0
162
+ @@col = 0
163
+
164
+ def initialize text, &block
165
+ super text, nil, &block
166
+ @items = []
167
+ @enabled = true
168
+ @current_menu = []
169
+ instance_eval &block if block_given?
170
+ @row ||=10
171
+ @col ||=10
172
+ @@menus ||= []
173
+ end
174
+ def to_s
175
+ @text
176
+ end
177
+ def clear_menus
178
+ @@menus = []
179
+ end
180
+ # create a Menuitem given an Action
181
+ # if menuitem.kind_of? RubyCurses::Action
182
+ def create_action_component action
183
+ m = PMenuItem.new(action.name, action.mnemonic)
184
+ m.command { action.call }
185
+ m.accelerator = action.accelerator
186
+ return m
187
+ end
188
+ # item could be menuitem or another menu or a string or a Action
189
+ # support for action added 2009-01-21 18:08
190
+ def add menuitem
191
+ insert menuitem, @items.size
192
+ return self
193
+ end
194
+ ##
195
+ # added 2009-01-20 13:28 NEW
196
+ def insert menuitem, ix
197
+ if menuitem.kind_of? RubyCurses::Action
198
+ menuitem = create_action_component menuitem
199
+ end
200
+ @items.insert ix, menuitem
201
+ return self
202
+ end
203
+ def insert_separator ix
204
+ @items.insert ix, PMenuSeparator.new
205
+ end
206
+ def add_separator
207
+ @items << PMenuSeparator.new
208
+ end
209
+ def get_item i
210
+ @items[i]
211
+ end
212
+ def remove n
213
+ if n.is_a? Fixnum
214
+ @items.delete_at n
215
+ else
216
+ @items.delete n
217
+ end
218
+ end
219
+ # menu -
220
+ def fire
221
+ $log.debug "menu fire called: #{@text} "
222
+ if @window.nil?
223
+ #repaint
224
+ create_window
225
+ if !@parent.is_a? RubyCurses::PMenuBar
226
+ $log.debug " ADDING self to current menu: #{self}"
227
+ # xxx highlight true
228
+ @parent.current_menu << self
229
+ @@menus << self
230
+
231
+ $log.debug "DDD #{@@menus} << #{self}"
232
+ end
233
+ else
234
+ ### shouod this not just show ?
235
+ $log.debug "menu fire called: #{@text} ELSE XXX WHEN IS THIS CALLED ? 658 "
236
+ return @items[@active_index].fire # this should happen if selected. else selected()
237
+ end
238
+ #@action.call if !@action.nil?
239
+ end
240
+ # user has clicked down, we shoud display items
241
+ # DRAW menuitems
242
+ def repaint # menu.repaint
243
+ return if @items.nil? or @items.empty?
244
+ $log.debug "menu repaint: #{@text} row #{@row} col #{@col} "
245
+ if !@parent.is_a? RubyCurses::PMenuBar
246
+ @parent.window.printstring( @row, 0, "|%-*s>|" % [@width-1, @text], $reversecolor)
247
+ # added 2009-01-23 00:49
248
+ if !@mnemonic.nil?
249
+ m = @mnemonic
250
+ ix = @text.index(m) || @text.index(m.swapcase)
251
+ charm = @text[ix,1]
252
+ #@parent.window.printstring( r, ix+1, charm, $datacolor) if !ix.nil?
253
+ # prev line changed since not working in vt100 and vt200
254
+ #@parent.window.printstring( @row, ix+1, charm, $reversecolor, 'reverse') if !ix.nil?
255
+ # 2009-01-23 13:03 replaced reverse with ul
256
+ @parent.window.mvchgat(y=@row, x=ix+1, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, $reversecolor, nil)
257
+ end
258
+ @parent.window.refresh
259
+ end
260
+ if @window.nil?
261
+ #create_window
262
+ else
263
+ @window.show
264
+ select_item 0
265
+ @window.refresh
266
+ end
267
+ end
268
+ ##
269
+ # recursive if given one not enabled goes to next enabled
270
+ def select_item ix0
271
+ return if @items.nil? or @items.empty?
272
+ $log.debug "insdie select item : #{ix0}"
273
+ if !@active_index.nil?
274
+ @items[@active_index].on_leave
275
+ end
276
+ previtem = @active_index
277
+ @active_index = ix0
278
+ if @items[ix0].enabled
279
+ @items[ix0].on_enter
280
+ else
281
+ $log.debug "insdie sele nxt item ENABLED FALSE : #{ix0}"
282
+ if @active_index > previtem
283
+ select_next_item
284
+ else
285
+ select_prev_item
286
+ end
287
+ end
288
+ @window.refresh
289
+ end
290
+ def select_next_item
291
+ return if @items.nil? or @items.empty?
292
+ $log.debug "insdie sele nxt item : #{@active_index}"
293
+ @active_index = -1 if @active_index.nil?
294
+ if @active_index < @items.length-1
295
+ select_item @active_index + 1
296
+ else
297
+ # select_item 0
298
+ end
299
+ end
300
+ def select_prev_item
301
+ return if @items.nil? or @items.empty?
302
+ $log.debug "insdie sele prv item : #{@active_index}"
303
+ if @active_index > 0
304
+ select_item @active_index - 1
305
+ else
306
+ #select_item @items.length-1
307
+ end
308
+ end
309
+ def on_enter # menu.on_enter
310
+ $log.debug "menu onenter: #{@text} #{@row} #{@col} "
311
+ # call parent method. XXX
312
+ if @parent.is_a? RubyCurses::PMenuBar
313
+ @parent.window.printstring( @row, @col, " %s " % @text, $datacolor)
314
+ else
315
+ highlight
316
+ end
317
+ if !@window.nil? #and @parent.selected
318
+ $log.debug "menu onenter: #{@text} calling window,show"
319
+ @window.show
320
+ select_item 0
321
+ elsif @parent.is_a? RubyCurses::PMenuBar and @parent.selected
322
+ # only on the top level do we open a window if a previous one was opened
323
+ $log.debug "menu onenter: #{@text} calling repaint CLASS: #{@parent.class}"
324
+ # repaint
325
+ create_window
326
+ end
327
+ end
328
+ def on_leave # menu.on_leave
329
+ $log.debug "menu onleave: #{@text} #{@row} #{@col} "
330
+ # call parent method. XXX
331
+ if @parent.is_a? RubyCurses::PMenuBar
332
+ @parent.window.printstring( @row, @col, " %s " % @text, $reversecolor)
333
+ @window.hide if !@window.nil?
334
+ else
335
+ $log.debug "MENU SUBMEN. menu onleave: #{@text} #{@row} #{@col} will pop !! "
336
+ # parent is a menu
337
+ highlight false
338
+ #@parent.current_menu.pop
339
+ #@@menus.pop
340
+ #destroy
341
+ end
342
+ end
343
+ def highlight tf=true # menu
344
+ $log.debug "MENU SUBMENU menu highlight: #{@text} #{@row} #{@col}, PW #{@parent.width} "
345
+ color = tf ? $datacolor : $reversecolor
346
+ att = tf ? Ncurses::A_REVERSE : Ncurses::A_NORMAL
347
+ #@parent.window.mvchgat(y=@row, x=1, @width, Ncurses::A_NORMAL, color, nil)
348
+ #@parent.window.mvchgat(y=@row, x=1, @parent.width, Ncurses::A_NORMAL, color, nil)
349
+ # above line did not work with vt100/vt200 next does
350
+ @parent.window.mvchgat(y=@row, x=1, @parent.width, att, $reversecolor, nil)
351
+ @parent.window.wrefresh
352
+ end
353
+ def create_window # menu XXX
354
+ margin = 3
355
+ @width = array_width @items
356
+ $log.debug "create window menu #{@text}: #{@row} ,#{@col},parent: #{@parent}, wd #{@width} "
357
+ #$log.debug "create window menu parent: #{@parent.row}, " unless @parent.nil?
358
+ @row_margin = 1 #+ @@row
359
+ @row_margin = @parent.row+@parent.row_margin unless @parent.nil?
360
+ #@row = @parent.row unless @parent.nil?
361
+ @layout = { :height => @items.length+3, :width => @width+margin, :top => @row+@row_margin, :left => @col }
362
+ @win = VER::Window.new(@layout)
363
+ @window = @win
364
+ @win.bkgd(Ncurses.COLOR_PAIR($datacolor));
365
+ @panel = @win.panel
366
+ @window.printstring( 0, 0, "+%s+" % ("-"*@width), $reversecolor)
367
+ r = 1
368
+ @items.each do |item|
369
+ item.row = r
370
+ item.col = 0
371
+ item.col = @col+@width+margin # margins???
372
+ item.width = @width
373
+ #item.window = @window
374
+ item.parent = self
375
+ item.repaint
376
+ #end
377
+ r+=1
378
+ end
379
+ @window.printstring( r, 0, "+%s+" % ("-"*@width), $reversecolor)
380
+ select_item 0
381
+ @window.refresh
382
+ return @window
383
+ end
384
+ # private
385
+ def array_width a
386
+ longest = a.max {|a,b| a.to_s.length <=> b.to_s.length }
387
+ $log.debug "array width #{longest}"
388
+ longest.to_s.length
389
+ end
390
+ def destroy
391
+ $log.debug "DESTRY menu #{@text}"
392
+ return if @window.nil?
393
+ @visible = false
394
+ panel = @window.panel
395
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
396
+ @window.delwin if !@window.nil? # FFI
397
+ @items.each do |item|
398
+ #next if item == :SEPARATOR
399
+ item.destroy
400
+ end
401
+ @window = nil
402
+ end
403
+ # menu LEFT, RIGHT, DOWN, UP, ENTER
404
+ # item could be Pmenuitem or another menu
405
+ #
406
+ def handle_key ch
407
+ #if !@current_menu.empty?
408
+ # cmenu = @current_menu.last
409
+ #else
410
+ # cmenu = self
411
+ #end
412
+ if !@@menus.empty?
413
+ cmenu = @@menus.last
414
+ else
415
+ cmenu = self
416
+ end
417
+ $log.debug " CMENU is #{cmenu}: #{@@menus} "
418
+ case ch
419
+ when KEY_DOWN
420
+ cmenu.select_next_item
421
+ when KEY_UP
422
+ cmenu.select_prev_item
423
+ when KEY_ENTER, 10, 13, 32 # added 32 2008-11-28 23:50
424
+ return cmenu.fire
425
+ when KEY_LEFT
426
+ if cmenu.parent.is_a? RubyCurses::PMenu
427
+ $log.debug "LEFT IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
428
+ $log.debug "left IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
429
+ end
430
+ if cmenu.parent.is_a? RubyCurses::PMenu and !cmenu.parent.current_menu.empty?
431
+ $log.debug " ABOU TO DESTROY DUE TO LEFT"
432
+ cmenu.parent.current_menu.pop
433
+ @@menus.pop
434
+ cmenu.destroy
435
+ else
436
+ $log.debug " returning UNHANDLED 370"
437
+ return :UNHANDLED
438
+ end
439
+ when KEY_RIGHT
440
+ $log.debug "RIGHTIN MENU : "
441
+ if cmenu.parent.is_a? RubyCurses::PMenu
442
+ $log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.parent.current_menu.length}"
443
+ $log.debug "right IN MENU : #{cmenu.parent.class} len: #{cmenu.current_menu.length}"
444
+ end
445
+ if cmenu.parent.is_a? RubyCurses::PMenu and !cmenu.parent.current_menu.empty?
446
+ $log.debug " ABOU TO DESTROY DUE TO RIGHT"
447
+ cmenu.parent.current_menu.pop
448
+ @@menus.pop
449
+ cmenu.destroy
450
+ end
451
+ return :UNHANDLED
452
+ else
453
+ ret = check_mnemonics cmenu, ch
454
+ return ret
455
+ end
456
+ end
457
+ ##
458
+ # checks given key against current menu's items and fires key if
459
+ # added on 2008-11-27 12:07
460
+ def check_mnemonics cmenu, ch
461
+ # $log.debug "inside check_mnemonics #{ch}"
462
+ key = ch.chr.downcase rescue ""
463
+ cmenu.items.each_with_index do |item, ix|
464
+ next if !item.respond_to? :mnemonic or item.mnemonic.nil?
465
+ # $log.debug "inside check_mnemonics #{item.mnemonic}"
466
+ if key == item.mnemonic.downcase
467
+ cmenu.select_item ix # 2009-01-23 13:32 so focus moves to menu
468
+ ret = item.fire
469
+ return ret # 0 # 2009-01-23 00:43 Pmenuitem returns CLOSE, menu 0
470
+ end
471
+ end
472
+ return :UNHANDLED
473
+ end
474
+ ## menu
475
+ def show # menu.show
476
+ $log.debug "show (menu) : #{@text} "
477
+ if @window.nil?
478
+ create_window
479
+ end
480
+ @window.show
481
+ select_item 0
482
+ end
483
+ end
484
+ class PopupMenu < PMenu
485
+ def initialize text, &block
486
+ @row_margin = 0
487
+ @@row = 0
488
+ @@col = 0
489
+ super
490
+ instance_eval &block if block_given?
491
+ end
492
+ def show component, x, y
493
+ @component = component
494
+ @@row = component.row
495
+ @@col = component.col
496
+ create_window
497
+ handle_keys
498
+ end
499
+ def handle_keys # popup
500
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
501
+ begin
502
+ catch(:menubarclose) do
503
+ while((ch = @window.getchar()) != @toggle_key )
504
+ case ch
505
+ when -1
506
+ next
507
+ else
508
+ ret = handle_key ch
509
+ $log.debug " POPUP got #{ret} added 2009-01-21 18:18 "
510
+ break if ret == :CLOSE
511
+ end
512
+ Ncurses::Panel.update_panels();
513
+ Ncurses.doupdate();
514
+
515
+ @window.wrefresh
516
+ end
517
+ end # catch
518
+ ensure
519
+ #ensure is required becos one can throw a :close
520
+ @@menus = [] # added 2009-01-23 13:21
521
+ destroy # Note that we destroy the menu bar upon exit
522
+ end
523
+ end
524
+ def destroy
525
+ $log.debug "DESTRY popup "
526
+ @visible = false
527
+ panel = @window.panel
528
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
529
+ @window.delwin if !@window.nil? # FFI
530
+ @items.each do |item|
531
+ item.destroy
532
+ end
533
+ @window = nil
534
+ end
535
+ end # class
536
+ ##
537
+ # An application related menubar.
538
+ # Currently, I am adding this to a form. But should this not be application specific ?
539
+ # It should popup no matter which window you are on ?? XXX
540
+ class PMenuBar
541
+ attr_reader :items
542
+ attr_reader :window
543
+ attr_reader :panel
544
+ attr_reader :selected
545
+ attr_accessor :visible
546
+ attr_accessor :active_index
547
+ attr_accessor :state # normal, selected, highlighted
548
+ attr_accessor :toggle_key # key used to popup, should be set prior to attaching to form
549
+ def initialize &block
550
+ @window = nil
551
+ @active_index = 0
552
+ @items = []
553
+ @visible = false
554
+ @cols = Ncurses.COLS-1
555
+ instance_eval &block if block_given?
556
+ end
557
+ def focusable
558
+ false
559
+ end
560
+ def add menu
561
+ @items << menu
562
+ return self
563
+ end
564
+ def next_menu
565
+ $log.debug "next meu: #{@active_index} "
566
+ if @active_index < @items.length-1
567
+ set_menu @active_index + 1
568
+ else
569
+ set_menu 0
570
+ end
571
+ end
572
+ def prev_menu
573
+ $log.debug "prev meu: #{@active_index} "
574
+ if @active_index > 0
575
+ set_menu @active_index-1
576
+ else
577
+ set_menu @items.length-1
578
+ end
579
+ end
580
+ def set_menu index
581
+ $log.debug "set meu: #{@active_index} #{index}"
582
+ menu = @items[@active_index]
583
+ menu.on_leave # hide its window, if open
584
+ @active_index = index
585
+ menu = @items[@active_index]
586
+ menu.on_enter #display window, if previous was displayed
587
+ @window.wmove menu.row, menu.col
588
+ # menu.show
589
+ # menu.window.wrefresh # XXX we need this
590
+ end
591
+ # menubar LEFT, RIGHT, DOWN
592
+ def handle_keys
593
+ @selected = false
594
+ @toggle_key ||= 27 # default switch off with ESC, if nothing else defined
595
+ set_menu 0
596
+ begin
597
+ catch(:popupclose) do
598
+ while((ch = @window.getchar()) != @toggle_key )
599
+ $log.debug "menuubar inside handle_keys : #{ch}" if ch != -1
600
+ case ch
601
+ when -1
602
+ next
603
+ when KEY_DOWN
604
+ $log.debug "insdie keyDOWN : #{ch}"
605
+ if !@selected
606
+ current_menu.fire
607
+ else
608
+ current_menu.handle_key ch
609
+ end
610
+
611
+ @selected = true
612
+ when KEY_ENTER, 10, 13, 32
613
+ @selected = true
614
+ $log.debug " mb insdie ENTER : #{current_menu}"
615
+ ret = current_menu.handle_key ch
616
+ $log.debug "ret = #{ret} mb insdie ENTER : #{current_menu}"
617
+ #break; ## 2008-12-29 18:00 This will close after firing
618
+ #anything
619
+ break if ret == :CLOSE
620
+ when KEY_UP
621
+ $log.debug " mb insdie keyUPP : #{ch}"
622
+ current_menu.handle_key ch
623
+ when KEY_LEFT
624
+ $log.debug " mb insdie KEYLEFT : #{ch}"
625
+ ret = current_menu.handle_key ch
626
+ prev_menu if ret == :UNHANDLED
627
+ #display_items if @selected
628
+ when KEY_RIGHT
629
+ $log.debug " mb insdie KEYRIGHT : #{ch}"
630
+ ret = current_menu.handle_key ch
631
+ next_menu if ret == :UNHANDLED
632
+ else
633
+ $log.debug " mb insdie ELSE : #{ch}"
634
+ ret = current_menu.handle_key ch
635
+ if ret == :UNHANDLED
636
+ Ncurses.beep
637
+ else
638
+ break # we handled a menu action, close menubar (THIS WORKS FOR MNEMONICS ONLY and always)
639
+ end
640
+ end
641
+ Ncurses::Panel.update_panels();
642
+ Ncurses.doupdate();
643
+
644
+ @window.wrefresh
645
+ end
646
+ end # catch
647
+ ensure
648
+ #ensure is required becos one can throw a :close
649
+ destroy # Note that we destroy the menu bar upon exit
650
+ end
651
+ end
652
+ def current_menu
653
+ @items[@active_index]
654
+ end
655
+ def toggle
656
+ @visible = !@visible
657
+ if !@visible
658
+ hide
659
+ else
660
+ show
661
+ end
662
+ end
663
+ def hide
664
+ @visible = false
665
+ @window.hide if !@window.nil?
666
+ end
667
+ def show
668
+ @visible = true
669
+ if @window.nil?
670
+ repaint # XXX FIXME
671
+ else
672
+ @window.show
673
+ end
674
+ end
675
+ ## menubar
676
+ def repaint
677
+ return if !@visible
678
+ @window ||= create_window
679
+ @window.printstring( 0, 0, "%-*s" % [@cols," "], $reversecolor)
680
+ c = 1; r = 0;
681
+ @items.each do |item|
682
+ item.row = r; item.col = c; item.parent = self
683
+ @window.printstring( r, c, " %s " % item.text, $reversecolor)
684
+ c += (item.text.length + 2)
685
+ end
686
+ @window.wrefresh
687
+ end
688
+ def create_window
689
+ @layout = { :height => 1, :width => 0, :top => 0, :left => 0 }
690
+ @win = VER::Window.new(@layout)
691
+ @window = @win
692
+ @win.bkgd(Ncurses.COLOR_PAIR(5));
693
+ @panel = @win.panel
694
+ return @window
695
+ end
696
+ def destroy
697
+ $log.debug "DESTRY menubar "
698
+ @visible = false
699
+ panel = @window.panel
700
+ Ncurses::Panel.del_panel(panel.pointer) if !panel.nil?
701
+ @window.delwin if !@window.nil?
702
+ @items.each do |item|
703
+ item.destroy
704
+ end
705
+ @window = nil
706
+ end
707
+ end # menubar
708
+
709
+ class PCheckBoxMenuItem < PMenuItem
710
+ attr_reader :checkbox
711
+ def initialize text, mnemonic=nil, &block
712
+ @checkbox = CheckBox.new nil
713
+ @checkbox.text text
714
+ super
715
+ end
716
+ def onvalue
717
+ @checkbox.onvalue onvalue
718
+ end
719
+ def offvalue
720
+ @checkbox.onvalue offvalue
721
+ end
722
+ def text=(t) # stack level too deep if no = .????
723
+ @checkbox.text t
724
+ end
725
+ ## added @ with text, else crashing on testmenu.rb
726
+ def to_s
727
+ " #{@text} "
728
+ end
729
+ def getvalue
730
+ checkbox.getvalue
731
+ end
732
+ def getvalue_for_paint
733
+ "|%-*s|" % [@width, checkbox.getvalue_for_paint]
734
+ end
735
+ def fire
736
+ checkbox.toggle
737
+ super
738
+ repaint
739
+ highlight true
740
+ end
741
+ def repaint
742
+ @parent.window.printstring( row, 0, getvalue_for_paint, $reversecolor)
743
+ parent.window.wrefresh
744
+ end
745
+ def method_missing(sym, *args)
746
+ if checkbox.respond_to? sym
747
+ $log.debug("calling CHECKBOXMENU #{sym} called #{args[0]}")
748
+ checkbox.send(sym, args)
749
+ else
750
+ $log.error("ERROR CHECKBOXMENU #{sym} called")
751
+ end
752
+ end
753
+
754
+ end
755
+ end # modul