rbhex-core 1.0.0

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