canis 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +45 -0
- data/CHANGES +52 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +24 -0
- data/Rakefile +2 -0
- data/canis.gemspec +25 -0
- data/examples/alpmenu.rb +46 -0
- data/examples/app.sample +19 -0
- data/examples/appemail.rb +191 -0
- data/examples/atree.rb +105 -0
- data/examples/bline.rb +181 -0
- data/examples/common/devel.rb +319 -0
- data/examples/common/file.rb +93 -0
- data/examples/data/README.markdown +9 -0
- data/examples/data/brew.txt +38 -0
- data/examples/data/color.2 +37 -0
- data/examples/data/gemlist.txt +59 -0
- data/examples/data/lotr.txt +12 -0
- data/examples/data/ports.txt +136 -0
- data/examples/data/table.txt +37 -0
- data/examples/data/tasks.csv +88 -0
- data/examples/data/tasks.txt +27 -0
- data/examples/data/todo.txt +16 -0
- data/examples/data/todocsv.csv +28 -0
- data/examples/data/unix1.txt +21 -0
- data/examples/data/unix2.txt +11 -0
- data/examples/dbdemo.rb +506 -0
- data/examples/dirtree.rb +177 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +92 -0
- data/examples/tabular.rb +212 -0
- data/examples/tasks.rb +179 -0
- data/examples/term2.rb +88 -0
- data/examples/testbuttons.rb +307 -0
- data/examples/testcombo.rb +102 -0
- data/examples/testdb.rb +182 -0
- data/examples/testfields.rb +208 -0
- data/examples/testflowlayout.rb +43 -0
- data/examples/testkeypress.rb +98 -0
- data/examples/testlistbox.rb +187 -0
- data/examples/testlistbox1.rb +199 -0
- data/examples/testmessagebox.rb +144 -0
- data/examples/testprogress.rb +116 -0
- data/examples/testree.rb +107 -0
- data/examples/testsplitlayout.rb +53 -0
- data/examples/testsplitlayout1.rb +49 -0
- data/examples/teststacklayout.rb +48 -0
- data/examples/testwsshortcuts.rb +68 -0
- data/examples/testwsshortcuts2.rb +129 -0
- data/lib/canis.rb +16 -0
- data/lib/canis/core/docs/index.txt +104 -0
- data/lib/canis/core/docs/list.txt +16 -0
- data/lib/canis/core/docs/style_help.yml +34 -0
- data/lib/canis/core/docs/tabbedpane.txt +15 -0
- data/lib/canis/core/docs/table.txt +31 -0
- data/lib/canis/core/docs/textpad.txt +48 -0
- data/lib/canis/core/docs/tree.txt +23 -0
- data/lib/canis/core/include/.DS_Store +0 -0
- data/lib/canis/core/include/action.rb +83 -0
- data/lib/canis/core/include/actionmanager.rb +49 -0
- data/lib/canis/core/include/appmethods.rb +179 -0
- data/lib/canis/core/include/bordertitle.rb +49 -0
- data/lib/canis/core/include/canisparser.rb +100 -0
- data/lib/canis/core/include/colorparser.rb +437 -0
- data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
- data/lib/canis/core/include/io.rb +320 -0
- data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
- data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
- data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
- data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
- data/lib/canis/core/include/listbindings.rb +89 -0
- data/lib/canis/core/include/listeditable.rb +319 -0
- data/lib/canis/core/include/listoperations.rb +61 -0
- data/lib/canis/core/include/listselectionmodel.rb +388 -0
- data/lib/canis/core/include/multibuffer.rb +173 -0
- data/lib/canis/core/include/ractionevent.rb +73 -0
- data/lib/canis/core/include/rchangeevent.rb +27 -0
- data/lib/canis/core/include/rhistory.rb +95 -0
- data/lib/canis/core/include/rinputdataevent.rb +47 -0
- data/lib/canis/core/include/textdocument.rb +111 -0
- data/lib/canis/core/include/vieditable.rb +175 -0
- data/lib/canis/core/include/widgetmenu.rb +66 -0
- data/lib/canis/core/system/colormap.rb +165 -0
- data/lib/canis/core/system/keydefs.rb +32 -0
- data/lib/canis/core/system/ncurses.rb +237 -0
- data/lib/canis/core/system/panel.rb +129 -0
- data/lib/canis/core/system/window.rb +1081 -0
- data/lib/canis/core/util/ansiparser.rb +119 -0
- data/lib/canis/core/util/app.rb +696 -0
- data/lib/canis/core/util/basestack.rb +412 -0
- data/lib/canis/core/util/defaultcolorparser.rb +84 -0
- data/lib/canis/core/util/extras/README +5 -0
- data/lib/canis/core/util/extras/bottomline.rb +1815 -0
- data/lib/canis/core/util/extras/padreader.rb +192 -0
- data/lib/canis/core/util/focusmanager.rb +31 -0
- data/lib/canis/core/util/helpmanager.rb +160 -0
- data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
- data/lib/canis/core/util/promptmenu.rb +235 -0
- data/lib/canis/core/util/rcommandwindow.rb +933 -0
- data/lib/canis/core/util/rdialogs.rb +520 -0
- data/lib/canis/core/util/textutils.rb +74 -0
- data/lib/canis/core/util/viewer.rb +238 -0
- data/lib/canis/core/util/widgetshortcuts.rb +508 -0
- data/lib/canis/core/widgets/applicationheader.rb +103 -0
- data/lib/canis/core/widgets/box.rb +58 -0
- data/lib/canis/core/widgets/divider.rb +310 -0
- data/lib/canis/core/widgets/extras/README.md +12 -0
- data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
- data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
- data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
- data/lib/canis/core/widgets/listbox.rb +326 -0
- data/lib/canis/core/widgets/listfooter.rb +86 -0
- data/lib/canis/core/widgets/rcombo.rb +210 -0
- data/lib/canis/core/widgets/rcontainer.rb +415 -0
- data/lib/canis/core/widgets/rlink.rb +30 -0
- data/lib/canis/core/widgets/rmenu.rb +970 -0
- data/lib/canis/core/widgets/rmenulink.rb +30 -0
- data/lib/canis/core/widgets/rmessagebox.rb +400 -0
- data/lib/canis/core/widgets/rprogress.rb +118 -0
- data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
- data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
- data/lib/canis/core/widgets/rwidget.rb +3634 -0
- data/lib/canis/core/widgets/scrollbar.rb +147 -0
- data/lib/canis/core/widgets/statusline.rb +113 -0
- data/lib/canis/core/widgets/table.rb +1072 -0
- data/lib/canis/core/widgets/tabular.rb +264 -0
- data/lib/canis/core/widgets/textpad.rb +1674 -0
- data/lib/canis/core/widgets/tree.rb +690 -0
- data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
- data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
- data/lib/canis/version.rb +3 -0
- metadata +229 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env ruby -w
|
|
2
|
+
=begin
|
|
3
|
+
* Name : A Quick take on tabular data. Readonly.
|
|
4
|
+
* Description : To show tabular data inside a control, rather than going by the huge
|
|
5
|
+
Table object, I want to create a simple, minimal table data generator.
|
|
6
|
+
This will be thrown into a TextView for the user to navigate, select
|
|
7
|
+
etc.
|
|
8
|
+
I would use this applications where the tabular data is fairly fixed
|
|
9
|
+
not where i want the user to select columns, move them, expand etc.
|
|
10
|
+
* :
|
|
11
|
+
* Author : jkepler
|
|
12
|
+
* Date :
|
|
13
|
+
* License :
|
|
14
|
+
Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
|
15
|
+
|
|
16
|
+
=end
|
|
17
|
+
|
|
18
|
+
#
|
|
19
|
+
# A simple tabular data generator. Given table data in arrays and a column heading row in arrays, it
|
|
20
|
+
# quickely generates tabular data. It only takes left and right alignment of columns into account.
|
|
21
|
+
# You may specify individual column widths. Else it will take the widths of the column names you supply
|
|
22
|
+
# in the startup array. You are encouraged to supply column widths.
|
|
23
|
+
# If no columns are specified, and no widths are given, it take the widths of the first row
|
|
24
|
+
# as a model to determine column widths.
|
|
25
|
+
#
|
|
26
|
+
module Canis
|
|
27
|
+
|
|
28
|
+
class Tabular
|
|
29
|
+
GUESSCOLUMNS = 20
|
|
30
|
+
|
|
31
|
+
def yield_or_eval &block
|
|
32
|
+
return unless block
|
|
33
|
+
if block.arity > 0
|
|
34
|
+
yield self
|
|
35
|
+
else
|
|
36
|
+
self.instance_eval(&block)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
# stores column info internally
|
|
40
|
+
class ColumnInfo < Struct.new(:name, :w, :align)
|
|
41
|
+
end
|
|
42
|
+
# an array of column titles
|
|
43
|
+
attr_reader :columns
|
|
44
|
+
# boolean, does user want lines numbered
|
|
45
|
+
attr_accessor :numbering
|
|
46
|
+
# x is the + character used a field delim in separators
|
|
47
|
+
# y is the field delim used in data rows, default is pipe or bar
|
|
48
|
+
attr_accessor :x, :y
|
|
49
|
+
|
|
50
|
+
# takes first optional argument as array of column names
|
|
51
|
+
# second optional argument as array of data arrays
|
|
52
|
+
# @yield self
|
|
53
|
+
#
|
|
54
|
+
def initialize cols=nil, *args, &block
|
|
55
|
+
@chash = {}
|
|
56
|
+
@cw = {}
|
|
57
|
+
@calign = {}
|
|
58
|
+
@separ = @columns = @numbering = nil
|
|
59
|
+
@y = '|'
|
|
60
|
+
@x = '+'
|
|
61
|
+
self.columns = cols if cols
|
|
62
|
+
if !args.empty?
|
|
63
|
+
#puts "ARGS after shift #{args} "
|
|
64
|
+
if !args.empty?
|
|
65
|
+
self.data = args
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
yield_or_eval(&block) if block_given?
|
|
69
|
+
end
|
|
70
|
+
#
|
|
71
|
+
# set columns names
|
|
72
|
+
# @param [Array<String>] column names, preferably padded out to width for column
|
|
73
|
+
def columns=(array)
|
|
74
|
+
$log.debug "tabular got columns #{array.count} #{array.inspect} " if $log
|
|
75
|
+
@columns = array
|
|
76
|
+
@columns.each_with_index { |c,i|
|
|
77
|
+
@chash[i] = ColumnInfo.new(c, c.to_s.length)
|
|
78
|
+
@cw[i] ||= c.to_s.length
|
|
79
|
+
#@calign[i] ||= :left # 2011-09-27 prevent setting later on
|
|
80
|
+
}
|
|
81
|
+
end
|
|
82
|
+
alias :headings= :columns=
|
|
83
|
+
#
|
|
84
|
+
# set data as an array of arrays
|
|
85
|
+
# @param [Array<Array>] data as array of arrays
|
|
86
|
+
def data=(list)
|
|
87
|
+
#puts "got data: #{list.size} " if !$log
|
|
88
|
+
#puts list if !$log
|
|
89
|
+
@list = list
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# add a row of data
|
|
93
|
+
# @param [Array] an array containing entries for each column
|
|
94
|
+
def add array
|
|
95
|
+
$log.debug "tabular got add #{array.count} #{array.inspect} " if $log
|
|
96
|
+
@list ||= []
|
|
97
|
+
@list << array
|
|
98
|
+
end
|
|
99
|
+
alias :<< :add
|
|
100
|
+
alias :add_row :add
|
|
101
|
+
|
|
102
|
+
# set width of a given column
|
|
103
|
+
# @param [Number] column offset, starting 0
|
|
104
|
+
# @param [Number] width
|
|
105
|
+
def column_width colindex, width
|
|
106
|
+
@cw[colindex] ||= width
|
|
107
|
+
if @chash[colindex].nil?
|
|
108
|
+
@chash[colindex] = ColumnInfo.new("", width)
|
|
109
|
+
else
|
|
110
|
+
@chash[colindex].w = width
|
|
111
|
+
end
|
|
112
|
+
@chash
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# set alignment of given column offset
|
|
116
|
+
# @param [Number] column offset, starting 0
|
|
117
|
+
# @param [Symbol] :left, :right
|
|
118
|
+
def align_column colindex, lrc
|
|
119
|
+
raise ArgumentError, "wrong alignment value sent" if ![:right, :left, :center].include? lrc
|
|
120
|
+
@calign[colindex] ||= lrc
|
|
121
|
+
if @chash[colindex].nil?
|
|
122
|
+
@chash[colindex] = ColumnInfo.new("", nil, lrc)
|
|
123
|
+
else
|
|
124
|
+
@chash[colindex].align = lrc
|
|
125
|
+
end
|
|
126
|
+
@chash
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
#
|
|
130
|
+
# Now returns an array with formatted data
|
|
131
|
+
# @return [Array<String>] array of formatted data
|
|
132
|
+
def render
|
|
133
|
+
buffer = []
|
|
134
|
+
_guess_col_widths
|
|
135
|
+
rows = @list.size.to_s.length
|
|
136
|
+
@rows = rows
|
|
137
|
+
_prepare_format
|
|
138
|
+
|
|
139
|
+
str = ""
|
|
140
|
+
if @numbering
|
|
141
|
+
str = " "*(rows+1)+@y
|
|
142
|
+
end
|
|
143
|
+
str << @fmstr % @columns
|
|
144
|
+
buffer << str
|
|
145
|
+
#puts "-" * str.length
|
|
146
|
+
buffer << separator
|
|
147
|
+
if @list
|
|
148
|
+
if @numbering
|
|
149
|
+
@fmstr = "%#{rows}d "+ @y + @fmstr
|
|
150
|
+
end
|
|
151
|
+
#@list.each { |e| puts e.join(@y) }
|
|
152
|
+
count = 0
|
|
153
|
+
@list.each_with_index { |r,i|
|
|
154
|
+
value = convert_value_to_text r, count
|
|
155
|
+
buffer << value
|
|
156
|
+
count += 1
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
buffer
|
|
160
|
+
end
|
|
161
|
+
def convert_value_to_text r, count
|
|
162
|
+
if r == :separator
|
|
163
|
+
return separator
|
|
164
|
+
end
|
|
165
|
+
if @numbering
|
|
166
|
+
r.insert 0, count+1
|
|
167
|
+
end
|
|
168
|
+
return @fmstr % r;
|
|
169
|
+
end
|
|
170
|
+
# use this for printing out on terminal
|
|
171
|
+
# @example
|
|
172
|
+
# puts t.to_s
|
|
173
|
+
def to_s
|
|
174
|
+
render().join "\n"
|
|
175
|
+
end
|
|
176
|
+
def add_separator
|
|
177
|
+
@list << :separator
|
|
178
|
+
end
|
|
179
|
+
def separator
|
|
180
|
+
return @separ if @separ
|
|
181
|
+
str = ""
|
|
182
|
+
if @numbering
|
|
183
|
+
str = "-"*(@rows+1)+@x
|
|
184
|
+
end
|
|
185
|
+
@cw.each_pair { |k,v| str << "-" * (v+1) + @x }
|
|
186
|
+
@separ = str.chop
|
|
187
|
+
end
|
|
188
|
+
private
|
|
189
|
+
def _guess_col_widths #:nodoc:
|
|
190
|
+
@list.each_with_index { |r, i|
|
|
191
|
+
break if i > GUESSCOLUMNS
|
|
192
|
+
next if r == :separator
|
|
193
|
+
r.each_with_index { |c, j|
|
|
194
|
+
x = c.to_s.length
|
|
195
|
+
if @cw[j].nil?
|
|
196
|
+
@cw[j] = x
|
|
197
|
+
else
|
|
198
|
+
@cw[j] = x if x > @cw[j]
|
|
199
|
+
end
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
end
|
|
203
|
+
def _prepare_format #:nodoc:
|
|
204
|
+
@fmtstr = nil
|
|
205
|
+
fmt = []
|
|
206
|
+
@cw.each_with_index { |c, i|
|
|
207
|
+
w = @cw[i]
|
|
208
|
+
case @calign[i]
|
|
209
|
+
when :right
|
|
210
|
+
fmt << "%#{w}s "
|
|
211
|
+
else
|
|
212
|
+
fmt << "%-#{w}s "
|
|
213
|
+
end
|
|
214
|
+
}
|
|
215
|
+
@fmstr = fmt.join(@y)
|
|
216
|
+
#puts "format: #{@fmstr} " # 2011-12-09 23:09:57
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
if __FILE__ == $PROGRAM_NAME
|
|
222
|
+
include Canis
|
|
223
|
+
$log = nil
|
|
224
|
+
t = Tabular.new(['a', 'b'], [1, 2], [3, 4])
|
|
225
|
+
puts t.to_s
|
|
226
|
+
puts
|
|
227
|
+
t = Tabular.new([" Name ", " Number ", " Email "])
|
|
228
|
+
t.add %w{ rahul 32 r@ruby.org }
|
|
229
|
+
t << %w{ _why 133 j@gnu.org }
|
|
230
|
+
t << %w{ Jane 1331 jane@gnu.org }
|
|
231
|
+
t.column_width 1, 10
|
|
232
|
+
t.align_column 1, :right
|
|
233
|
+
puts t.to_s
|
|
234
|
+
puts
|
|
235
|
+
|
|
236
|
+
s = Tabular.new do |b|
|
|
237
|
+
b.columns = %w{ country continent text }
|
|
238
|
+
b << ["india","asia","a warm country" ]
|
|
239
|
+
b << ["japan","asia","a cool country" ]
|
|
240
|
+
b << ["russia","europe","a hot country" ]
|
|
241
|
+
b.column_width 2, 30
|
|
242
|
+
end
|
|
243
|
+
puts s.to_s
|
|
244
|
+
puts
|
|
245
|
+
puts "::::"
|
|
246
|
+
puts
|
|
247
|
+
s = Tabular.new do |b|
|
|
248
|
+
b.columns = %w{ place continent text }
|
|
249
|
+
b << ["india","asia","a warm country" ]
|
|
250
|
+
b << ["japan","asia","a cool country" ]
|
|
251
|
+
b << ["russia","europe","a hot country" ]
|
|
252
|
+
b << ["sydney","australia","a dry country" ]
|
|
253
|
+
b << ["canberra","australia","a dry country" ]
|
|
254
|
+
b << ["ross island","antarctica","a dry country" ]
|
|
255
|
+
b << ["mount terror","antarctica","a windy country" ]
|
|
256
|
+
b << ["mt erebus","antarctica","a cold place" ]
|
|
257
|
+
b << ["siberia","russia","an icy city" ]
|
|
258
|
+
b << ["new york","USA","a fun place" ]
|
|
259
|
+
b.column_width 0, 12
|
|
260
|
+
b.column_width 1, 12
|
|
261
|
+
b.numbering = true
|
|
262
|
+
end
|
|
263
|
+
puts s.to_s
|
|
264
|
+
end
|
|
@@ -0,0 +1,1674 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# header {{{
|
|
3
|
+
# vim: set foldlevel=0 foldmethod=marker :
|
|
4
|
+
# ----------------------------------------------------------------------------- #
|
|
5
|
+
# File: textpad.rb
|
|
6
|
+
# Description: A class that displays text using a pad.
|
|
7
|
+
# The motivation for this is to put formatted text and not care about truncating and
|
|
8
|
+
# stuff. Also, there will be only one write, not each time scrolling happens.
|
|
9
|
+
# I found textview code for repaint being more complex than required.
|
|
10
|
+
# Author: jkepler http://github.com/mare-imbrium/mancurses/
|
|
11
|
+
# Date: 2011-11-09 - 16:59
|
|
12
|
+
# License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
|
13
|
+
# Last update: 2014-07-08 13:04
|
|
14
|
+
#
|
|
15
|
+
# == CHANGES
|
|
16
|
+
# - changed @content to @list since all multirow widgets use that and so do utils etc
|
|
17
|
+
# == TODO
|
|
18
|
+
# Take care of 3 cases:
|
|
19
|
+
# 1. complete data change, then recreate pad, and call init_vars resetting row, col and curpos etc
|
|
20
|
+
# This is done by method text().
|
|
21
|
+
# 2. row added or minor change - recreate pad, repaint data but don't call initvars. must maintain cursor
|
|
22
|
+
# ignore recreate of pad if width or ht is less than w and h of container.
|
|
23
|
+
# 3. only rewrite a row - row data changed, no recreate pad or anything else
|
|
24
|
+
#
|
|
25
|
+
# ----------------------------------------------------------------------------- #
|
|
26
|
+
# header }}}
|
|
27
|
+
#
|
|
28
|
+
require 'canis'
|
|
29
|
+
require 'canis/core/include/bordertitle'
|
|
30
|
+
require 'canis/core/include/textdocument'
|
|
31
|
+
require 'forwardable'
|
|
32
|
+
|
|
33
|
+
include Canis
|
|
34
|
+
module Canis
|
|
35
|
+
extend self
|
|
36
|
+
class TextPad < Widget #
|
|
37
|
+
include BorderTitle
|
|
38
|
+
extend Forwardable
|
|
39
|
+
|
|
40
|
+
# ---- Section initialization start ----- {{{
|
|
41
|
+
# boolean, whether border to be suppressed or not, default false
|
|
42
|
+
dsl_accessor :suppress_borders
|
|
43
|
+
# boolean, whether footer is to be printed or not, default true
|
|
44
|
+
dsl_accessor :print_footer
|
|
45
|
+
#dsl_accessor :list_footer # attempt at making a footer object
|
|
46
|
+
# index of focussed row, starting 0, index into the data supplied
|
|
47
|
+
attr_reader :current_index
|
|
48
|
+
# rows is the actual number of rows the pad has which is slightly less than height (taking
|
|
49
|
+
# into account borders. Same for cols and width.
|
|
50
|
+
attr_reader :rows , :cols
|
|
51
|
+
dsl_accessor :footer_attrib # bold, reverse, normal
|
|
52
|
+
# adding these only for debugging table, to see where cursor is.
|
|
53
|
+
attr_reader :lastrow, :lastcol
|
|
54
|
+
# for external methods or classes to advance cursor
|
|
55
|
+
#attr_accessor :curpos
|
|
56
|
+
# the object that handles keys that are sent to this object by the form.
|
|
57
|
+
# This widget creates its own default handler if not overridden by user.
|
|
58
|
+
attr_accessor :key_handler
|
|
59
|
+
|
|
60
|
+
# an array of 4 items for h w t and l which can be nil, padrefresh will check
|
|
61
|
+
# its bounds against this to ensure no caller messes up. I don't use this any longer it was experimental.
|
|
62
|
+
dsl_accessor :fixed_bounds
|
|
63
|
+
# You may pass height, width, row and col for creating a window otherwise a fullscreen window
|
|
64
|
+
# will be created. If you pass a window from caller then that window will be used.
|
|
65
|
+
# Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
|
|
66
|
+
# This is currently very minimal and was created to get me started to integrating
|
|
67
|
+
# pads into other classes such as textview.
|
|
68
|
+
|
|
69
|
+
# a map of symbols and patterns, used currently in jumping to next or prev occurence of that
|
|
70
|
+
# pattern. Default contains :word. Callers may add patterns, or modify existing ones and
|
|
71
|
+
# create key bindings for the same.
|
|
72
|
+
attr_reader :text_patterns
|
|
73
|
+
# type of content in case parsing is required. Values :tmux, :ansi, :none
|
|
74
|
+
# trying to put content+type into document
|
|
75
|
+
#attr_accessor :content_type
|
|
76
|
+
# path of yaml file which contains conversion of style names to color, bgcolor and attrib
|
|
77
|
+
#attr_accessor :stylesheet
|
|
78
|
+
attr_accessor :pad
|
|
79
|
+
attr_reader :document
|
|
80
|
+
|
|
81
|
+
def initialize form=nil, config={}, &block
|
|
82
|
+
|
|
83
|
+
@editable = false
|
|
84
|
+
@focusable = true
|
|
85
|
+
@config = config
|
|
86
|
+
@row = @col = 0
|
|
87
|
+
@prow = @pcol = 0
|
|
88
|
+
@startrow = 0
|
|
89
|
+
@startcol = 0
|
|
90
|
+
register_events( [:ENTER_ROW, :PRESS, :DIMENSION_CHANGED, :ROW_CHANGED])
|
|
91
|
+
@text_patterns = {}
|
|
92
|
+
# FIXME we need to be compatible with vim's word
|
|
93
|
+
@text_patterns[:word] = /[[:punct:][:space:]]\S/
|
|
94
|
+
# we need to be compatible with vim's WORD
|
|
95
|
+
@text_patterns[:WORD] = /[[:space:]]\S/
|
|
96
|
+
super
|
|
97
|
+
|
|
98
|
+
init_vars
|
|
99
|
+
end
|
|
100
|
+
def init_vars
|
|
101
|
+
$multiplier = 0
|
|
102
|
+
@oldindex = @current_index = 0
|
|
103
|
+
# column cursor
|
|
104
|
+
@prow = @pcol = @curpos = 0
|
|
105
|
+
if @row && @col
|
|
106
|
+
@lastrow = @row + @row_offset
|
|
107
|
+
@lastcol = @col + @col_offset
|
|
108
|
+
end
|
|
109
|
+
# next 4 are required because in some cases a descendant may not use methods such as list or text
|
|
110
|
+
# to populate. +tree+ has an option of using +root()+.
|
|
111
|
+
@repaint_required = true
|
|
112
|
+
@repaint_all = true
|
|
113
|
+
@_populate_needed = true
|
|
114
|
+
map_keys unless @mapped_keys
|
|
115
|
+
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# calculates the dimensions of the pad which will be used when the pad refreshes, taking into account
|
|
119
|
+
# whether borders are printed or not. This must be called whenever there is a change in height or width
|
|
120
|
+
# otherwise @rows will not be recalculated.
|
|
121
|
+
# Internal.
|
|
122
|
+
def __calc_dimensions
|
|
123
|
+
## NOTE
|
|
124
|
+
# ---------------------------------------------------
|
|
125
|
+
# Since we are using pads, you need to get your height, width and rows correct
|
|
126
|
+
# Make sure the height factors in the row, else nothing may show
|
|
127
|
+
# ---------------------------------------------------
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
raise " CALC inside #{@name} h or w is nil #{@height} , #{@width} " if @height.nil? or @width.nil?
|
|
131
|
+
@rows = @height
|
|
132
|
+
@cols = @width
|
|
133
|
+
# NOTE XXX if cols is > COLS then padrefresh can fail
|
|
134
|
+
@startrow = @row
|
|
135
|
+
@startcol = @col
|
|
136
|
+
unless @suppress_borders
|
|
137
|
+
@row_offset = @col_offset = 1
|
|
138
|
+
@startrow += 1
|
|
139
|
+
@startcol += 1
|
|
140
|
+
@rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
|
141
|
+
@cols -=3
|
|
142
|
+
@scrollatrows = @height - 3
|
|
143
|
+
else
|
|
144
|
+
# no borders printed
|
|
145
|
+
@rows -= 1 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
|
146
|
+
## if next is 0 then padrefresh doesn't print, gives -1 sometimes.
|
|
147
|
+
## otoh, if we reduce 1, then there is a blank or white left at 128 since clear_pad clears 128
|
|
148
|
+
# but this only writes 127 2014-05-01 - 12:31 CLEAR_PAD
|
|
149
|
+
#@cols -=0
|
|
150
|
+
@cols -=1
|
|
151
|
+
@scrollatrows = @height - 1 # check this out 0 or 1
|
|
152
|
+
@row_offset = @col_offset = 0
|
|
153
|
+
end
|
|
154
|
+
@top = @row
|
|
155
|
+
@left = @col
|
|
156
|
+
@lastrow = @row + @row_offset
|
|
157
|
+
@lastcol = @col + @col_offset
|
|
158
|
+
$log.debug " CALC_DIMENSION #{@rows} , #{@cols}, #{@height} , #{@width} , #{@top} , #{@left} "
|
|
159
|
+
end
|
|
160
|
+
def scrollatrows
|
|
161
|
+
unless @suppress_borders
|
|
162
|
+
@height - 3
|
|
163
|
+
else
|
|
164
|
+
@height - 1
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
alias :rows :scrollatrows
|
|
168
|
+
# returns the row and col where the cursor is initially placed, and where printing starts from.
|
|
169
|
+
def rowcol #:nodoc:
|
|
170
|
+
return @row+@row_offset, @col+@col_offset
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# update the height
|
|
174
|
+
# This also calls fire_dimension_changed so that the dimensions can be recalculated
|
|
175
|
+
def height=(val)
|
|
176
|
+
super
|
|
177
|
+
fire_dimension_changed :height
|
|
178
|
+
end
|
|
179
|
+
# set the width
|
|
180
|
+
# This also calls fire_dimension_changed so that the dimensions can be recalculated
|
|
181
|
+
def width=(val)
|
|
182
|
+
super
|
|
183
|
+
fire_dimension_changed :width
|
|
184
|
+
end
|
|
185
|
+
# ---- Section initialization end ----- }}}
|
|
186
|
+
# ---- Section pad related start ----------- {{{
|
|
187
|
+
|
|
188
|
+
private
|
|
189
|
+
## creates the pad
|
|
190
|
+
def create_pad
|
|
191
|
+
destroy if @pad
|
|
192
|
+
#$log.debug "XXXCP: create_pad #{@content_rows} #{@content_cols} , w:#{@width} c #{@cols} , r: #{@rows}"
|
|
193
|
+
|
|
194
|
+
@content_rows = @content_cols = nil
|
|
195
|
+
@content_rows = pad_rows()
|
|
196
|
+
@content_cols = pad_cols()
|
|
197
|
+
$log.debug "XXXCP: create_pad :#{@content_rows} , #{@content_cols} . w:#{@width} c #{@cols} , r: #{@rows}"
|
|
198
|
+
raise "create_pad content_rows is nil " unless @content_rows
|
|
199
|
+
raise "create_pad content_cols is nil " unless @content_cols
|
|
200
|
+
|
|
201
|
+
@pad = @window.get_pad(@content_rows, @content_cols )
|
|
202
|
+
|
|
203
|
+
end
|
|
204
|
+
# content_rows can be more than size of pad, but never less. Same for cols.
|
|
205
|
+
# height of pad, or number of row, earlier called @content_rows
|
|
206
|
+
public
|
|
207
|
+
def pad_rows
|
|
208
|
+
# content_rows can be more than size of pad, but never less. Same for cols.
|
|
209
|
+
return @content_rows if @content_rows
|
|
210
|
+
content_rows = @list.count
|
|
211
|
+
content_rows = @rows if content_rows < @rows
|
|
212
|
+
return content_rows
|
|
213
|
+
end
|
|
214
|
+
# content_rows can be more than size of pad, but never less. Same for cols.
|
|
215
|
+
# width or cols of pad, earlier called @content_cols
|
|
216
|
+
public
|
|
217
|
+
def pad_cols
|
|
218
|
+
return @content_cols if @content_cols
|
|
219
|
+
_content_cols = content_cols()
|
|
220
|
+
_content_cols = @cols if _content_cols < @cols
|
|
221
|
+
return _content_cols
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
private
|
|
225
|
+
# creates pad and sets repaint_all so populate happens
|
|
226
|
+
# FIXME - earlier used to create and populate, but now i have decoupled populate
|
|
227
|
+
# since that was forcing create to happen.
|
|
228
|
+
def populate_pad
|
|
229
|
+
@_populate_needed = false
|
|
230
|
+
|
|
231
|
+
create_pad
|
|
232
|
+
|
|
233
|
+
# clearstring is the string required to clear the pad to background color
|
|
234
|
+
@clearstring = nil
|
|
235
|
+
$log.debug " populate pad color = #{@color} , bg = #{@bgcolor} "
|
|
236
|
+
#cp = get_color($datacolor, color(), bgcolor())
|
|
237
|
+
# commenting off next line meant that textdialog had a black background 2014-05-01 - 23:37
|
|
238
|
+
#@cp = FFI::NCurses.COLOR_PAIR(cp)
|
|
239
|
+
# we seem to be clearing always since a pad is often reused. so making the variable whenever pad created.
|
|
240
|
+
|
|
241
|
+
@repaint_all = true
|
|
242
|
+
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
# clear the pad.
|
|
246
|
+
# There seem to be some cases when previous content of a pad remains
|
|
247
|
+
# in the last row or last col. So we clear.
|
|
248
|
+
# WARNING : pad can only clear the portion of the component placed on the window.
|
|
249
|
+
# As of 2014-05-01 - 16:07 this is no longer called since it messes with messagboxes.
|
|
250
|
+
# If you make this operational, pls test testmessageboxes.rb and look for black areas
|
|
251
|
+
# and see if the left-most column is missing.
|
|
252
|
+
public
|
|
253
|
+
def clear_pad
|
|
254
|
+
# this still doesn't work since somehow content_rows is less than height.
|
|
255
|
+
# this is ineffectual if the rest of the code is functioning.
|
|
256
|
+
# But REQUIRED for listbox which has its own clear_row needed in cases of white background.
|
|
257
|
+
# as in testmessagebox.rb 5
|
|
258
|
+
|
|
259
|
+
# if called directly then cp does not reflect changes to bgcolor
|
|
260
|
+
_color = color()
|
|
261
|
+
_bgcolor = bgcolor()
|
|
262
|
+
cp = get_color($datacolor, _color, _bgcolor)
|
|
263
|
+
@cp = FFI::NCurses.COLOR_PAIR(cp)
|
|
264
|
+
|
|
265
|
+
raise "pad not created" unless @pad
|
|
266
|
+
$log.debug " clear pad content Rows is #{@content_rows} "
|
|
267
|
+
raise "content_rows is nil" unless @content_rows
|
|
268
|
+
(0..@content_rows).each do |n|
|
|
269
|
+
clear_row @pad, n
|
|
270
|
+
end
|
|
271
|
+
# REST IS REQUIRED otherwise sometimes last line of window is not cleared
|
|
272
|
+
# Happens in bline.rb. i think the above clears the new pad size in the window
|
|
273
|
+
# which if it is smaller then does not clear complete window.
|
|
274
|
+
## TRYING OUT COMMENTING OFF THE REMAINDER 2014-05-31 - 14:35
|
|
275
|
+
# next part is messing up messageboxes which have a white background
|
|
276
|
+
# so i use this copied from print_border
|
|
277
|
+
# In messageboxes the border is more inside. but pad cannot clear the entire
|
|
278
|
+
# window. The component may be just a part of the window.
|
|
279
|
+
r,c = rowcol
|
|
280
|
+
# width - 2 was okay if we started from @col + 1 as print_border does in window
|
|
281
|
+
# but here we start from @col so reduce only 1 2014-06-20 - 01:13 TEST THIS XXX
|
|
282
|
+
ww=width-2
|
|
283
|
+
ww=width-1
|
|
284
|
+
_col = @col + 0
|
|
285
|
+
startcol = 1
|
|
286
|
+
startcol = 0 if @suppress_borders
|
|
287
|
+
# need to account for borders. in col+1 and ww
|
|
288
|
+
ww=@width-0 if @suppress_borders
|
|
289
|
+
color = @cp || $datacolor # check this out XXX @cp is already converted to COLOR_PAIR !!
|
|
290
|
+
color = get_color($datacolor, _color, _bgcolor)
|
|
291
|
+
att = attr() || NORMAL
|
|
292
|
+
sp = " " * ww
|
|
293
|
+
#if color == $datacolor
|
|
294
|
+
$log.debug " clear_pad: colors #{@cp}, ( #{_bgcolor} #{_color} ) #{$datacolor} , attrib #{att} . r #{r} w #{ww}, h #{@height} top #{@window.top} "
|
|
295
|
+
# 2014-05-15 - 11:01 seems we were clearing an extra row at bottom.
|
|
296
|
+
# earlier it was r+1 but that was missing the first row, so now made it r+0 2014-06-20 - 01:15 XXX
|
|
297
|
+
(r+0).upto(r+@height-startcol-1) do |rr|
|
|
298
|
+
@window.printstring( rr, _col ,sp , color, att)
|
|
299
|
+
end
|
|
300
|
+
#end
|
|
301
|
+
end
|
|
302
|
+
# destroy the pad, this needs to be called from somewhere, like when the app
|
|
303
|
+
# closes or the current window closes , or else we could have a seg fault
|
|
304
|
+
# or some ugliness on the screen below this one (if nested).
|
|
305
|
+
|
|
306
|
+
# Now since we use get_pad from window, upon the window being destroyed,
|
|
307
|
+
# it will call this. Else it will destroy pad
|
|
308
|
+
def destroy
|
|
309
|
+
FFI::NCurses.delwin(@pad) if @pad # when do i do this ? FIXME
|
|
310
|
+
@pad = nil
|
|
311
|
+
end
|
|
312
|
+
# write pad onto window
|
|
313
|
+
#private
|
|
314
|
+
# padrefresh can fail if width is greater than NCurses.COLS
|
|
315
|
+
# padrefresh can fail if height (@rows + sr) is greater than NCurses.LINES or tput lines
|
|
316
|
+
# try reducing height when creating textpad.
|
|
317
|
+
public
|
|
318
|
+
def padrefresh
|
|
319
|
+
# sometimes padref is called directly from somewhere but dimensions have changed.
|
|
320
|
+
# 2014-05-27 - 11:42
|
|
321
|
+
unless @__first_time
|
|
322
|
+
__calc_dimensions
|
|
323
|
+
@__first_time = true
|
|
324
|
+
end
|
|
325
|
+
# startrow is the row of TP plus 1 for border
|
|
326
|
+
top = @window.top
|
|
327
|
+
left = @window.left
|
|
328
|
+
sr = @startrow + top
|
|
329
|
+
sc = @startcol + left
|
|
330
|
+
ser = @rows + sr
|
|
331
|
+
sec = @cols + sc
|
|
332
|
+
|
|
333
|
+
if @fixed_bounds
|
|
334
|
+
#retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , ser , sec );
|
|
335
|
+
$log.debug "PAD going into fixed_bounds with #{@fixed_bounds}"
|
|
336
|
+
sr = @fixed_bounds[0] if @fixed_bounds[0]
|
|
337
|
+
sc = @fixed_bounds[1] if @fixed_bounds[1]
|
|
338
|
+
ser = @fixed_bounds[2] if @fixed_bounds[2]
|
|
339
|
+
sec = @fixed_bounds[3] if @fixed_bounds[3]
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
# this is a fix, but the entire popup is not moved up. title and borders are still
|
|
343
|
+
# drawn in wrong positions, and left there after popup is off.
|
|
344
|
+
maxr = FFI::NCurses.LINES - 1
|
|
345
|
+
maxc = FFI::NCurses.COLS
|
|
346
|
+
if ser > maxr
|
|
347
|
+
$log.warn "XXX PADRE ser > max. sr= #{sr} and ser #{ser}. sr:#{@startrow}+ #{top} , sc:#{@startcol}+ #{left}, rows:#{@rows}+ #{sr} cols:#{@cols}+ #{sc} top #{top} left #{left} "
|
|
348
|
+
#_t = ser - maxr
|
|
349
|
+
#ser = maxr
|
|
350
|
+
#sr -= _t
|
|
351
|
+
#$log.warn "XXX PADRE after correcting ser #{sr} and #{ser} "
|
|
352
|
+
end
|
|
353
|
+
# there are some freak cases where prow or pcol comes as -1, but prefresh does not return a -1. However, this
|
|
354
|
+
# could affect some other calculation somewhere.
|
|
355
|
+
|
|
356
|
+
retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , ser , sec );
|
|
357
|
+
$log.warn "XXXPADREFRESH #{retval} #{self.class}:#{self.name}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == -1
|
|
358
|
+
# remove next debug statement after some testing DELETE
|
|
359
|
+
$log.debug "0PADREFRESH #{retval} #{self.class}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == 0
|
|
360
|
+
if retval < 0
|
|
361
|
+
Ncurses.beep
|
|
362
|
+
if sr > maxr
|
|
363
|
+
$log.warn "XXXPADREF #{sr} should be <= #{maxr} "
|
|
364
|
+
end
|
|
365
|
+
if sc < 0 || sc >= maxc
|
|
366
|
+
$log.warn "XXXPADREF #{sc} should be less than #{maxc} "
|
|
367
|
+
end
|
|
368
|
+
if ser > maxr || ser < sr
|
|
369
|
+
$log.warn "XXXPADREF #{ser} should be less than #{maxr} and gt #{sr} "
|
|
370
|
+
end
|
|
371
|
+
if sec > maxc || sec < sc
|
|
372
|
+
$log.warn "XXXPADREF #{sec} should be less than #{maxc} and gt #{sc} "
|
|
373
|
+
end
|
|
374
|
+
$log.warn "XXXPADRE sr= #{sr} and ser #{ser}. sr:#{@startrow}+ #{top} , sc:#{@startcol}+ #{left}, rows:#{@rows}+ #{sr} cols:#{@cols}+ #{sc} top #{top} left #{left} "
|
|
375
|
+
end
|
|
376
|
+
#$log.debug "XXX: PADREFRESH #{retval} #{self.class}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{ser}, #{sec}." if retval == 0
|
|
377
|
+
# padrefresh can fail if width is greater than NCurses.COLS
|
|
378
|
+
# or if height exceeds tput lines. As long as content is less, it will work
|
|
379
|
+
# the moment content_rows exceeds then this issue happens.
|
|
380
|
+
# @rows + sr < tput lines
|
|
381
|
+
#FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow + top, @startcol + left, @rows + @startrow + top, @cols+@startcol + left);
|
|
382
|
+
end
|
|
383
|
+
# length of longest string in array
|
|
384
|
+
# This will give a 'wrong' max length if the array has ansi color escape sequences in it
|
|
385
|
+
# which inc the length but won't be printed. Such lines actually have less length when printed
|
|
386
|
+
# So in such cases, give more space to the pad.
|
|
387
|
+
def content_cols
|
|
388
|
+
longest = @list.max_by(&:length)
|
|
389
|
+
## 2013-03-06 - 20:41 crashes here for some reason when man gives error message no man entry
|
|
390
|
+
return 0 unless longest
|
|
391
|
+
longest.length
|
|
392
|
+
end
|
|
393
|
+
public
|
|
394
|
+
# to be called with program / user has added a row or changed column widths so that
|
|
395
|
+
# the pad needs to be recreated. However, cursor positioning is maintained since this
|
|
396
|
+
# is considered to be a minor change.
|
|
397
|
+
# We do not call `init_vars` since user is continuing to do some work on a row/col.
|
|
398
|
+
# NOTE : if height and width are changed then only render_all is required
|
|
399
|
+
# not a reparse since content has not changed.
|
|
400
|
+
def fire_dimension_changed _method=nil
|
|
401
|
+
# recreate pad since width or ht has changed (row count or col width changed)
|
|
402
|
+
@_populate_needed = true
|
|
403
|
+
@repaint_required = true
|
|
404
|
+
@repaint_all = true
|
|
405
|
+
fire_handler :DIMENSION_CHANGED, _method
|
|
406
|
+
@__first_time = nil
|
|
407
|
+
end
|
|
408
|
+
# repaint only one row since content of that row has changed.
|
|
409
|
+
# No recreate of pad is done.
|
|
410
|
+
def fire_row_changed ix
|
|
411
|
+
return if ix >= @list.length
|
|
412
|
+
clear_row @pad, ix
|
|
413
|
+
# allow documents to reparse that line
|
|
414
|
+
fire_handler :ROW_CHANGED, ix
|
|
415
|
+
_arr = _getarray
|
|
416
|
+
render @pad, ix, _arr[ix]
|
|
417
|
+
|
|
418
|
+
end
|
|
419
|
+
# ---- end pad related ----- }}}
|
|
420
|
+
# ---- Section render related ----- {{{
|
|
421
|
+
#
|
|
422
|
+
# iterate through content rendering each row
|
|
423
|
+
# 2013-03-27 - 01:51 separated so that widgets with headers such as tables can
|
|
424
|
+
# override this for better control
|
|
425
|
+
def render_all
|
|
426
|
+
_arr = _getarray
|
|
427
|
+
raise "textpad:render_all: array is nil " unless _arr
|
|
428
|
+
@renderer.source ||= self
|
|
429
|
+
@renderer.render_all @pad, _arr
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
public
|
|
433
|
+
# supply a custom renderer that implements +render()+
|
|
434
|
+
# @see render
|
|
435
|
+
def renderer r
|
|
436
|
+
@renderer = r
|
|
437
|
+
end
|
|
438
|
+
# This is to render a row, for those overriding classes who have overridden
|
|
439
|
+
# render_all, but not +render+. e.g. +Table+. THis is also required
|
|
440
|
+
# for row modifications.
|
|
441
|
+
def render pad, lineno, text
|
|
442
|
+
@renderer.render pad, lineno, text
|
|
443
|
+
end
|
|
444
|
+
#
|
|
445
|
+
## ---- the next 2 methods deal with printing chunks
|
|
446
|
+
# we should put it int a common module and include it
|
|
447
|
+
# in Window and Pad stuff and perhaps include it conditionally.
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
# before updating a single row in a table
|
|
451
|
+
# we need to clear the row otherwise previous contents can show through
|
|
452
|
+
def clear_row pad, lineno
|
|
453
|
+
if @renderer and @renderer.respond_to? :clear_row
|
|
454
|
+
@renderer.clear_row pad, lineno
|
|
455
|
+
else
|
|
456
|
+
# need pad width not window width, the other clearstring uses width of
|
|
457
|
+
# widget to paint on window.
|
|
458
|
+
@_clearstring ||= " " * @content_cols
|
|
459
|
+
# what about bg color ??? XXX, left_margin and internal width
|
|
460
|
+
#cp = get_color($datacolor, @color, @bgcolor)
|
|
461
|
+
cp = @cp || FFI::NCurses.COLOR_PAIR($datacolor)
|
|
462
|
+
att = attr() || NORMAL
|
|
463
|
+
FFI::NCurses.wattron(pad,cp | att)
|
|
464
|
+
FFI::NCurses.mvwaddstr(pad,lineno, 0, @_clearstring)
|
|
465
|
+
FFI::NCurses.wattroff(pad,cp | att)
|
|
466
|
+
end
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# print footer containing line and position
|
|
470
|
+
def print_foot #:nodoc:
|
|
471
|
+
return unless @print_footer
|
|
472
|
+
return unless @suppress_borders
|
|
473
|
+
footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
|
|
474
|
+
@graphic.printstring( @row + @height -1 , @col+2, footer, @color_pair || $datacolor, @footer_attrib)
|
|
475
|
+
=begin
|
|
476
|
+
if @list_footer
|
|
477
|
+
if false
|
|
478
|
+
# if we want to print ourselves
|
|
479
|
+
footer = @list_footer.text(self)
|
|
480
|
+
footer_attrib = @list_footer.config[:attrib] || Ncurses::A_REVERSE
|
|
481
|
+
#footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
|
|
482
|
+
$log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
|
|
483
|
+
@graphic.printstring( @row + @height -1 , @col+2, footer, @color_pair || $datacolor, footer_attrib)
|
|
484
|
+
end
|
|
485
|
+
# use default print method which only prints on left
|
|
486
|
+
@list_footer.print self
|
|
487
|
+
end
|
|
488
|
+
=end
|
|
489
|
+
@repaint_footer_required = false # 2010-01-23 22:55
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# ---- Section render related end ----- }}}
|
|
493
|
+
# ---- Section data related start {{{
|
|
494
|
+
|
|
495
|
+
# supply a filename as source for textpad
|
|
496
|
+
# Reads up file into @list
|
|
497
|
+
# One can optionally send in a method which takes a filename and returns an array of data
|
|
498
|
+
# This is required if you are processing files which are binary such as zip/archives and wish
|
|
499
|
+
# to print the contents. (e.g. cygnus gem sends in :get_file_contents).
|
|
500
|
+
# filename("a.c", method(:get_file_contents))
|
|
501
|
+
#
|
|
502
|
+
# TODO: i think this should be sent to +text+ in case content_type has been set. FIXME
|
|
503
|
+
#
|
|
504
|
+
def filename(filename, reader=nil)
|
|
505
|
+
@file = filename
|
|
506
|
+
unless File.exists? filename
|
|
507
|
+
alert "#{filename} does not exist"
|
|
508
|
+
return
|
|
509
|
+
end
|
|
510
|
+
@filetype = File.extname filename
|
|
511
|
+
if reader
|
|
512
|
+
@list = reader.call(filename)
|
|
513
|
+
else
|
|
514
|
+
@list = File.open(filename,"r").read.split("\n")
|
|
515
|
+
end
|
|
516
|
+
if @filetype == ""
|
|
517
|
+
if @list.first.index("ruby")
|
|
518
|
+
@filetype = ".rb"
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
init_vars
|
|
522
|
+
@repaint_all = true
|
|
523
|
+
@_populate_needed = true
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
## NOTE this breaks widgets and everyone's text which returns text of object
|
|
527
|
+
# also list by itself should return the list as in listbox, not just set
|
|
528
|
+
# Supply an array of string to be displayed
|
|
529
|
+
# This will replace existing text
|
|
530
|
+
|
|
531
|
+
# display text given in an array format. This is the principal way of giving content
|
|
532
|
+
# to a textpad, other than filename().
|
|
533
|
+
# @param Array of lines
|
|
534
|
+
# @param format (optional) can be :tmux :ansi or :none
|
|
535
|
+
# If a format other than :none is given, then formatted_text is called.
|
|
536
|
+
def text(*val)
|
|
537
|
+
if val.empty?
|
|
538
|
+
return @list
|
|
539
|
+
end
|
|
540
|
+
$log.debug " TEXTPAD inside text() with #{val.class} "
|
|
541
|
+
case val
|
|
542
|
+
when Array
|
|
543
|
+
# either its an array of strings
|
|
544
|
+
# or its an array, and val[0] is an array of strings, and val[1] is a hash / symbol TODO
|
|
545
|
+
case val[0]
|
|
546
|
+
when String
|
|
547
|
+
# This is the usual simple case of an array of strings
|
|
548
|
+
@list = val
|
|
549
|
+
$log.debug " creating TEXTDOC 0 with String"
|
|
550
|
+
when TextDocument
|
|
551
|
+
# this is repeating it seems FIXME
|
|
552
|
+
$log.debug " creating TEXTDOC 04 with TextDocu #{val[0].content_type} "
|
|
553
|
+
|
|
554
|
+
@document = val[0]
|
|
555
|
+
@document.source ||= self
|
|
556
|
+
@list = @document.text
|
|
557
|
+
when Array
|
|
558
|
+
# This is the complex case which i would like to phase out.
|
|
559
|
+
# Earlier this was what was used where the second arg was an optional hash
|
|
560
|
+
@list = val[0]
|
|
561
|
+
if val[1].is_a? Symbol
|
|
562
|
+
content_type = val[1]
|
|
563
|
+
hsh = { :text => @list, :content_type => content_type }
|
|
564
|
+
$log.debug " creating TEXTDOC 1 with #{content_type} "
|
|
565
|
+
@document = TextDocument.new hsh
|
|
566
|
+
@document.source = self
|
|
567
|
+
elsif val[1].is_a? Hash
|
|
568
|
+
# this is hack for those cases where ct is there, but the caller may not
|
|
569
|
+
# pass it in config
|
|
570
|
+
if val[1].key? :content_type and val[1][:content_type].nil?
|
|
571
|
+
;
|
|
572
|
+
else
|
|
573
|
+
# content type comes nil from viewer/help which sets it later using block yield
|
|
574
|
+
content_type = val[1][:content_type]
|
|
575
|
+
stylesheet = val[1][:stylesheet]
|
|
576
|
+
@title = val[1][:title] if val[1].key? :title
|
|
577
|
+
$log.debug " creating TEXTDOC 2 with ct=#{content_type}, #{val[1]} "
|
|
578
|
+
@document = TextDocument.new val[1]
|
|
579
|
+
@document.text = @list
|
|
580
|
+
@document.source = self
|
|
581
|
+
end
|
|
582
|
+
else
|
|
583
|
+
#raise "val_1 Unable to do anything with #{val[1].class} "
|
|
584
|
+
$log.debug " val_1 Unable to do anything with #{val[1].class} in textpad text()"
|
|
585
|
+
end
|
|
586
|
+
else
|
|
587
|
+
raise "val_0 Unable to do anything with #{val[0].class} "
|
|
588
|
+
end
|
|
589
|
+
when Hash
|
|
590
|
+
$log.debug " creating TEXTDOC 3 with #{val[:content_type]} "
|
|
591
|
+
@document = TextDocument.new val
|
|
592
|
+
@document.source ||= self
|
|
593
|
+
@list = @document.text
|
|
594
|
+
when TextDocument
|
|
595
|
+
$log.debug " creating TEXTDOC 4 with TextDocu #{val.content_type} "
|
|
596
|
+
|
|
597
|
+
@document = val
|
|
598
|
+
@document.source ||= self
|
|
599
|
+
@list = @document.text
|
|
600
|
+
end
|
|
601
|
+
@_populate_needed = true
|
|
602
|
+
@repaint_all = true
|
|
603
|
+
@repaint_required = true
|
|
604
|
+
init_vars
|
|
605
|
+
# i don't want the whole thing going into the event 2014-06-04
|
|
606
|
+
fire_property_change :text, "dummmy", "text has changed"
|
|
607
|
+
self
|
|
608
|
+
end
|
|
609
|
+
# old text {{{
|
|
610
|
+
def ORIG_text(*val)
|
|
611
|
+
if val.empty?
|
|
612
|
+
return @list
|
|
613
|
+
end
|
|
614
|
+
lines = val[0]
|
|
615
|
+
raise "Textpad: text() received nil" unless lines
|
|
616
|
+
fmt = val.size == 2 ? val[1] : nil
|
|
617
|
+
case fmt
|
|
618
|
+
when Hash
|
|
619
|
+
#raise "textpad.text expected content_type in Hash : #{fmt}"
|
|
620
|
+
c = fmt[:content_type]
|
|
621
|
+
t = fmt[:title]
|
|
622
|
+
@title = t if t
|
|
623
|
+
@content_type = c if c
|
|
624
|
+
@stylesheet = fmt[:stylesheet] if fmt.key? :stylesheet
|
|
625
|
+
$log.debug " TEXTPAD text() got #{@content_type} and #{@stylesheet} "
|
|
626
|
+
fmt = c
|
|
627
|
+
#raise "textpad.text expected content_type in Hash : #{fmt}" unless fmt
|
|
628
|
+
when Symbol
|
|
629
|
+
@content_type = fmt
|
|
630
|
+
when NilClass
|
|
631
|
+
else
|
|
632
|
+
raise "textpad.text expected symbol or content_type in Hash, got #{fmt.class} "
|
|
633
|
+
end
|
|
634
|
+
|
|
635
|
+
## some programs like testlistbox which uses multibuffers calls this with a config
|
|
636
|
+
# in arg2 containing :content_type and :title
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
# added so callers can have one interface and avoid an if condition
|
|
640
|
+
#return formatted_text(lines, fmt) unless @content_type == :none
|
|
641
|
+
# 2014-05-20 - 13:21 change and simplication of conversion process
|
|
642
|
+
# We maintain original text in @list
|
|
643
|
+
# but use another variable for native format (chunks).
|
|
644
|
+
@parse_required = true
|
|
645
|
+
@list = lines
|
|
646
|
+
|
|
647
|
+
# 2014-06-15 - 13:39 i am commenting this off, since a user can change or set content_type
|
|
648
|
+
# at any time, and also content handler.
|
|
649
|
+
if @content_type
|
|
650
|
+
# this will create a default content handler if not yet specified
|
|
651
|
+
# which is wrong since user may specify it after setting text. he may not have
|
|
652
|
+
# set the content type yet too. Too much dependent on order !
|
|
653
|
+
#preprocess_text
|
|
654
|
+
#parse_formatted_text lines, :content_type => @content_type, :stylesheet => @stylesheet
|
|
655
|
+
end
|
|
656
|
+
|
|
657
|
+
return @list if lines.empty?
|
|
658
|
+
#@list = lines
|
|
659
|
+
#@native_text ||= @list
|
|
660
|
+
@_populate_needed = true
|
|
661
|
+
@repaint_all = true
|
|
662
|
+
@repaint_required = true
|
|
663
|
+
init_vars
|
|
664
|
+
# i don't want the whole thing going into the event 2014-06-04
|
|
665
|
+
fire_property_change :text, "dummmy", "text has changed"
|
|
666
|
+
self
|
|
667
|
+
end # ORIG_text
|
|
668
|
+
# old text }}}
|
|
669
|
+
alias :list :text
|
|
670
|
+
# for compat with textview, FIXME keep one consistent name for this
|
|
671
|
+
alias :set_content :text
|
|
672
|
+
# this is returning the original content
|
|
673
|
+
# Who is using this, should it return native ?
|
|
674
|
+
def content
|
|
675
|
+
raise "content is nil " unless @list
|
|
676
|
+
return @list
|
|
677
|
+
end
|
|
678
|
+
alias :get_content :content
|
|
679
|
+
#
|
|
680
|
+
# internal method to return the correct list.
|
|
681
|
+
# Rather than trying to synch list and native text for those who do not use the latter
|
|
682
|
+
# let us just use the correct array
|
|
683
|
+
# NOTE there are some cases where document can return a nil since native_text has not been
|
|
684
|
+
# calculated yet. Happens in back button of help. Earlier preprocess was done from +text+
|
|
685
|
+
# not it is only done from +repaint+
|
|
686
|
+
def _getarray
|
|
687
|
+
if @document.nil?
|
|
688
|
+
return @list
|
|
689
|
+
else
|
|
690
|
+
return @document.native_text
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
# textpad's preprocess
|
|
694
|
+
def preprocess_text
|
|
695
|
+
if @document
|
|
696
|
+
@document.preprocess_text @list
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
#
|
|
700
|
+
# returns focussed value (what cursor is on)
|
|
701
|
+
# This may not be a string. A tree may return a node, a table an array or row
|
|
702
|
+
def current_value
|
|
703
|
+
# many descendants do not set native_text - note that list and tree and table use just @list.
|
|
704
|
+
#@native_text[@current_index]
|
|
705
|
+
_getarray[@current_index]
|
|
706
|
+
end
|
|
707
|
+
## NOTE : 2014-04-09 - 14:05 i think this does not have line wise operations since we deal with
|
|
708
|
+
# formatting of data
|
|
709
|
+
# But what if data is not formatted. This imposes a severe limitation. listbox does have linewise
|
|
710
|
+
# operations, so lets try them
|
|
711
|
+
#
|
|
712
|
+
## append a row to the list
|
|
713
|
+
# @deprecated pls use << or push as per Array semantics
|
|
714
|
+
def append text
|
|
715
|
+
raise "append: deprecated pls use << or push as per Array semantics"
|
|
716
|
+
@list ||= []
|
|
717
|
+
@list.push text
|
|
718
|
+
fire_dimension_changed :append
|
|
719
|
+
self
|
|
720
|
+
end
|
|
721
|
+
# @deprecated : row_count used just for compat, use length or size
|
|
722
|
+
def row_count ; @list.length ; end
|
|
723
|
+
|
|
724
|
+
## ------ LIST / ARRAY OPERATIONS ----
|
|
725
|
+
# All multirow widgets must use Array semantics 2014-04-10 - 17:29
|
|
726
|
+
# NOTE some operations will make selected indices in selection modules invalid
|
|
727
|
+
# clear will need to clear indices, delete_at and insert may need to also adjust
|
|
728
|
+
# selection or focus index/es.
|
|
729
|
+
#
|
|
730
|
+
# delegate some operations to Array
|
|
731
|
+
# ---- operations that reference Array, no modifications
|
|
732
|
+
def_delegators :@list, :include?, :each, :values_at, :size, :length, :[]
|
|
733
|
+
|
|
734
|
+
# ---- operations that modify data
|
|
735
|
+
# delegate some modify operations to Array: insert, clear, delete_at, []= <<
|
|
736
|
+
# However, we should check if content array is nil ?
|
|
737
|
+
# fire_dim is called, although it is not required in []=
|
|
738
|
+
%w[ insert delete_at << push].each { |e|
|
|
739
|
+
eval %{
|
|
740
|
+
def #{e}(*args)
|
|
741
|
+
@list ||= []
|
|
742
|
+
fire_dimension_changed :#{e}
|
|
743
|
+
@list.send(:#{e}, *args)
|
|
744
|
+
self
|
|
745
|
+
end
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
# clear all items in the object.
|
|
749
|
+
# NOTE: requires to be separate since init_vars is called to reset index of focus etc.
|
|
750
|
+
# Also, listbox will extend this to clear selected_indices
|
|
751
|
+
def clear
|
|
752
|
+
return unless @list
|
|
753
|
+
@list.clear
|
|
754
|
+
@native_text.clear
|
|
755
|
+
fire_dimension_changed :clear
|
|
756
|
+
init_vars
|
|
757
|
+
end
|
|
758
|
+
# update the value at index with given value, returning self
|
|
759
|
+
def []=(index, val)
|
|
760
|
+
@list[index]=val
|
|
761
|
+
fire_row_changed index
|
|
762
|
+
self
|
|
763
|
+
end
|
|
764
|
+
# ---- Section data related end }}}
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
#---- Section: movement -----# {{{
|
|
770
|
+
# goto first line of file
|
|
771
|
+
public
|
|
772
|
+
def goto_start
|
|
773
|
+
$multiplier ||= 0
|
|
774
|
+
if $multiplier > 0
|
|
775
|
+
goto_line $multiplier - 1
|
|
776
|
+
return
|
|
777
|
+
end
|
|
778
|
+
@current_index = 0
|
|
779
|
+
@curpos = @pcol = @prow = 0
|
|
780
|
+
@prow = 0
|
|
781
|
+
$multiplier = 0
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
# goto last line of file
|
|
785
|
+
def goto_end
|
|
786
|
+
$multiplier ||= 0
|
|
787
|
+
if $multiplier > 0
|
|
788
|
+
goto_line $multiplier - 1
|
|
789
|
+
return
|
|
790
|
+
end
|
|
791
|
+
@current_index = @list.count() - 1
|
|
792
|
+
@prow = @current_index - @scrollatrows
|
|
793
|
+
$multiplier = 0
|
|
794
|
+
end
|
|
795
|
+
def goto_line line
|
|
796
|
+
## we may need to calculate page, zfm style and place at right position for ensure visible
|
|
797
|
+
#line -= 1
|
|
798
|
+
@current_index = line
|
|
799
|
+
ensure_visible line
|
|
800
|
+
bounds_check
|
|
801
|
+
$multiplier = 0
|
|
802
|
+
end
|
|
803
|
+
def top_of_window
|
|
804
|
+
@current_index = @prow
|
|
805
|
+
$multiplier ||= 0
|
|
806
|
+
if $multiplier > 0
|
|
807
|
+
@current_index += $multiplier
|
|
808
|
+
$multiplier = 0
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
def bottom_of_window
|
|
812
|
+
@current_index = @prow + @scrollatrows
|
|
813
|
+
$multiplier ||= 0
|
|
814
|
+
if $multiplier > 0
|
|
815
|
+
@current_index -= $multiplier
|
|
816
|
+
$multiplier = 0
|
|
817
|
+
end
|
|
818
|
+
end
|
|
819
|
+
|
|
820
|
+
def middle_of_window
|
|
821
|
+
@current_index = @prow + (@scrollatrows/2)
|
|
822
|
+
$multiplier = 0
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
# move down a line mimicking vim's j key
|
|
826
|
+
# @param [int] multiplier entered prior to invoking key
|
|
827
|
+
def down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
828
|
+
#@oldindex = @current_index if num > 10
|
|
829
|
+
@current_index += num
|
|
830
|
+
# no , i don't like this here. it scrolls up too much making prow = current_index
|
|
831
|
+
unless is_visible? @current_index
|
|
832
|
+
#alert "#{@current_index} not visible prow #{@prow} #{@scrollatrows} "
|
|
833
|
+
@prow += num
|
|
834
|
+
end
|
|
835
|
+
#ensure_visible
|
|
836
|
+
$multiplier = 0
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
# move up a line mimicking vim's k key
|
|
840
|
+
# @param [int] multiplier entered prior to invoking key
|
|
841
|
+
def up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
842
|
+
#@oldindex = @current_index if num > 10
|
|
843
|
+
@current_index -= num
|
|
844
|
+
#unless is_visible? @current_index
|
|
845
|
+
#if @prow > @current_index
|
|
846
|
+
##$status_message.value = "1 #{@prow} > #{@current_index} "
|
|
847
|
+
#@prow -= 1
|
|
848
|
+
#else
|
|
849
|
+
#end
|
|
850
|
+
#end
|
|
851
|
+
$multiplier = 0
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
# scrolls window down mimicking vim C-e
|
|
855
|
+
# @param [int] multiplier entered prior to invoking key
|
|
856
|
+
def scroll_window_down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
857
|
+
@prow += num
|
|
858
|
+
if @prow > @current_index
|
|
859
|
+
@current_index += 1
|
|
860
|
+
end
|
|
861
|
+
#check_prow
|
|
862
|
+
$multiplier = 0
|
|
863
|
+
end
|
|
864
|
+
|
|
865
|
+
# scrolls window up mimicking vim C-y
|
|
866
|
+
# @param [int] multiplier entered prior to invoking key
|
|
867
|
+
def scroll_window_up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
868
|
+
@prow -= num
|
|
869
|
+
unless is_visible? @current_index
|
|
870
|
+
# one more check may be needed here TODO
|
|
871
|
+
@current_index -= num
|
|
872
|
+
end
|
|
873
|
+
$multiplier = 0
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
# scrolls lines a window full at a time, on pressing SPACE or C-d or pagedown
|
|
877
|
+
def scroll_forward
|
|
878
|
+
#@oldindex = @current_index
|
|
879
|
+
@current_index += @scrollatrows
|
|
880
|
+
@prow = @current_index - @scrollatrows
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
# scrolls lines backward a window full at a time, on pressing pageup
|
|
884
|
+
# C-u may not work since it is trapped by form earlier. Need to fix
|
|
885
|
+
def scroll_backward
|
|
886
|
+
#@oldindex = @current_index
|
|
887
|
+
@current_index -= @scrollatrows
|
|
888
|
+
@prow = @current_index - @scrollatrows
|
|
889
|
+
end
|
|
890
|
+
def goto_last_position
|
|
891
|
+
return unless @oldindex
|
|
892
|
+
tmp = @current_index
|
|
893
|
+
@current_index = @oldindex
|
|
894
|
+
@oldindex = tmp
|
|
895
|
+
bounds_check
|
|
896
|
+
end
|
|
897
|
+
def scroll_right
|
|
898
|
+
# I don't think it will ever be less since we've increased it to cols
|
|
899
|
+
if @content_cols <= @cols
|
|
900
|
+
maxpcol = 0
|
|
901
|
+
@pcol = 0
|
|
902
|
+
else
|
|
903
|
+
maxpcol = @content_cols - @cols - 1
|
|
904
|
+
@pcol += 1
|
|
905
|
+
@pcol = maxpcol if @pcol > maxpcol
|
|
906
|
+
end
|
|
907
|
+
# to prevent right from retaining earlier painted values
|
|
908
|
+
# padreader does not do a clear, yet works fine.
|
|
909
|
+
# OK it has an update_panel after padrefresh, that clears it seems.
|
|
910
|
+
#this clears entire window not just the pad
|
|
911
|
+
#FFI::NCurses.wclear(@window.get_window)
|
|
912
|
+
# so border and title is repainted after window clearing
|
|
913
|
+
#
|
|
914
|
+
# Next line was causing all sorts of problems when scrolling with ansi formatted text
|
|
915
|
+
#@repaint_all = true
|
|
916
|
+
end
|
|
917
|
+
def scroll_left
|
|
918
|
+
@pcol -= 1
|
|
919
|
+
end
|
|
920
|
+
#
|
|
921
|
+
# jumps cursor to next word, like vim's w key
|
|
922
|
+
#
|
|
923
|
+
def forward_word
|
|
924
|
+
#forward_regex(/[[:punct:][:space:]]\w/)
|
|
925
|
+
forward_regex(:word)
|
|
926
|
+
end
|
|
927
|
+
# jump to the next occurence of given regex in the current line.
|
|
928
|
+
# It only jumps to next line after exhausting current.
|
|
929
|
+
# @param [Regexp] passed to String.index
|
|
930
|
+
def forward_regex regex
|
|
931
|
+
if regex.is_a? Symbol
|
|
932
|
+
regex = @text_patterns[regex]
|
|
933
|
+
raise "Pattern specified #{regex} does not exist in text_patterns " unless regex
|
|
934
|
+
end
|
|
935
|
+
$multiplier = 1 if !$multiplier || $multiplier == 0
|
|
936
|
+
line = @current_index
|
|
937
|
+
_arr = _getarray
|
|
938
|
+
buff = _arr[line].to_s
|
|
939
|
+
return unless buff
|
|
940
|
+
pos = @curpos || 0 # list does not have curpos
|
|
941
|
+
$multiplier.times {
|
|
942
|
+
found = buff.index(regex, pos)
|
|
943
|
+
if !found
|
|
944
|
+
# if not found, we've lost a counter
|
|
945
|
+
if line+1 < _arr.length
|
|
946
|
+
line += 1
|
|
947
|
+
else
|
|
948
|
+
return
|
|
949
|
+
end
|
|
950
|
+
pos = 0
|
|
951
|
+
else
|
|
952
|
+
pos = found + 1
|
|
953
|
+
end
|
|
954
|
+
$log.debug " forward_word: pos #{pos} line #{line} buff: #{buff}"
|
|
955
|
+
}
|
|
956
|
+
$multiplier = 0
|
|
957
|
+
@current_index = line
|
|
958
|
+
@curpos = pos
|
|
959
|
+
ensure_visible
|
|
960
|
+
@repaint_required = true
|
|
961
|
+
end
|
|
962
|
+
# jump to previous word, like vim's "b"
|
|
963
|
+
def backward_word
|
|
964
|
+
#backward_regex(/[[:punct:][:space:]]\w/)
|
|
965
|
+
backward_regex(:word)
|
|
966
|
+
end
|
|
967
|
+
# jump to previous occurence of given regexp within a line, and then to previous line
|
|
968
|
+
# This is more line 'w' or 'b' in vim, not like '/'
|
|
969
|
+
# @param [Regexp] pattern to go back to.
|
|
970
|
+
def backward_regex regex
|
|
971
|
+
if regex.is_a? Symbol
|
|
972
|
+
regex = @text_patterns[regex]
|
|
973
|
+
raise "Pattern specified #{regex} does not exist in text_patterns " unless regex
|
|
974
|
+
end
|
|
975
|
+
$multiplier = 1 if !$multiplier || $multiplier == 0
|
|
976
|
+
_arr = _getarray
|
|
977
|
+
pos = @curpos || 0 # list does not have curpos
|
|
978
|
+
line = @current_index
|
|
979
|
+
#buff = _arr[line].to_s
|
|
980
|
+
#return unless buff
|
|
981
|
+
# if curpos is at zero , we should be checking previous line !
|
|
982
|
+
$multiplier.times {
|
|
983
|
+
# if at start of line, go to previous line
|
|
984
|
+
if pos == 0
|
|
985
|
+
if @current_index > 0
|
|
986
|
+
line -= 1
|
|
987
|
+
pos = _arr[line].to_s.size
|
|
988
|
+
else
|
|
989
|
+
# we are on first line of file at start
|
|
990
|
+
break
|
|
991
|
+
end
|
|
992
|
+
end
|
|
993
|
+
buff = _arr[line].to_s
|
|
994
|
+
return unless buff
|
|
995
|
+
pos2 = pos - 2
|
|
996
|
+
pos2 = 0 if pos2 < 0
|
|
997
|
+
found = buff.rindex(regex, pos2)
|
|
998
|
+
$log.debug " backward: pos #{pos} , found #{found}, pos2 = #{pos2} "
|
|
999
|
+
if !found || found == 0
|
|
1000
|
+
# if not found, we've lost a counter
|
|
1001
|
+
if pos > 0
|
|
1002
|
+
pos = 0
|
|
1003
|
+
elsif line > 0
|
|
1004
|
+
#line -= 1
|
|
1005
|
+
#pos = _arr[line].to_s.size
|
|
1006
|
+
else
|
|
1007
|
+
break
|
|
1008
|
+
end
|
|
1009
|
+
else
|
|
1010
|
+
pos = found + 1
|
|
1011
|
+
end
|
|
1012
|
+
$log.debug " backward_word: pos #{pos} line #{line} buff: #{buff}"
|
|
1013
|
+
}
|
|
1014
|
+
$multiplier = 0
|
|
1015
|
+
@current_index = line
|
|
1016
|
+
@curpos = pos
|
|
1017
|
+
ensure_visible
|
|
1018
|
+
@repaint_required = true
|
|
1019
|
+
end
|
|
1020
|
+
#
|
|
1021
|
+
# move cursor forward by one char (currently will not pan)
|
|
1022
|
+
def cursor_forward
|
|
1023
|
+
$multiplier = 1 if $multiplier == 0
|
|
1024
|
+
if @curpos < @cols
|
|
1025
|
+
@curpos += $multiplier
|
|
1026
|
+
if @curpos > @cols
|
|
1027
|
+
@curpos = @cols
|
|
1028
|
+
end
|
|
1029
|
+
@repaint_required = true
|
|
1030
|
+
end
|
|
1031
|
+
$multiplier = 0
|
|
1032
|
+
end
|
|
1033
|
+
#
|
|
1034
|
+
# move cursor backward by one char (currently will not pan)
|
|
1035
|
+
def cursor_backward
|
|
1036
|
+
$multiplier = 1 if $multiplier == 0
|
|
1037
|
+
if @curpos > 0
|
|
1038
|
+
@curpos -= $multiplier
|
|
1039
|
+
@curpos = 0 if @curpos < 0
|
|
1040
|
+
@repaint_required = true
|
|
1041
|
+
end
|
|
1042
|
+
$multiplier = 0
|
|
1043
|
+
end
|
|
1044
|
+
# moves cursor to end of line also panning window if necessary
|
|
1045
|
+
# NOTE: if one line on another page (not displayed) is way longer than any
|
|
1046
|
+
# displayed line, then this will pan way ahead, so may not be very intelligent
|
|
1047
|
+
# in such situations.
|
|
1048
|
+
def cursor_eol
|
|
1049
|
+
# pcol is based on max length not current line's length
|
|
1050
|
+
@pcol = @content_cols - @cols - 1
|
|
1051
|
+
_arr = _getarray
|
|
1052
|
+
@curpos = _arr[@current_index].size
|
|
1053
|
+
@repaint_required = true
|
|
1054
|
+
end
|
|
1055
|
+
#
|
|
1056
|
+
# moves cursor to start of line, panning if required
|
|
1057
|
+
def cursor_bol
|
|
1058
|
+
# copy of C-a - start of line
|
|
1059
|
+
@repaint_required = true if @pcol > 0
|
|
1060
|
+
@pcol = 0
|
|
1061
|
+
@curpos = 0
|
|
1062
|
+
end
|
|
1063
|
+
#
|
|
1064
|
+
# return true if the given row is visible
|
|
1065
|
+
def is_visible? index
|
|
1066
|
+
j = index - @prow #@toprow
|
|
1067
|
+
j >= 0 && j <= scrollatrows()
|
|
1068
|
+
end
|
|
1069
|
+
#---- Section: movement end -----# }}}
|
|
1070
|
+
#---- Section: internal stuff start -----# {{{
|
|
1071
|
+
public
|
|
1072
|
+
def create_default_renderer
|
|
1073
|
+
@renderer = DefaultRenderer.new self
|
|
1074
|
+
end
|
|
1075
|
+
def create_default_keyhandler
|
|
1076
|
+
@key_handler = DefaultKeyHandler.new self
|
|
1077
|
+
end
|
|
1078
|
+
#
|
|
1079
|
+
def handle_key ch
|
|
1080
|
+
return :UNHANDLED unless @list
|
|
1081
|
+
|
|
1082
|
+
unless @key_handler
|
|
1083
|
+
create_default_keyhandler
|
|
1084
|
+
end
|
|
1085
|
+
@oldrow = @prow
|
|
1086
|
+
@oldcol = @pcol
|
|
1087
|
+
$log.debug "XXX: TEXTPAD got #{ch} prow = #{@prow}"
|
|
1088
|
+
ret = @key_handler.handle_key(ch)
|
|
1089
|
+
end
|
|
1090
|
+
# this is a barebones handler to be used only if an overriding key handler
|
|
1091
|
+
# wishes to fall back to default processing after it has handled some keys.
|
|
1092
|
+
# The complete version is in Defaultkeyhandler.
|
|
1093
|
+
# BUT the key will be executed again.
|
|
1094
|
+
def _handle_key ch
|
|
1095
|
+
begin
|
|
1096
|
+
ret = process_key ch, self
|
|
1097
|
+
$multiplier = 0
|
|
1098
|
+
bounds_check
|
|
1099
|
+
rescue => err
|
|
1100
|
+
$log.error " TEXTPAD ERROR _handle_key #{err} "
|
|
1101
|
+
$log.debug(err.backtrace.join("\n"))
|
|
1102
|
+
alert "#{err}"
|
|
1103
|
+
#textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
|
1104
|
+
ensure
|
|
1105
|
+
padrefresh
|
|
1106
|
+
Ncurses::Panel.update_panels
|
|
1107
|
+
end
|
|
1108
|
+
return 0
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
|
|
1112
|
+
#
|
|
1113
|
+
# event when user hits ENTER on a row, user would bind :PRESS
|
|
1114
|
+
# callers may use +text()+ to get the value of the row, +source+ to get parent object.
|
|
1115
|
+
#
|
|
1116
|
+
# obj.bind :PRESS { |eve| eve.text }
|
|
1117
|
+
#
|
|
1118
|
+
def fire_action_event
|
|
1119
|
+
return if @list.nil? || @list.size == 0
|
|
1120
|
+
require 'canis/core/include/ractionevent'
|
|
1121
|
+
aev = text_action_event
|
|
1122
|
+
fire_handler :PRESS, aev
|
|
1123
|
+
end
|
|
1124
|
+
# creates and returns a textactionevent object with current_value , line number and cursor position
|
|
1125
|
+
def text_action_event
|
|
1126
|
+
aev = TextActionEvent.new self, :PRESS, current_value().to_s, @current_index, @curpos
|
|
1127
|
+
end
|
|
1128
|
+
#
|
|
1129
|
+
# execute binding when a row is entered, used more in lists to display some text
|
|
1130
|
+
# in a header or footer as one traverses
|
|
1131
|
+
#
|
|
1132
|
+
def on_enter_row arow
|
|
1133
|
+
return nil if @list.nil? || @list.size == 0
|
|
1134
|
+
|
|
1135
|
+
@repaint_footer_required = true
|
|
1136
|
+
#alert "on_enter rr #{@repaint_required}, #{@repaint_all} oi #{@oldindex}, ci #{@current_index}, or #{@oldrow} "
|
|
1137
|
+
|
|
1138
|
+
## can this be done once and stored, and one instance used since a lot of traversal will be done
|
|
1139
|
+
require 'canis/core/include/ractionevent'
|
|
1140
|
+
aev = TextActionEvent.new self, :ENTER_ROW, current_value().to_s, @current_index, @curpos
|
|
1141
|
+
fire_handler :ENTER_ROW, aev
|
|
1142
|
+
#@repaint_required = true
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
#
|
|
1146
|
+
# called when this widget is entered, by form
|
|
1147
|
+
# 2014-05-27 - 17:02 we were not calling super, so :ENTER was not triggered !!!
|
|
1148
|
+
def on_enter
|
|
1149
|
+
super
|
|
1150
|
+
set_form_row
|
|
1151
|
+
end
|
|
1152
|
+
# called by form
|
|
1153
|
+
def set_form_row
|
|
1154
|
+
setrowcol @lastrow, @lastcol
|
|
1155
|
+
end
|
|
1156
|
+
# called by form
|
|
1157
|
+
def set_form_col
|
|
1158
|
+
end
|
|
1159
|
+
|
|
1160
|
+
private
|
|
1161
|
+
|
|
1162
|
+
# check that current_index and prow are within correct ranges
|
|
1163
|
+
# sets row (and someday col too)
|
|
1164
|
+
# sets repaint_required
|
|
1165
|
+
|
|
1166
|
+
public
|
|
1167
|
+
def bounds_check
|
|
1168
|
+
raise "@list is empty in textpad. Bounds_check." unless @list
|
|
1169
|
+
r,c = rowcol
|
|
1170
|
+
@current_index = 0 if @current_index < 0
|
|
1171
|
+
@current_index = @list.count()-1 if @current_index > @list.count()-1
|
|
1172
|
+
ensure_visible
|
|
1173
|
+
|
|
1174
|
+
check_prow
|
|
1175
|
+
@crow = @current_index + r - @prow
|
|
1176
|
+
@crow = r if @crow < r
|
|
1177
|
+
#$log.debug "XXX: PAD BOUNDS ci:#{@current_index} , old #{@oldrow},pr #{@prow}, crow #{@crow}, max #{@maxrow} pcol #{@pcol} maxcol #{@maxcol}"
|
|
1178
|
+
# 2 depends on whetehr suppress_borders
|
|
1179
|
+
if @suppress_borders
|
|
1180
|
+
@crow = @row + @height -1 if @crow >= r + @height -1
|
|
1181
|
+
else
|
|
1182
|
+
@crow = @row + @height -2 if @crow >= r + @height -2
|
|
1183
|
+
end
|
|
1184
|
+
#$log.debug " PAD BOUNDS CROW #{@crow} calling setrowcol"
|
|
1185
|
+
setrowcol @crow, @curpos+c
|
|
1186
|
+
lastcurpos @crow, @curpos+c
|
|
1187
|
+
if @oldindex != @current_index
|
|
1188
|
+
on_leave_row @oldindex if respond_to? :on_leave_row
|
|
1189
|
+
on_enter_row @current_index
|
|
1190
|
+
@oldindex = @current_index
|
|
1191
|
+
end
|
|
1192
|
+
if @oldrow != @prow || @oldcol != @pcol
|
|
1193
|
+
# only if scrolling has happened.
|
|
1194
|
+
@repaint_required = true
|
|
1195
|
+
end
|
|
1196
|
+
end
|
|
1197
|
+
#
|
|
1198
|
+
# save last cursor position so when reentering, cursor can be repositioned
|
|
1199
|
+
def lastcurpos r,c
|
|
1200
|
+
@lastrow = r
|
|
1201
|
+
@lastcol = c
|
|
1202
|
+
end
|
|
1203
|
+
|
|
1204
|
+
|
|
1205
|
+
# check that prow and pcol are within bounds
|
|
1206
|
+
#
|
|
1207
|
+
def check_prow
|
|
1208
|
+
@prow = 0 if @prow < 0
|
|
1209
|
+
@pcol = 0 if @pcol < 0
|
|
1210
|
+
|
|
1211
|
+
cc = @list.count
|
|
1212
|
+
@rows = rows()
|
|
1213
|
+
|
|
1214
|
+
#$log.debug " check_prow prow #{@prow} , list count #{cc}, rows #{@rows} "
|
|
1215
|
+
# 2014-05-28 - 22:41 changed < to <= otherwise prow became -1 when equal
|
|
1216
|
+
if cc <= @rows
|
|
1217
|
+
@prow = 0
|
|
1218
|
+
else
|
|
1219
|
+
maxrow = cc - @rows - 1
|
|
1220
|
+
if @prow > maxrow
|
|
1221
|
+
@prow = maxrow
|
|
1222
|
+
end
|
|
1223
|
+
end
|
|
1224
|
+
#$log.debug " check_prow after prow #{@prow} , list count #{cc} "
|
|
1225
|
+
# we still need to check the max that prow can go otherwise
|
|
1226
|
+
# the pad shows earlier stuff.
|
|
1227
|
+
#
|
|
1228
|
+
return
|
|
1229
|
+
end
|
|
1230
|
+
public
|
|
1231
|
+
def repaint
|
|
1232
|
+
unless @__first_time
|
|
1233
|
+
__calc_dimensions
|
|
1234
|
+
@__first_time = true
|
|
1235
|
+
end
|
|
1236
|
+
return unless @list # trying out since it goes into padrefresh even when no data 2014-04-10 - 00:32
|
|
1237
|
+
@graphic = @form.window unless @graphic
|
|
1238
|
+
@window ||= @graphic
|
|
1239
|
+
raise "Window not set in textpad" unless @window
|
|
1240
|
+
unless @renderer
|
|
1241
|
+
create_default_renderer
|
|
1242
|
+
end
|
|
1243
|
+
|
|
1244
|
+
## 2013-03-08 - 21:01 This is the fix to the issue of form callign an event like ? or F1
|
|
1245
|
+
# which throws up a messagebox which leaves a black rect. We have no place to put a refresh
|
|
1246
|
+
# However, form does call repaint for all objects, so we can do a padref here. Otherwise,
|
|
1247
|
+
# it would get rejected. UNfortunately this may happen more often we want, but we never know
|
|
1248
|
+
# when something pops up on the screen.
|
|
1249
|
+
#$log.debug " repaint textpad RR #{@repaint_required} #{@window.top} "
|
|
1250
|
+
unless @repaint_required
|
|
1251
|
+
print_foot if @repaint_footer_required # set in on_enter_row
|
|
1252
|
+
# trying out removing this, since too many refreshes 2014-05-01 - 12:45
|
|
1253
|
+
#padrefresh
|
|
1254
|
+
return
|
|
1255
|
+
end
|
|
1256
|
+
# if repaint is required, print_foot not called. unless repaint_all is set, and that
|
|
1257
|
+
# is rarely set.
|
|
1258
|
+
|
|
1259
|
+
preprocess_text
|
|
1260
|
+
|
|
1261
|
+
# in textdialog, @window was nil going into create_pad 2014-04-15 - 01:28
|
|
1262
|
+
|
|
1263
|
+
# creates pad and calls render_all
|
|
1264
|
+
populate_pad if !@pad or @_populate_needed
|
|
1265
|
+
if @repaint_all
|
|
1266
|
+
clear_pad
|
|
1267
|
+
Ncurses::Panel.update_panels
|
|
1268
|
+
render_all
|
|
1269
|
+
end
|
|
1270
|
+
|
|
1271
|
+
raise "PAD IS NIL -- populate_pad was not called ??" unless @pad
|
|
1272
|
+
|
|
1273
|
+
_do_borders
|
|
1274
|
+
print_foot if @repaint_footer_required # if still not done
|
|
1275
|
+
|
|
1276
|
+
padrefresh
|
|
1277
|
+
# in some cases next line prevents overlapped window from refreshing again, leaving black rows.
|
|
1278
|
+
# removing it causes problems in other cases. (tasks.rb, confirm window. dbdemo, F2 closing)
|
|
1279
|
+
Ncurses::Panel.update_panels
|
|
1280
|
+
@repaint_required = false
|
|
1281
|
+
@repaint_all = false
|
|
1282
|
+
end
|
|
1283
|
+
|
|
1284
|
+
def _do_borders
|
|
1285
|
+
unless @suppress_borders
|
|
1286
|
+
if @repaint_all
|
|
1287
|
+
## XXX im not getting the background color.
|
|
1288
|
+
#@window.print_border_only @top, @left, @height-1, @width, $datacolor
|
|
1289
|
+
clr = get_color $datacolor, color(), bgcolor()
|
|
1290
|
+
#@window.print_border @top, @left, @height-1, @width, clr
|
|
1291
|
+
@window.print_border_only @top, @left, @height-1, @width, clr
|
|
1292
|
+
print_title
|
|
1293
|
+
|
|
1294
|
+
# oldrow changed to oldindex 2014-04-13 - 16:55
|
|
1295
|
+
@repaint_footer_required = true if @oldindex != @current_index
|
|
1296
|
+
print_foot if @print_footer && !@suppress_borders && @repaint_footer_required
|
|
1297
|
+
|
|
1298
|
+
@window.wrefresh
|
|
1299
|
+
end
|
|
1300
|
+
end
|
|
1301
|
+
end
|
|
1302
|
+
|
|
1303
|
+
#
|
|
1304
|
+
# key mappings
|
|
1305
|
+
#
|
|
1306
|
+
# TODO take from listbindings so that emacs and vim can be selected. also user can change in one place.
|
|
1307
|
+
def map_keys
|
|
1308
|
+
@mapped_keys = true
|
|
1309
|
+
require 'canis/core/include/listbindings'
|
|
1310
|
+
bindings
|
|
1311
|
+
=begin
|
|
1312
|
+
bind_key([?g,?g], 'goto_start'){ goto_start } # mapping double keys like vim
|
|
1313
|
+
bind_key(279, 'goto_start'){ goto_start }
|
|
1314
|
+
bind_keys([?G,277], 'goto end'){ goto_end }
|
|
1315
|
+
bind_keys([?k,KEY_UP], "Up"){ up }
|
|
1316
|
+
bind_keys([?j,KEY_DOWN], "Down"){ down }
|
|
1317
|
+
bind_key(?\C-e, "Scroll Window Down"){ scroll_window_down }
|
|
1318
|
+
bind_key(?\C-y, "Scroll Window Up"){ scroll_window_up }
|
|
1319
|
+
bind_keys([32,338, ?\C-d], "Scroll Forward"){ scroll_forward }
|
|
1320
|
+
# adding CTRL_SPACE as back scroll 2014-04-14
|
|
1321
|
+
bind_keys([0,?\C-b,339], "Scroll Backward"){ scroll_backward }
|
|
1322
|
+
# the next one invalidates the single-quote binding for bookmarks
|
|
1323
|
+
#bind_key([?',?']){ goto_last_position } # vim , goto last row position (not column)
|
|
1324
|
+
bind_key(?/, :ask_search)
|
|
1325
|
+
bind_key(?n, :find_more)
|
|
1326
|
+
bind_key([?\C-x, ?>], :scroll_right)
|
|
1327
|
+
bind_key([?\C-x, ?<], :scroll_left)
|
|
1328
|
+
bind_key(?\M-l, :scroll_right)
|
|
1329
|
+
bind_key(?\M-h, :scroll_left)
|
|
1330
|
+
bind_key(?L, :bottom_of_window)
|
|
1331
|
+
bind_key(?M, :middle_of_window)
|
|
1332
|
+
bind_key(?H, :top_of_window)
|
|
1333
|
+
bind_key(?w, :forward_word)
|
|
1334
|
+
bind_key(?b, :backward_word)
|
|
1335
|
+
bind_key(?l, :cursor_forward)
|
|
1336
|
+
bind_key(?h, :cursor_backward)
|
|
1337
|
+
bind_key(?$, :cursor_eol)
|
|
1338
|
+
bind_key(KEY_ENTER, :fire_action_event)
|
|
1339
|
+
=end
|
|
1340
|
+
end
|
|
1341
|
+
# convenience method to return byte -- is it used ???
|
|
1342
|
+
private
|
|
1343
|
+
def key x
|
|
1344
|
+
x.getbyte(0)
|
|
1345
|
+
end
|
|
1346
|
+
|
|
1347
|
+
# ----------- end internal stuff --------------- }}}
|
|
1348
|
+
public
|
|
1349
|
+
# ---- Section search related start ----- {{{
|
|
1350
|
+
##
|
|
1351
|
+
# Ask user for string to search for
|
|
1352
|
+
# This uses the dialog, but what if user wants the old style.
|
|
1353
|
+
# Isn't there a cleaner way to let user override style, or allow user
|
|
1354
|
+
# to use own UI for getting pattern and then passing here.
|
|
1355
|
+
# @param str default nil. If not passed, then user is prompted using get_string dialog
|
|
1356
|
+
# This allows caller to use own method to prompt for string such as 'get_line' or 'rbgetstr' /
|
|
1357
|
+
# 'ask()'
|
|
1358
|
+
def ask_search str=nil
|
|
1359
|
+
# the following is a change that enables callers to prompt for the string
|
|
1360
|
+
# using some other style, basically the classical style and send the string in
|
|
1361
|
+
str = get_string("Enter pattern: ", :title => "Find pattern") unless str
|
|
1362
|
+
return if str.nil?
|
|
1363
|
+
str = @last_regex if str == ""
|
|
1364
|
+
return if !str or str == ""
|
|
1365
|
+
str = Regexp.new str if str.is_a? String
|
|
1366
|
+
ix = next_match str
|
|
1367
|
+
return unless ix
|
|
1368
|
+
@last_regex = str
|
|
1369
|
+
|
|
1370
|
+
#@oldindex = @current_index
|
|
1371
|
+
@current_index = ix[0]
|
|
1372
|
+
@curpos = ix[1]
|
|
1373
|
+
ensure_visible
|
|
1374
|
+
end
|
|
1375
|
+
##
|
|
1376
|
+
# Find next matching row for string accepted in ask_search
|
|
1377
|
+
#
|
|
1378
|
+
def find_more
|
|
1379
|
+
return unless @last_regex
|
|
1380
|
+
ix = next_match @last_regex
|
|
1381
|
+
return unless ix
|
|
1382
|
+
#@oldindex = @current_index
|
|
1383
|
+
@current_index = ix[0]
|
|
1384
|
+
@curpos = ix[1]
|
|
1385
|
+
ensure_visible
|
|
1386
|
+
end
|
|
1387
|
+
|
|
1388
|
+
##
|
|
1389
|
+
# Find the next row that contains given string
|
|
1390
|
+
# @return row and col offset of match, or nil
|
|
1391
|
+
# FIXME: 2014-05-26 - 01:26 currently if there are two matches in a row skips the second
|
|
1392
|
+
# one, which is a pain if two matches only on same row.
|
|
1393
|
+
# Ok, now it goes to second but won't come back to first, if only two matches, and both in one row.
|
|
1394
|
+
# @param String to find
|
|
1395
|
+
def ORIGnext_match str
|
|
1396
|
+
return unless str
|
|
1397
|
+
first = nil
|
|
1398
|
+
## content can be string or Chunkline, so we had to write <tt>index</tt> for this.
|
|
1399
|
+
## =~ does not give an error, but it does not work.
|
|
1400
|
+
@native_text.each_with_index do |line, ix|
|
|
1401
|
+
offset = 0
|
|
1402
|
+
# next line just a hack and not correct if only one match in file FIXME
|
|
1403
|
+
offset = @curpos + 1 if ix == @current_index
|
|
1404
|
+
_col = line.index str, offset
|
|
1405
|
+
if _col
|
|
1406
|
+
first ||= [ ix, _col ]
|
|
1407
|
+
if ix > @current_index || ( ix == @current_index && _col > @curpos)
|
|
1408
|
+
return [ix, _col]
|
|
1409
|
+
end
|
|
1410
|
+
end
|
|
1411
|
+
end
|
|
1412
|
+
# if first is nil, then none found in current line also, so don't increment offset in current line
|
|
1413
|
+
# next time. FIXME TODO
|
|
1414
|
+
return first
|
|
1415
|
+
end
|
|
1416
|
+
# since 2014-05-26 - 12:13 new logic to take into account multiple matches in one line
|
|
1417
|
+
#
|
|
1418
|
+
# First time, starts searching current line from cursor position onwards to end of file
|
|
1419
|
+
# If no match, then checks from start of file to current line.
|
|
1420
|
+
# @param [String, Regex] pattern to search (uses +:index+)
|
|
1421
|
+
# @param [Fixnum] line number to start with. Optional. +nil+ means start search from current position.
|
|
1422
|
+
# @param [Fixnum] line number to end with. Optional. +nil+ means end search at end of array
|
|
1423
|
+
# @return [Array<Fixnum, Fixnum>] index of line where pattern found, index of offset in line.
|
|
1424
|
+
# +nil+ returned if no match.
|
|
1425
|
+
# NOTE:
|
|
1426
|
+
# startline and endline are more for internal purposes, externally we would call this only with
|
|
1427
|
+
# the pattern.
|
|
1428
|
+
# @example
|
|
1429
|
+
# pos = next_match /abc/
|
|
1430
|
+
# # pos is nil or [line, col]
|
|
1431
|
+
#
|
|
1432
|
+
# 2014-05-28 - Added to_s before index() so that other descendants can use, such as treemodel or tablemodel
|
|
1433
|
+
def next_match str, startline=nil, endline=nil
|
|
1434
|
+
# 1. look in current row after the curpos
|
|
1435
|
+
# 2. if no match look in rest of array
|
|
1436
|
+
# 3. if no match and you started from current_index, then search
|
|
1437
|
+
# from start of file to current_index. call _next_match with 0.
|
|
1438
|
+
_arr = _getarray
|
|
1439
|
+
if !startline
|
|
1440
|
+
startline = @current_index
|
|
1441
|
+
pos = @curpos + 1
|
|
1442
|
+
# FIXME you could be at end of line
|
|
1443
|
+
#_line = _arr[startline]
|
|
1444
|
+
_line = to_searchable(startline)
|
|
1445
|
+
_col = _line.index(str, pos) if _line
|
|
1446
|
+
if _col
|
|
1447
|
+
return [startline, _col]
|
|
1448
|
+
end
|
|
1449
|
+
startline += 1 # FIXME check this end of file
|
|
1450
|
+
end
|
|
1451
|
+
# FIXME iterate only through the ones we need, not all
|
|
1452
|
+
_arr.each_with_index do |line, ix|
|
|
1453
|
+
next if ix < startline
|
|
1454
|
+
break if endline && ix > endline
|
|
1455
|
+
# next line just a hack and not correct if only one match in file FIXME
|
|
1456
|
+
line = to_searchable(ix)
|
|
1457
|
+
_col = line.index str
|
|
1458
|
+
if _col
|
|
1459
|
+
return [ix, _col]
|
|
1460
|
+
end
|
|
1461
|
+
end
|
|
1462
|
+
if startline > 0
|
|
1463
|
+
return next_match str, 0, @current_index
|
|
1464
|
+
end
|
|
1465
|
+
return nil
|
|
1466
|
+
|
|
1467
|
+
end
|
|
1468
|
+
# search for the next occurence of given regexp. Returns line and col if found, else nil.
|
|
1469
|
+
# @param [String, Regexp] pattern to search for (uses :index)
|
|
1470
|
+
# @return [nil] return value of no consequence
|
|
1471
|
+
def next_regex regex
|
|
1472
|
+
if regex.is_a? Symbol
|
|
1473
|
+
regex = @text_patterns[regex]
|
|
1474
|
+
raise "Pattern specified #{regex} does not exist in text_patterns " unless regex
|
|
1475
|
+
end
|
|
1476
|
+
@last_regex = regex
|
|
1477
|
+
find_more
|
|
1478
|
+
end
|
|
1479
|
+
# convert the row to something we can run +index+ on. it should be exactly
|
|
1480
|
+
# as the display will be, so find offsets are correct. Required for descendants such as Table.
|
|
1481
|
+
def to_searchable index
|
|
1482
|
+
_getarray[index].to_s
|
|
1483
|
+
end
|
|
1484
|
+
##
|
|
1485
|
+
# Ensure current row is visible, if not make it first row
|
|
1486
|
+
# NOTE - need to check if its at end and then reduce scroll at rows, check_prow does that
|
|
1487
|
+
#
|
|
1488
|
+
# @param current_index (default if not given)
|
|
1489
|
+
#
|
|
1490
|
+
def ensure_visible row = @current_index
|
|
1491
|
+
unless is_visible? row
|
|
1492
|
+
@prow = row
|
|
1493
|
+
end
|
|
1494
|
+
end
|
|
1495
|
+
|
|
1496
|
+
# returns the row offset of the focussed row, based on what is visible
|
|
1497
|
+
# this takes into account scrolling, and is useful if some caller needs to know
|
|
1498
|
+
# where the current index is actually being displayed (example if it wishes to display
|
|
1499
|
+
# a popup at that row)
|
|
1500
|
+
# An argument is not being taken since the index should be visible.
|
|
1501
|
+
def visual_index
|
|
1502
|
+
row = @current_index
|
|
1503
|
+
row - @prow
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
|
|
1508
|
+
# ---- Section search related end ----- }}}
|
|
1509
|
+
##---- dead unused {{{
|
|
1510
|
+
## some general methods for highlighting a row or changing attribute. However, these
|
|
1511
|
+
# will change the moment panning is done, or a repaint happens.
|
|
1512
|
+
# If these should be maintained then they should be called from the repaint method
|
|
1513
|
+
#
|
|
1514
|
+
# This was just indicative, and is not used anywhere
|
|
1515
|
+
def DEADhighlight_row index = @current_index, cfg={}
|
|
1516
|
+
return unless index
|
|
1517
|
+
c = 0 # we are using pads so no col except for left_margin if present
|
|
1518
|
+
# in a pad we don't need to convert index to printable
|
|
1519
|
+
r = index
|
|
1520
|
+
defcolor = cfg[:defaultcolor] || $promptcolor
|
|
1521
|
+
acolor ||= get_color defcolor, cfg[:color], cfg[:bgcolor]
|
|
1522
|
+
att = FFI::NCurses::A_REVERSE
|
|
1523
|
+
att = get_attrib(cfg[:attrib]) if cfg[:attrib]
|
|
1524
|
+
#@graphic.mvchgat(y=r, x=c, @width-2, att , acolor , nil)
|
|
1525
|
+
FFI::NCurses.mvwchgat(@pad, y=r, x=c, @width-2, att, acolor, nil)
|
|
1526
|
+
end
|
|
1527
|
+
##---- dead unused }}}
|
|
1528
|
+
|
|
1529
|
+
end # class textpad
|
|
1530
|
+
|
|
1531
|
+
# renderer {{{
|
|
1532
|
+
# Very basic renderer that only prints based on color pair of the textpad
|
|
1533
|
+
class AbstractTextPadRenderer
|
|
1534
|
+
# attribute for row, color_pair, and the Ncurses int for the colorpair
|
|
1535
|
+
attr_accessor :attr, :color_pair, :cp
|
|
1536
|
+
# content cols is the width in columns of pad
|
|
1537
|
+
# list is the data array
|
|
1538
|
+
attr_accessor :content_cols, :list
|
|
1539
|
+
# the widget this is associated with.
|
|
1540
|
+
attr_accessor :source
|
|
1541
|
+
|
|
1542
|
+
def initialize source=nil
|
|
1543
|
+
@source = source
|
|
1544
|
+
end
|
|
1545
|
+
# have the renderer get the latest colors from the widget.
|
|
1546
|
+
# Override this if for some reason the renderer wishes to hardcode its own.
|
|
1547
|
+
# But then the widgets colors would be changed ?
|
|
1548
|
+
def pre_render
|
|
1549
|
+
@attr = @source.attr
|
|
1550
|
+
cp = get_color($datacolor, @source.color(), @source.bgcolor())
|
|
1551
|
+
@color_pair = @source.color_pair || cp
|
|
1552
|
+
@cp = FFI::NCurses.COLOR_PAIR(cp)
|
|
1553
|
+
end
|
|
1554
|
+
alias :update_colors :pre_render
|
|
1555
|
+
|
|
1556
|
+
# derived classes may choose to override this.
|
|
1557
|
+
# However, they should set size and color and attrib at the start since these
|
|
1558
|
+
# can change after the object has been created depending on the application.
|
|
1559
|
+
def render_all pad, arr
|
|
1560
|
+
pre_render
|
|
1561
|
+
@content_cols = @source.pad_cols
|
|
1562
|
+
@clearstring = " " * @content_cols
|
|
1563
|
+
@list = arr
|
|
1564
|
+
|
|
1565
|
+
att = @attr || NORMAL
|
|
1566
|
+
FFI::NCurses.wattron(pad, @cp | att)
|
|
1567
|
+
|
|
1568
|
+
arr.each_with_index { |line, ix|
|
|
1569
|
+
render pad, ix, line
|
|
1570
|
+
}
|
|
1571
|
+
FFI::NCurses.wattroff(pad, @cp | att)
|
|
1572
|
+
end
|
|
1573
|
+
# concrete / derived classes should override this for their specific uses.
|
|
1574
|
+
# Called if only a row is changed.
|
|
1575
|
+
def render pad, lineno, text
|
|
1576
|
+
FFI::NCurses.mvwaddstr(pad,lineno, 0, @clearstring) if @clearstring
|
|
1577
|
+
FFI::NCurses.mvwaddstr(pad,lineno, 0, text)
|
|
1578
|
+
end
|
|
1579
|
+
end
|
|
1580
|
+
|
|
1581
|
+
# An extension of Abstracttextpadrenderer which takes care of AbstractChunkLine objects
|
|
1582
|
+
# calling their +print+ method.
|
|
1583
|
+
class DefaultRenderer < AbstractTextPadRenderer
|
|
1584
|
+
|
|
1585
|
+
#
|
|
1586
|
+
# default method for rendering a line
|
|
1587
|
+
# If it is a chunkline, then we take care of it.
|
|
1588
|
+
# Only if it is a String do we pass to renderer.
|
|
1589
|
+
# Should a renderer be allowed to handle chunks. Or be yielded chunks?
|
|
1590
|
+
#
|
|
1591
|
+
def render pad, lineno, text
|
|
1592
|
+
if text.is_a? AbstractChunkLine
|
|
1593
|
+
text.print pad, lineno, 0, @content_cols, color_pair, attr
|
|
1594
|
+
return
|
|
1595
|
+
end
|
|
1596
|
+
## messabox does have a method to paint the whole window in bg color its in rwidget.rb
|
|
1597
|
+
att = NORMAL
|
|
1598
|
+
FFI::NCurses.wattron(pad, @cp | att)
|
|
1599
|
+
FFI::NCurses.mvwaddstr(pad,lineno, 0, @clearstring) if @clearstring
|
|
1600
|
+
FFI::NCurses.mvwaddstr(pad,lineno, 0, @list[lineno])
|
|
1601
|
+
|
|
1602
|
+
#FFI::NCurses.mvwaddstr(pad, lineno, 0, text)
|
|
1603
|
+
FFI::NCurses.wattroff(pad, @cp | att)
|
|
1604
|
+
end
|
|
1605
|
+
end
|
|
1606
|
+
# renderer }}}
|
|
1607
|
+
# This is the default key handler.
|
|
1608
|
+
# It takes care of catching numbers so that vim's movement can use numeric args.
|
|
1609
|
+
# That is taken care of by multiplier. Other than that it has the key_map process the key.
|
|
1610
|
+
#
|
|
1611
|
+
class DefaultKeyHandler # ---- {{{
|
|
1612
|
+
def initialize source
|
|
1613
|
+
@source = source
|
|
1614
|
+
end
|
|
1615
|
+
|
|
1616
|
+
def handle_key ch
|
|
1617
|
+
begin
|
|
1618
|
+
case ch
|
|
1619
|
+
when ?0.getbyte(0)..?9.getbyte(0)
|
|
1620
|
+
if ch == ?0.getbyte(0) && $multiplier == 0
|
|
1621
|
+
@source.cursor_bol
|
|
1622
|
+
@source.bounds_check
|
|
1623
|
+
return 0
|
|
1624
|
+
end
|
|
1625
|
+
# storing digits entered so we can multiply motion actions
|
|
1626
|
+
$multiplier *= 10 ; $multiplier += (ch-48)
|
|
1627
|
+
return 0
|
|
1628
|
+
when ?\C-c.getbyte(0)
|
|
1629
|
+
$multiplier = 0
|
|
1630
|
+
return 0
|
|
1631
|
+
else
|
|
1632
|
+
# check for bindings, these cannot override above keys since placed at end
|
|
1633
|
+
begin
|
|
1634
|
+
ret = @source.process_key ch, self
|
|
1635
|
+
$multiplier = 0
|
|
1636
|
+
@source.bounds_check
|
|
1637
|
+
## If i press C-x > i get an alert from rwidgets which blacks the screen
|
|
1638
|
+
# if i put a padrefresh here it becomes okay but only for one pad,
|
|
1639
|
+
# i still need to do it for all pads.
|
|
1640
|
+
rescue => err
|
|
1641
|
+
$log.error " TEXTPAD ERROR handle_key #{err} "
|
|
1642
|
+
$log.debug(err.backtrace.join("\n"))
|
|
1643
|
+
alert "#{err}"
|
|
1644
|
+
#textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
|
1645
|
+
end
|
|
1646
|
+
# --- NOTE ABOUT BLACK RECT LEFT on SCREEN {{{
|
|
1647
|
+
## NOTE if textpad does not handle the event and it goes to form which pops
|
|
1648
|
+
# up a messagebox, then padrefresh does not happen, since control does not
|
|
1649
|
+
# come back here, so a black rect is left on screen
|
|
1650
|
+
# please note that a bounds check will not happen for stuff that
|
|
1651
|
+
# is triggered by form, so you'll have to to it yourself or
|
|
1652
|
+
# call setrowcol explicity if the cursor is not updated
|
|
1653
|
+
# --- }}}
|
|
1654
|
+
|
|
1655
|
+
return :UNHANDLED if ret == :UNHANDLED
|
|
1656
|
+
end
|
|
1657
|
+
rescue => err
|
|
1658
|
+
$log.error " TEXTPAD ERROR 591 #{err} "
|
|
1659
|
+
$log.debug( err) if err
|
|
1660
|
+
$log.debug(err.backtrace.join("\n")) if err
|
|
1661
|
+
# NOTE: textdialog itself is based on this class.
|
|
1662
|
+
alert "#{err}"
|
|
1663
|
+
#textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
|
1664
|
+
$error_message.value = ""
|
|
1665
|
+
ensure
|
|
1666
|
+
# this means that even unhandled will trigger a refresh
|
|
1667
|
+
@source.padrefresh
|
|
1668
|
+
Ncurses::Panel.update_panels
|
|
1669
|
+
end
|
|
1670
|
+
return 0
|
|
1671
|
+
end # def
|
|
1672
|
+
end # class }}}
|
|
1673
|
+
|
|
1674
|
+
end # mod
|