rbhex-core 1.0.0
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 +20 -0
- data/CHANGELOG +2000 -0
- data/LICENSE +56 -0
- data/README.md +44 -0
- data/examples/abasiclist.rb +179 -0
- data/examples/alpmenu.rb +50 -0
- data/examples/app.sample +19 -0
- data/examples/atree.rb +100 -0
- data/examples/bline.rb +136 -0
- data/examples/common/file.rb +45 -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 +60 -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 +10 -0
- data/examples/data/todo.txt.bak +10 -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 +502 -0
- data/examples/dirtree.rb +94 -0
- data/examples/newtabbedwindow.rb +100 -0
- data/examples/newtesttabp.rb +92 -0
- data/examples/tabular.rb +146 -0
- data/examples/tasks.rb +178 -0
- data/examples/term2.rb +84 -0
- data/examples/testbuttons.rb +296 -0
- data/examples/testcombo.rb +102 -0
- data/examples/testfields.rb +195 -0
- data/examples/testkeypress.rb +72 -0
- data/examples/testlistbox.rb +170 -0
- data/examples/testmessagebox.rb +140 -0
- data/examples/testprogress.rb +116 -0
- data/examples/testree.rb +106 -0
- data/examples/testwsshortcuts.rb +66 -0
- data/examples/testwsshortcuts2.rb +128 -0
- data/lib/rbhex.rb +6 -0
- data/lib/rbhex/core/docs/index.txt +73 -0
- data/lib/rbhex/core/include/action.rb +80 -0
- data/lib/rbhex/core/include/actionmanager.rb +49 -0
- data/lib/rbhex/core/include/appmethods.rb +214 -0
- data/lib/rbhex/core/include/bordertitle.rb +48 -0
- data/lib/rbhex/core/include/chunk.rb +203 -0
- data/lib/rbhex/core/include/io.rb +553 -0
- data/lib/rbhex/core/include/listbindings.rb +74 -0
- data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
- data/lib/rbhex/core/include/listeditable.rb +317 -0
- data/lib/rbhex/core/include/listscrollable.rb +663 -0
- data/lib/rbhex/core/include/listselectable.rb +271 -0
- data/lib/rbhex/core/include/multibuffer.rb +83 -0
- data/lib/rbhex/core/include/orderedhash.rb +77 -0
- data/lib/rbhex/core/include/ractionevent.rb +73 -0
- data/lib/rbhex/core/include/rchangeevent.rb +27 -0
- data/lib/rbhex/core/include/rhistory.rb +95 -0
- data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
- data/lib/rbhex/core/include/vieditable.rb +172 -0
- data/lib/rbhex/core/include/widgetmenu.rb +66 -0
- data/lib/rbhex/core/system/colormap.rb +165 -0
- data/lib/rbhex/core/system/keyboard.rb +150 -0
- data/lib/rbhex/core/system/keydefs.rb +30 -0
- data/lib/rbhex/core/system/ncurses.rb +236 -0
- data/lib/rbhex/core/system/panel.rb +162 -0
- data/lib/rbhex/core/system/window.rb +913 -0
- data/lib/rbhex/core/util/ansiparser.rb +119 -0
- data/lib/rbhex/core/util/app.rb +1228 -0
- data/lib/rbhex/core/util/basestack.rb +410 -0
- data/lib/rbhex/core/util/bottomline.rb +1859 -0
- data/lib/rbhex/core/util/colorparser.rb +77 -0
- data/lib/rbhex/core/util/focusmanager.rb +31 -0
- data/lib/rbhex/core/util/padreader.rb +192 -0
- data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
- data/lib/rbhex/core/util/rdialogs.rb +574 -0
- data/lib/rbhex/core/util/viewer.rb +149 -0
- data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
- data/lib/rbhex/core/version.rb +5 -0
- data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
- data/lib/rbhex/core/widgets/box.rb +58 -0
- data/lib/rbhex/core/widgets/divider.rb +310 -0
- data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
- data/lib/rbhex/core/widgets/rcombo.rb +253 -0
- data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
- data/lib/rbhex/core/widgets/rlink.rb +30 -0
- data/lib/rbhex/core/widgets/rlist.rb +696 -0
- data/lib/rbhex/core/widgets/rmenu.rb +958 -0
- data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
- data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
- data/lib/rbhex/core/widgets/rprogress.rb +118 -0
- data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
- data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
- data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
- data/lib/rbhex/core/widgets/rtextview.rb +739 -0
- data/lib/rbhex/core/widgets/rtree.rb +768 -0
- data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
- data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
- data/lib/rbhex/core/widgets/statusline.rb +113 -0
- data/lib/rbhex/core/widgets/tabular.rb +264 -0
- data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
- data/lib/rbhex/core/widgets/textpad.rb +995 -0
- data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
- data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
- data/rbhex-core.gemspec +32 -0
- metadata +172 -0
|
@@ -0,0 +1,995 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# ----------------------------------------------------------------------------- #
|
|
3
|
+
# File: textpad.rb
|
|
4
|
+
# Description: A class that displays text using a pad.
|
|
5
|
+
# The motivation for this is to put formatted text and not care about truncating and
|
|
6
|
+
# stuff. Also, there will be only one write, not each time scrolling happens.
|
|
7
|
+
# I found textview code for repaint being more complex than required.
|
|
8
|
+
# Author: rkumar http://github.com/rkumar/mancurses/
|
|
9
|
+
# Date: 2011-11-09 - 16:59
|
|
10
|
+
# License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
|
11
|
+
# Last update: 2014-04-07 00:24
|
|
12
|
+
#
|
|
13
|
+
# == CHANGES
|
|
14
|
+
# == TODO
|
|
15
|
+
# Take care of 3 cases:
|
|
16
|
+
# 1. complete data change, then recreate pad, and call init_vars resetting row, col and curpos etc
|
|
17
|
+
# This is done by method text().
|
|
18
|
+
# 2. row added or minor change - recreate pad, repaint data but don't call initvars. must maintain cursor
|
|
19
|
+
# ignore recreate of pad if width or ht is less than w and h of container.
|
|
20
|
+
# 3. only rewrite a row - row data changed, no recreate pad or anything else
|
|
21
|
+
#
|
|
22
|
+
#
|
|
23
|
+
#
|
|
24
|
+
# ----------------------------------------------------------------------------- #
|
|
25
|
+
#
|
|
26
|
+
require 'rbhex'
|
|
27
|
+
require 'rbhex/core/include/bordertitle'
|
|
28
|
+
|
|
29
|
+
include RubyCurses
|
|
30
|
+
module RubyCurses
|
|
31
|
+
extend self
|
|
32
|
+
class TextPad < Widget
|
|
33
|
+
include BorderTitle
|
|
34
|
+
|
|
35
|
+
dsl_accessor :suppress_borders
|
|
36
|
+
dsl_accessor :print_footer
|
|
37
|
+
attr_reader :current_index
|
|
38
|
+
attr_reader :rows , :cols
|
|
39
|
+
# adding these only for debugging table, to see where cursor is.
|
|
40
|
+
attr_reader :lastrow, :lastcol
|
|
41
|
+
# for external methods or classes to advance cursor
|
|
42
|
+
#attr_accessor :curpos
|
|
43
|
+
# You may pass height, width, row and col for creating a window otherwise a fullscreen window
|
|
44
|
+
# will be created. If you pass a window from caller then that window will be used.
|
|
45
|
+
# Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
|
|
46
|
+
# This is currently very minimal and was created to get me started to integrating
|
|
47
|
+
# pads into other classes such as textview.
|
|
48
|
+
def initialize form=nil, config={}, &block
|
|
49
|
+
|
|
50
|
+
@editable = false
|
|
51
|
+
@focusable = true
|
|
52
|
+
@config = config
|
|
53
|
+
@row = @col = 0
|
|
54
|
+
@prow = @pcol = 0
|
|
55
|
+
@startrow = 0
|
|
56
|
+
@startcol = 0
|
|
57
|
+
# @list is unused, think it can be removed
|
|
58
|
+
#@list = []
|
|
59
|
+
super
|
|
60
|
+
|
|
61
|
+
## code of calc_dimensions used to be here but moved late for listbox to work in flows
|
|
62
|
+
|
|
63
|
+
@_events << :PRESS
|
|
64
|
+
@_events << :ENTER_ROW
|
|
65
|
+
init_vars
|
|
66
|
+
end
|
|
67
|
+
# calculate height width row col etc as late as possible so they need not be set on creation
|
|
68
|
+
# seeing if we can move this later, so flows etc can call textpad based objects
|
|
69
|
+
def __calc_dimensions
|
|
70
|
+
## NOTE
|
|
71
|
+
# ---------------------------------------------------
|
|
72
|
+
# Since we are using pads, you need to get your height, width and rows correct
|
|
73
|
+
# Make sure the height factors in the row, else nothing may show
|
|
74
|
+
# ---------------------------------------------------
|
|
75
|
+
#@height = @height.ifzero(FFI::NCurses.LINES)
|
|
76
|
+
#@width = @width.ifzero(FFI::NCurses.COLS)
|
|
77
|
+
@rows = @height
|
|
78
|
+
@cols = @width
|
|
79
|
+
# NOTE XXX if cols is > COLS then padrefresh can fail
|
|
80
|
+
@startrow = @row
|
|
81
|
+
@startcol = @col
|
|
82
|
+
unless @suppress_borders
|
|
83
|
+
@row_offset = @col_offset = 1
|
|
84
|
+
@startrow += 1
|
|
85
|
+
@startcol += 1
|
|
86
|
+
@rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
|
87
|
+
@cols -=3
|
|
88
|
+
@scrollatrows = @height - 3
|
|
89
|
+
else
|
|
90
|
+
# no borders printed
|
|
91
|
+
@rows -= 1 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
|
92
|
+
## if next is 0 then padrefresh doesn't print
|
|
93
|
+
@cols -=1
|
|
94
|
+
@scrollatrows = @height - 1 # check this out 0 or 1
|
|
95
|
+
@row_offset = @col_offset = 0
|
|
96
|
+
end
|
|
97
|
+
@top = @row
|
|
98
|
+
@left = @col
|
|
99
|
+
@lastrow = @row + @row_offset
|
|
100
|
+
@lastcol = @col + @col_offset
|
|
101
|
+
end
|
|
102
|
+
def init_vars
|
|
103
|
+
$multiplier = 0
|
|
104
|
+
@oldindex = @current_index = 0
|
|
105
|
+
# column cursor
|
|
106
|
+
@prow = @pcol = @curpos = 0
|
|
107
|
+
if @row && @col
|
|
108
|
+
@lastrow = @row + @row_offset
|
|
109
|
+
@lastcol = @col + @col_offset
|
|
110
|
+
end
|
|
111
|
+
@repaint_required = true
|
|
112
|
+
map_keys unless @mapped_keys
|
|
113
|
+
end
|
|
114
|
+
def rowcol #:nodoc:
|
|
115
|
+
return @row+@row_offset, @col+@col_offset
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
## XXX in list text returns the selected row, list returns the full thing, keep consistent
|
|
120
|
+
def create_pad
|
|
121
|
+
destroy if @pad
|
|
122
|
+
#@pad = FFI::NCurses.newpad(@content_rows, @content_cols)
|
|
123
|
+
@pad = @window.get_pad(@content_rows, @content_cols )
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
private
|
|
127
|
+
# create and populate pad
|
|
128
|
+
def populate_pad
|
|
129
|
+
@_populate_needed = false
|
|
130
|
+
@content_rows = @content.count
|
|
131
|
+
@content_cols = content_cols()
|
|
132
|
+
@content_rows = @rows if @content_rows < @rows
|
|
133
|
+
@content_cols = @cols if @content_cols < @cols
|
|
134
|
+
|
|
135
|
+
create_pad
|
|
136
|
+
|
|
137
|
+
# clearstring is the string required to clear the pad to background color
|
|
138
|
+
@clearstring = nil
|
|
139
|
+
cp = get_color($datacolor, @color, @bgcolor)
|
|
140
|
+
@cp = FFI::NCurses.COLOR_PAIR(cp)
|
|
141
|
+
if cp != $datacolor
|
|
142
|
+
@clearstring ||= " " * @width
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
Ncurses::Panel.update_panels
|
|
146
|
+
render_all
|
|
147
|
+
|
|
148
|
+
end
|
|
149
|
+
#
|
|
150
|
+
# iterate through content rendering each row
|
|
151
|
+
# 2013-03-27 - 01:51 separated so that widgets with headers such as tables can
|
|
152
|
+
# override this for better control
|
|
153
|
+
def render_all
|
|
154
|
+
@content.each_with_index { |line, ix|
|
|
155
|
+
#FFI::NCurses.mvwaddstr(@pad,ix, 0, @content[ix])
|
|
156
|
+
render @pad, ix, line
|
|
157
|
+
}
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
public
|
|
161
|
+
# supply a custom renderer that implements +render()+
|
|
162
|
+
# @see render
|
|
163
|
+
def renderer r
|
|
164
|
+
@renderer = r
|
|
165
|
+
end
|
|
166
|
+
#
|
|
167
|
+
# default method for rendering a line
|
|
168
|
+
#
|
|
169
|
+
def render pad, lineno, text
|
|
170
|
+
if text.is_a? Chunks::ChunkLine
|
|
171
|
+
FFI::NCurses.wmove @pad, lineno, 0
|
|
172
|
+
a = get_attrib @attrib
|
|
173
|
+
|
|
174
|
+
show_colored_chunks text, nil, a
|
|
175
|
+
return
|
|
176
|
+
end
|
|
177
|
+
if @renderer
|
|
178
|
+
@renderer.render @pad, lineno, text
|
|
179
|
+
else
|
|
180
|
+
## messabox does have a method to paint the whole window in bg color its in rwidget.rb
|
|
181
|
+
att = NORMAL
|
|
182
|
+
FFI::NCurses.wattron(@pad, @cp | att)
|
|
183
|
+
FFI::NCurses.mvwaddstr(@pad,lineno, 0, @clearstring) if @clearstring
|
|
184
|
+
FFI::NCurses.mvwaddstr(@pad,lineno, 0, @content[lineno])
|
|
185
|
+
|
|
186
|
+
#FFI::NCurses.mvwaddstr(pad, lineno, 0, text)
|
|
187
|
+
FFI::NCurses.wattroff(@pad, @cp | att)
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# supply a filename as source for textpad
|
|
192
|
+
# Reads up file into @content
|
|
193
|
+
# One can optionally send in a method which takes a filename and returns an array of data
|
|
194
|
+
# This is required if you are processing files which are binary such as zip/archives and wish
|
|
195
|
+
# to print the contents. (e.g. cygnus gem sends in :get_file_contents).
|
|
196
|
+
# filename("a.c", method(:get_file_contents))
|
|
197
|
+
#
|
|
198
|
+
def filename(filename, reader=nil)
|
|
199
|
+
@file = filename
|
|
200
|
+
unless File.exists? filename
|
|
201
|
+
alert "#{filename} does not exist"
|
|
202
|
+
return
|
|
203
|
+
end
|
|
204
|
+
@filetype = File.extname filename
|
|
205
|
+
if reader
|
|
206
|
+
@content = reader.call(filename)
|
|
207
|
+
else
|
|
208
|
+
@content = File.open(filename,"r").readlines
|
|
209
|
+
end
|
|
210
|
+
if @filetype == ""
|
|
211
|
+
if @content.first.index("ruby")
|
|
212
|
+
@filetype = ".rb"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
init_vars
|
|
216
|
+
@repaint_all = true
|
|
217
|
+
@_populate_needed = true
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Supply an array of string to be displayed
|
|
221
|
+
# This will replace existing text
|
|
222
|
+
|
|
223
|
+
# display text given in an array format. This is the principal way of giving content
|
|
224
|
+
# to a textpad, other than filename().
|
|
225
|
+
# @param Array of lines
|
|
226
|
+
# @param format (optional) can be :tmux :ansi or :none
|
|
227
|
+
# If a format other than :none is given, then formatted_text is called.
|
|
228
|
+
def text(lines, fmt=:none)
|
|
229
|
+
# added so callers can have one interface and avoid an if condition
|
|
230
|
+
return formatted_text(lines, fmt) unless fmt == :none
|
|
231
|
+
|
|
232
|
+
return @content if lines.empty?
|
|
233
|
+
@content = lines
|
|
234
|
+
@_populate_needed = true
|
|
235
|
+
@repaint_all = true
|
|
236
|
+
init_vars
|
|
237
|
+
self
|
|
238
|
+
end
|
|
239
|
+
alias :list :text
|
|
240
|
+
# for compat with textview
|
|
241
|
+
alias :set_content :text
|
|
242
|
+
def content
|
|
243
|
+
raise "content is nil " unless @content
|
|
244
|
+
return @content
|
|
245
|
+
end
|
|
246
|
+
alias :get_content :content
|
|
247
|
+
|
|
248
|
+
# print footer containing line and position
|
|
249
|
+
# XXX UNTESTED TODO TESTING
|
|
250
|
+
def print_foot #:nodoc:
|
|
251
|
+
@footer_attrib ||= Ncurses::A_REVERSE
|
|
252
|
+
footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
|
|
253
|
+
$log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
|
|
254
|
+
@graphic.printstring( @row + @height -1 , @col+2, footer, @color_pair || $datacolor, @footer_attrib)
|
|
255
|
+
@repaint_footer_required = false # 2010-01-23 22:55
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
## ---- the next 2 methods deal with printing chunks
|
|
259
|
+
# we should put it int a common module and include it
|
|
260
|
+
# in Window and Pad stuff and perhaps include it conditionally.
|
|
261
|
+
|
|
262
|
+
## 2013-03-07 - 19:57 changed width to @content_cols since data not printing
|
|
263
|
+
# in some cases fully when ansi sequences were present int some line but not in others
|
|
264
|
+
# lines without ansi were printing less by a few chars.
|
|
265
|
+
# This was prolly copied from rwindow, where it is okay since its for a specific width
|
|
266
|
+
def print(string, _width = @content_cols)
|
|
267
|
+
#return unless visible?
|
|
268
|
+
w = _width == 0? Ncurses.COLS : _width
|
|
269
|
+
FFI::NCurses.waddnstr(@pad,string.to_s, w) # changed 2011 dts
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def show_colored_chunks(chunks, defcolor = nil, defattr = nil)
|
|
273
|
+
#return unless visible?
|
|
274
|
+
chunks.each do |chunk| #|color, chunk, attrib|
|
|
275
|
+
case chunk
|
|
276
|
+
when Chunks::Chunk
|
|
277
|
+
color = chunk.color
|
|
278
|
+
attrib = chunk.attrib
|
|
279
|
+
text = chunk.text
|
|
280
|
+
when Array
|
|
281
|
+
# for earlier demos that used an array
|
|
282
|
+
color = chunk[0]
|
|
283
|
+
attrib = chunk[2]
|
|
284
|
+
text = chunk[1]
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
color ||= defcolor
|
|
288
|
+
attrib ||= defattr || NORMAL
|
|
289
|
+
|
|
290
|
+
#cc, bg = ColorMap.get_colors_for_pair color
|
|
291
|
+
#$log.debug "XXX: CHUNK textpad #{text}, cp #{color} , attrib #{attrib}. #{cc}, #{bg} "
|
|
292
|
+
FFI::NCurses.wcolor_set(@pad, color,nil) if color
|
|
293
|
+
FFI::NCurses.wattron(@pad, attrib) if attrib
|
|
294
|
+
print(text)
|
|
295
|
+
FFI::NCurses.wattroff(@pad, attrib) if attrib
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
#
|
|
300
|
+
# pass in formatted text along with parser (:tmux or :ansi)
|
|
301
|
+
# NOTE this does not call init_vars, i think it should, text() does
|
|
302
|
+
def formatted_text text, fmt
|
|
303
|
+
|
|
304
|
+
require 'rbhex/core/include/chunk'
|
|
305
|
+
@formatted_text = text
|
|
306
|
+
@color_parser = fmt
|
|
307
|
+
@repaint_required = true
|
|
308
|
+
_convert_formatted
|
|
309
|
+
# don't know if start is always required. so putting in caller
|
|
310
|
+
#goto_start
|
|
311
|
+
#remove_all
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# write pad onto window
|
|
315
|
+
#private
|
|
316
|
+
def padrefresh
|
|
317
|
+
top = @window.top
|
|
318
|
+
left = @window.left
|
|
319
|
+
sr = @startrow + top
|
|
320
|
+
sc = @startcol + left
|
|
321
|
+
retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , @rows + sr , @cols+ sc );
|
|
322
|
+
$log.warn "XXX: PADREFRESH #{retval}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{@rows+sr}, #{@cols+sc}." if retval == -1
|
|
323
|
+
# padrefresh can fail if width is greater than NCurses.COLS
|
|
324
|
+
#FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow + top, @startcol + left, @rows + @startrow + top, @cols+@startcol + left);
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# convenience method to return byte
|
|
328
|
+
private
|
|
329
|
+
def key x
|
|
330
|
+
x.getbyte(0)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# length of longest string in array
|
|
334
|
+
# This will give a 'wrong' max length if the array has ansi color escape sequences in it
|
|
335
|
+
# which inc the length but won't be printed. Such lines actually have less length when printed
|
|
336
|
+
# So in such cases, give more space to the pad.
|
|
337
|
+
def content_cols
|
|
338
|
+
longest = @content.max_by(&:length)
|
|
339
|
+
## 2013-03-06 - 20:41 crashes here for some reason when man gives error message no man entry
|
|
340
|
+
return 0 unless longest
|
|
341
|
+
longest.length
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
public
|
|
345
|
+
# to be called with program / user has added a row or changed column widths so that
|
|
346
|
+
# the pad needs to be recreated. However, cursor positioning is maintained since this
|
|
347
|
+
# is considered to be a minor change.
|
|
348
|
+
# We do not call init_vars since user is continuing to do some work on a row/col.
|
|
349
|
+
def fire_dimension_changed
|
|
350
|
+
# recreate pad since width or ht has changed (row count or col width changed)
|
|
351
|
+
@_populate_needed = true
|
|
352
|
+
@repaint_required = true
|
|
353
|
+
@repaint_all = true
|
|
354
|
+
end
|
|
355
|
+
# repaint only one row since content of that row has changed.
|
|
356
|
+
# No recreate of pad is done.
|
|
357
|
+
def fire_row_changed ix
|
|
358
|
+
render @pad, ix, @content[ix]
|
|
359
|
+
# may need to call padrefresh TODO TESTING
|
|
360
|
+
end
|
|
361
|
+
def repaint
|
|
362
|
+
if @__first_time
|
|
363
|
+
__calc_dimensions
|
|
364
|
+
@__first_time = true
|
|
365
|
+
end
|
|
366
|
+
## 2013-03-08 - 21:01 This is the fix to the issue of form callign an event like ? or F1
|
|
367
|
+
# which throws up a messagebox which leaves a black rect. We have no place to put a refresh
|
|
368
|
+
# However, form does call repaint for all objects, so we can do a padref here. Otherwise,
|
|
369
|
+
# it would get rejected. UNfortunately this may happen more often we want, but we never know
|
|
370
|
+
# when something pops up on the screen.
|
|
371
|
+
unless @repaint_required
|
|
372
|
+
padrefresh
|
|
373
|
+
return
|
|
374
|
+
end
|
|
375
|
+
# I can't recall why we are doing this late. Is the rreason relevant any longer
|
|
376
|
+
# Some methods that expect data are crashing like tablewidgets model_row
|
|
377
|
+
_convert_formatted
|
|
378
|
+
|
|
379
|
+
@window ||= @graphic
|
|
380
|
+
populate_pad if @_populate_needed
|
|
381
|
+
#HERE we need to populate once so user can pass a renderer
|
|
382
|
+
|
|
383
|
+
_do_borders
|
|
384
|
+
|
|
385
|
+
padrefresh
|
|
386
|
+
Ncurses::Panel.update_panels
|
|
387
|
+
@repaint_required = false
|
|
388
|
+
@repaint_all = false
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def _do_borders
|
|
392
|
+
unless @suppress_borders
|
|
393
|
+
if @repaint_all
|
|
394
|
+
## XXX im not getting the background color.
|
|
395
|
+
#@window.print_border_only @top, @left, @height-1, @width, $datacolor
|
|
396
|
+
clr = get_color $datacolor, @color, @bgcolor
|
|
397
|
+
#@window.print_border @top, @left, @height-1, @width, clr
|
|
398
|
+
@window.print_border_only @top, @left, @height-1, @width, clr
|
|
399
|
+
print_title
|
|
400
|
+
|
|
401
|
+
@repaint_footer_required = true if @oldrow != @current_index
|
|
402
|
+
print_foot if @print_footer && !@suppress_borders && @repaint_footer_required
|
|
403
|
+
|
|
404
|
+
@window.wrefresh
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
def _convert_formatted
|
|
409
|
+
if @formatted_text
|
|
410
|
+
|
|
411
|
+
l = RubyCurses::Utils.parse_formatted_text(@color_parser,
|
|
412
|
+
@formatted_text)
|
|
413
|
+
|
|
414
|
+
text(l)
|
|
415
|
+
@formatted_text = nil
|
|
416
|
+
end
|
|
417
|
+
end
|
|
418
|
+
|
|
419
|
+
#
|
|
420
|
+
# key mappings
|
|
421
|
+
#
|
|
422
|
+
def map_keys
|
|
423
|
+
@mapped_keys = true
|
|
424
|
+
bind_key([?g,?g], 'goto_start'){ goto_start } # mapping double keys like vim
|
|
425
|
+
bind_key(279, 'goto_start'){ goto_start }
|
|
426
|
+
bind_keys([?G,277], 'goto end'){ goto_end }
|
|
427
|
+
bind_keys([?k,KEY_UP], "Up"){ up }
|
|
428
|
+
bind_keys([?j,KEY_DOWN], "Down"){ down }
|
|
429
|
+
bind_key(?\C-e, "Scroll Window Down"){ scroll_window_down }
|
|
430
|
+
bind_key(?\C-y, "Scroll Window Up"){ scroll_window_up }
|
|
431
|
+
bind_keys([32,338, ?\C-d], "Scroll Forward"){ scroll_forward }
|
|
432
|
+
bind_keys([?\C-b,339]){ scroll_backward }
|
|
433
|
+
# the next one invalidates the single-quote binding for bookmarks
|
|
434
|
+
#bind_key([?',?']){ goto_last_position } # vim , goto last row position (not column)
|
|
435
|
+
bind_key(?/, :ask_search)
|
|
436
|
+
bind_key(?n, :find_more)
|
|
437
|
+
bind_key([?\C-x, ?>], :scroll_right)
|
|
438
|
+
bind_key([?\C-x, ?<], :scroll_left)
|
|
439
|
+
bind_key(?\M-l, :scroll_right)
|
|
440
|
+
bind_key(?\M-h, :scroll_left)
|
|
441
|
+
bind_key(?L, :bottom_of_window)
|
|
442
|
+
bind_key(?M, :middle_of_window)
|
|
443
|
+
bind_key(?H, :top_of_window)
|
|
444
|
+
bind_key(?w, :forward_word)
|
|
445
|
+
bind_key(?b, :backward_word)
|
|
446
|
+
bind_key(?l, :cursor_forward)
|
|
447
|
+
bind_key(?h, :cursor_backward)
|
|
448
|
+
bind_key(?$, :cursor_eol)
|
|
449
|
+
bind_key(KEY_ENTER, :fire_action_event)
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# goto first line of file
|
|
453
|
+
def goto_start
|
|
454
|
+
#@oldindex = @current_index
|
|
455
|
+
$multiplier ||= 0
|
|
456
|
+
if $multiplier > 0
|
|
457
|
+
goto_line $multiplier - 1
|
|
458
|
+
return
|
|
459
|
+
end
|
|
460
|
+
@current_index = 0
|
|
461
|
+
@curpos = @pcol = @prow = 0
|
|
462
|
+
@prow = 0
|
|
463
|
+
$multiplier = 0
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# goto last line of file
|
|
467
|
+
def goto_end
|
|
468
|
+
#@oldindex = @current_index
|
|
469
|
+
$multiplier ||= 0
|
|
470
|
+
if $multiplier > 0
|
|
471
|
+
goto_line $multiplier - 1
|
|
472
|
+
return
|
|
473
|
+
end
|
|
474
|
+
@current_index = @content.count() - 1
|
|
475
|
+
@prow = @current_index - @scrollatrows
|
|
476
|
+
$multiplier = 0
|
|
477
|
+
end
|
|
478
|
+
def goto_line line
|
|
479
|
+
## we may need to calculate page, zfm style and place at right position for ensure visible
|
|
480
|
+
#line -= 1
|
|
481
|
+
@current_index = line
|
|
482
|
+
ensure_visible line
|
|
483
|
+
bounds_check
|
|
484
|
+
$multiplier = 0
|
|
485
|
+
end
|
|
486
|
+
def top_of_window
|
|
487
|
+
@current_index = @prow
|
|
488
|
+
$multiplier ||= 0
|
|
489
|
+
if $multiplier > 0
|
|
490
|
+
@current_index += $multiplier
|
|
491
|
+
$multiplier = 0
|
|
492
|
+
end
|
|
493
|
+
end
|
|
494
|
+
def bottom_of_window
|
|
495
|
+
@current_index = @prow + @scrollatrows
|
|
496
|
+
$multiplier ||= 0
|
|
497
|
+
if $multiplier > 0
|
|
498
|
+
@current_index -= $multiplier
|
|
499
|
+
$multiplier = 0
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
def middle_of_window
|
|
503
|
+
@current_index = @prow + (@scrollatrows/2)
|
|
504
|
+
$multiplier = 0
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# move down a line mimicking vim's j key
|
|
508
|
+
# @param [int] multiplier entered prior to invoking key
|
|
509
|
+
def down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
510
|
+
#@oldindex = @current_index if num > 10
|
|
511
|
+
@current_index += num
|
|
512
|
+
# no , i don't like this here. it scrolls up too much making prow = current_index
|
|
513
|
+
unless is_visible? @current_index
|
|
514
|
+
@prow += num
|
|
515
|
+
end
|
|
516
|
+
#ensure_visible
|
|
517
|
+
$multiplier = 0
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# move up a line mimicking vim's k key
|
|
521
|
+
# @param [int] multiplier entered prior to invoking key
|
|
522
|
+
def up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
523
|
+
#@oldindex = @current_index if num > 10
|
|
524
|
+
@current_index -= num
|
|
525
|
+
#unless is_visible? @current_index
|
|
526
|
+
#if @prow > @current_index
|
|
527
|
+
##$status_message.value = "1 #{@prow} > #{@current_index} "
|
|
528
|
+
#@prow -= 1
|
|
529
|
+
#else
|
|
530
|
+
#end
|
|
531
|
+
#end
|
|
532
|
+
$multiplier = 0
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
# scrolls window down mimicking vim C-e
|
|
536
|
+
# @param [int] multiplier entered prior to invoking key
|
|
537
|
+
def scroll_window_down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
538
|
+
@prow += num
|
|
539
|
+
if @prow > @current_index
|
|
540
|
+
@current_index += 1
|
|
541
|
+
end
|
|
542
|
+
#check_prow
|
|
543
|
+
$multiplier = 0
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# scrolls window up mimicking vim C-y
|
|
547
|
+
# @param [int] multiplier entered prior to invoking key
|
|
548
|
+
def scroll_window_up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
|
549
|
+
@prow -= num
|
|
550
|
+
unless is_visible? @current_index
|
|
551
|
+
# one more check may be needed here TODO
|
|
552
|
+
@current_index -= num
|
|
553
|
+
end
|
|
554
|
+
$multiplier = 0
|
|
555
|
+
end
|
|
556
|
+
|
|
557
|
+
# scrolls lines a window full at a time, on pressing ENTER or C-d or pagedown
|
|
558
|
+
def scroll_forward
|
|
559
|
+
#@oldindex = @current_index
|
|
560
|
+
@current_index += @scrollatrows
|
|
561
|
+
@prow = @current_index - @scrollatrows
|
|
562
|
+
end
|
|
563
|
+
|
|
564
|
+
# scrolls lines backward a window full at a time, on pressing pageup
|
|
565
|
+
# C-u may not work since it is trapped by form earlier. Need to fix
|
|
566
|
+
def scroll_backward
|
|
567
|
+
#@oldindex = @current_index
|
|
568
|
+
@current_index -= @scrollatrows
|
|
569
|
+
@prow = @current_index - @scrollatrows
|
|
570
|
+
end
|
|
571
|
+
def goto_last_position
|
|
572
|
+
return unless @oldindex
|
|
573
|
+
tmp = @current_index
|
|
574
|
+
@current_index = @oldindex
|
|
575
|
+
@oldindex = tmp
|
|
576
|
+
bounds_check
|
|
577
|
+
end
|
|
578
|
+
def scroll_right
|
|
579
|
+
# I don't think it will ever be less since we've increased it to cols
|
|
580
|
+
if @content_cols <= @cols
|
|
581
|
+
maxpcol = 0
|
|
582
|
+
@pcol = 0
|
|
583
|
+
else
|
|
584
|
+
maxpcol = @content_cols - @cols - 1
|
|
585
|
+
@pcol += 1
|
|
586
|
+
@pcol = maxpcol if @pcol > maxpcol
|
|
587
|
+
end
|
|
588
|
+
# to prevent right from retaining earlier painted values
|
|
589
|
+
# padreader does not do a clear, yet works fine.
|
|
590
|
+
# OK it has an update_panel after padrefresh, that clears it seems.
|
|
591
|
+
#this clears entire window not just the pad
|
|
592
|
+
#FFI::NCurses.wclear(@window.get_window)
|
|
593
|
+
# so border and title is repainted after window clearing
|
|
594
|
+
#
|
|
595
|
+
# Next line was causing all sorts of problems when scrolling with ansi formatted text
|
|
596
|
+
#@repaint_all = true
|
|
597
|
+
end
|
|
598
|
+
def scroll_left
|
|
599
|
+
@pcol -= 1
|
|
600
|
+
end
|
|
601
|
+
#
|
|
602
|
+
#
|
|
603
|
+
#
|
|
604
|
+
#
|
|
605
|
+
def handle_key ch
|
|
606
|
+
return :UNHANDLED unless @content
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
@oldrow = @prow
|
|
610
|
+
@oldcol = @pcol
|
|
611
|
+
$log.debug "XXX: PAD got #{ch} prow = #{@prow}"
|
|
612
|
+
begin
|
|
613
|
+
case ch
|
|
614
|
+
when ?0.getbyte(0)..?9.getbyte(0)
|
|
615
|
+
if ch == ?0.getbyte(0) && $multiplier == 0
|
|
616
|
+
cursor_bol
|
|
617
|
+
return 0
|
|
618
|
+
end
|
|
619
|
+
# storing digits entered so we can multiply motion actions
|
|
620
|
+
$multiplier *= 10 ; $multiplier += (ch-48)
|
|
621
|
+
return 0
|
|
622
|
+
when ?\C-c.getbyte(0)
|
|
623
|
+
$multiplier = 0
|
|
624
|
+
return 0
|
|
625
|
+
else
|
|
626
|
+
# check for bindings, these cannot override above keys since placed at end
|
|
627
|
+
begin
|
|
628
|
+
ret = process_key ch, self
|
|
629
|
+
$multiplier = 0
|
|
630
|
+
bounds_check
|
|
631
|
+
## If i press C-x > i get an alert from rwidgets which blacks the screen
|
|
632
|
+
# if i put a padrefresh here it becomes okay but only for one pad,
|
|
633
|
+
# i still need to do it for all pads.
|
|
634
|
+
rescue => err
|
|
635
|
+
$log.error " TEXTPAD ERROR INS #{err} "
|
|
636
|
+
$log.debug(err.backtrace.join("\n"))
|
|
637
|
+
textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
|
638
|
+
end
|
|
639
|
+
## NOTE if textpad does not handle the event and it goes to form which pops
|
|
640
|
+
# up a messagebox, then padrefresh does not happen, since control does not
|
|
641
|
+
# come back here, so a black rect is left on screen
|
|
642
|
+
# please note that a bounds check will not happen for stuff that
|
|
643
|
+
# is triggered by form, so you'll have to to it yourself or
|
|
644
|
+
# call setrowcol explicity if the cursor is not updated
|
|
645
|
+
return :UNHANDLED if ret == :UNHANDLED
|
|
646
|
+
end
|
|
647
|
+
rescue => err
|
|
648
|
+
$log.error " TEXTPAD ERROR 591 #{err} "
|
|
649
|
+
$log.debug( err) if err
|
|
650
|
+
$log.debug(err.backtrace.join("\n")) if err
|
|
651
|
+
textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
|
652
|
+
$error_message.value = ""
|
|
653
|
+
ensure
|
|
654
|
+
padrefresh
|
|
655
|
+
Ncurses::Panel.update_panels
|
|
656
|
+
end
|
|
657
|
+
return 0
|
|
658
|
+
end # while loop
|
|
659
|
+
|
|
660
|
+
#
|
|
661
|
+
# event when user hits enter on a row, user would bind :PRESS
|
|
662
|
+
#
|
|
663
|
+
def fire_action_event
|
|
664
|
+
return if @content.nil? || @content.size == 0
|
|
665
|
+
require 'rbhex/core/include/ractionevent'
|
|
666
|
+
aev = TextActionEvent.new self, :PRESS, current_value().to_s, @current_index, @curpos
|
|
667
|
+
fire_handler :PRESS, aev
|
|
668
|
+
end
|
|
669
|
+
#
|
|
670
|
+
# returns current value (what cursor is on)
|
|
671
|
+
def current_value
|
|
672
|
+
@content[@current_index]
|
|
673
|
+
end
|
|
674
|
+
#
|
|
675
|
+
# execute binding when a row is entered, used more in lists to display some text
|
|
676
|
+
# in a header or footer as one traverses
|
|
677
|
+
#
|
|
678
|
+
def on_enter_row arow
|
|
679
|
+
return if @content.nil? || @content.size == 0
|
|
680
|
+
require 'rbhex/core/include/ractionevent'
|
|
681
|
+
aev = TextActionEvent.new self, :ENTER_ROW, current_value().to_s, @current_index, @curpos
|
|
682
|
+
fire_handler :ENTER_ROW, aev
|
|
683
|
+
@repaint_required = true
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# destroy the pad, this needs to be called from somewhere, like when the app
|
|
687
|
+
# closes or the current window closes , or else we could have a seg fault
|
|
688
|
+
# or some ugliness on the screen below this one (if nested).
|
|
689
|
+
|
|
690
|
+
# Now since we use get_pad from window, upon the window being destroyed,
|
|
691
|
+
# it will call this. Else it will destroy pad
|
|
692
|
+
def destroy
|
|
693
|
+
FFI::NCurses.delwin(@pad) if @pad # when do i do this ? FIXME
|
|
694
|
+
@pad = nil
|
|
695
|
+
end
|
|
696
|
+
#
|
|
697
|
+
# return true if the given row is visible
|
|
698
|
+
def is_visible? index
|
|
699
|
+
j = index - @prow #@toprow
|
|
700
|
+
j >= 0 && j <= @scrollatrows
|
|
701
|
+
end
|
|
702
|
+
#
|
|
703
|
+
# called when this widget is entered, by form
|
|
704
|
+
def on_enter
|
|
705
|
+
set_form_row
|
|
706
|
+
end
|
|
707
|
+
# called by form
|
|
708
|
+
def set_form_row
|
|
709
|
+
setrowcol @lastrow, @lastcol
|
|
710
|
+
end
|
|
711
|
+
# called by form
|
|
712
|
+
def set_form_col
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
private
|
|
716
|
+
|
|
717
|
+
# check that current_index and prow are within correct ranges
|
|
718
|
+
# sets row (and someday col too)
|
|
719
|
+
# sets repaint_required
|
|
720
|
+
|
|
721
|
+
def bounds_check
|
|
722
|
+
r,c = rowcol
|
|
723
|
+
@current_index = 0 if @current_index < 0
|
|
724
|
+
@current_index = @content.count()-1 if @current_index > @content.count()-1
|
|
725
|
+
ensure_visible
|
|
726
|
+
|
|
727
|
+
check_prow
|
|
728
|
+
#$log.debug "XXX: PAD BOUNDS ci:#{@current_index} , old #{@oldrow},pr #{@prow}, max #{@maxrow} pcol #{@pcol} maxcol #{@maxcol}"
|
|
729
|
+
@crow = @current_index + r - @prow
|
|
730
|
+
@crow = r if @crow < r
|
|
731
|
+
# 2 depends on whetehr suppressborders
|
|
732
|
+
if @suppress_borders
|
|
733
|
+
@crow = @row + @height -1 if @crow >= r + @height -1
|
|
734
|
+
else
|
|
735
|
+
@crow = @row + @height -2 if @crow >= r + @height -2
|
|
736
|
+
end
|
|
737
|
+
setrowcol @crow, @curpos+c
|
|
738
|
+
lastcurpos @crow, @curpos+c
|
|
739
|
+
if @oldindex != @current_index
|
|
740
|
+
on_enter_row @current_index
|
|
741
|
+
@oldindex = @current_index
|
|
742
|
+
end
|
|
743
|
+
if @oldrow != @prow || @oldcol != @pcol
|
|
744
|
+
# only if scrolling has happened.
|
|
745
|
+
@repaint_required = true
|
|
746
|
+
end
|
|
747
|
+
end
|
|
748
|
+
#
|
|
749
|
+
# save last cursor position so when reentering, cursor can be repositioned
|
|
750
|
+
def lastcurpos r,c
|
|
751
|
+
@lastrow = r
|
|
752
|
+
@lastcol = c
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
# check that prow and pcol are within bounds
|
|
757
|
+
#
|
|
758
|
+
def check_prow
|
|
759
|
+
@prow = 0 if @prow < 0
|
|
760
|
+
@pcol = 0 if @pcol < 0
|
|
761
|
+
|
|
762
|
+
cc = @content.count
|
|
763
|
+
|
|
764
|
+
if cc < @rows
|
|
765
|
+
@prow = 0
|
|
766
|
+
else
|
|
767
|
+
maxrow = cc - @rows - 1
|
|
768
|
+
if @prow > maxrow
|
|
769
|
+
@prow = maxrow
|
|
770
|
+
end
|
|
771
|
+
end
|
|
772
|
+
# we still need to check the max that prow can go otherwise
|
|
773
|
+
# the pad shows earlier stuff.
|
|
774
|
+
#
|
|
775
|
+
return
|
|
776
|
+
end
|
|
777
|
+
public
|
|
778
|
+
##
|
|
779
|
+
# Ask user for string to search for
|
|
780
|
+
# This uses the dialog, but what if user wants the old style.
|
|
781
|
+
# Isn't there a cleaner way to let user override style, or allow user
|
|
782
|
+
# to use own UI for getting pattern and then passing here.
|
|
783
|
+
# @param str default nil. If not passed, then user is prompted using get_string dialog
|
|
784
|
+
# This allows caller to use own method to prompt for string such as 'get_line' or 'rbgetstr' /
|
|
785
|
+
# 'ask()'
|
|
786
|
+
def ask_search str=nil
|
|
787
|
+
# the following is a change that enables callers to prompt for the string
|
|
788
|
+
# using some other style, basically the classical style and send the string in
|
|
789
|
+
str = get_string("Enter pattern: ") unless str
|
|
790
|
+
return if str.nil?
|
|
791
|
+
str = @last_regex if str == ""
|
|
792
|
+
return if str == ""
|
|
793
|
+
ix = next_match str
|
|
794
|
+
return unless ix
|
|
795
|
+
@last_regex = str
|
|
796
|
+
|
|
797
|
+
#@oldindex = @current_index
|
|
798
|
+
@current_index = ix[0]
|
|
799
|
+
@curpos = ix[1]
|
|
800
|
+
ensure_visible
|
|
801
|
+
end
|
|
802
|
+
##
|
|
803
|
+
# Find next matching row for string accepted in ask_search
|
|
804
|
+
#
|
|
805
|
+
def find_more
|
|
806
|
+
return unless @last_regex
|
|
807
|
+
ix = next_match @last_regex
|
|
808
|
+
return unless ix
|
|
809
|
+
#@oldindex = @current_index
|
|
810
|
+
@current_index = ix[0]
|
|
811
|
+
@curpos = ix[1]
|
|
812
|
+
ensure_visible
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
##
|
|
816
|
+
# Find the next row that contains given string
|
|
817
|
+
# @return row and col offset of match, or nil
|
|
818
|
+
# @param String to find
|
|
819
|
+
def next_match str
|
|
820
|
+
first = nil
|
|
821
|
+
## content can be string or Chunkline, so we had to write <tt>index</tt> for this.
|
|
822
|
+
## =~ does not give an error, but it does not work.
|
|
823
|
+
@content.each_with_index do |line, ix|
|
|
824
|
+
col = line.index str
|
|
825
|
+
if col
|
|
826
|
+
first ||= [ ix, col ]
|
|
827
|
+
if ix > @current_index
|
|
828
|
+
return [ix, col]
|
|
829
|
+
end
|
|
830
|
+
end
|
|
831
|
+
end
|
|
832
|
+
return first
|
|
833
|
+
end
|
|
834
|
+
##
|
|
835
|
+
# Ensure current row is visible, if not make it first row
|
|
836
|
+
# NOTE - need to check if its at end and then reduce scroll at rows, check_prow does that
|
|
837
|
+
#
|
|
838
|
+
# @param current_index (default if not given)
|
|
839
|
+
#
|
|
840
|
+
def ensure_visible row = @current_index
|
|
841
|
+
unless is_visible? row
|
|
842
|
+
@prow = @current_index
|
|
843
|
+
end
|
|
844
|
+
end
|
|
845
|
+
#
|
|
846
|
+
# jumps cursor to next work, like vim's w key
|
|
847
|
+
#
|
|
848
|
+
def forward_word
|
|
849
|
+
$multiplier = 1 if !$multiplier || $multiplier == 0
|
|
850
|
+
line = @current_index
|
|
851
|
+
buff = @content[line].to_s
|
|
852
|
+
return unless buff
|
|
853
|
+
pos = @curpos || 0 # list does not have curpos
|
|
854
|
+
$multiplier.times {
|
|
855
|
+
found = buff.index(/[[:punct:][:space:]]\w/, pos)
|
|
856
|
+
if !found
|
|
857
|
+
# if not found, we've lost a counter
|
|
858
|
+
if line+1 < @content.length
|
|
859
|
+
line += 1
|
|
860
|
+
else
|
|
861
|
+
return
|
|
862
|
+
end
|
|
863
|
+
pos = 0
|
|
864
|
+
else
|
|
865
|
+
pos = found + 1
|
|
866
|
+
end
|
|
867
|
+
$log.debug " forward_word: pos #{pos} line #{line} buff: #{buff}"
|
|
868
|
+
}
|
|
869
|
+
$multiplier = 0
|
|
870
|
+
@current_index = line
|
|
871
|
+
@curpos = pos
|
|
872
|
+
ensure_visible
|
|
873
|
+
@repaint_required = true
|
|
874
|
+
end
|
|
875
|
+
def backward_word
|
|
876
|
+
$multiplier = 1 if !$multiplier || $multiplier == 0
|
|
877
|
+
line = @current_index
|
|
878
|
+
buff = @content[line].to_s
|
|
879
|
+
return unless buff
|
|
880
|
+
pos = @curpos || 0 # list does not have curpos
|
|
881
|
+
$multiplier.times {
|
|
882
|
+
found = buff.rindex(/[[:punct:][:space:]]\w/, pos-2)
|
|
883
|
+
if !found || found == 0
|
|
884
|
+
# if not found, we've lost a counter
|
|
885
|
+
if pos > 0
|
|
886
|
+
pos = 0
|
|
887
|
+
elsif line > 0
|
|
888
|
+
line -= 1
|
|
889
|
+
pos = @content[line].to_s.size
|
|
890
|
+
else
|
|
891
|
+
return
|
|
892
|
+
end
|
|
893
|
+
else
|
|
894
|
+
pos = found + 1
|
|
895
|
+
end
|
|
896
|
+
$log.debug " backward_word: pos #{pos} line #{line} buff: #{buff}"
|
|
897
|
+
}
|
|
898
|
+
$multiplier = 0
|
|
899
|
+
@current_index = line
|
|
900
|
+
@curpos = pos
|
|
901
|
+
ensure_visible
|
|
902
|
+
@repaint_required = true
|
|
903
|
+
end
|
|
904
|
+
#
|
|
905
|
+
# move cursor forward by one char (currently will not pan)
|
|
906
|
+
def cursor_forward
|
|
907
|
+
$multiplier = 1 if $multiplier == 0
|
|
908
|
+
if @curpos < @cols
|
|
909
|
+
@curpos += $multiplier
|
|
910
|
+
if @curpos > @cols
|
|
911
|
+
@curpos = @cols
|
|
912
|
+
end
|
|
913
|
+
@repaint_required = true
|
|
914
|
+
end
|
|
915
|
+
$multiplier = 0
|
|
916
|
+
end
|
|
917
|
+
#
|
|
918
|
+
# move cursor backward by one char (currently will not pan)
|
|
919
|
+
def cursor_backward
|
|
920
|
+
$multiplier = 1 if $multiplier == 0
|
|
921
|
+
if @curpos > 0
|
|
922
|
+
@curpos -= $multiplier
|
|
923
|
+
@curpos = 0 if @curpos < 0
|
|
924
|
+
@repaint_required = true
|
|
925
|
+
end
|
|
926
|
+
$multiplier = 0
|
|
927
|
+
end
|
|
928
|
+
# moves cursor to end of line also panning window if necessary
|
|
929
|
+
# NOTE: if one line on another page (not displayed) is way longer than any
|
|
930
|
+
# displayed line, then this will pan way ahead, so may not be very intelligent
|
|
931
|
+
# in such situations.
|
|
932
|
+
def cursor_eol
|
|
933
|
+
# pcol is based on max length not current line's length
|
|
934
|
+
@pcol = @content_cols - @cols - 1
|
|
935
|
+
@curpos = @content[@current_index].size
|
|
936
|
+
@repaint_required = true
|
|
937
|
+
end
|
|
938
|
+
#
|
|
939
|
+
# moves cursor to start of line, panning if required
|
|
940
|
+
def cursor_bol
|
|
941
|
+
# copy of C-a - start of line
|
|
942
|
+
@repaint_required = true if @pcol > 0
|
|
943
|
+
@pcol = 0
|
|
944
|
+
@curpos = 0
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
end # class textpad
|
|
948
|
+
|
|
949
|
+
# a test renderer to see how things go
|
|
950
|
+
class DefaultFileRenderer
|
|
951
|
+
#
|
|
952
|
+
# @param pad for calling print methods on
|
|
953
|
+
# @param lineno the line number on the pad to print on
|
|
954
|
+
# @param text data to print
|
|
955
|
+
def render pad, lineno, text
|
|
956
|
+
bg = :black
|
|
957
|
+
fg = :white
|
|
958
|
+
att = NORMAL
|
|
959
|
+
#cp = $datacolor
|
|
960
|
+
cp = get_color($datacolor, fg, bg)
|
|
961
|
+
## XXX believe it or not, the next line can give you "invalid byte sequence in UTF-8
|
|
962
|
+
# even when processing filename at times. Or if its an mp3 or non-text file.
|
|
963
|
+
if text =~ /^\s*# / || text =~ /^\s*## /
|
|
964
|
+
fg = :red
|
|
965
|
+
#att = BOLD
|
|
966
|
+
cp = get_color($datacolor, fg, bg)
|
|
967
|
+
elsif text =~ /^\s*#/
|
|
968
|
+
fg = :blue
|
|
969
|
+
cp = get_color($datacolor, fg, bg)
|
|
970
|
+
elsif text =~ /^\s*(class|module) /
|
|
971
|
+
fg = :cyan
|
|
972
|
+
att = BOLD
|
|
973
|
+
cp = get_color($datacolor, fg, bg)
|
|
974
|
+
elsif text =~ /^\s*def / || text =~ /^\s*function /
|
|
975
|
+
fg = :yellow
|
|
976
|
+
att = BOLD
|
|
977
|
+
cp = get_color($datacolor, fg, bg)
|
|
978
|
+
elsif text =~ /^\s*(end|if |elsif|else|begin|rescue|ensure|include|extend|while|unless|case |when )/
|
|
979
|
+
fg = :magenta
|
|
980
|
+
att = BOLD
|
|
981
|
+
cp = get_color($datacolor, fg, bg)
|
|
982
|
+
elsif text =~ /^\s*=/
|
|
983
|
+
# rdoc case
|
|
984
|
+
fg = :blue
|
|
985
|
+
bg = :white
|
|
986
|
+
cp = get_color($datacolor, fg, bg)
|
|
987
|
+
att = REVERSE
|
|
988
|
+
end
|
|
989
|
+
FFI::NCurses.wattron(pad,FFI::NCurses.COLOR_PAIR(cp) | att)
|
|
990
|
+
FFI::NCurses.mvwaddstr(pad, lineno, 0, text)
|
|
991
|
+
FFI::NCurses.wattroff(pad,FFI::NCurses.COLOR_PAIR(cp) | att)
|
|
992
|
+
|
|
993
|
+
end
|
|
994
|
+
end
|
|
995
|
+
end
|