rbcurse-core 0.0.10 → 0.0.11
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.
- data/CHANGELOG +4 -0
- data/VERSION +1 -1
- data/lib/rbcurse/core/widgets/textpad.rb +505 -125
- data/rbcurse-core.gemspec +2 -2
- metadata +2 -2
data/CHANGELOG
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.11
|
@@ -1,26 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
1
2
|
# ----------------------------------------------------------------------------- #
|
2
3
|
# File: textpad.rb
|
3
4
|
# Description: A class that displays text using a pad.
|
4
5
|
# The motivation for this is to put formatted text and not care about truncating and
|
5
6
|
# stuff. Also, there will be only one write, not each time scrolling happens.
|
6
7
|
# I found textview code for repaint being more complex than required.
|
7
|
-
# Author: rkumar http://github.com/rkumar/
|
8
|
+
# Author: rkumar http://github.com/rkumar/mancurses/
|
8
9
|
# Date: 2011-11-09 - 16:59
|
9
10
|
# License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
|
10
|
-
# Last update: 2013-03-
|
11
|
+
# Last update: 2013-03-22 20:09
|
11
12
|
#
|
12
13
|
# == CHANGES
|
13
14
|
# == TODO
|
14
|
-
# when
|
15
|
+
# _ in popup case allowing scrolling when it should not so you get an extra char at end
|
16
|
+
# _ The list one needs a f-char like functionality.
|
17
|
+
# x handle putting data again and overwriting existing
|
18
|
+
# When reputting data, the underlying pad needs to be properly cleared
|
19
|
+
# esp last row and last col
|
20
|
+
#
|
15
21
|
# x add mappings and process key in handle_keys and other widget things
|
16
|
-
# - user can put text or list
|
17
|
-
# - handle putting data again and overwriting existing
|
18
|
-
# - formatted text
|
19
|
-
# - search and other features
|
20
22
|
# - can pad movement and other ops be abstracted into module for reuse
|
21
23
|
# / get scrolling like in vim (C-f e y b d)
|
22
|
-
# - alert issue of leaving a blank is poss due to using prefresh i/o copywin
|
23
24
|
#
|
25
|
+
# == TODO 2013-03-07 - 20:34
|
26
|
+
# _ key bindings not showing up -- bind properly
|
24
27
|
# ----------------------------------------------------------------------------- #
|
25
28
|
#
|
26
29
|
require 'rbcurse'
|
@@ -33,6 +36,9 @@ module RubyCurses
|
|
33
36
|
include BorderTitle
|
34
37
|
|
35
38
|
dsl_accessor :suppress_border
|
39
|
+
dsl_accessor :print_footer
|
40
|
+
attr_reader :current_index
|
41
|
+
attr_reader :rows , :cols
|
36
42
|
# You may pass height, width, row and col for creating a window otherwise a fullscreen window
|
37
43
|
# will be created. If you pass a window from caller then that window will be used.
|
38
44
|
# Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
|
@@ -43,20 +49,23 @@ module RubyCurses
|
|
43
49
|
@editable = false
|
44
50
|
@focusable = true
|
45
51
|
@config = config
|
46
|
-
|
47
|
-
#@cols = FFI::NCurses.COLS-1
|
52
|
+
@row = @col = 0
|
48
53
|
@prow = @pcol = 0
|
49
54
|
@startrow = 0
|
50
55
|
@startcol = 0
|
51
56
|
@list = []
|
52
57
|
super
|
53
58
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
59
|
+
## NOTE
|
60
|
+
# ---------------------------------------------------
|
61
|
+
# Since we are using pads, you need to get your height, width and rows correct
|
62
|
+
# Make sure the height factors in the row, else nothing may show
|
63
|
+
# ---------------------------------------------------
|
64
|
+
#@height = @height.ifzero(FFI::NCurses.LINES)
|
65
|
+
#@width = @width.ifzero(FFI::NCurses.COLS)
|
58
66
|
@rows = @height
|
59
67
|
@cols = @width
|
68
|
+
# NOTE XXX if cols is > COLS then padrefresh can fail
|
60
69
|
@startrow = @row
|
61
70
|
@startcol = @col
|
62
71
|
#@suppress_border = config[:suppress_border]
|
@@ -66,15 +75,31 @@ module RubyCurses
|
|
66
75
|
@startcol += 1
|
67
76
|
@rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
68
77
|
@cols -=3
|
78
|
+
else
|
79
|
+
# seeing why nothing is printing
|
80
|
+
@rows -=0 # 3 is since print_border_only reduces one from width, to check whether this is correct
|
81
|
+
## if next is 0 then padrefresh doesn't print
|
82
|
+
@cols -=1
|
69
83
|
end
|
70
84
|
@row_offset = @col_offset = 0 if @suppress_borders
|
71
85
|
@top = @row
|
72
86
|
@left = @col
|
87
|
+
@lastrow = @row + 1
|
88
|
+
@lastcol = @col + 1
|
89
|
+
@_events << :PRESS
|
90
|
+
@_events << :ENTER_ROW
|
91
|
+
@scrollatrows = @height - 3
|
73
92
|
init_vars
|
74
93
|
end
|
75
94
|
def init_vars
|
76
|
-
|
95
|
+
$multiplier = 0
|
77
96
|
@oldindex = @current_index = 0
|
97
|
+
# column cursor
|
98
|
+
@prow = @pcol = @curpos = 0
|
99
|
+
if @row && @col
|
100
|
+
@lastrow = @row + 1
|
101
|
+
@lastcol = @col + 1
|
102
|
+
end
|
78
103
|
@repaint_required = true
|
79
104
|
end
|
80
105
|
def rowcol #:nodoc:
|
@@ -82,10 +107,11 @@ module RubyCurses
|
|
82
107
|
end
|
83
108
|
|
84
109
|
private
|
110
|
+
## XXX in list text returns the selected row, list returns the full thing, keep consistent
|
85
111
|
def create_pad
|
86
112
|
destroy if @pad
|
87
113
|
#@pad = FFI::NCurses.newpad(@content_rows, @content_cols)
|
88
|
-
@pad = @window.get_pad(@content_rows, @content_cols)
|
114
|
+
@pad = @window.get_pad(@content_rows, @content_cols )
|
89
115
|
end
|
90
116
|
|
91
117
|
private
|
@@ -93,12 +119,25 @@ module RubyCurses
|
|
93
119
|
def populate_pad
|
94
120
|
@_populate_needed = false
|
95
121
|
# how can we make this more sensible ? FIXME
|
96
|
-
|
122
|
+
#@renderer ||= DefaultFileRenderer.new #if ".rb" == @filetype
|
97
123
|
@content_rows = @content.count
|
98
124
|
@content_cols = content_cols()
|
125
|
+
# this should be explicit and not "intelligent"
|
126
|
+
#@title += " [ #{@content_rows},#{@content_cols}] " if @cols > 50
|
127
|
+
@content_rows = @rows if @content_rows < @rows
|
128
|
+
@content_cols = @cols if @content_cols < @cols
|
129
|
+
#$log.debug "XXXX content_cols = #{@content_cols}"
|
99
130
|
|
100
131
|
create_pad
|
101
132
|
|
133
|
+
# clearstring is the string required to clear the pad to backgroud color
|
134
|
+
@clearstring = nil
|
135
|
+
cp = get_color($datacolor, @color, @bgcolor)
|
136
|
+
@cp = FFI::NCurses.COLOR_PAIR(cp)
|
137
|
+
if cp != $datacolor
|
138
|
+
@clearstring ||= " " * @width
|
139
|
+
end
|
140
|
+
|
102
141
|
Ncurses::Panel.update_panels
|
103
142
|
@content.each_index { |ix|
|
104
143
|
#FFI::NCurses.mvwaddstr(@pad,ix, 0, @content[ix])
|
@@ -127,35 +166,93 @@ module RubyCurses
|
|
127
166
|
if @renderer
|
128
167
|
@renderer.render @pad, lineno, text
|
129
168
|
else
|
169
|
+
## messabox does have a method to paint the whole window in bg color its in rwidget.rb
|
170
|
+
att = NORMAL
|
171
|
+
FFI::NCurses.wattron(@pad, @cp | att)
|
172
|
+
FFI::NCurses.mvwaddstr(@pad,lineno, 0, @clearstring) if @clearstring
|
130
173
|
FFI::NCurses.mvwaddstr(@pad,lineno, 0, @content[lineno])
|
174
|
+
|
175
|
+
#FFI::NCurses.mvwaddstr(pad, lineno, 0, text)
|
176
|
+
FFI::NCurses.wattroff(@pad, @cp | att)
|
131
177
|
end
|
132
178
|
end
|
133
179
|
|
134
180
|
# supply a filename as source for textpad
|
135
181
|
# Reads up file into @content
|
136
|
-
|
137
|
-
|
182
|
+
# One can optionally send in a method which takes a filename and returns an array of data
|
183
|
+
# This is required if you are processing files which are binary such as zip/archives and wish
|
184
|
+
# to print the contents. (e.g. cygnus gem sends in :get_file_contents).
|
185
|
+
# filename("a.c", method(:get_file_contents))
|
186
|
+
#
|
187
|
+
def filename(filename, reader=nil)
|
138
188
|
@file = filename
|
189
|
+
unless File.exists? filename
|
190
|
+
alert "#{filename} does not exist"
|
191
|
+
return
|
192
|
+
end
|
139
193
|
@filetype = File.extname filename
|
140
|
-
|
194
|
+
if reader
|
195
|
+
@content = reader.call(filename)
|
196
|
+
else
|
197
|
+
@content = File.open(filename,"r").readlines
|
198
|
+
end
|
199
|
+
if @filetype == ""
|
200
|
+
if @content.first.index("ruby")
|
201
|
+
@filetype = ".rb"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
init_vars
|
205
|
+
@repaint_all = true
|
141
206
|
@_populate_needed = true
|
142
207
|
end
|
143
208
|
|
144
209
|
# Supply an array of string to be displayed
|
145
210
|
# This will replace existing text
|
146
211
|
|
147
|
-
|
212
|
+
# display text given in an array format. This is the principal way of giving content
|
213
|
+
# to a textpad, other than filename().
|
214
|
+
# @param Array of lines
|
215
|
+
# @param format (optional) can be :tmux :ansi or :none
|
216
|
+
# If a format other than :none is given, then formatted_text is called.
|
217
|
+
def text(lines, fmt=:none)
|
218
|
+
# added so callers can have one interface and avoid an if condition
|
219
|
+
return formatted_text(lines, fmt) unless fmt == :none
|
220
|
+
|
221
|
+
return @content if lines.empty?
|
148
222
|
@content = lines
|
149
223
|
@_populate_needed = true
|
224
|
+
@repaint_all = true
|
225
|
+
init_vars
|
226
|
+
self
|
227
|
+
end
|
228
|
+
alias :list :text
|
229
|
+
def content
|
230
|
+
raise "content is nil " unless @content
|
231
|
+
return @content
|
232
|
+
end
|
233
|
+
alias :get_content :content
|
234
|
+
|
235
|
+
# print footer containing line and position
|
236
|
+
# XXX UNTESTED TODO TESTING
|
237
|
+
def print_foot #:nodoc:
|
238
|
+
@footer_attrib ||= Ncurses::A_REVERSE
|
239
|
+
footer = "R: #{@current_index+1}, C: #{@curpos+@pcol}, #{@list.length} lines "
|
240
|
+
$log.debug " print_foot calling printstring with #{@row} + #{@height} -1, #{@col}+2"
|
241
|
+
@graphic.printstring( @row + @height -1 , @col+2, footer, @color_pair || $datacolor, @footer_attrib)
|
242
|
+
@repaint_footer_required = false # 2010-01-23 22:55
|
150
243
|
end
|
151
244
|
|
152
245
|
## ---- the next 2 methods deal with printing chunks
|
153
246
|
# we should put it int a common module and include it
|
154
247
|
# in Window and Pad stuff and perhaps include it conditionally.
|
155
248
|
|
156
|
-
|
249
|
+
## 2013-03-07 - 19:57 changed width to @content_cols since data not printing
|
250
|
+
# in some cases fully when ansi sequences were present int some line but not in others
|
251
|
+
# lines without ansi were printing less by a few chars.
|
252
|
+
# This was prolly copied from rwindow, where it is okay since its for a specific width
|
253
|
+
def print(string, _width = @content_cols)
|
157
254
|
#return unless visible?
|
158
|
-
w =
|
255
|
+
w = _width == 0? Ncurses.COLS : _width
|
159
256
|
FFI::NCurses.waddnstr(@pad,string.to_s, w) # changed 2011 dts
|
160
257
|
end
|
161
258
|
|
@@ -186,17 +283,30 @@ module RubyCurses
|
|
186
283
|
end
|
187
284
|
end
|
188
285
|
|
286
|
+
#
|
287
|
+
# pass in formatted text along with parser (:tmux or :ansi)
|
189
288
|
def formatted_text text, fmt
|
289
|
+
|
190
290
|
require 'rbcurse/core/include/chunk'
|
191
291
|
@formatted_text = text
|
192
292
|
@color_parser = fmt
|
293
|
+
@repaint_required = true
|
294
|
+
# don't know if start is always required. so putting in caller
|
295
|
+
#goto_start
|
193
296
|
#remove_all
|
194
297
|
end
|
195
298
|
|
196
299
|
# write pad onto window
|
197
|
-
private
|
300
|
+
#private
|
198
301
|
def padrefresh
|
199
|
-
|
302
|
+
top = @window.top
|
303
|
+
left = @window.left
|
304
|
+
sr = @startrow + top
|
305
|
+
sc = @startcol + left
|
306
|
+
retval = FFI::NCurses.prefresh(@pad,@prow,@pcol, sr , sc , @rows + sr , @cols+ sc );
|
307
|
+
$log.warn "XXX: PADREFRESH #{retval}, #{@prow}, #{@pcol}, #{sr}, #{sc}, #{@rows+sr}, #{@cols+sc}." if retval == -1
|
308
|
+
# padrefresh can fail if width is greater than NCurses.COLS
|
309
|
+
#FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow + top, @startcol + left, @rows + @startrow + top, @cols+@startcol + left);
|
200
310
|
end
|
201
311
|
|
202
312
|
# convenience method to return byte
|
@@ -206,13 +316,24 @@ module RubyCurses
|
|
206
316
|
end
|
207
317
|
|
208
318
|
# length of longest string in array
|
319
|
+
# This will give a 'wrong' max length if the array has ansi color escape sequences in it
|
320
|
+
# which inc the length but won't be printed. Such lines actually have less length when printed
|
321
|
+
# So in such cases, give more space to the pad.
|
209
322
|
def content_cols
|
210
323
|
longest = @content.max_by(&:length)
|
324
|
+
## 2013-03-06 - 20:41 crashes here for some reason when man gives error message no man entry
|
325
|
+
return 0 unless longest
|
211
326
|
longest.length
|
212
327
|
end
|
213
328
|
|
214
329
|
public
|
215
330
|
def repaint
|
331
|
+
## 2013-03-08 - 21:01 This is the fix to the issue of form callign an event like ? or F1
|
332
|
+
# which throws up a messagebox which leaves a black rect. We have no place to put a refresh
|
333
|
+
# However, form does call repaint for all objects, so we can do a padref here. Otherwise,
|
334
|
+
# it would get rejected. UNfortunately this may happen more often we want, but we never know
|
335
|
+
# when something pops up on the screen.
|
336
|
+
padrefresh unless @repaint_required
|
216
337
|
return unless @repaint_required
|
217
338
|
if @formatted_text
|
218
339
|
$log.debug "XXX: INSIDE FORMATTED TEXT "
|
@@ -230,13 +351,22 @@ module RubyCurses
|
|
230
351
|
#HERE we need to populate once so user can pass a renderer
|
231
352
|
unless @suppress_border
|
232
353
|
if @repaint_all
|
233
|
-
|
354
|
+
## XXX im not getting the background color.
|
355
|
+
#@window.print_border_only @top, @left, @height-1, @width, $datacolor
|
356
|
+
clr = get_color $datacolor, @color, @bgcolor
|
357
|
+
#@window.print_border @top, @left, @height-1, @width, clr
|
358
|
+
@window.print_border_only @top, @left, @height-1, @width, clr
|
234
359
|
print_title
|
360
|
+
|
361
|
+
@repaint_footer_required = true if @oldrow != @current_index
|
362
|
+
print_foot if @print_footer && !@suppress_borders && @repaint_footer_required
|
363
|
+
|
235
364
|
@window.wrefresh
|
236
365
|
end
|
237
366
|
end
|
238
367
|
|
239
368
|
padrefresh
|
369
|
+
Ncurses::Panel.update_panels
|
240
370
|
@repaint_required = false
|
241
371
|
@repaint_all = false
|
242
372
|
end
|
@@ -246,66 +376,109 @@ module RubyCurses
|
|
246
376
|
#
|
247
377
|
def map_keys
|
248
378
|
@mapped_keys = true
|
249
|
-
bind_key([?g,?g]){ goto_start } # mapping double keys like vim
|
250
|
-
bind_key(279){ goto_start }
|
251
|
-
bind_keys([?G,277]){ goto_end }
|
252
|
-
bind_keys([?k,KEY_UP]){ up }
|
253
|
-
bind_keys([?j,KEY_DOWN]){ down }
|
254
|
-
bind_key(?\C-e){ scroll_window_down }
|
255
|
-
bind_key(?\C-y){ scroll_window_up }
|
256
|
-
bind_keys([32,338, ?\C-d]){ scroll_forward }
|
379
|
+
bind_key([?g,?g], 'goto_start'){ goto_start } # mapping double keys like vim
|
380
|
+
bind_key(279, 'goto_start'){ goto_start }
|
381
|
+
bind_keys([?G,277], 'goto end'){ goto_end }
|
382
|
+
bind_keys([?k,KEY_UP], "Up"){ up }
|
383
|
+
bind_keys([?j,KEY_DOWN], "Down"){ down }
|
384
|
+
bind_key(?\C-e, "Scroll Window Down"){ scroll_window_down }
|
385
|
+
bind_key(?\C-y, "Scroll Window Up"){ scroll_window_up }
|
386
|
+
bind_keys([32,338, ?\C-d], "Scroll Forward"){ scroll_forward }
|
257
387
|
bind_keys([?\C-b,339]){ scroll_backward }
|
258
|
-
|
259
|
-
#bind_key(
|
260
|
-
|
388
|
+
# the next one invalidates the single-quote binding for bookmarks
|
389
|
+
#bind_key([?',?']){ goto_last_position } # vim , goto last row position (not column)
|
390
|
+
bind_key(?/, :ask_search)
|
391
|
+
bind_key(?n, :find_more)
|
261
392
|
bind_key([?\C-x, ?>], :scroll_right)
|
262
393
|
bind_key([?\C-x, ?<], :scroll_left)
|
263
394
|
bind_key(?\M-l, :scroll_right)
|
264
395
|
bind_key(?\M-h, :scroll_left)
|
265
|
-
bind_key(
|
266
|
-
|
267
|
-
bind_key(?
|
396
|
+
bind_key(?L, :bottom_of_window)
|
397
|
+
bind_key(?M, :middle_of_window)
|
398
|
+
bind_key(?H, :top_of_window)
|
399
|
+
bind_key(?w, :forward_word)
|
400
|
+
bind_key(?l, :cursor_forward)
|
401
|
+
bind_key(?h, :cursor_backward)
|
402
|
+
bind_key(?$, :cursor_eol)
|
403
|
+
bind_key(KEY_ENTER, :fire_action_event)
|
268
404
|
end
|
269
405
|
|
270
406
|
# goto first line of file
|
271
407
|
def goto_start
|
272
|
-
|
408
|
+
#@oldindex = @current_index
|
409
|
+
$multiplier ||= 0
|
410
|
+
if $multiplier > 0
|
411
|
+
goto_line $multiplier - 1
|
412
|
+
return
|
413
|
+
end
|
273
414
|
@current_index = 0
|
415
|
+
@curpos = @pcol = @prow = 0
|
274
416
|
@prow = 0
|
417
|
+
$multiplier = 0
|
275
418
|
end
|
276
419
|
|
277
420
|
# goto last line of file
|
278
421
|
def goto_end
|
279
|
-
|
280
|
-
|
422
|
+
#@oldindex = @current_index
|
423
|
+
$multiplier ||= 0
|
424
|
+
if $multiplier > 0
|
425
|
+
goto_line $multiplier - 1
|
426
|
+
return
|
427
|
+
end
|
428
|
+
@current_index = @content.count() - 1
|
281
429
|
@prow = @current_index - @scrollatrows
|
430
|
+
$multiplier = 0
|
431
|
+
end
|
432
|
+
def goto_line line
|
433
|
+
## we may need to calculate page, zfm style and place at right position for ensure visible
|
434
|
+
#line -= 1
|
435
|
+
@current_index = line
|
436
|
+
ensure_visible line
|
437
|
+
bounds_check
|
438
|
+
$multiplier = 0
|
439
|
+
end
|
440
|
+
def top_of_window
|
441
|
+
@current_index = @prow
|
442
|
+
$multiplier ||= 0
|
443
|
+
if $multiplier > 0
|
444
|
+
@current_index += $multiplier
|
445
|
+
$multiplier = 0
|
446
|
+
end
|
447
|
+
end
|
448
|
+
def bottom_of_window
|
449
|
+
@current_index = @prow + @scrollatrows
|
450
|
+
$multiplier ||= 0
|
451
|
+
if $multiplier > 0
|
452
|
+
@current_index -= $multiplier
|
453
|
+
$multiplier = 0
|
454
|
+
end
|
455
|
+
end
|
456
|
+
def middle_of_window
|
457
|
+
@current_index = @prow + (@scrollatrows/2)
|
458
|
+
$multiplier = 0
|
282
459
|
end
|
283
460
|
|
284
461
|
# move down a line mimicking vim's j key
|
285
462
|
# @param [int] multiplier entered prior to invoking key
|
286
463
|
def down num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
287
|
-
|
464
|
+
#@oldindex = @current_index if num > 10
|
288
465
|
@current_index += num
|
289
|
-
|
290
|
-
if @current_index > @scrollatrows
|
291
|
-
@prow += 1
|
292
|
-
end
|
293
|
-
end
|
466
|
+
ensure_visible
|
294
467
|
$multiplier = 0
|
295
468
|
end
|
296
469
|
|
297
470
|
# move up a line mimicking vim's k key
|
298
471
|
# @param [int] multiplier entered prior to invoking key
|
299
472
|
def up num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
|
300
|
-
|
473
|
+
#@oldindex = @current_index if num > 10
|
301
474
|
@current_index -= num
|
302
|
-
unless is_visible? @current_index
|
303
|
-
if @prow > @current_index
|
304
|
-
|
305
|
-
|
306
|
-
else
|
307
|
-
end
|
308
|
-
end
|
475
|
+
#unless is_visible? @current_index
|
476
|
+
#if @prow > @current_index
|
477
|
+
##$status_message.value = "1 #{@prow} > #{@current_index} "
|
478
|
+
#@prow -= 1
|
479
|
+
#else
|
480
|
+
#end
|
481
|
+
#end
|
309
482
|
$multiplier = 0
|
310
483
|
end
|
311
484
|
|
@@ -333,7 +506,7 @@ module RubyCurses
|
|
333
506
|
|
334
507
|
# scrolls lines a window full at a time, on pressing ENTER or C-d or pagedown
|
335
508
|
def scroll_forward
|
336
|
-
|
509
|
+
#@oldindex = @current_index
|
337
510
|
@current_index += @scrollatrows
|
338
511
|
@prow = @current_index - @scrollatrows
|
339
512
|
end
|
@@ -341,7 +514,7 @@ module RubyCurses
|
|
341
514
|
# scrolls lines backward a window full at a time, on pressing pageup
|
342
515
|
# C-u may not work since it is trapped by form earlier. Need to fix
|
343
516
|
def scroll_backward
|
344
|
-
|
517
|
+
#@oldindex = @current_index
|
345
518
|
@current_index -= @scrollatrows
|
346
519
|
@prow = @current_index - @scrollatrows
|
347
520
|
end
|
@@ -352,34 +525,46 @@ module RubyCurses
|
|
352
525
|
@oldindex = tmp
|
353
526
|
bounds_check
|
354
527
|
end
|
528
|
+
def scroll_right
|
529
|
+
# I don't think it will ever be less since we've increased it to cols
|
530
|
+
if @content_cols <= @cols
|
531
|
+
maxpcol = 0
|
532
|
+
@pcol = 0
|
533
|
+
else
|
534
|
+
maxpcol = @content_cols - @cols - 1
|
535
|
+
@pcol += 1
|
536
|
+
@pcol = maxpcol if @pcol > maxpcol
|
537
|
+
end
|
538
|
+
# to prevent right from retaining earlier painted values
|
539
|
+
# padreader does not do a clear, yet works fine.
|
540
|
+
# OK it has an update_panel after padrefresh, that clears it seems.
|
541
|
+
#this clears entire window not just the pad
|
542
|
+
#FFI::NCurses.wclear(@window.get_window)
|
543
|
+
# so border and title is repainted after window clearing
|
544
|
+
#
|
545
|
+
# Next line was causing all sorts of problems when scrolling with ansi formatted text
|
546
|
+
#@repaint_all = true
|
547
|
+
end
|
548
|
+
def scroll_left
|
549
|
+
@pcol -= 1
|
550
|
+
end
|
551
|
+
#
|
552
|
+
#
|
553
|
+
#
|
554
|
+
#
|
355
555
|
def handle_key ch
|
356
556
|
return :UNHANDLED unless @content
|
357
557
|
map_keys unless @mapped_keys
|
358
558
|
|
359
|
-
|
360
|
-
@maxcol = @content_cols - @cols
|
559
|
+
|
361
560
|
@oldrow = @prow
|
362
561
|
@oldcol = @pcol
|
363
|
-
$log.debug "XXX: PAD got #{ch} "
|
562
|
+
$log.debug "XXX: PAD got #{ch} prow = #{@prow}"
|
364
563
|
begin
|
365
564
|
case ch
|
366
|
-
when key(?l)
|
367
|
-
# TODO take multipler
|
368
|
-
@pcol += 1
|
369
|
-
when key(?$)
|
370
|
-
@pcol = @maxcol - 1
|
371
|
-
when key(?h)
|
372
|
-
# TODO take multipler
|
373
|
-
if @pcol > 0
|
374
|
-
@pcol -= 1
|
375
|
-
end
|
376
|
-
when key(?q)
|
377
|
-
#throw :close
|
378
565
|
when ?0.getbyte(0)..?9.getbyte(0)
|
379
566
|
if ch == ?0.getbyte(0) && $multiplier == 0
|
380
|
-
|
381
|
-
@repaint_required = true if @pcol > 0 # tried other things but did not work
|
382
|
-
@pcol = 0
|
567
|
+
cursor_bol
|
383
568
|
return 0
|
384
569
|
end
|
385
570
|
# storing digits entered so we can multiply motion actions
|
@@ -392,6 +577,8 @@ module RubyCurses
|
|
392
577
|
# check for bindings, these cannot override above keys since placed at end
|
393
578
|
begin
|
394
579
|
ret = process_key ch, self
|
580
|
+
$multiplier = 0
|
581
|
+
bounds_check
|
395
582
|
## If i press C-x > i get an alert from rwidgets which blacks the screen
|
396
583
|
# if i put a padrefresh here it becomes okay but only for one pad,
|
397
584
|
# i still need to do it for all pads.
|
@@ -399,23 +586,54 @@ module RubyCurses
|
|
399
586
|
$log.error " TEXTPAD ERROR INS #{err} "
|
400
587
|
$log.debug(err.backtrace.join("\n"))
|
401
588
|
textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
402
|
-
# FIXME why does this result in a blank spot on screen till its refreshed again
|
403
|
-
# should not happen if its deleting its panel and doing an update panel
|
404
589
|
end
|
590
|
+
## NOTE if textpad does not handle the event and it goes to form which pops
|
591
|
+
# up a messagebox, then padrefresh does not happen, since control does not
|
592
|
+
# come back here, so a black rect is left on screen
|
593
|
+
# please note that a bounds check will not happen for stuff that
|
594
|
+
# is triggered by form, so you'll have to to it yourself or
|
595
|
+
# call setrowcol explicity if the cursor is not updated
|
405
596
|
return :UNHANDLED if ret == :UNHANDLED
|
406
597
|
end
|
407
|
-
bounds_check
|
408
598
|
rescue => err
|
409
|
-
$log.error " TEXTPAD ERROR
|
599
|
+
$log.error " TEXTPAD ERROR 591 #{err} "
|
410
600
|
$log.debug( err) if err
|
411
601
|
$log.debug(err.backtrace.join("\n")) if err
|
412
602
|
textdialog ["Error in TextPad: #{err} ", *err.backtrace], :title => "Exception"
|
413
603
|
$error_message.value = ""
|
414
604
|
ensure
|
605
|
+
padrefresh
|
606
|
+
Ncurses::Panel.update_panels
|
415
607
|
end
|
416
608
|
return 0
|
417
609
|
end # while loop
|
418
610
|
|
611
|
+
#
|
612
|
+
# event when user hits enter on a row, user would bind :PRESS
|
613
|
+
#
|
614
|
+
def fire_action_event
|
615
|
+
return if @content.nil? || @content.size == 0
|
616
|
+
require 'rbcurse/core/include/ractionevent'
|
617
|
+
aev = TextActionEvent.new self, :PRESS, current_value().to_s, @current_index, @curpos
|
618
|
+
fire_handler :PRESS, aev
|
619
|
+
end
|
620
|
+
#
|
621
|
+
# returns current value (what cursor is on)
|
622
|
+
def current_value
|
623
|
+
@content[@current_index]
|
624
|
+
end
|
625
|
+
#
|
626
|
+
# execute binding when a row is entered, used more in lists to display some text
|
627
|
+
# in a header or footer as one traverses
|
628
|
+
#
|
629
|
+
def on_enter_row arow
|
630
|
+
return if @content.nil? || @content.size == 0
|
631
|
+
require 'rbcurse/core/include/ractionevent'
|
632
|
+
aev = TextActionEvent.new self, :ENTER_ROW, current_value().to_s, @current_index, @curpos
|
633
|
+
fire_handler :ENTER_ROW, aev
|
634
|
+
@repaint_required = true
|
635
|
+
end
|
636
|
+
|
419
637
|
# destroy the pad, this needs to be called from somewhere, like when the app
|
420
638
|
# closes or the current window closes , or else we could have a seg fault
|
421
639
|
# or some ugliness on the screen below this one (if nested).
|
@@ -426,10 +644,24 @@ module RubyCurses
|
|
426
644
|
FFI::NCurses.delwin(@pad) if @pad # when do i do this ? FIXME
|
427
645
|
@pad = nil
|
428
646
|
end
|
647
|
+
#
|
648
|
+
# return true if the given row is visible
|
429
649
|
def is_visible? index
|
430
650
|
j = index - @prow #@toprow
|
431
651
|
j >= 0 && j <= @scrollatrows
|
432
652
|
end
|
653
|
+
#
|
654
|
+
# called when this widget is entered, by form
|
655
|
+
def on_enter
|
656
|
+
set_form_row
|
657
|
+
end
|
658
|
+
# called by form
|
659
|
+
def set_form_row
|
660
|
+
setrowcol @lastrow, @lastcol
|
661
|
+
end
|
662
|
+
# called by form
|
663
|
+
def set_form_col
|
664
|
+
end
|
433
665
|
|
434
666
|
private
|
435
667
|
|
@@ -440,65 +672,228 @@ module RubyCurses
|
|
440
672
|
def bounds_check
|
441
673
|
r,c = rowcol
|
442
674
|
@current_index = 0 if @current_index < 0
|
443
|
-
@current_index = @
|
444
|
-
|
445
|
-
|
446
|
-
if @prow > @current_index
|
447
|
-
$status_message.value = "1 #{@prow} > #{@current_index} "
|
448
|
-
@prow -= 1
|
449
|
-
else
|
450
|
-
end
|
451
|
-
end
|
452
|
-
#end
|
675
|
+
@current_index = @content.count()-1 if @current_index > @content.count()-1
|
676
|
+
ensure_visible
|
677
|
+
|
453
678
|
check_prow
|
454
|
-
|
679
|
+
#$log.debug "XXX: PAD BOUNDS ci:#{@current_index} , old #{@oldrow},pr #{@prow}, max #{@maxrow} pcol #{@pcol} maxcol #{@maxcol}"
|
455
680
|
@crow = @current_index + r - @prow
|
456
681
|
@crow = r if @crow < r
|
457
682
|
# 2 depends on whetehr suppressborders
|
458
683
|
@crow = @row + @height -2 if @crow >= r + @height -2
|
459
|
-
setrowcol @crow,
|
684
|
+
setrowcol @crow, @curpos+c
|
685
|
+
lastcurpos @crow, @curpos+c
|
686
|
+
if @oldindex != @current_index
|
687
|
+
on_enter_row @current_index
|
688
|
+
@oldindex = @current_index
|
689
|
+
end
|
460
690
|
if @oldrow != @prow || @oldcol != @pcol
|
691
|
+
# only if scrolling has happened.
|
461
692
|
@repaint_required = true
|
462
693
|
end
|
463
694
|
end
|
464
|
-
|
695
|
+
#
|
696
|
+
# save last cursor position so when reentering, cursor can be repositioned
|
697
|
+
def lastcurpos r,c
|
698
|
+
@lastrow = r
|
699
|
+
@lastcol = c
|
700
|
+
end
|
701
|
+
|
465
702
|
|
466
|
-
|
703
|
+
# check that prow and pcol are within bounds
|
704
|
+
#
|
705
|
+
def check_prow
|
706
|
+
@prow = 0 if @prow < 0
|
707
|
+
@pcol = 0 if @pcol < 0
|
708
|
+
|
709
|
+
cc = @content.count
|
467
710
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
711
|
+
if cc < @rows
|
712
|
+
@prow = 0
|
713
|
+
else
|
714
|
+
maxrow = cc - @rows - 1
|
715
|
+
if @prow > maxrow
|
716
|
+
@prow = maxrow
|
717
|
+
end
|
718
|
+
end
|
719
|
+
# we still need to check the max that prow can go otherwise
|
720
|
+
# the pad shows earlier stuff.
|
721
|
+
#
|
722
|
+
return
|
473
723
|
end
|
474
|
-
|
475
|
-
|
724
|
+
public
|
725
|
+
##
|
726
|
+
# Ask user for string to search for
|
727
|
+
# This uses the dialog, but what if user wants the old style.
|
728
|
+
# Isn't there a cleaner way to let user override style, or allow user
|
729
|
+
# to use own UI for getting pattern and then passing here.
|
730
|
+
def ask_search
|
731
|
+
str = get_string("Enter pattern: ")
|
732
|
+
return if str.nil?
|
733
|
+
str = @last_regex if str == ""
|
734
|
+
return if str == ""
|
735
|
+
ix = next_match str
|
736
|
+
return unless ix
|
737
|
+
@last_regex = str
|
738
|
+
|
739
|
+
#@oldindex = @current_index
|
740
|
+
@current_index = ix[0]
|
741
|
+
@curpos = ix[1]
|
742
|
+
ensure_visible
|
743
|
+
end
|
744
|
+
##
|
745
|
+
# Find next matching row for string accepted in ask_search
|
746
|
+
#
|
747
|
+
def find_more
|
748
|
+
return unless @last_regex
|
749
|
+
ix = next_match @last_regex
|
750
|
+
return unless ix
|
751
|
+
#@oldindex = @current_index
|
752
|
+
@current_index = ix[0]
|
753
|
+
@curpos = ix[1]
|
754
|
+
ensure_visible
|
755
|
+
end
|
756
|
+
|
757
|
+
##
|
758
|
+
# Find the next row that contains given string
|
759
|
+
# @return row and col offset of match, or nil
|
760
|
+
# @param String to find
|
761
|
+
def next_match str
|
762
|
+
first = nil
|
763
|
+
## content can be string or Chunkline, so we had to write <tt>index</tt> for this.
|
764
|
+
## =~ does not give an error, but it does not work.
|
765
|
+
@content.each_with_index do |line, ix|
|
766
|
+
col = line.index str
|
767
|
+
if col
|
768
|
+
first ||= [ ix, col ]
|
769
|
+
if ix > @current_index
|
770
|
+
return [ix, col]
|
771
|
+
end
|
772
|
+
end
|
773
|
+
end
|
774
|
+
return first
|
476
775
|
end
|
477
|
-
|
776
|
+
##
|
777
|
+
# Ensure current row is visible, if not make it first row
|
778
|
+
# NOTE - need to check if its at end and then reduce scroll at rows, check_prow does that
|
779
|
+
#
|
780
|
+
# @param current_index (default if not given)
|
781
|
+
#
|
782
|
+
def ensure_visible row = @current_index
|
783
|
+
unless is_visible? row
|
784
|
+
@prow = @current_index
|
785
|
+
end
|
786
|
+
end
|
787
|
+
#
|
788
|
+
# jumps cursor to next work, like vim's w key
|
789
|
+
#
|
790
|
+
def forward_word
|
791
|
+
$multiplier = 1 if !$multiplier || $multiplier == 0
|
792
|
+
line = @current_index
|
793
|
+
buff = @content[line].to_s
|
794
|
+
return unless buff
|
795
|
+
pos = @curpos || 0 # list does not have curpos
|
796
|
+
$multiplier.times {
|
797
|
+
found = buff.index(/[[:punct:][:space:]]\w/, pos)
|
798
|
+
if !found
|
799
|
+
# if not found, we've lost a counter
|
800
|
+
if line+1 < @content.length
|
801
|
+
line += 1
|
802
|
+
else
|
803
|
+
return
|
804
|
+
end
|
805
|
+
pos = 0
|
806
|
+
else
|
807
|
+
pos = found + 1
|
808
|
+
end
|
809
|
+
$log.debug " forward_word: pos #{pos} line #{line} buff: #{buff}"
|
810
|
+
}
|
811
|
+
$multiplier = 0
|
812
|
+
@current_index = line
|
813
|
+
@curpos = pos
|
814
|
+
ensure_visible
|
815
|
+
@repaint_required = true
|
816
|
+
end
|
817
|
+
#
|
818
|
+
# move cursor forward by one char (currently will not pan)
|
819
|
+
def cursor_forward
|
820
|
+
$multiplier = 1 if $multiplier == 0
|
821
|
+
if @curpos < @cols
|
822
|
+
@curpos += $multiplier
|
823
|
+
if @curpos > @cols
|
824
|
+
@curpos = @cols
|
825
|
+
end
|
826
|
+
@repaint_required = true
|
827
|
+
end
|
828
|
+
$multiplier = 0
|
829
|
+
end
|
830
|
+
#
|
831
|
+
# move cursor backward by one char (currently will not pan)
|
832
|
+
def cursor_backward
|
833
|
+
$multiplier = 1 if $multiplier == 0
|
834
|
+
if @curpos > 0
|
835
|
+
@curpos -= $multiplier
|
836
|
+
@curpos = 0 if @curpos < 0
|
837
|
+
@repaint_required = true
|
838
|
+
end
|
839
|
+
$multiplier = 0
|
840
|
+
end
|
841
|
+
# moves cursor to end of line also panning window if necessary
|
842
|
+
# NOTE: if one line on another page (not displayed) is way longer than any
|
843
|
+
# displayed line, then this will pan way ahead, so may not be very intelligent
|
844
|
+
# in such situations.
|
845
|
+
def cursor_eol
|
846
|
+
# pcol is based on max length not current line's length
|
847
|
+
@pcol = @content_cols - @cols - 1
|
848
|
+
@curpos = @content[@current_index].size
|
849
|
+
@repaint_required = true
|
850
|
+
end
|
851
|
+
#
|
852
|
+
# moves cursor to start of line, panning if required
|
853
|
+
def cursor_bol
|
854
|
+
# copy of C-a - start of line
|
855
|
+
@repaint_required = true if @pcol > 0
|
856
|
+
@pcol = 0
|
857
|
+
@curpos = 0
|
858
|
+
end
|
859
|
+
|
860
|
+
end # class textpad
|
861
|
+
|
478
862
|
# a test renderer to see how things go
|
479
|
-
class
|
863
|
+
class DefaultFileRenderer
|
480
864
|
def render pad, lineno, text
|
481
865
|
bg = :black
|
482
866
|
fg = :white
|
483
867
|
att = NORMAL
|
484
|
-
cp = $datacolor
|
485
|
-
|
868
|
+
#cp = $datacolor
|
869
|
+
cp = get_color($datacolor, fg, bg)
|
870
|
+
## XXX believe it or not, the next line can give you "invalid byte sequence in UTF-8
|
871
|
+
# even when processing filename at times. Or if its an mp3 or non-text file.
|
872
|
+
if text =~ /^\s*# / || text =~ /^\s*## /
|
486
873
|
fg = :red
|
874
|
+
#att = BOLD
|
487
875
|
cp = get_color($datacolor, fg, bg)
|
488
876
|
elsif text =~ /^\s*#/
|
489
877
|
fg = :blue
|
490
878
|
cp = get_color($datacolor, fg, bg)
|
491
|
-
elsif text =~ /^\s*class /
|
492
|
-
fg = :
|
879
|
+
elsif text =~ /^\s*(class|module) /
|
880
|
+
fg = :cyan
|
881
|
+
att = BOLD
|
493
882
|
cp = get_color($datacolor, fg, bg)
|
494
|
-
elsif text =~ /^\s*def /
|
883
|
+
elsif text =~ /^\s*def / || text =~ /^\s*function /
|
495
884
|
fg = :yellow
|
496
885
|
att = BOLD
|
497
886
|
cp = get_color($datacolor, fg, bg)
|
498
|
-
elsif text =~ /^\s*(begin|rescue|ensure|
|
887
|
+
elsif text =~ /^\s*(end|if |elsif|else|begin|rescue|ensure|include|extend|while|unless|case |when )/
|
499
888
|
fg = :magenta
|
500
889
|
att = BOLD
|
501
890
|
cp = get_color($datacolor, fg, bg)
|
891
|
+
elsif text =~ /^\s*=/
|
892
|
+
# rdoc case
|
893
|
+
fg = :blue
|
894
|
+
bg = :white
|
895
|
+
cp = get_color($datacolor, fg, bg)
|
896
|
+
att = REVERSE
|
502
897
|
end
|
503
898
|
FFI::NCurses.wattron(pad,FFI::NCurses.COLOR_PAIR(cp) | att)
|
504
899
|
FFI::NCurses.mvwaddstr(pad, lineno, 0, text)
|
@@ -507,18 +902,3 @@ module RubyCurses
|
|
507
902
|
end
|
508
903
|
end
|
509
904
|
end
|
510
|
-
if __FILE__ == $PROGRAM_NAME
|
511
|
-
require 'rbcurse/core/util/app'
|
512
|
-
App.new do
|
513
|
-
w = 50
|
514
|
-
w2 = FFI::NCurses.COLS-w-1
|
515
|
-
## create two side by side pads on for ansi and one for ruby
|
516
|
-
p = RubyCurses::TextPad.new @form, :height => FFI::NCurses.LINES, :width => w, :row => 0, :col => 0 , :title => " ansi "
|
517
|
-
fn = "../../../../examples/data/color.2"
|
518
|
-
text = File.open(fn,"r").readlines
|
519
|
-
p.formatted_text(text, :ansi)
|
520
|
-
RubyCurses::TextPad.new @form, :filename => "textpad.rb", :height => FFI::NCurses.LINES, :width => w2, :row => 0, :col => w+1 , :title => " ruby "
|
521
|
-
#throw :close
|
522
|
-
#status_line
|
523
|
-
end
|
524
|
-
end
|
data/rbcurse-core.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "rbcurse-core"
|
8
|
-
s.version = "0.0.
|
8
|
+
s.version = "0.0.11"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Rahul Kumar"]
|
12
|
-
s.date = "2013-03-
|
12
|
+
s.date = "2013-03-23"
|
13
13
|
s.description = "Ruby curses/ncurses widgets for easy application development on text terminals (ruby 1.9)"
|
14
14
|
s.email = "sentinel1879@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbcurse-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.11
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi-ncurses
|