rbcurse-extras 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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