rbcurse 0.1.3 → 1.1.1

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