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 CHANGED
@@ -1,3 +1,7 @@
1
+ **2013-03-22**
2
+ ## 0.0.11 rbcurse-core
3
+ * Added textpad (tested in cygnus gem)
4
+
1
5
  **2013-03-22**
2
6
  ## 0.0.10 rbcurse-core
3
7
  * Added method_missing to chunkline, required for ribhu gem
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.10
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/rbcurse/
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-05 19:55
11
+ # Last update: 2013-03-22 20:09
11
12
  #
12
13
  # == CHANGES
13
14
  # == TODO
14
- # when moving right, also don't pan straight away
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
- #@rows = FFI::NCurses.LINES-1
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
- # FIXME 0 as height craps out. need to make it LINES
55
-
56
- @height = @height.ifzero(FFI::NCurses.LINES)
57
- @width = @width.ifzero(FFI::NCurses.COLS)
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
- @scrollatrows = @height - 3
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
- @renderer ||= DefaultRubyRenderer.new if ".rb" == @filetype
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
- def filename(filename)
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
- @content = File.open(filename,"r").readlines
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
- def text lines
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
- def print(string, width = width)
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 = width == 0? Ncurses.COLS : width
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
- FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,@startcol, @rows + @startrow,@cols+@startcol);
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
- @window.print_border_only @top, @left, @height-1, @width, $datacolor
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
- bind_key([?',?']){ goto_last_position } # vim , goto last row position (not column)
259
- #bind_key(?/, :ask_search)
260
- #bind_key(?n, :find_more)
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([?\C-x, ?\C-s], :saveas)
266
- #bind_key(?r) { getstr("Enter a word: ") }
267
- bind_key(?m, :disp_menu)
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
- @oldindex = @current_index
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
- @oldindex = @current_index
280
- @current_index = @content_rows-1
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
- @oldindex = @current_index if num > 10
464
+ #@oldindex = @current_index if num > 10
288
465
  @current_index += num
289
- unless is_visible? @current_index
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
- @oldindex = @current_index if num > 10
473
+ #@oldindex = @current_index if num > 10
301
474
  @current_index -= num
302
- unless is_visible? @current_index
303
- if @prow > @current_index
304
- $status_message.value = "1 #{@prow} > #{@current_index} "
305
- @prow -= 1
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
- @oldindex = @current_index
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
- @oldindex = @current_index
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
- @maxrow = @content_rows - @rows
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
- # copy of C-a - start of line
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 111 #{err} "
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 = @content_rows-1 if @current_index > @content_rows-1
444
- $status_message.value = "visible #{@prow} , #{@current_index} "
445
- unless is_visible? @current_index
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
- $log.debug "XXX: PAD BOUNDS ci:#{@current_index} , old #{@oldrow},pr #{@prow}, max #{@maxrow} "
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, nil
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
- end
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
- # check that prow and pcol are within bounds
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
- def check_prow
469
- @prow = 0 if @prow < 0
470
- @pcol = 0 if @pcol < 0
471
- if @prow > @maxrow-1
472
- @prow = @maxrow-1
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
- if @pcol > @maxcol-1
475
- @pcol = @maxcol-1
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
- end
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 DefaultRubyRenderer
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
- if text =~ /^\s*# /
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 = :magenta
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|end)/
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
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "rbcurse-core"
8
- s.version = "0.0.10"
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-21"
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.10
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-21 00:00:00.000000000 Z
12
+ date: 2013-03-23 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ffi-ncurses