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