rbcurse-core 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/README.md +69 -0
  2. data/VERSION +1 -0
  3. data/examples/abasiclist.rb +151 -0
  4. data/examples/alpmenu.rb +46 -0
  5. data/examples/app.sample +17 -0
  6. data/examples/atree.rb +100 -0
  7. data/examples/common/file.rb +45 -0
  8. data/examples/data/README.markdown +9 -0
  9. data/examples/data/brew.txt +38 -0
  10. data/examples/data/color.2 +37 -0
  11. data/examples/data/gemlist.txt +60 -0
  12. data/examples/data/lotr.txt +12 -0
  13. data/examples/data/ports.txt +136 -0
  14. data/examples/data/table.txt +37 -0
  15. data/examples/data/tasks.csv +88 -0
  16. data/examples/data/tasks.txt +27 -0
  17. data/examples/data/todo.txt +10 -0
  18. data/examples/data/todocsv.csv +28 -0
  19. data/examples/data/unix1.txt +21 -0
  20. data/examples/data/unix2.txt +11 -0
  21. data/examples/dbdemo.rb +487 -0
  22. data/examples/dirtree.rb +90 -0
  23. data/examples/newtabbedwindow.rb +100 -0
  24. data/examples/newtesttabp.rb +92 -0
  25. data/examples/tabular.rb +132 -0
  26. data/examples/tasks.rb +167 -0
  27. data/examples/term2.rb +83 -0
  28. data/examples/testkeypress.rb +72 -0
  29. data/examples/testlistbox.rb +158 -0
  30. data/examples/testmessagebox.rb +140 -0
  31. data/examples/testree.rb +106 -0
  32. data/examples/testwsshortcuts.rb +66 -0
  33. data/examples/testwsshortcuts2.rb +127 -0
  34. data/lib/rbcurse.rb +8 -0
  35. data/lib/rbcurse/core/docs/index.txt +73 -0
  36. data/lib/rbcurse/core/include/action.rb +40 -0
  37. data/lib/rbcurse/core/include/appmethods.rb +112 -0
  38. data/lib/rbcurse/core/include/bordertitle.rb +41 -0
  39. data/lib/rbcurse/core/include/chunk.rb +182 -0
  40. data/lib/rbcurse/core/include/io.rb +953 -0
  41. data/lib/rbcurse/core/include/listcellrenderer.rb +140 -0
  42. data/lib/rbcurse/core/include/listeditable.rb +317 -0
  43. data/lib/rbcurse/core/include/listscrollable.rb +590 -0
  44. data/lib/rbcurse/core/include/listselectable.rb +264 -0
  45. data/lib/rbcurse/core/include/multibuffer.rb +83 -0
  46. data/lib/rbcurse/core/include/orderedhash.rb +77 -0
  47. data/lib/rbcurse/core/include/ractionevent.rb +67 -0
  48. data/lib/rbcurse/core/include/rchangeevent.rb +27 -0
  49. data/lib/rbcurse/core/include/rhistory.rb +62 -0
  50. data/lib/rbcurse/core/include/rinputdataevent.rb +47 -0
  51. data/lib/rbcurse/core/include/vieditable.rb +170 -0
  52. data/lib/rbcurse/core/system/colormap.rb +163 -0
  53. data/lib/rbcurse/core/system/keyboard.rb +150 -0
  54. data/lib/rbcurse/core/system/keydefs.rb +30 -0
  55. data/lib/rbcurse/core/system/ncurses.rb +218 -0
  56. data/lib/rbcurse/core/system/panel.rb +162 -0
  57. data/lib/rbcurse/core/system/window.rb +901 -0
  58. data/lib/rbcurse/core/util/ansiparser.rb +117 -0
  59. data/lib/rbcurse/core/util/app.rb +1235 -0
  60. data/lib/rbcurse/core/util/basestack.rb +407 -0
  61. data/lib/rbcurse/core/util/bottomline.rb +1850 -0
  62. data/lib/rbcurse/core/util/colorparser.rb +71 -0
  63. data/lib/rbcurse/core/util/focusmanager.rb +31 -0
  64. data/lib/rbcurse/core/util/padreader.rb +189 -0
  65. data/lib/rbcurse/core/util/rcommandwindow.rb +587 -0
  66. data/lib/rbcurse/core/util/rdialogs.rb +619 -0
  67. data/lib/rbcurse/core/util/viewer.rb +149 -0
  68. data/lib/rbcurse/core/util/widgetshortcuts.rb +505 -0
  69. data/lib/rbcurse/core/widgets/applicationheader.rb +102 -0
  70. data/lib/rbcurse/core/widgets/box.rb +58 -0
  71. data/lib/rbcurse/core/widgets/divider.rb +310 -0
  72. data/lib/rbcurse/core/widgets/keylabelprinter.rb +178 -0
  73. data/lib/rbcurse/core/widgets/rcombo.rb +238 -0
  74. data/lib/rbcurse/core/widgets/rcontainer.rb +415 -0
  75. data/lib/rbcurse/core/widgets/rlink.rb +30 -0
  76. data/lib/rbcurse/core/widgets/rlist.rb +723 -0
  77. data/lib/rbcurse/core/widgets/rmenu.rb +939 -0
  78. data/lib/rbcurse/core/widgets/rmenulink.rb +22 -0
  79. data/lib/rbcurse/core/widgets/rmessagebox.rb +373 -0
  80. data/lib/rbcurse/core/widgets/rprogress.rb +118 -0
  81. data/lib/rbcurse/core/widgets/rtabbedpane.rb +615 -0
  82. data/lib/rbcurse/core/widgets/rtabbedwindow.rb +68 -0
  83. data/lib/rbcurse/core/widgets/rtextarea.rb +920 -0
  84. data/lib/rbcurse/core/widgets/rtextview.rb +780 -0
  85. data/lib/rbcurse/core/widgets/rtree.rb +787 -0
  86. data/lib/rbcurse/core/widgets/rwidget.rb +3040 -0
  87. data/lib/rbcurse/core/widgets/scrollbar.rb +143 -0
  88. data/lib/rbcurse/core/widgets/statusline.rb +94 -0
  89. data/lib/rbcurse/core/widgets/tabular.rb +264 -0
  90. data/lib/rbcurse/core/widgets/tabularwidget.rb +1211 -0
  91. data/lib/rbcurse/core/widgets/textpad.rb +516 -0
  92. data/lib/rbcurse/core/widgets/tree/treecellrenderer.rb +150 -0
  93. data/lib/rbcurse/core/widgets/tree/treemodel.rb +428 -0
  94. metadata +156 -0
@@ -0,0 +1,71 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: colorparser.rb
3
+ # Description: Default parse for our tmux format
4
+ # The aim is to be able to specify parsers so different kinds
5
+ # of formatting or documents can be used, such as ANSI formatted
6
+ # manpages.
7
+ # Author: rkumar http://github.com/rkumar/rbcurse/
8
+ # Date: 07.11.11 - 13:17
9
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+ # Last update: use ,,L
11
+ # ----------------------------------------------------------------------------- #
12
+ # == TODO
13
+ # - perhaps we can compile the regexp once and reuse
14
+ #
15
+
16
+ class DefaultColorParser
17
+
18
+ # NOTE: Experimental and minimal
19
+ # parses the formatted string and yields either an array of color, bgcolor and attrib
20
+ # or the text. This will be called by convert_to_chunk.
21
+ #
22
+ # Currently, assumes colors and attributes are correct. No error checking or fancy stuff.
23
+ # s="#[fg=green]hello there#[fg=yellow, bg=black, dim]"
24
+ # @since 1.4.1 2011-11-3 experimental, can change
25
+ # @return [nil] knows nothign about output format.
26
+
27
+ def parse_format s # yields attribs or text
28
+ ## set default colors
29
+ color = :white
30
+ bgcolor = :black
31
+ attrib = FFI::NCurses::A_NORMAL
32
+ text = ""
33
+
34
+ ## split #[...]
35
+ a = s.split /(#\[[^\]]*\])/
36
+ a.each { |e|
37
+ ## process color or attrib portion
38
+ if e[0,2] == "#[" && e[-1] == "]"
39
+ # now resetting 1:20 PM November 3, 2011 , earlier we were carrying over
40
+ color, bgcolor, attrib = nil, nil, nil
41
+ catch(:done) do
42
+ e = e[2..-2]
43
+ ## first split on commas to separate fg, bg and attr
44
+ atts = e.split /\s*,\s*/
45
+ atts.each { |att|
46
+ ## next split on =
47
+ part = att.split /\s*=\s*/
48
+ case part[0]
49
+ when "fg"
50
+ color = part[1]
51
+ when "bg"
52
+ bgcolor = part[1]
53
+ when "/end", "end"
54
+ yield :endcolor if block_given?
55
+ #next
56
+ throw :done
57
+ else
58
+ # attrib
59
+ attrib = part[0]
60
+ end
61
+ }
62
+ yield [color,bgcolor,attrib] if block_given?
63
+ end # catch
64
+ else
65
+ text = e
66
+ yield text if block_given?
67
+ end
68
+ }
69
+ end
70
+
71
+ end
@@ -0,0 +1,31 @@
1
+ # Allow some objects to take focus when a certain key is pressed.
2
+ # This is for objects like scrollbars and grabbars. We don't want these always
3
+ # getting focus, only sometimes when we want to resize panes.
4
+ # This will not only be included by Form but by containers such as Vimsplit
5
+ # or MasterDetail.
6
+ # Usage: the idea is that when you create grabbars, you would add them to the FocusManager
7
+ # Thus they would remain non-focusable on creation. When hte user presses (say F3) then
8
+ # make_focusable is called, or toggle_focusable. Now user can press TAB and access
9
+ # these bars. When he is done he can toggle again.
10
+ # TODO: we might add a Circular class here so user can traverse only these objects
11
+ module RubyCurses
12
+ module FocusManager
13
+ extend self
14
+ attr_reader :focusables
15
+ # add a component to this list so it can be made focusable later
16
+ def add component
17
+ @focusables ||= []
18
+ @focusables << component
19
+ self
20
+ end
21
+ def make_focusable bool=true
22
+ @focusing = bool
23
+ @focusables.each { |e| e.focusable(bool) }
24
+ end
25
+ def toggle_focusable
26
+ return unless @focusables
27
+ alert "FocusManager Making #{@focusables.length} objects #{!@focusing} "
28
+ make_focusable !@focusing
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,189 @@
1
+ =begin
2
+ * Name: PadReader.rb
3
+ * Description : This is an independent file viewer that uses a Pad and traps keys
4
+ * Author: rkumar (http://github.com/rkumar/rbcurse/)
5
+ * Date: 22.10.11 - 20:35
6
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ * Last update: 2011-11-14 - 20:57
8
+
9
+ == CHANGES
10
+ == TODO
11
+ make the window configurable so we can move to a textview that is pad based, later even list ?
12
+ Note that cursor does not move, in real life applicatino cursor must move to bottom row
13
+ and only then scrolling should start.
14
+ NOTE: I have continued this in textpad which is a widget that uses pads to scroll.
15
+ This is very rough, i may work on this more later.
16
+ =end
17
+ require 'rbcurse'
18
+
19
+ include RubyCurses
20
+ class PadReader
21
+
22
+ # You may pass height, width, row and col for creating a window otherwise a fullscreen window
23
+ # will be created. If you pass a window from caller then that window will be used.
24
+ # Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
25
+ # This is currently very minimal and was created to get me started to integrating
26
+ # pads into other classes such as textview.
27
+ def initialize config={}, &block
28
+
29
+ @config = config
30
+ @rows = FFI::NCurses.LINES-1
31
+ @cols = FFI::NCurses.COLS-1
32
+ @prow = @pcol = 0
33
+ @startrow = 0
34
+ @startcol = 0
35
+
36
+ h = config.fetch(:height, 0)
37
+ w = config.fetch(:width, 0)
38
+ t = config.fetch(:row, 0)
39
+ l = config.fetch(:col, 0)
40
+ @rows = h unless h == 0
41
+ @cols = w unless w == 0
42
+ @startrow = t unless t == 0
43
+ @startcol = l unless l == 0
44
+ @suppress_border = config[:suppress_border]
45
+ unless @suppress_border
46
+ @startrow += 1
47
+ @startcol += 1
48
+ @rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
49
+ @cols -=3
50
+ end
51
+ @top = t
52
+ @left = l
53
+ view_file config[:filename]
54
+ @window = config[:window] || VER::Window.new(:height => h, :width => w, :top => t, :left => l)
55
+ # print border reduces on from width for some historical reason
56
+ @window.print_border_only @top, @left, h-1, w, $datacolor
57
+ @ph = @content_rows
58
+ @pw = @content_cols # get max col
59
+ @pad = FFI::NCurses.newpad(@ph, @pw)
60
+
61
+ Ncurses::Panel.update_panels
62
+ @content.each_index { |ix|
63
+
64
+ FFI::NCurses.mvwaddstr(@pad,ix, 0, @content[ix])
65
+ }
66
+ @window.wrefresh
67
+ padrefresh
68
+ #FFI::NCurses.prefresh(@pad, 0,0, @startrow ,@startcol, @rows,@cols);
69
+
70
+ @window.bkgd(Ncurses.COLOR_PAIR(5));
71
+ FFI::NCurses.keypad(@pad, true);
72
+ #@form = Form.new @window
73
+ config[:row] = config[:col] = 0 # ??? XXX
74
+ end
75
+
76
+ private
77
+ def view_file(filename)
78
+ @file = filename
79
+ @content = File.open(filename,"r").readlines
80
+ @content_rows = @content.count
81
+ @content_cols = content_cols()
82
+ #run()
83
+ end
84
+ # write pad onto window
85
+ private
86
+ def padrefresh
87
+ FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,@startcol, @rows + @startrow,@cols+@startcol);
88
+ end
89
+ # returns button index
90
+ # Call this after instantiating the window
91
+ public
92
+ def run
93
+ #@form.repaint
94
+ #@window.wrefresh
95
+ return handle_keys
96
+ end
97
+
98
+ # convenience method
99
+ private
100
+ def key x
101
+ x.getbyte(0)
102
+ end
103
+ def content_cols
104
+ longest = @content.max_by(&:length)
105
+ longest.length
106
+ end
107
+
108
+ # returns button index
109
+ private
110
+ def handle_keys
111
+ ht = @window.height.ifzero FFI::NCurses.LINES-1
112
+ buttonindex = catch(:close) do
113
+ @maxrow = @content_rows - @rows
114
+ @maxcol = @content_cols - @cols
115
+ while((ch = @window.getchar()) != FFI::NCurses::KEY_F10 )
116
+ #while((ch = FFI::NCurses.wgetch(@pad)) != FFI::NCurses::KEY_F10 )
117
+ break if ch == ?\C-q.getbyte(0)
118
+ begin
119
+ case ch
120
+ when key(?g), 279 # home as per iterm2
121
+ @prow = 0
122
+ when key(?b), key(?G), 277 # end as per iterm2
123
+ @prow = @maxrow-1
124
+ when key(?j)
125
+ @prow += 1
126
+ when key(?k)
127
+ @prow -= 1
128
+ when 32, 338 # Page Down abd Page Up as per iTerm2
129
+ @prow += 10
130
+ when key(?\C-d)
131
+ @prow += ht
132
+ when key(?\C-b)
133
+ @prow -= ht
134
+ when 339
135
+ @prow -= 10
136
+ when key(?l)
137
+ @pcol += 1
138
+ when key(?$)
139
+ @pcol = @maxcol - 1
140
+ when key(?h)
141
+ @pcol -= 1
142
+ when key(?0)
143
+ @pcol = 0
144
+ when key(?q)
145
+ throw :close
146
+ else
147
+ alert " #{ch} not mapped "
148
+ end
149
+ @prow = 0 if @prow < 0
150
+ @pcol = 0 if @pcol < 0
151
+ if @prow > @maxrow-1
152
+ @prow = @maxrow-1
153
+ end
154
+ if @pcol > @maxcol-1
155
+ @pcol = @maxcol-1
156
+ end
157
+ #@window.wclear
158
+ #FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,0, @rows,@cols);
159
+ padrefresh
160
+ Ncurses::Panel.update_panels
161
+ #@form.handle_key(ch)
162
+ #@window.wrefresh
163
+ rescue => err
164
+ $log.debug( err) if err
165
+ $log.debug(err.backtrace.join("\n")) if err
166
+ alert "Got an exception in PadReader: #{err}. Check log"
167
+ $error_message.value = ""
168
+ ensure
169
+ end
170
+
171
+ end # while loop
172
+ end # close
173
+ $log.debug "XXX: CALLER GOT #{buttonindex} "
174
+ @window.destroy unless @config[:window]
175
+ FFI::NCurses.delwin(@pad)
176
+ return buttonindex
177
+ end
178
+ end
179
+ if __FILE__ == $PROGRAM_NAME
180
+ require 'rbcurse/core/util/app'
181
+ App.new do
182
+ #status_line :row => FFI::NCurses.LINES-1
183
+ @form.repaint
184
+ #p = PadReader.new :filename => "padreader.rb", :height => 20, :width => 60, :row => 4, :col => 4, :window => @window, :suppress_border => true
185
+ p = PadReader.new :filename => "padreader.rb", :height => FFI::NCurses.LINES-1, :width => 0, :row => 0, :col => 0, :window => @window, :suppress_border => true
186
+ p.run
187
+ throw :close
188
+ end
189
+ end
@@ -0,0 +1,587 @@
1
+ =begin
2
+ * Name: rcommandwindow: pops up a status message at bottom of screen
3
+ creating a new window, so we don't have to worry about having window
4
+ handle.
5
+ Can use with say, ask etc. wsay wask wagree etc !
6
+ * Description
7
+ * Author: rkumar (arunachalesha)
8
+ * Date: 2008-11-19 12:49
9
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+ * file separated on 2009-01-13 22:39
11
+
12
+ =end
13
+ require 'rbcurse'
14
+
15
+ module RubyCurses
16
+ ##
17
+ #
18
+ #
19
+ class CommandWindow
20
+ include RubyCurses::Utils
21
+ dsl_accessor :box
22
+ dsl_accessor :title
23
+ attr_reader :config
24
+ attr_reader :layout
25
+ attr_reader :window # required for keyboard or printing
26
+ dsl_accessor :height, :width, :top, :left # 2009-01-06 00:05 after removing meth missing
27
+
28
+ def initialize form=nil, aconfig={}, &block
29
+ @config = aconfig
30
+ @config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
31
+ instance_eval &block if block_given?
32
+ if @layout.nil?
33
+ set_layout(1,80, -1, 0)
34
+ end
35
+ @height = @layout[:height]
36
+ @width = @layout[:width]
37
+ @window = VER::Window.new(@layout)
38
+ @start = 0 # row for display of text with paging
39
+ @list = []
40
+ require 'forwardable'
41
+ require 'rbcurse/core/util/bottomline'
42
+ @bottomline = Bottomline.new @window, 0
43
+ @bottomline.name = "rcommandwindow's bl"
44
+ extend Forwardable
45
+ def_delegators :@bottomline, :ask, :say, :agree, :choose #, :display_text_interactive
46
+ if @box == :border
47
+ @window.box 0,0
48
+ elsif @box
49
+ @window.attron(Ncurses.COLOR_PAIR($normalcolor) | Ncurses::A_REVERSE)
50
+ @window.mvhline 0,0,1,@width
51
+ @window.printstring 0,0,@title, $normalcolor #, 'normal' if @title
52
+ @window.attroff(Ncurses.COLOR_PAIR($normalcolor) | Ncurses::A_REVERSE)
53
+ else
54
+ #@window.printstring 0,0,@title, $normalcolor, 'reverse' if @title
55
+ title @title
56
+ end
57
+ @window.wrefresh
58
+ @panel = @window.panel
59
+ Ncurses::Panel.update_panels
60
+ @window.wrefresh
61
+ @row_offset = 0
62
+ if @box
63
+ @row_offset = 1
64
+ end
65
+ end
66
+ # modify the window title, or get it if no params passed.
67
+ def title t=nil
68
+ return @title unless t
69
+ @title = t
70
+ @window.printstring 0,0,@title, $normalcolor, 'reverse' if @title
71
+ end
72
+ ##
73
+ ## message box
74
+ def stopping?
75
+ @stop
76
+ end
77
+ # todo handle mappings, so user can map keys TODO
78
+ def handle_keys
79
+ begin
80
+ while((ch = @window.getchar()) != 999 )
81
+ case ch
82
+ when -1
83
+ next
84
+ else
85
+ press ch
86
+ break if @stop
87
+ yield ch if block_given?
88
+ end
89
+ end
90
+ ensure
91
+ destroy
92
+ end
93
+ return #@selected_index
94
+ end
95
+ # handles a key, commandline
96
+ def press ch
97
+ ch = ch.getbyte(0) if ch.class==String ## 1.9
98
+ $log.debug " XXX press #{ch} " if $log.debug?
99
+ case ch
100
+ when -1
101
+ return
102
+ when KEY_F1, 27, ?\C-q.getbyte(0)
103
+ @stop = true
104
+ return
105
+ when KEY_ENTER, 10, 13
106
+ #$log.debug "popup ENTER : #{@selected_index} "
107
+ #$log.debug "popup ENTER : #{field.name}" if !field.nil?
108
+ @stop = true
109
+ return
110
+ when ?\C-d.getbyte(0)
111
+ @start += @height-1
112
+ bounds_check
113
+ when KEY_UP
114
+ @start -= 1
115
+ @start = 0 if @start < 0
116
+ when KEY_DOWN
117
+ @start += 1
118
+ bounds_check
119
+ when ?\C-b.getbyte(0)
120
+ @start -= @height-1
121
+ @start = 0 if @start < 0
122
+ when 0
123
+ @start = 0
124
+ end
125
+ Ncurses::Panel.update_panels();
126
+ Ncurses.doupdate();
127
+ @window.wrefresh
128
+ end
129
+ # might as well add more keys for paging.
130
+ def configure(*val , &block)
131
+ case val.size
132
+ when 1
133
+ return @config[val[0]]
134
+ when 2
135
+ @config[val[0]] = val[1]
136
+ instance_variable_set("@#{val[0]}", val[1])
137
+ end
138
+ instance_eval &block if block_given?
139
+ end
140
+ def cget param
141
+ @config[param]
142
+ end
143
+
144
+ def set_layout(height=0, width=0, top=0, left=0)
145
+ # negative means top should be n rows from last line. -1 is last line
146
+ if top < 0
147
+ top = Ncurses.LINES-top
148
+ end
149
+ @layout = { :height => height, :width => width, :top => top, :left => left }
150
+ @height = height
151
+ @width = width
152
+ end
153
+ def destroy
154
+ $log.debug "DESTROY : rcommandwindow"
155
+ if @window
156
+ begin
157
+ panel = @window.panel
158
+ Ncurses::Panel.del_panel(panel.pointer) if panel
159
+ @window.delwin
160
+ rescue => exc
161
+ end
162
+ end
163
+ end
164
+ #
165
+ # Displays list in a window at bottom of screen, if large then 2 or 3 columns.
166
+ # @param [Array] list of string to be displayed
167
+ # @param [Hash] configuration options: indexing and indexcolor
168
+ # indexing - can be letter or number. Anything else will be ignored, however
169
+ # it will result in first letter being highlighted in indexcolor
170
+ # indexcolor - color of mnemonic, default green
171
+ def display_menu list, options={}
172
+ indexing = options[:indexing]
173
+ indexcolor = options[:indexcolor] || get_color($normalcolor, :yellow, :black)
174
+ indexatt = Ncurses::A_BOLD
175
+ #
176
+ # the index to start from (used when scrolling a long menu such as file list)
177
+ startindex = options[:startindex] || 0
178
+
179
+ max_cols = 3 # maximum no of columns, we will reduce based on data size
180
+ l_succ = "`"
181
+ act_height = @height
182
+ if @box
183
+ act_height = @height - 2
184
+ end
185
+ lh = list.size
186
+ if lh < act_height
187
+ $log.debug "DDD inside one window" if $log.debug?
188
+ list.each_with_index { |e, i|
189
+ text = e
190
+ case e
191
+ when Array
192
+ text = e.first + " ..."
193
+ end
194
+ if indexing == :number
195
+ mnem = i+1
196
+ text = "%d. %s" % [i+1, text]
197
+ elsif indexing == :letter
198
+ mnem = l_succ.succ!
199
+ text = "%s. %s" % [mnem, text]
200
+ end
201
+ @window.printstring i+@row_offset, 1, text, $normalcolor
202
+ if indexing
203
+ window.mvchgat(y=i+@row_offset, x=1, max=1, indexatt, indexcolor, nil)
204
+ end
205
+ }
206
+ else
207
+ $log.debug "DDD inside two window" if $log.debug?
208
+ row = 0
209
+ h = act_height
210
+ cols = (lh*1.0 / h).ceil
211
+ cols = max_cols if cols > max_cols
212
+ # sometimes elements are large like directory paths, so check size
213
+ datasize = list.first.length
214
+ if datasize > @width/3 # keep safety margin since checking only first row
215
+ cols = 1
216
+ elsif datasize > @width/2
217
+ cols = [2,cols].min
218
+ end
219
+ adv = (@width/cols).to_i
220
+ colct = 0
221
+ col = 1
222
+ $log.debug "DDDcols #{cols}, adv #{adv} size: #{lh} h: #{act_height} w #{@width} " if $log.debug?
223
+ list.each_with_index { |e, i|
224
+ text = e
225
+ # signify that there's a deeper level
226
+ case e
227
+ when Array
228
+ text = e.first + "..."
229
+ end
230
+ if indexing == :number
231
+ mnem = i+1
232
+ text = "%d. %s" % [mnem, text]
233
+ elsif indexing == :letter
234
+ mnem = l_succ.succ!
235
+ text = "%s. %s" % [mnem, text]
236
+ end
237
+ # print only within range and window height
238
+ if i >= startindex && row < @window.actual_height
239
+ $log.debug "XXX: MENU #{i} > #{startindex} row #{row} col #{col} "
240
+ @window.printstring row+@row_offset, col, text, $normalcolor
241
+ if indexing
242
+ @window.mvchgat(y=row+@row_offset, x=col, max=1, indexatt, indexcolor, nil)
243
+ end
244
+ colct += 1
245
+ if colct == cols
246
+ col = 1
247
+ row += 1
248
+ colct = 0
249
+ else
250
+ col += adv
251
+ end
252
+ end # startindex
253
+ }
254
+ end
255
+ Ncurses::Panel.update_panels();
256
+ Ncurses.doupdate();
257
+ @window.wrefresh
258
+ end
259
+ # refresh whatevers painted onto the window
260
+ def refresh
261
+ Ncurses::Panel.update_panels();
262
+ Ncurses.doupdate();
263
+ @window.wrefresh
264
+ end
265
+ # clears the window, leaving the title line as is, from row 1 onwards
266
+ def clear
267
+ @window.wmove 1,1
268
+ @window.wclrtobot
269
+ @window.box 0,0 if @box == :border
270
+ # lower line of border will get erased currently since we are writing to
271
+ # last line FIXME
272
+ end
273
+ def display_interactive text, config={}
274
+ if @to
275
+ @to.content text
276
+ else
277
+ config[:box] = @box
278
+ @to = ListObject.new self, text, config
279
+ end
280
+ yield @to if block_given?
281
+ @to.display_interactive # this returns the item selected
282
+ @to # this will return the ListObject to the user with list and current_index
283
+ end
284
+ # non interactive list display - EACH CALL IS CREATING A LIST OBJECT
285
+ def udisplay_list text, config={}
286
+ if @to
287
+ @to.content text
288
+ else
289
+ config[:box] = @box
290
+ @to = ListObject.new self, text, config
291
+ end
292
+ #@to ||= ListObject.new self, text, config
293
+ yield @to if block_given?
294
+ @to.display_content
295
+ @to
296
+ end
297
+ # displays a list
298
+ class ListObject
299
+ attr_reader :cw
300
+ attr_reader :list
301
+ attr_reader :current_index
302
+ attr_accessor :focussed_attrib
303
+ attr_accessor :focussed_symbol
304
+ def initialize cw, _list, config={}
305
+ @cw = cw
306
+ layout = @cw.layout
307
+ @window = @cw.window
308
+ @height = layout[:height]
309
+ @width = layout[:width]
310
+ content(_list)
311
+ @height_adjust = config.fetch(:box, true) == :border ? 3 : 2
312
+ @selected_index = nil
313
+ @current_index = 0
314
+ @row_offset = 1
315
+ @toprow = 0
316
+ $multiplier = 0 # till we can do something
317
+
318
+ @focussed_symbol = ''
319
+ @row_selected_symbol = ''
320
+ #@show_selector = true
321
+ if @show_selector
322
+ @row_selected_symbol ||= '*'
323
+ @row_unselected_symbol ||= ' '
324
+ @left_margin ||= @row_selected_symbol.length
325
+ end
326
+ #@show_selector = true
327
+ #@row_selected_symbol = '*'
328
+ #@row_unselected_symbol = ' '
329
+ end
330
+ def content txt, config={}
331
+ @current_index = 0 # sometimes it gets left at a higher value than there are rows to show
332
+ case txt
333
+ when String
334
+ txt = wrap_text(txt, @width-2).split("\n")
335
+ when Array
336
+ # okay
337
+ end
338
+ @list = txt
339
+ end
340
+ # maybe we should just use a textview or label rather than try to
341
+ # do it all voer again !
342
+ def display_interactive
343
+ display_content
344
+ while !@stop
345
+ @window.wrefresh # FFI 2011-09-12
346
+ # FIXME only clear and redisplay if change has happened (repaint_require)
347
+ handle_keys { |ch| @cw.clear; display_content }
348
+ end
349
+ return @list[@current_index]
350
+ end
351
+ def is_row_selected row
352
+ @selected_index == row
353
+ end
354
+
355
+ def scrollatrow
356
+ @height - 3
357
+ end
358
+ def row_count
359
+ @list.size
360
+ end
361
+ def rowcol
362
+ return @row_offset, @col_offset
363
+ end
364
+ def display_content #:nodoc:
365
+
366
+ @graphic = @window
367
+ @start ||= 0
368
+ @toprow ||= 0
369
+ @left_margin ||= @row_selected_symbol.length + @focussed_symbol.length
370
+
371
+ #print_borders unless @suppress_borders # do this once only, unless everything changes
372
+ #maxlen = @maxlen ||= @width-2
373
+ tm = @list
374
+ rc = tm.size
375
+ @col_offset = 1
376
+ #@longest_line = @width
377
+ if rc > 0 # just added in case no data passed
378
+ #tr = @start #@toprow
379
+ tr = @toprow
380
+ acolor = get_color $datacolor
381
+ #h = @height - 3 #2
382
+ h = @height - @height_adjust
383
+ r,c = @row_offset, @col_offset
384
+ 0.upto(h) do |hh|
385
+ crow = tr+hh
386
+ if crow < rc
387
+ focussed = @current_index == crow # row focussed ?
388
+ selected = is_row_selected crow
389
+ content = tm[crow] # 2009-01-17 18:37 chomp giving error in some cases says frozen
390
+ # by now it has to be a String
391
+ ## set the selector symbol if requested
392
+ selection_symbol = ''
393
+ if @show_selector
394
+ if selected
395
+ selection_symbol = @row_selected_symbol
396
+ else
397
+ selection_symbol = @row_unselected_symbol
398
+ end
399
+ @graphic.printstring r+hh, c, selection_symbol, acolor,@attr
400
+ end
401
+ #renderer = get_default_cell_renderer_for_class content.class.to_s
402
+ if focussed
403
+ if @focussed_symbol
404
+ @graphic.printstring r+hh, c, @focussed_symbol, acolor,@attr
405
+ end
406
+ @graphic.printstring r+hh, c+@left_margin, content, acolor, @focussed_attrib || 'reverse'
407
+ else
408
+ @graphic.printstring r+hh, c+@left_margin, content, acolor,@attr
409
+ end
410
+ #renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, focussed, selected
411
+ else
412
+ # clear rows
413
+ @graphic.printstring r+hh, c, " " * (@width-2), acolor,@attr
414
+ end
415
+ end
416
+ end # rc == 0
417
+ set_form_row
418
+ end
419
+ # listobject
420
+ def press ch # list TODO copy from rbasiclist
421
+ ch = ch.getbyte(0) if ch.class==String ## 1.9
422
+ $log.debug " XXX press #{ch} " if $log.debug?
423
+ case ch
424
+ when -1
425
+ return
426
+ when KEY_F1, 27, ?\C-q.getbyte(0)
427
+ @stop = true
428
+ return
429
+ when KEY_ENTER, 10, 13
430
+ #$log.debug "popup ENTER : #{@selected_index} "
431
+ #$log.debug "popup ENTER : #{field.name}" if !field.nil?
432
+ @stop = true
433
+ return
434
+ when 32, ?\C-d.getbyte(0)
435
+ scroll_forward
436
+ #@start += @height-1
437
+ #bounds_check
438
+ when KEY_UP, ?k.getbyte(0)
439
+ previous_row
440
+ #@start -= 1
441
+ #@current_index -= 1
442
+ #@current_index = 0 if @current_index < 0
443
+ #@start = 0 if @start < 0
444
+ when KEY_DOWN, ?j.getbyte(0)
445
+ next_row
446
+ #@start += 1
447
+ #@current_index += 1
448
+ #bounds_check
449
+ when ?\C-b.getbyte(0)
450
+ scroll_backward
451
+ #@start -= @height-1
452
+ #@start = 0 if @start < 0
453
+ when 0, ?g.getbyte(0)
454
+ goto_top
455
+ when ?G.getbyte(0)
456
+ goto_bottom
457
+ end
458
+ #@form.repaint
459
+ Ncurses::Panel.update_panels();
460
+ Ncurses.doupdate();
461
+ @window.wrefresh
462
+ end
463
+
464
+ def previous_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
465
+ #return :UNHANDLED if @current_index == 0 # EVIL
466
+ return false if @current_index == 0
467
+ @oldrow = @current_index
468
+ num.times {
469
+ @current_index -= 1 if @current_index > 0
470
+ }
471
+ bounds_check
472
+ $multiplier = 0
473
+ end
474
+ alias :up :previous_row
475
+ def next_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
476
+ rc = row_count
477
+ return false if @current_index == rc-1
478
+ @oldrow = @current_index
479
+ @current_index += 1*num if @current_index < rc
480
+ bounds_check
481
+ $multiplier = 0
482
+ end
483
+ alias :down :next_row
484
+ def goto_bottom
485
+ @oldrow = @current_index
486
+ rc = row_count
487
+ @current_index = rc -1
488
+ bounds_check
489
+ end
490
+ alias :goto_end :goto_bottom
491
+ def goto_top
492
+ @oldrow = @current_index
493
+ @current_index = 0
494
+ bounds_check
495
+ end
496
+ alias :goto_start :goto_top
497
+ def scroll_backward
498
+ @oldrow = @current_index
499
+ h = scrollatrow()
500
+ m = $multiplier == 0? 1 : $multiplier
501
+ @current_index -= h * m
502
+ bounds_check
503
+ $multiplier = 0
504
+ end
505
+ def scroll_forward
506
+ @oldrow = @current_index
507
+ h = scrollatrow()
508
+ rc = row_count
509
+ m = $multiplier == 0? 1 : $multiplier
510
+ # more rows than box
511
+ if h * m < rc
512
+ @toprow += h+1 #if @current_index+h < rc
513
+ @current_index = @toprow
514
+ else
515
+ # fewer rows than box
516
+ @current_index = rc -1
517
+ end
518
+ #@current_index += h+1 #if @current_index+h < rc
519
+ bounds_check
520
+ end
521
+
522
+ ##
523
+ # please set oldrow before calling this. Store current_index as oldrow before changing. NOTE
524
+ def bounds_check
525
+ h = scrollatrow()
526
+ rc = row_count
527
+
528
+ @current_index = 0 if @current_index < 0 # not lt 0
529
+ @current_index = rc-1 if @current_index >= rc && rc>0 # not gt rowcount
530
+ @toprow = rc-h-1 if rc > h && @toprow > rc - h - 1 # toprow shows full page if possible
531
+ # curr has gone below table, move toprow forward
532
+ if @current_index - @toprow > h
533
+ @toprow = @current_index - h
534
+ elsif @current_index < @toprow
535
+ # curr has gone above table, move toprow up
536
+ @toprow = @current_index
537
+ end
538
+ set_form_row
539
+
540
+ end
541
+
542
+ def set_form_row
543
+ r,c = rowcol
544
+
545
+
546
+ # when the toprow is set externally then cursor can be mispositioned since
547
+ # bounds_check has not been called
548
+ if @current_index < @toprow
549
+ # cursor is outside table
550
+ @current_index = @toprow # ??? only if toprow 2010-10-19 12:56
551
+ end
552
+
553
+ row = r + (@current_index-@toprow)
554
+ # row should not be < r or greater than r+height TODO FIXME
555
+
556
+ #setrowcol row, nil
557
+ @window.wmove row, c
558
+ @window.wrefresh # FFI added to keep cursor display in synch with selection
559
+ end
560
+ def OLDbounds_check #:nodoc:
561
+ @start = 0 if @start < 0
562
+ row_offset = 1
563
+ last = (@list.length)-(@height-row_offset-1)
564
+ if @start > last
565
+ @start = last
566
+ end
567
+ end # bounds_check
568
+ def handle_keys #:nodoc:
569
+ begin
570
+ while((ch = @window.getchar()) != 999 )
571
+ case ch
572
+ when -1
573
+ next
574
+ else
575
+ press ch
576
+ break if @stop
577
+ yield ch if block_given?
578
+ end
579
+ end
580
+ ensure
581
+ @cw.destroy
582
+ end
583
+ return @current_index
584
+ end
585
+ end # class ListObject
586
+ end # class CommandWindow
587
+ end # module