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