rbcurse-core 0.0.10 → 0.0.11

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