rbcurse 0.1.3 → 1.1.1

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.
Files changed (74) hide show
  1. data/CHANGELOG +126 -0
  2. data/Manifest.txt +53 -20
  3. data/README.markdown +423 -0
  4. data/Rakefile +3 -1
  5. data/examples/keytest.rb +177 -0
  6. data/examples/mpad2.rb +156 -0
  7. data/examples/newtesttabp.rb +121 -0
  8. data/examples/rfe.rb +48 -10
  9. data/examples/rfe_renderer.rb +4 -4
  10. data/examples/rvimsplit.rb +376 -0
  11. data/examples/sqlc.rb +97 -106
  12. data/examples/sqlm.rb +446 -0
  13. data/examples/test1.rb +4 -4
  14. data/examples/test2.rb +12 -12
  15. data/examples/testchars.rb +140 -0
  16. data/examples/testkeypress.rb +9 -4
  17. data/examples/testmulticomp.rb +72 -0
  18. data/examples/testscroller.rb +136 -0
  19. data/examples/testscrolllb.rb +86 -0
  20. data/examples/testscrollp.rb +87 -0
  21. data/examples/testscrollta.rb +80 -0
  22. data/examples/testscrolltable.rb +166 -0
  23. data/examples/testsplit.rb +87 -0
  24. data/examples/testsplit2.rb +123 -0
  25. data/examples/testsplit3.rb +215 -0
  26. data/examples/testsplit3_1.rb +244 -0
  27. data/examples/testsplit3a.rb +215 -0
  28. data/examples/testsplit3b.rb +237 -0
  29. data/examples/testsplitta.rb +148 -0
  30. data/examples/testsplittv.rb +142 -0
  31. data/examples/testsplittvv.rb +144 -0
  32. data/examples/testtable.rb +1 -1
  33. data/examples/testtabp.rb +3 -2
  34. data/examples/testtestw.rb +69 -0
  35. data/examples/testtodo.rb +5 -3
  36. data/examples/testtpane.rb +203 -0
  37. data/examples/testtpane2.rb +145 -0
  38. data/examples/testtpanetable.rb +199 -0
  39. data/examples/viewtodo.rb +5 -3
  40. data/lib/rbcurse.rb +1 -1
  41. data/lib/rbcurse/celleditor.rb +2 -2
  42. data/lib/rbcurse/colormap.rb +5 -5
  43. data/lib/rbcurse/defaultlistselectionmodel.rb +3 -3
  44. data/lib/rbcurse/io.rb +663 -0
  45. data/lib/rbcurse/listeditable.rb +306 -0
  46. data/lib/rbcurse/listkeys.rb +15 -15
  47. data/lib/rbcurse/listscrollable.rb +168 -27
  48. data/lib/rbcurse/mapper.rb +35 -13
  49. data/lib/rbcurse/rchangeevent.rb +28 -0
  50. data/lib/rbcurse/rform.rb +845 -0
  51. data/lib/rbcurse/rlistbox.rb +144 -34
  52. data/lib/rbcurse/rmessagebox.rb +10 -5
  53. data/lib/rbcurse/rmulticontainer.rb +325 -0
  54. data/lib/rbcurse/rmultitextview.rb +306 -0
  55. data/lib/rbcurse/rscrollform.rb +369 -0
  56. data/lib/rbcurse/rscrollpane.rb +511 -0
  57. data/lib/rbcurse/rsplitpane.rb +820 -0
  58. data/lib/rbcurse/rtabbedpane.rb +737 -109
  59. data/lib/rbcurse/rtabbedwindow.rb +326 -0
  60. data/lib/rbcurse/rtable.rb +220 -64
  61. data/lib/rbcurse/rtextarea.rb +340 -181
  62. data/lib/rbcurse/rtextview.rb +237 -101
  63. data/lib/rbcurse/rviewport.rb +203 -0
  64. data/lib/rbcurse/rwidget.rb +919 -95
  65. data/lib/rbcurse/scrollable.rb +7 -7
  66. data/lib/rbcurse/selectable.rb +4 -4
  67. data/lib/rbcurse/table/tablecellrenderer.rb +3 -0
  68. data/lib/rbcurse/undomanager.rb +181 -0
  69. data/lib/rbcurse/vieditable.rb +100 -0
  70. data/lib/ver/window.rb +471 -21
  71. metadata +66 -22
  72. data/README.txt +0 -312
  73. data/examples/testd.db +0 -0
  74. data/examples/todocsv.csv +0 -28
@@ -221,18 +221,18 @@ module Scrollable
221
221
  begin
222
222
  pre_key
223
223
  case ch
224
- when ?\C-n
224
+ when ?\C-n.getbyte(0)
225
225
  scroll_forward
226
226
  when 32
227
227
  scroll_forward
228
- when ?\C-p
228
+ when ?\C-p.getbyte(0)
229
229
  scroll_backward
230
- when ?0
230
+ when ?0.getbyte(0)
231
231
  goto_start
232
- when ?9
232
+ when ?9.getbyte(0)
233
233
  goto_end
234
- when ?[
235
- when ?[
234
+ when ?[.getbyte(0)
235
+ when ?[.getbyte(0)
236
236
  when KEY_UP
237
237
  #select_prev_row
238
238
  up
@@ -245,7 +245,7 @@ module Scrollable
245
245
  if respond_to? :fire
246
246
  fire
247
247
  end
248
- when ?A..?Z, ?a..?z
248
+ when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0)
249
249
  ret = set_selection_for_char ch.chr
250
250
  else
251
251
  return :UNHANDLED #if ret == -1
@@ -71,17 +71,17 @@ module Selectable
71
71
  def selectable_handle_key ch
72
72
  begin
73
73
  case ch
74
- when ?;, 32 # x no more selecting since we now jump to row matching char 2008-12-18 13:13
74
+ when ?;.getbyte(0), 32 # x no more selecting since we now jump to row matching char 2008-12-18 13:13
75
75
  return if is_popup and @select_mode == 'single' # not allowing select this way since there will be a difference
76
76
  # between pressing ENTER and space. Enter is trapped by Listbox!
77
77
  do_select
78
- when ?'
78
+ when ?'.getbyte(0)
79
79
  $log.debug "insdie next selection"
80
80
  do_next_selection if @select_mode == 'multiple'
81
- when ?"
81
+ when ?".getbyte(0)
82
82
  $log.debug "insdie prev selection"
83
83
  do_prev_selection if @select_mode == 'multiple'
84
- when ?\C-e
84
+ when ?\C-e.getbyte(0)
85
85
  do_clear_selection if @select_mode == 'multiple'
86
86
  else
87
87
  return :UNHANDLED
@@ -77,6 +77,9 @@ module RubyCurses
77
77
  padding = 0 if padding < 0
78
78
  _value = " "*padding + _value + " "*padding # so its cleared if we change it midway
79
79
  end
80
+ # XXX 2009-10-05 23:01 since the len can vary when scrolling
81
+ # right justification for numbers suffers.
82
+ # perhaps one should use display_length and then truncate using len
80
83
  graphic.printstring r, c, str % [len, _value], acolor,_attr
81
84
  r += 1
82
85
  end
@@ -0,0 +1,181 @@
1
+ =begin
2
+ * Name: UndoManager
3
+ * Description: Manages undo of text components
4
+ * Author: rkumar (arunachalesha)
5
+
6
+ ISSUES
7
+
8
+ This is a very simple, undo facility. This could change in the near future.
9
+
10
+ Todo:
11
+ We need to handle block updates - several undo ops to be done together.
12
+
13
+
14
+ --------
15
+ * Date: 2010-03-07 19:42
16
+ * License:
17
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
18
+
19
+ =end
20
+
21
+ #
22
+ #
23
+ module RubyCurses
24
+ #
25
+ # AbstractUndo has the basic workings of the undo redo facility.
26
+ # It leaves the actual undo and redo to the implementing source object. However,
27
+ # it does the work of storing edits, and passing the correct edit to the implementor
28
+ # when the source object calls for an undo or redo operation. It thus manages the edit (undo) queue.
29
+ #
30
+ class AbstractUndo
31
+ # initialize the source object which will issue undo requests
32
+ def initialize _source
33
+ source(_source) #if _source
34
+ @pointer = 0
35
+ @actions = []
36
+ $log.debug " INSIDE UNDO CONSTR "
37
+ end
38
+ def source(_source)
39
+ $log.debug " calling source= "
40
+ raise "Cannot pass a nil source" unless _source
41
+ @source = _source
42
+ # currently this is very hardcode again. we need to see this in the light of other objects
43
+ #@source.bind(:CHANGE){|eve| add_edit(eve) }
44
+ # a little roundabout but done for getting things up fast
45
+ @source.undo_handler(self)
46
+ $log.debug " I am listening to change events on #{@source.name} "
47
+ end
48
+ # this is called whenever an undoable edit has happened.
49
+ # Currently, it is linked above in the bind statement. We've attached this
50
+ # method as a listener to the source.
51
+ def add_edit event
52
+ # this debug is very specific. it should be removed later. We do not know about the object
53
+ $log.debug " UNDO GOT #{event}: #{event.type}, (#{event.text}), rej: #{@reject_update} "
54
+ return if @reject_update
55
+ if @pointer < @actions.length
56
+ $log.debug " removing some actions since #{@pointer} < #{@actions.length} "
57
+ @actions.slice!(@pointer..-1)
58
+ $log.debug " removed actions since #{@pointer} , #{@actions.length} "
59
+ end
60
+ @actions << event
61
+ @pointer = @actions.length
62
+ end
63
+ # this has to be bound in source component
64
+ # typically bind C-_ to undo()
65
+ # this method figures out the correct undo object to be sent
66
+ # to the implementor
67
+ def undo
68
+ $log.debug " got UNDO call #{@pointer}, sz:#{@actions.size} "
69
+ return if @pointer == 0
70
+ @reject_update = true
71
+ @pointer -=1 #if @pointer > 0
72
+ @source.repaint_required true
73
+ @reject_update = false
74
+ edit = @actions[@pointer]
75
+ perform_undo edit
76
+ end
77
+ # this has to be bound in source
78
+ # typically bind C-r to redo()
79
+ # this method figures out the correct redo object to be sent
80
+ # to the implementor
81
+ def redo
82
+ $log.debug "UNDO GOT REDO call #{@pointer}, #{@actions.size} "
83
+ return if @pointer >= @actions.size
84
+ @reject_update = true
85
+ edit = @actions[@pointer]
86
+ perform_redo edit
87
+ @source.repaint_required true
88
+ @pointer +=1 #if @pointer > 0
89
+ @reject_update = false
90
+ end
91
+ def perform_redo edit
92
+ raise "You must implement this for your undoable component "
93
+ end
94
+ def perform_undo edit
95
+ raise "You must implement this for your undoable component "
96
+ # to be implemented
97
+ end
98
+ #def to_s
99
+ #inspect
100
+ #end
101
+ #def inspect
102
+ ### now that textarea.to_s prints content we shouldn pass it here.
103
+ ##"#{@type.to_s}, #{@source}, ind0:#{@index0}, ind1:#{@index1}, row:#{@row}, text:#{@text}"
104
+ #"#{@type.to_s}, ind0:#{@index0}, ind1:#{@index1}, row:#{@row}, text:#{@text}"
105
+ #end
106
+ end
107
+ ## An implementation of AbstractUndo for textarea.
108
+ # Very basic.
109
+ class SimpleUndo < AbstractUndo
110
+ def initialize _source
111
+ super
112
+ end
113
+ def source(_source)
114
+ super
115
+ _source.bind(:CHANGE){|eve| add_edit(eve) }
116
+ end
117
+ def perform_undo act
118
+ row = act.row
119
+ col = act.index0
120
+ $log.debug " processing #{act} "
121
+ case act.type
122
+ when :INSERT
123
+ howmany = act.index1 - col
124
+ @source.list[row].slice!(col,howmany)
125
+ when :DELETE
126
+ $log.debug " UNDO processing DELETE #{col}, (#{act.text}) "
127
+ @source.list[row].insert(col, act.text.chomp)
128
+ when :DELETE_LINE
129
+ $log.debug " UNDO inside delete-line #{row} "
130
+ #@source.list.insert(row, act.text) # insert a blank line, since one was deleted
131
+ case act.text
132
+ when Array
133
+ index = row
134
+ act.text.each_with_index{|r,i| @source.list.insert index+i, r}
135
+ when String
136
+ @source.list.insert row, act.text
137
+ end
138
+ when :INSERT_LINE
139
+ $log.debug " UNDO inside insert-line #{row} "
140
+ case act.text
141
+ when Array
142
+ act.text.size.times { @source.list.delete_at row }
143
+ when String
144
+ @source.list.delete_at row
145
+ end
146
+ else
147
+ $log.warn "unhandled change type #{act.type} "
148
+ end
149
+ @source.repaint_required true
150
+ @reject_update = false
151
+ end
152
+ # this has to be bound in source
153
+ def perform_redo act
154
+ row = act.row
155
+ col = act.index0
156
+ $log.debug " REDO processing #{act} "
157
+ case act.type
158
+ when :INSERT
159
+ @source.list[row].insert(col, act.text)
160
+ when :DELETE
161
+ row = act.row
162
+ col = act.index0
163
+ howmany = act.index1 - col
164
+ @source.list[row].slice!(col,howmany)
165
+ when :DELETE_LINE
166
+ #$log.debug " UNDO redo got deleteline #{row} "
167
+ @source.list.delete_at row
168
+ when :INSERT_LINE
169
+ case act.text
170
+ when Array
171
+ index = row
172
+ act.text.each_with_index{|r,i| @source.list.insert index+i, r}
173
+ when String
174
+ @source.list.insert row, act.text
175
+ end
176
+ else
177
+ $log.warn "unhandled change type #{act.type} "
178
+ end
179
+ end
180
+ end
181
+ end # module
@@ -0,0 +1,100 @@
1
+ #**************************************************************
2
+ # Author: rkumar (arunachalesha)
3
+ # Date: 2010-03-11 22:18
4
+ # Provides the caller ability to do some edit operations
5
+ # on list widgets using either keys (vim largely)
6
+ # or a menu. made originally for textview and multitextview
7
+ #
8
+ #**************************************************************
9
+ #hscrollcols = $multiplier > 0 ? $multiplier : @width/2
10
+ #def previous_row num=(($multiplier.nil? or $multiplier == 0) ? 1 : $multiplier)
11
+ require 'rbcurse/listeditable'
12
+ module ViEditable
13
+ include ListEditable
14
+
15
+ #def ViEditable.vieditable_init
16
+ def vieditable_init
17
+ $log.debug " inside vieditable_init "
18
+ @editable = true
19
+ bind_key( ?C, :edit_line)
20
+ bind_key( ?o, :insert_line)
21
+ bind_key( ?O) { insert_line(@current_index-1) }
22
+ bind_key( ?D, :delete_eol)
23
+ bind_key( [?d, ?$], :delete_eol)
24
+ bind_key( [?d, ?d] , :delete_line )
25
+ bind_key( [?d, ?w], :delete_word )
26
+ bind_key( [?d, ?t], :delete_till )
27
+ bind_key( [?d, ?f], :delete_forward )
28
+ bind_key( ?\C-_ ) { @undo_handler.undo if @undo_handler }
29
+ bind_key( ?u ) { @undo_handler.undo if @undo_handler }
30
+ bind_key( ?\C-r ) { @undo_handler.redo if @undo_handler }
31
+ bind_key( ?x, :delete_curr_char )
32
+ bind_key( ?X, :delete_prev_char )
33
+ bind_key( [?y, ?y] , :kill_ring_save )
34
+ bind_key( ?p, :yank ) # paste after this line
35
+ bind_key( ?P ) { yank(@current_index - 1) } # should be before this line
36
+ bind_key(?\w, :forward_word)
37
+ bind_key(?f, :forward_char)
38
+
39
+ end
40
+
41
+ ##
42
+ # edit current or given line
43
+ def edit_line lineno=@current_index
44
+ line = @list[lineno]
45
+ prompt = "Edit: "
46
+ maxlen = 80
47
+ config={};
48
+ config[:default] = line
49
+ ret, str = rbgetstr(@form.window, $error_message_row, $error_message_col, prompt, maxlen, config)
50
+ $log.debug " rbgetstr returned #{ret} , #{str} "
51
+ return if ret != 0
52
+ @list[lineno].replace(str)
53
+ @repaint_required = true
54
+ end
55
+ ##
56
+ # insert a line
57
+ def insert_line lineno=@current_index
58
+ prompt = "Insert: "
59
+ maxlen = 80
60
+ #config={};
61
+ #config[:default] = line
62
+ #ret, str = rbgetstr(@form.window, $error_message_row, $error_message_col, prompt, maxlen, config)
63
+ ret, str = input_string prompt
64
+ #ret, str = rbgetstr(@form.window, @row+@height-1, @col+1, prompt, maxlen, config)
65
+ $log.debug " rbgetstr returned #{ret} , #{str} "
66
+ return if ret != 0
67
+ @list.insert lineno, str
68
+ @repaint_required = true
69
+ end
70
+ ##
71
+ # common method to edit given string
72
+ # @param [String] string to edit/modify
73
+ # @param [String] prompt to display before string
74
+ # @param [int] max length of input
75
+ # @return [0, -1] return value 0 if okay, -1 if error
76
+ #
77
+ def edit_string string, prompt="Edit: ", maxlen=80
78
+ config={};
79
+ config[:default] = string
80
+ ret, str = rbgetstr(@form.window, $error_message_row, $error_message_col, prompt, maxlen, config)
81
+ #return str if ret == 0
82
+ #return ""
83
+ end
84
+ ##
85
+ # common method to input a blank string
86
+ # @param [String] prompt to display before string
87
+ # @param [int] max length of input
88
+ # @return [0, -1] return value 0 if okay, -1 if error
89
+ def input_string prompt="Insert: ", maxlen=80
90
+ ret, str = rbgetstr(@form.window, $error_message_row, $error_message_col, prompt, maxlen, config)
91
+ #return str if ret == 0
92
+ #return ""
93
+ end
94
+ def edit_chars
95
+
96
+ end
97
+ def edit_word
98
+
99
+ end
100
+ end # module
@@ -1,17 +1,12 @@
1
1
  require 'ver/ncurses'
2
2
  module VER
3
- # Responsibilities:
4
- # * Interface to Ncurses::WINDOW and Ncurses::Panel
5
- # * behave IO like: (print puts write read readline)
6
- # * hide and show itself
7
-
8
- # There's a very strange bug when i tried subclassing this, as Ncurses seems
9
- # to overwrite WINDOW::new, which will not return the Window instance we
10
- # want. So we have to wrap instead of subclass.
11
- class Window # < Ncurses::WINDOW
3
+ class Window
12
4
  attr_reader :width, :height, :top, :left
13
5
  attr_accessor :layout
14
- attr_reader :panel # XXX reader requires so he can del it in end
6
+ attr_reader :panel # reader requires so he can del it in end
7
+ attr_reader :window_type # window or pad to distinguish 2009-11-02 23:11
8
+ attr_accessor :name # more for debugging log files. 2010-02-02 19:58
9
+ attr_accessor :modified # has it been modified and may need a refresh
15
10
 
16
11
  def initialize(layout)
17
12
  @visible = true
@@ -19,24 +14,48 @@ module VER
19
14
 
20
15
  @window = Ncurses::WINDOW.new(height, width, top, left)
21
16
  @panel = Ncurses::Panel.new_panel(@window)
17
+ init_vars
22
18
  ## eeks XXX next line will wreak havoc when multiple windows opened like a mb or popup
23
- $error_message_row = $status_message_row = Ncurses.LINES-1
19
+ #$error_message_row = $status_message_row = Ncurses.LINES-1
20
+ $error_message_row ||= Ncurses.LINES-1
21
+ $error_message_col ||= 1
22
+
24
23
 
24
+ end
25
+ def init_vars
26
+ @window_type = :WINDOW
25
27
  Ncurses::keypad(@window, true)
26
28
  @stack = []
29
+ @name ||="#{self}"
30
+ @modified = true
31
+ $catch_alt_digits ||= false # is this where is should put globals ? 2010-03-14 14:00 XXX
27
32
  end
33
+ ##
34
+ # this is an alternative constructor
28
35
  def self.root_window(layout = { :height => 0, :width => 0, :top => 0, :left => 0 })
29
36
  #VER::start_ncurses
30
37
  @layout = layout
31
38
  @window = Window.new(@layout)
39
+ @window.name = "Window::ROOTW"
32
40
  @window.wrefresh
33
41
  Ncurses::Panel.update_panels
34
42
  return @window
35
43
  end
44
+ # 2009-10-13 12:24
45
+ # not used as yet
46
+ # this is an alternative constructor
47
+ # created if you don't want to create a hash first
48
+ def self.create_window(h=0, w=0, t=0, l=0)
49
+ layout = { :height => h, :width => w, :top => t, :left => l }
50
+ @window = Window.new(layout)
51
+ return @window
52
+ end
36
53
 
37
54
  def resize_with(layout)
55
+ $log.debug " DARN ! This awready duz a resize!! if h or w or even top or left changed!!! XXX"
38
56
  reset_layout(layout)
39
57
  @window.wresize(height, width)
58
+ # this is dicey since we often change top and left in pads only for panning !! XXX
40
59
  @window.mvwin(top, left)
41
60
  end
42
61
 
@@ -130,6 +149,10 @@ module VER
130
149
  @window.mvchgat(y, x, max, Ncurses::A_NORMAL, color, nil)
131
150
  end
132
151
 
152
+ def ungetch(ch)
153
+ Ncurses.ungetch(ch)
154
+ end
155
+
133
156
  def getch
134
157
  @window.getch
135
158
  rescue Interrupt => ex
@@ -276,6 +299,7 @@ module VER
276
299
  # @panel = @window.panel if @window
277
300
  #Ncurses::Panel.del_panel(@panel) if !@panel.nil?
278
301
  #@window.delwin if !@window.nil?
302
+ $log.debug "win destroy"
279
303
 
280
304
  #@panel = @window.panel if @window
281
305
  Ncurses::Panel.del_panel(@panel) if !@panel.nil?
@@ -291,10 +315,17 @@ module VER
291
315
  # @ param att - ncurses attribute: normal, bold, reverse, blink,
292
316
  # underline
293
317
  def printstring(r,c,string, color, att = Ncurses::A_NORMAL)
318
+ prv_printstring(r,c,string, color, att )
319
+ end
320
+
321
+ ## name changed from printstring to prv_prinstring
322
+ def prv_printstring(r,c,string, color, att = Ncurses::A_NORMAL)
294
323
 
295
- ## XXX check if row is exceeding height and don't print
296
- att = Ncurses::A_NORMAL if att.nil?
324
+ #$log.debug " #{@name} inside window printstring r #{r} c #{c} #{string} "
325
+ att = Ncurses::A_NORMAL if att.nil?
297
326
  case att.to_s.downcase
327
+ when 'normal'
328
+ att = Ncurses::A_NORMAL
298
329
  when 'underline'
299
330
  att = Ncurses::A_UNDERLINE
300
331
  when 'bold'
@@ -311,17 +342,22 @@ module VER
311
342
  width = Ncurses.COLS
312
343
  # the next line won't ensure we don't write outside some bounds like table
313
344
  #string = string[0..(width-c)] if c + string.length > width
314
- #$log.debug "PRINT #{string.length}, #{Ncurses.COLS}, #{c} "
345
+ #$log.debug "PRINT len:#{string.length}, #{Ncurses.COLS}, #{r}, #{c} w: #{@window} "
315
346
  mvprintw(r, c, "%s", string);
316
347
  attroff(Ncurses.COLOR_PAIR(color) | att)
317
348
  end
318
349
  # added by rk 2008-11-29 19:01
350
+ # Since these methods write directly to window they are not advised
351
+ # since clearing previous message we don't know how much to clear.
352
+ # Best to map error_message to a label.
319
353
  def print_error_message text=$error_message
320
354
  r = $error_message_row || Ncurses.LINES-1
321
- $log.debug "got ERROR MEASSAGE #{text} row #{r} "
355
+ c = $error_message_col || (Ncurses.COLS-text.length)/2
356
+
357
+ $log.debug "got ERROR MESSAGE #{text} row #{r} "
322
358
  clear_error r, $datacolor
323
- # print it in centre
324
- printstring r, (Ncurses.COLS-text.length)/2, text, color = $promptcolor
359
+ printstring r, c, text, color = $promptcolor
360
+ $error_message_clear_pending = true
325
361
  end
326
362
  # added by rk 2008-11-29 19:01
327
363
  def print_status_message text=$status_message
@@ -330,10 +366,19 @@ module VER
330
366
  # print it in centre
331
367
  printstring r, (Ncurses.COLS-text.length)/2, text, color = $promptcolor
332
368
  end
333
- # added by rk 2008-11-29 19:01
369
+ # Clear error message printed
370
+ # I am not only clearing if something was printed. This is since
371
+ # certain small forms like TabbedForm top form throw an error on printstring.
372
+ #
334
373
  def clear_error r = $error_message_row, color = $datacolor
335
- printstring(r, 0, "%-*s" % [Ncurses.COLS," "], color)
374
+ return unless $error_message_clear_pending
375
+ c = $error_message_col || (Ncurses.COLS-text.length)/2
376
+ sz = $error_message_size || Ncurses.COLS
377
+ printstring(r, c, "%-*s" % [sz, " "], color)
378
+ $error_message_clear_pending = false
336
379
  end
380
+ ##
381
+ # CAUTION : FOR MESSAGEBOXES ONLY !!!! XXX
337
382
  def print_border_mb row, col, height, width, color, attr
338
383
  mvwaddch row, col, ACS_ULCORNER
339
384
  mvwhline( row, col+1, ACS_HLINE, width-6)
@@ -345,15 +390,35 @@ module VER
345
390
  mvwaddch row+height-3, col+width-5, Ncurses::ACS_LRCORNER
346
391
  mvwvline( row+1, col+width-5, ACS_VLINE, height-4)
347
392
  end
393
+ ##
394
+ # prints a border around a widget, CLEARING the area.
395
+ # If calling with a pad, you would typically use 0,0, h-1, w-1.
348
396
  def print_border row, col, height, width, color, att=Ncurses::A_NORMAL
349
397
  att ||= Ncurses::A_NORMAL
350
398
 
399
+ $log.debug " inside window print_border r #{row} c #{col} h #{height} w #{width} "
400
+
401
+ # 2009-11-02 00:45 made att nil for blanking out
402
+ # FIXME - in tabbedpanes this clears one previous line ??? XXX when using a textarea/view
403
+ # when using a pad this calls pads printstring which again reduces top and left !!! 2010-01-26 23:53
351
404
  (row+1).upto(row+height-1) do |r|
352
- printstring( r, col+1," "*(width-2) , color, att)
405
+ #printstring( r, col+1," "*(width-2) , $datacolor, nil)
406
+ prv_printstring( r, col+1," "*(width-2) , $datacolor, nil)
353
407
  end
354
- attron(Ncurses.COLOR_PAIR(color) | att)
408
+ prv_print_border_only row, col, height, width, color, att
409
+ end
410
+ def print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
411
+ prv_print_border_only row, col, height, width, color, att
412
+ end
355
413
 
356
414
 
415
+ ## print just the border, no cleanup
416
+ #+ Earlier, we would clean up. Now in some cases, i'd like
417
+ #+ to print border over what's been done.
418
+ # XXX this reduces 1 from width but not height !!! FIXME
419
+ def prv_print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
420
+ att ||= Ncurses::A_NORMAL
421
+ attron(Ncurses.COLOR_PAIR(color) | att)
357
422
  mvwaddch row, col, ACS_ULCORNER
358
423
  mvwhline( row, col+1, ACS_HLINE, width-2)
359
424
  mvwaddch row, col+width-1, Ncurses::ACS_URCORNER
@@ -365,5 +430,390 @@ module VER
365
430
  mvwvline( row+1, col+width-1, ACS_VLINE, height-1)
366
431
  attroff(Ncurses.COLOR_PAIR(color) | att)
367
432
  end
433
+ # added RK 2009-10-08 23:57 for tabbedpanes
434
+ # THIS IS EXPERIMENTAL -
435
+ # Acco to most sources, derwin and subwin are not thoroughly tested, avoid usage
436
+ # subwin moving and resizing not functioning.
437
+ def derwin(layout)
438
+ $log.debug " #{self} EXP: returning a subwin in derwin"
439
+ v = VER::SubWindow.new(self, layout)
440
+ $log.debug " #{self} EXP: returning a subwin in derwin: #{v} "
441
+ return v
442
+ end
443
+ def _subwin(layout)
444
+ t = @layout[:top]
445
+ l = @layout[:left]
446
+ layout[:top] = layout[:top] + t
447
+ layout[:left] = layout[:left] + l
448
+ $log.debug " #{self} EXP: returning a subwin in derwin. Adding #{t} and #{l} "
449
+ v = VER::SubWindow.new(self, layout)
450
+ $log.debug " #{self} EXP: returning a subwin in derwin: #{v} "
451
+ return v
452
+ end
453
+ def get_window; @window; end
454
+ def to_s; @name || self; end
455
+ # use in place of mvwhline if your widget could be using a pad or window
456
+ def rb_mvwhline row, col, char, width
457
+ mvwhline row, col, char, width
458
+ end
459
+ # use in place of mvwvline if your widget could be using a pad or window
460
+ def rb_mvwvline row, col, char, width
461
+ mvwvline row, col, char, width
462
+ end
463
+ # use in place of mvaddch if your widget could be using a pad or window
464
+ def rb_mvaddch row, col, char
465
+ mvaddch row, col, char
466
+ end
467
+ end
468
+ ##
469
+ # added RK 2009-10-08 23:57 for tabbedpanes
470
+ # THIS IS EXPERIMENTAL -
471
+ # I have not called super in the initializer so any methods you try on subwin
472
+ # that exist in the superclass which use @window will bomb
473
+ # @since 0.1.3
474
+ class SubWindow < VER::Window
475
+ attr_reader :width, :height, :top, :left
476
+ attr_accessor :layout
477
+ attr_reader :panel # XXX reader requires so he can del it in end
478
+ attr_reader :subwin #
479
+ attr_reader :parent #
480
+
481
+ def initialize(parent, layout)
482
+ @visible = true
483
+ reset_layout(layout)
484
+
485
+ @parent = parent
486
+ #@subwin = @parent.get_window().derwin(@height, @width, @top, @left)
487
+ @subwin = @parent.get_window().subwin(@height, @width, @top, @left)
488
+ $log.debug "SUBWIN init #{@height} #{@width} #{@top} #{@left} "
489
+ #$log.debug "SUBWIN init #{@subwin.getbegx} #{@subwin.getbegy} #{@top} #{@left} "
490
+ @panel = Ncurses::Panel.new_panel(@subwin)
491
+
492
+ @window = @subwin # makes more mthods available
493
+ init_vars
494
+
495
+ end
496
+ # no need really now
497
+ def reset_layout layout
498
+ @layout = layout # 2010-02-13 22:23
499
+ @height = layout[:height]
500
+ @width = layout[:width]
501
+ @top = layout[:top]
502
+ @left = layout[:left]
503
+ end
504
+ def _destroy
505
+ # typically the ensure block should have this
506
+ # or should window do it for all subwins, or would we want to wait that long ?
507
+ $log.debug "subwin destroy"
508
+
509
+ Ncurses::Panel.del_panel(@panel) if !@panel.nil?
510
+ @window.delwin if !@window.nil?
511
+ end
368
512
  end
513
+
514
+ ##
515
+ # Pad
516
+ # This is EXPERIMENTAL
517
+ # A pad cannot be used interchangeable since some application functions such as wrefresh
518
+ # are illegal. Cannot expect the application to take care.
519
+ # Internally we can make it easier. Mostly a pad is used to map to one portion of the screen.
520
+ # So we allow that to be defined once. Then only start row and col of pad change.
521
+ # Maybe we should check pad coordinates so no errors
522
+ # Also check screen coordinates (if we know)
523
+ # We need padheight and padwidth only to ensure we don't keep recreating.
524
+ # Howevre, when comp's height increases, then decreases, pad height remains larger
525
+ # but we keep printing an extra row in copywin. so Pad needs to maintain comp height
526
+ # and padheight.
527
+ # @since 0.1.3
528
+ class Pad < VER::Window
529
+ # top and left correspond to screen's top and left wich will mostly be fixed
530
+ attr_accessor :top, :left
531
+ # start row and col correspond to pad's top and left which will change if scrolling
532
+ attr_accessor :pminrow, :pmincol
533
+ # screen's height and width, now it reflects components height and width
534
+ attr_accessor :sheight, :swidth
535
+ attr_reader :otherwin
536
+ # dimensions the pad was created with, used so we don't keep recreating pad, only if increase.
537
+ attr_reader :padheight, :padwidth
538
+ #attr_accessor :name # more for debugging log files. 2010-02-02 19:58
539
+ def initialize(height, width)
540
+ @visible = true
541
+ # do we set height and width ?? XXX
542
+ @window = Ncurses.newpad(height, width)
543
+ @padheight = height
544
+ @padwidth = width
545
+ @height = height
546
+ @width = width
547
+ @sheight = height
548
+ @swidth = width
549
+ init_vars
550
+ end
551
+ def init_vars
552
+ super
553
+ @top ||= 0; @left ||= 0
554
+ @pmincol ||= 0 # pad will print from this col
555
+ @pminrow ||= 0 # pad will print from this row
556
+ @window_type = :PAD
557
+ @name ||="#{self}"
558
+ $log.debug " PAD constructor #{self} , #{@window} "
559
+ end
560
+ #
561
+ # @param layout is a hash (@see Window.initialize)
562
+ def self.create_with_layout(layout)
563
+ @window = Pad.new(layout[:height], layout[:width])
564
+ @window.reset_layout(layout)
565
+ return @window
566
+ end
567
+ ##
568
+ # increases the pad size, since the widget may have been resized
569
+ # checks that one of ht or width has been increased
570
+ # destroys earlier pad and returns new one
571
+ # Updates sheight and swidth even if reduced so copywin works fine.
572
+ # @param [Fixnum] height to resize to
573
+ # @param [Fixnum] width to resize to
574
+ # @return [Pad]
575
+ # 2009-10-29 23:18
576
+ def resize(ht = 0, w = 0)
577
+ # update sheight and swidth even if reduced, so that pad doesn't overwrite.
578
+ @sheight = ht if ht > 0
579
+ @swidth = w if w > 0
580
+ return if ht < @padheight and w < @padwidth
581
+ @padheight = ht if ht > @padheight
582
+ @padwidth = w if w > @padwidth
583
+ destroy
584
+ $log.debug " L502 resize, creating newpad with #{@padheight} and #{@padwidth} "
585
+ @window = Ncurses.newpad(@padheight, @padwidth)
586
+ $log.debug " L502 resize created #{@window} "
587
+ return @window
588
+ end
589
+ ## used if pad and window are same size only
590
+ # creates a similar sized window
591
+ # assumes window is backed by this pad
592
+ # @param object of Window class
593
+ def self.create_for_window(win)
594
+ # get coordinates for win
595
+ @otherwin = win
596
+ smaxx = win.getmaxx()
597
+ smaxy = win.getmaxy()
598
+ top = win.getminx()
599
+ left = win.getminy()
600
+ sheight = win.height
601
+ swidth = win.width
602
+ # make pad based on size of window
603
+ window = Pad.create_with_layout(layout = { :height => sheight, :width => swidth, :top => top, :left => sleft })
604
+ window.sheight = sheight
605
+ window.swidth = swidth
606
+ return window
607
+
608
+ end
609
+ # top and left correspond to screen's top and left wich will mostly be fixed.
610
+ # In cases where the component may float around, as in Splitpanes second component
611
+ # this would be set using component's row and col.
612
+ def set_screen_row_col top, left=-1
613
+ @top = top
614
+ @left = left unless left < 0
615
+ end
616
+ alias :set_screen_pad_left :set_screen_row_col
617
+
618
+ ## added user setting screens max row and col (e.g splitpanes first component)
619
+ def set_screen_max_row_col mr, mc
620
+ $log.debug "#{@name} set_screen_max_row_col #{mr},#{mc}. earlier #{@screen_maxrow}, #{@screen_maxcol} "
621
+ # added || check on 2010-01-09 18:39 since crashing if mr > sh + top ..
622
+ # I removed the check, since it results in a blank area on screen since the
623
+ # widget has not expanded itself. Without the check it will crash on copywin so you
624
+ # should increase widget size or disallow calling this in this situation.
625
+ if mr > (@sheight + @top -1 -@pminrow)
626
+ $log.warn " ->>> ** set_screen_max_row_col #{mr} > #{@sheight} + #{@top} -1 - #{@pminrow} ** "
627
+ $log.warn " ->>> can result in error in copy_win or in some rows not displaying"
628
+ return # some situations actually require this ...
629
+ end unless mr.nil?
630
+ @screen_maxrow = mr unless mr.nil? # || mr > (@sheight + @top -1 -@pminrow)
631
+ @screen_maxcol = mc unless mc.nil?
632
+ end
633
+ # start row and col correspond to pad's top and left which will change if scrolling
634
+ # However, if we use this as a backing store for subwindows it could remain the same
635
+ def set_pad_top_left top, left=-1
636
+ $log.debug "#{@name} inside set_pad_top_left to #{top} #{left} earlier #{@pminrow}, #{@pmincol}"
637
+ @pminrow = top unless top < 0
638
+ @pmincol = left unless left < 0
639
+ end
640
+ # return screen max row which will be used for writing to window
641
+ # XXX what if user sets/overrides sheight
642
+ def smaxrow
643
+ #$log.debug " ... niside smaxrow #{@sheight} + #{@top} -1 "
644
+ #@sheight + @top -1
645
+ $log.debug "smr: #{@screen_maxrow} ... niside smaxrow #{@sheight} + #{@top} -1 - #{@pminrow}"
646
+ @screen_maxrow || @sheight + @top -1 -@pminrow
647
+ end
648
+ ##
649
+ # return screen max col which will be used for writing to window
650
+ def smaxcol
651
+ #$log.debug " ... niside smaxcol #{@swidth} + #{@left} -1 "
652
+ #@swidth + @left -1
653
+ # $log.debug " ... niside smaxcol #{@swidth} + #{@left} -1 - #{@pmincol} "
654
+ @screen_maxcol || @swidth + @left -1 - @pmincol
655
+ end
656
+ ##
657
+ # specify the window or subwin that the pad is writing to
658
+ # 2010-02-20 22:45 - actually since there are pad methods smaxrow used on otherwin
659
+ # therefor it can only be a Pad !! NOTE
660
+ def set_backing_window win
661
+ @otherwin = win
662
+ # XX should we extract the coordinates and use for printing ??
663
+ # or for setting maxrow and maxcol
664
+ end
665
+ # trying to make things as easy as possible
666
+ # returns -1 if error in prefresh
667
+ def wrefresh
668
+ $log.debug " inside pad's wrefresh #{@window}. minr,minc,top,left,smaxr,c: #{@pminrow}, #{@pmincol}, #{@top} #{@left} #{smaxrow()} #{smaxcol()} self: #{self.name} "
669
+
670
+ # caution, prefresh uses maxrow and maxcol not height and width
671
+ # so we have to add top and less one since we are zero based
672
+ ret = @window.prefresh(@pminrow, @pmincol, @top, @left, smaxrow(), smaxcol())
673
+ $log.warn " WREFRESH returns -1 ERROR - width or height must be exceeding " if ret == -1
674
+ @modified = false
675
+ return ret
676
+ end
677
+ ##
678
+ # copy the window to the pad (assumes we are writing onto win and keeping
679
+ # pad as backup
680
+ # also assuming only one win so, window not passed as param
681
+ # @return return value of copywin which should be 0 (-1 is ERR)
682
+ def copy_pad_to_win
683
+ # check that we don't exceed other windows height/maxrow
684
+ smr = smaxrow()
685
+ # SHIT, this means the otherwin has to be a Pad, cannot be a window
686
+ osw = @otherwin.width
687
+ osh = @otherwin.height
688
+ osh = @height if osh == 0 # root window has 0
689
+ osw = @width if osw == 0 # root window has 0
690
+ osmr = @otherwin.smaxrow() rescue osh # TRYING for windows
691
+ osmc = @otherwin.smaxcol() rescue osw
692
+ if smr >= osmr
693
+ $log.debug " adjusted smr from #{smr} to #{osmr} -1 causing issues in viewfooter"
694
+ smr = osmr-1 # XXX causing issues in viewport, wont print footer with this
695
+ end
696
+ if smr > @sheight + @top -1 -@pminrow # 2010-01-17 13:27
697
+ smr = @sheight + @top -1 -@pminrow
698
+ $log.debug " adjusted smr to #{smr} to prevent crash "
699
+ end
700
+ smc = smaxcol()
701
+ $log.debug " SMC original = #{smc} "
702
+ if smc >= osmc
703
+ smc = osmc-1
704
+ smc = @width # XXX ??? THIS WAS WORKING< but throwing error in viewport case
705
+ smc = [osmc-1, @width].min # yet another hack
706
+ $log.debug " SMC o-1 #{osmc-1} wdth #{@width}, smc #{smc} "
707
+ end
708
+ ### XXX commented out since it doesn't let a comp print fully if widget expanded (splitpane)
709
+ #smc = osw -1 if smc >= osw; # added 2009-11-02 17:01 for tabbedpanes
710
+
711
+ # dang, this is coming up a lot. 2010-01-16 20:34
712
+ # the second scrollpane was one row too large in testsplit3a.rb
713
+ if smr - @top > @padheight
714
+ $log.debug " fixing smr to padheight 2010-01-16 20:35 HOPE THIS DOESNT BREAK ANYTHING"
715
+ smr = @padheight
716
+ end
717
+ @pminrow = 0 if @pminrow < 0
718
+ @pmincol = 0 if @pmincol < 0
719
+ $log.debug " COPYING #{self.name} to #{@otherwin.name} "
720
+ $log.debug " calling copy pad #{@pminrow} #{@pmincol}, #{@top} #{@left}, #{smr} #{smc} self #{self.name} "
721
+ $log.debug " calling copy pad H: #{@height} W: #{@width}, PH #{@padheight} PW #{@padwidth} WIN:#{@window} "
722
+ # $log.debug " -otherwin target copy pad #{@otherwin.pminrow} #{@otherwin.pmincol}, #{@otherwin.top} #{@otherwin.left}, #{osmr} #{osmc} OTHERWIN:#{@otherwin.name} "
723
+ ret="-"
724
+ #if ret == -1
725
+ #x XXX $log.debug " #{ret} otherwin copy pad #{@otherwin.pminrow} #{@otherwin.pmincol}, #{@otherwin.top} #{@otherwin.left}, #{osmr} #{osmc} "
726
+ $log.debug " #{ret} otherwin copy pad H: #{osh} W: #{osw}"
727
+ if @top >= osh
728
+ $log.debug " #{ret} ERROR top exceeds other ht #{@top} H: #{osh} "
729
+ end
730
+ if @left >= osw
731
+ $log.debug " #{ret} ERROR left exceeds other wt #{@left} W: #{osw} "
732
+ end
733
+ if smr >= osh
734
+ $log.debug " #{ret} ERROR smrow exceeds other ht #{smr} H: #{osh} "
735
+ smr = osh() -1 # testing 2010-01-31 21:47 , again 2010-02-05 20:22
736
+ end
737
+ if smc >= osw
738
+ $log.debug " #{ret} ERROR smcol exceeds other wt #{smc} W: #{osw} "
739
+ end
740
+ if smc - @left > @padwidth
741
+ $log.debug " #{ret} ERROR smcol - left exceeds padwidth #{smc}- #{@left} PW: #{@padwidth} "
742
+ end
743
+ if smr - @top > @padheight
744
+ $log.debug " #{ret} ERROR smr - top exceeds padheight #{smr}- #{@top} PH: #{@padheight} "
745
+ end
746
+ ret = @window.copywin(@otherwin.get_window,@pminrow,@pmincol, @top, @left, smr, smc, 0)
747
+ $log.debug " copywin ret #{ret} "
748
+ # 2010-01-11 19:42 one more cause of -1 coming is that padheight (actual height which never
749
+ # changes unless pad increases) or padwidth is smaller than area being printed. Solution: increase
750
+ # buffer by increasing widgets w or h. smc - left should not exceed padwidth. smr-top should not
751
+ # exceed padheight
752
+ #end
753
+ @modified = false
754
+ return ret
755
+ end
756
+ def copy_win_to_pad
757
+ smr = smaxrow()
758
+ if smr >= @window.smaxrow()
759
+ smr = @window.smaxrow()-1
760
+ end
761
+ $log.debug " copy_win_to_pad #{@otherwin.name}, #{@window.name}, pminr:#{@pminrow} pminc:#{@pmincol} top:#{@top} left:#{@left} smr:#{smr} "
762
+ ret = @otherwin.copywin(@window.get_window,@pminrow,@pmincol, @top, @left, smr, smaxcol(), 1)
763
+ @modified = false
764
+ return ret
765
+ end
766
+ ##
767
+ #Used to overwrite the pad onto the screen window
768
+ # A window should have been specified as window to back (@see set_backing_window) or (@see create_with_window)
769
+ def overwrite_window
770
+ return @window.overwrite(@otherwin.get_window)
771
+ end
772
+
773
+ ##
774
+ # convenience method so that pad can use printstring but remove screen's row and col
775
+ # The absolute row and col will be taken into consideration when printing on screen.
776
+ #
777
+ # @param [Fixnum] row row to print on
778
+ # @param [Fixnum] col column to print on
779
+ # @param [String] value to print
780
+ # @param [Fixnum] color - color combination
781
+ # @param [Fixnum, nil] attrib defaults to NORMAL
782
+
783
+ # Pls remove the raise once the program is working, extra line can slow things down
784
+ # Keep it on when testing.
785
+ # If the raise is thrown, it means your object could be positioned higher than it should be,
786
+ # or at some point you have increased top, without increasing the objects row.
787
+ def printstring(row,col,value,color,attrib=Ncurses::A_NORMAL)
788
+ #$log.debug " pad printstring #{row} - #{@top} , #{col} - #{@left} "
789
+ raise "printstring row < top, pls correct code #{row} #{@top}, #{col} #{@left} " if row < @top or col < @left
790
+ #$log.warn "printstring row < top, pls correct code #{row} #{@top} " if row < @top
791
+ super(row - @top, col - @left, value, color, attrib)
792
+ end # printstring
793
+ # convenience method so that pad can use print_border but remove screen's row and col
794
+ # Please note that this requires that buffer have latest top and left.
795
+ def print_border row, col, height, width, color, att=Ncurses::A_NORMAL
796
+ $log.debug " pad printborder #{row} - #{@top} , #{col} - #{@left}, #{height} , #{width} "
797
+ raise "print_border: row < top, pls correct code #{row} #{@top}, #{col} #{@left} " if row < @top or col < @left
798
+ #$log.warn "print_border: row < top, pls correct code #{row} #{@top} " if row < @top
799
+ super(row - @top, col - @left, height, width, color, att)
800
+ end
801
+ def print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
802
+ $log.debug " pad printborder_only #{row} - #{@top} , #{col} - #{@left}, #{height} , #{width} "
803
+ raise "print_border row < top, pls correct code #{row} #{@top}, #{col} #{@left} " if row < @top or col < @left
804
+ super(row - @top, col - @left, height, width, color, att)
805
+ end
806
+ # use in place of mvwhline if your widget could be using a pad or window
807
+ def rb_mvwhline row, col, char, width
808
+ super(row-@top, col-@left, char, width)
809
+ end
810
+ # use in place of mvwvline if your widget could be using a pad or window
811
+ def rb_mvwvline row, col, char, width
812
+ super(row-@top, col-@left, char, width)
813
+ end
814
+ # use in place of mvaddch if your widget could be using a pad or window
815
+ def rb_mvaddch row, col, char
816
+ super(row-@top, col-@left, char)
817
+ end
818
+ end # class Pad
369
819
  end