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
@@ -0,0 +1,203 @@
1
+ =begin
2
+ * Name: Viewport
3
+ * $Id$
4
+ * Description: a viewport thru which you see an underlying form or widget. Scrolling
5
+ the viewport reveals new sections of the underlying object.
6
+ * Author: rkumar arunachala
7
+ TODO
8
+ * file created 2009-10-27 18:05
9
+ Major change 2010-02-11 23:32
10
+ * removed buffering, was totally unnecessary
11
+ * FIXME avoid repaint and handle_keys totally, let scrollpane talk to child.
12
+ Do we really need this ?
13
+ If we do strip it down to its major function - nothign extraneous. Currently its a huge layer in between.
14
+ --------
15
+ * License:
16
+ Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
17
+
18
+ =end
19
+ #require 'rubygems'
20
+ require 'ncurses'
21
+ require 'logger'
22
+ require 'rbcurse'
23
+ require 'rbcurse/rchangeevent'
24
+
25
+ include Ncurses
26
+ include RubyCurses
27
+ module RubyCurses
28
+ extend self
29
+
30
+ ##
31
+ # A viewport or porthole throgh which one can see portions of underlying object
32
+ # such as textarea, table or a form, usually the underlying data is larger
33
+ # than what can be displayed and thus must be seen through a viewport.
34
+ # TODO -
35
+
36
+ class Viewport < Widget
37
+ # row and col also present int widget
38
+ dsl_accessor :child # component that is being viewed
39
+ attr_accessor :cascade_changes
40
+ #attr_reader :top_margin, :left_margin
41
+
42
+ def initialize form, config={}, &block
43
+ @focusable = false
44
+ @editable = false
45
+ #@top_margin = @left_margin = 1 # 2010-02-06 23:27 reduce in scrollpane
46
+ @row = 1 # 1 # 0 2010-02-06 22:46 so we don't write on scrollpanes border
47
+ @col = 1 # 1 # 0 2010-02-06 22:47
48
+ super
49
+ #@row_offset = @col_offset = 1
50
+ #@orig_col = @col
51
+ init_vars
52
+ end
53
+ def init_vars
54
+ #@curpos = @pcol = @toprow = @current_index = 0
55
+ should_create_buffer = true
56
+ @border_width = 2
57
+ end
58
+ # set the component to be viewed
59
+ def set_view ch
60
+ @child = ch
61
+ end
62
+ def set_view_size h,w
63
+ # calling the property shoudl uniformally trigger fire_property_change
64
+ $log.debug " setting viewport to h #{h} , w #{w} "
65
+ height(h)
66
+ width(w)
67
+ fire_state_changed
68
+ #fire_handler :PROPERTY_CHANGE, self # XXX should it be an event STATE_CHANGED with details
69
+ end
70
+ ##
71
+ # Set the row and col of the child, that the viewport starts displaying.
72
+ # Used to initialize the view, and later if scrolling.
73
+ # Initially would be set to 0,0.
74
+ #
75
+ def set_view_position r,c
76
+ return false if r < 0 or c < 0
77
+ # 2010-02-04 19:29 TRYING -2 for borders
78
+ $log.debug " set_view if #{r} +1+ #{@height} - #{@border_width} )} >=#{@child.height} "
79
+ # 2010-02-12 15:33 XXX made > into >= and added +1 since -1 in set_buffering
80
+ # testscrollp was crashing out when child height exceeded
81
+ if r+ (@height+1-@border_width) >= @child.height
82
+ # basically r + (:bottom - :screen_top) < @child.height
83
+ #if r+ (@height) >= @child.height
84
+ $log.debug " set_view_position : trying to exceed ht #{r} + #{@height} > #{@child.height} returned false"
85
+ return false
86
+ end
87
+ # testscrollp was printing junk on right end. Why the 1?
88
+ if c+ (@width+1-@border_width) >=@child.width
89
+ #if c+ (@width) >=@child.width
90
+ $log.debug " set_view_position : trying to exceed width #{c} + #{@width} . returned false"
91
+ return false
92
+ end
93
+ $log.debug " VP row #{@row} col #{@col}, now will be #{r} , #{c} "
94
+ row(r) # commnting off 2010-02-06 22:47
95
+ col(c) # commnting off 2010-02-06 22:47
96
+ # next call sets pminrow and pmincol
97
+ $log.debug " VP setting child buffer pmin to #{r} #{c} "
98
+ @child.get_buffer().set_pad_top_left(r, c)
99
+ # replaced this on 2010-02-26 11:49 HOPE IT WORKS XXX
100
+ #@child.fire_property_change("row", r, r) # XXX quick dirty, this should happen
101
+ @child.repaint_required(true)
102
+ @repaint_required = true
103
+ #fire_handler :PROPERTY_CHANGE, self
104
+ fire_state_changed
105
+ return true
106
+ end
107
+ # instead of using row and col to increment, try using this, since this is what's actually incremented.
108
+ def get_pad_top_left
109
+ p = @child.get_buffer()
110
+ return p.pminrow, p.pmincol
111
+ end
112
+ def repaint # viewport
113
+ return unless @repaint_required
114
+ # should call child's repaint onto pad
115
+ # then this should return clipped pad
116
+ $log.debug "VP calling child #{@child.name} repaint"
117
+ #x @graphic.wclear # required otherwise bottom of scrollpane had old rows still repeated. 2010-01-17 22:51
118
+ @child.repaint_all
119
+ @child.repaint
120
+ paint
121
+ end
122
+ def getvalue
123
+ # TODO ???
124
+ end
125
+ ## most likely should just return an unhandled and not try being intelligent
126
+ # should viewport handle keys or should parent do so directly to child
127
+ def handle_key ch
128
+ # if this gets key it should just hand it to child
129
+ if @child != nil
130
+ ret = @child.handle_key ch
131
+ # 2010-01-19 19:26 commenting off repaint to see.
132
+ return :UNHANDLED if ret == :UNHANDLED
133
+ # moved below return so only if table handles
134
+ @repaint_required=true # added 2009-12-27 22:25 BUFFERED WHY ??
135
+ else
136
+ return :UNHANDLED
137
+ end
138
+ return 0
139
+ #$log.debug "TV after loop : curpos #{@curpos} blen: #{@buffer.length}"
140
+ end
141
+ def paint
142
+ @repaint_required = false
143
+ @repaint_all = false
144
+ end
145
+ # set height
146
+ # a container must pass down changes in size to it's children
147
+ #+ 2010-02-04 18:06 - i am not sure about this. When viewport is set then it passes down
148
+ #+ changes to child which user did not intend. Maybe in splitpane it is okay but other cases?
149
+ #+ Perhaps its okay if scrollpane gets larger than child, not otherwise.
150
+ # added 2010-01-16 23:55
151
+ def height(*val)
152
+ return @height if val.empty?
153
+ oldvalue = @height || 0
154
+ super
155
+ @height = val[0]
156
+ return if @child == nil
157
+ delta = @height - oldvalue
158
+ return if delta == 0
159
+ @repaint_required = true
160
+ if @child.height.nil?
161
+ @child.height = @height
162
+ $log.warn " viewport setting child #{@child.name} to default h of #{@height} -- child is usually larger. "
163
+ else
164
+ if @cascade_changes
165
+ $log.debug "warn!! viewport adding #{delta} to child ht #{child.height} "
166
+ @child.height += delta
167
+ end
168
+ end
169
+ end
170
+ # set width
171
+ # a container must pass down changes in size to it's children
172
+ # added 2010-01-16 23:55
173
+ def width(*val)
174
+ return @width if val.empty?
175
+ oldvalue = @width || 0
176
+ super
177
+ @width = val[0]
178
+ return if @child == nil
179
+ delta = @width - oldvalue
180
+ return if delta == 0
181
+ @repaint_required = true
182
+ # another safeguard if user did not enter. usesomething sensible 2010-01-17 15:23
183
+ if @child.width.nil?
184
+ @child.width = @width
185
+ $log.warn " viewport setting child #{@child.name} to default w of #{@width}. Usually child is larger. "
186
+ else
187
+ ## sometime we are needless increasing. this happens when we set viewport and
188
+ ##+ child has been set. Or may do only if scrollpane is getting larger than child
189
+ ##+ largely a situation with splitpanes.
190
+ if @cascade_changes
191
+ $log.debug "warn!! viewport adding #{delta} to child wt #{child.width} "
192
+ @child.width += delta
193
+ end
194
+ end
195
+ end
196
+ # should this be in widget ?
197
+ # inform listeners that state has changed
198
+ def fire_state_changed
199
+ @sce = ChangeEvent.new(self) if @sce.nil?
200
+ fire_handler :STATE_CHANGE, @sce
201
+ end
202
+ end # class viewport
203
+ end # module
@@ -23,10 +23,10 @@ TODO
23
23
  require 'rubygems'
24
24
  require 'ncurses'
25
25
  require 'logger'
26
- require 'rbcurse/mapper'
26
+ #require 'rbcurse/mapper'
27
27
  require 'rbcurse/colormap'
28
- #require 'rbcurse/rdialogs'
29
- #require 'rbcurse/listcellrenderer'
28
+ require 'rbcurse/orderedhash'
29
+ require 'rbcurse/io'
30
30
 
31
31
  module DSL
32
32
  ## others may not want this, if = sent, it creates DSL and sets
@@ -85,6 +85,18 @@ class Module
85
85
 
86
86
  end
87
87
 
88
+ # 2009-10-04 14:13 added RK after suggestion on http://www.ruby-forum.com/topic/196618#856703
89
+ # these are for 1.8 compatibility
90
+ class Fixnum
91
+ def ord
92
+ self
93
+ end
94
+ ## mostly for control and meta characters
95
+ def getbyte(n)
96
+ self
97
+ end
98
+ end unless "a"[0] == "a"
99
+
88
100
  include Ncurses
89
101
  module RubyCurses
90
102
  extend self
@@ -92,6 +104,14 @@ module RubyCurses
92
104
  class FieldValidationException < RuntimeError
93
105
  end
94
106
  module Utils
107
+ ## this is the numeric argument used to repeat and action by repeatm()
108
+ $multiplier = 0
109
+
110
+ # 2010-03-04 18:01
111
+ ## this may come in handy for methods to know whether they are inside a batch action or not
112
+ # e.g. a single call of foo() may set a var, a repeated call of foo() may append to var
113
+ $inside_multiplier_action = true
114
+
95
115
  ##
96
116
  # wraps text given max length, puts newlines in it.
97
117
  # it does not take into account existing newlines
@@ -111,19 +131,19 @@ module RubyCurses
111
131
  case keycode
112
132
  when 33..126
113
133
  return keycode.chr
114
- when ?\C-a .. ?\C-z
115
- return "C-" + (keycode + ?a -1).chr
116
- when ?\M-A..?\M-z
134
+ when ?\C-a.getbyte(0) .. ?\C-z.getbyte(0)
135
+ return "C-" + (keycode + ?a.getbyte(0) -1).chr
136
+ when ?\M-A.getbyte(0)..?\M-z.getbyte(0)
117
137
  return "M-"+ (keycode - 128).chr
118
- when ?\M-\C-A..?\M-\C-Z
138
+ when ?\M-\C-A.getbyte(0)..?\M-\C-Z.getbyte(0)
119
139
  return "M-C-"+ (keycode - 32).chr
120
- when ?\M-0..?\M-9
121
- return "M-"+ (keycode-?\M-0).to_s
122
- when 32:
140
+ when ?\M-0.getbyte(0)..?\M-9.getbyte(0)
141
+ return "M-"+ (keycode-?\M-0.getbyte(0)).to_s
142
+ when 32
123
143
  return "Space"
124
- when 27:
144
+ when 27
125
145
  return "Esc"
126
- when ?\C-]
146
+ when ?\C-].getbyte(0)
127
147
  return "C-]"
128
148
  when 258
129
149
  return "down"
@@ -145,8 +165,11 @@ module RubyCurses
145
165
  return "M-S-tab"
146
166
  when 393..402
147
167
  return "M-F"+ (keycode-392).to_s
168
+ when 0
169
+ return "C-space" # i hope this is correct, just guessing
148
170
  else
149
171
  others=[?\M--,?\M-+,?\M-=,?\M-',?\M-",?\M-;,?\M-:,?\M-\,, ?\M-.,?\M-<,?\M->,?\M-?,?\M-/]
172
+ others.collect! {|x| x.getbyte(0) } ## added 2009-10-04 14:25 for 1.9
150
173
  s_others=%w[M-- M-+ M-= M-' M-" M-; M-: M-, M-. M-< M-> M-? M-/ ]
151
174
  if others.include? keycode
152
175
  index = others.index keycode
@@ -165,13 +188,91 @@ module RubyCurses
165
188
  end
166
189
  return acolor
167
190
  end
191
+ ## repeats the given action based on how value of universal numerica argument
192
+ ##+ set using the C-u key. Or in vim-mode using numeric keys
193
+ def repeatm
194
+ $inside_multiplier_action = true
195
+ _multiplier = ( ($multiplier.nil? || $multiplier == 0) ? 1 : $multiplier )
196
+ _multiplier.times { yield }
197
+ $multiplier = 0
198
+ $inside_multiplier_action = false
199
+ end
200
+
201
+ ##
202
+ # bind an action to a key, required if you create a button which has a hotkey
203
+ # or a field to be focussed on a key, or any other user defined action based on key
204
+ # e.g. bind_key ?\C-x, object, block
205
+ # added 2009-01-06 19:13 since widgets need to handle keys properly
206
+ # 2010-02-24 12:43 trying to take in multiple key bindings, TODO unbind
207
+ # TODO add symbol so easy to map from config file or mapping file
208
+ def bind_key keycode, *args, &blk
209
+ @key_handler ||= {}
210
+ if !block_given?
211
+ blk = args.pop
212
+ raise "If block not passed, last arg should be a method symbol" if !blk.is_a? Symbol
213
+ $log.debug " #{@name} bind_key received a symbol #{blk} "
214
+ end
215
+ case keycode
216
+ when String
217
+ keycode = keycode.getbyte(0) #if keycode.class==String ## 1.9 2009-10-05 19:40
218
+ $log.debug " #{name} Widg String called bind_key BIND #{keycode}, #{keycode_tos(keycode)} "
219
+ @key_handler[keycode] = blk
220
+ when Array
221
+ # for starters lets try with 2 keys only
222
+ a0 = keycode[0]
223
+ a0 = keycode[0].getbyte(0) if keycode[0].class == String
224
+ a1 = keycode[1]
225
+ a1 = keycode[1].getbyte(0) if keycode[1].class == String
226
+ @key_handler[a0] ||= OrderedHash.new
227
+ @key_handler[a0][a1] = blk
228
+ else
229
+ @key_handler[keycode] = blk
230
+ end
231
+ @key_args ||= {}
232
+ @key_args[keycode] = args
233
+ end
234
+ # e.g. process_key ch, self
235
+ # returns UNHANDLED if no block for it
236
+ # after form handles basic keys, it gives unhandled key to current field, if current field returns
237
+ # unhandled, then it checks this map.
238
+ # added 2009-01-06 19:13 since widgets need to handle keys properly
239
+ # added 2009-01-18 12:58 returns ret val of blk.call
240
+ # so that if block does not handle, the key can still be handled
241
+ # e.g. table last row, last col does not handle, so it will auto go to next field
242
+ # 2010-02-24 13:45 handles 2 key combinations, copied from Form, must be identical in logic
243
+ # except maybe for window pointer. TODO not tested
244
+ def _process_key keycode, object, window
245
+ return :UNHANDLED if @key_handler.nil?
246
+ blk = @key_handler[keycode]
247
+ return :UNHANDLED if blk.nil?
248
+ if blk.is_a? OrderedHash
249
+ ch = window.getch
250
+ if ch < 0 || ch > 255
251
+ #next
252
+ return nil
253
+ end
254
+ $log.debug " process_key: got #{keycode} , #{ch} "
255
+ yn = ch.chr
256
+ blk1 = blk[ch]
257
+ return nil if blk1.nil?
258
+ $log.debug " process_key: found block for #{keycode} , #{ch} "
259
+ blk = blk1
260
+ end
261
+ #$log.debug "called process_key #{object}, kc: #{keycode}, args #{@key_args[keycode]}"
262
+ if blk.is_a? Symbol
263
+ return send(blk, *@key_args[keycode])
264
+ else
265
+ return blk.call object, *@key_args[keycode]
266
+ end
267
+ #0
268
+ end
168
269
  end
169
270
 
170
271
  module EventHandler
171
272
  ##
172
273
  # bind an event to a block, optional args will also be passed when calling
173
274
  def bind event, *xargs, &blk
174
- #$log.debug "#{self} called EventHandler BIND #{event}, args:#{xargs} "
275
+ #$log.debug "#{self} called EventHandler BIND #{event}, args:#{xargs} "
175
276
  @handler ||= {}
176
277
  @event_args ||= {}
177
278
  #@handler[event] = blk
@@ -192,7 +293,7 @@ module RubyCurses
192
293
  # currently object usually contains self which is perhaps a bit of a waste,
193
294
  # could contain an event object with source, and some relevant methods or values
194
295
  def fire_handler event, object
195
- #$log.debug " def fire_handler evt:#{event}, o: #{object}, #{self}, hdnler:#{@handler}"
296
+ $log.debug " def fire_handler evt:#{event}, o: #{object.class}, hdnler:#{@handler}"
196
297
  if !@handler.nil?
197
298
  #blk = @handler[event]
198
299
  ablk = @handler[event]
@@ -200,6 +301,7 @@ module RubyCurses
200
301
  aeve = @event_args[event]
201
302
  ablk.each_with_index do |blk, ix|
202
303
  #$log.debug "#{self} called EventHandler firehander #{@name}, #{event}, obj: #{object},args: #{aeve[ix]}"
304
+ $log.debug "#{self} called EventHandler firehander #{@name}, #{event}"
203
305
  blk.call object, *aeve[ix]
204
306
  end
205
307
  end # if
@@ -207,10 +309,16 @@ module RubyCurses
207
309
  end
208
310
  ## added on 2009-01-08 00:33
209
311
  # goes with dsl_property
210
- # Need to inform listeners
312
+ # Need to inform listeners - done 2010-02-25 23:09
211
313
  def fire_property_change text, oldvalue, newvalue
212
314
  #$log.debug " FPC #{self}: #{text} #{oldvalue}, #{newvalue}"
213
- @repaint_required = true
315
+ if @pce.nil?
316
+ @pce = PropertyChangeEvent.new(self, text, oldvalue, newvalue)
317
+ else
318
+ @pce.set( self, text, oldvalue, newvalue)
319
+ end
320
+ fire_handler :PROPERTY_CHANGE, @pce
321
+ @repaint_required = true # this was a hack and shoudl go, someone wanted to set this so it would repaint (viewport line 99 fire_prop
214
322
  end
215
323
 
216
324
  end # module eventh
@@ -252,11 +360,8 @@ module RubyCurses
252
360
  include EventHandler
253
361
  include ConfigSetup
254
362
  include RubyCurses::Utils
363
+ include Io # added 2010-03-06 13:05
255
364
  dsl_property :text
256
- #dsl_accessor :text_variable
257
- #dsl_accessor :underline # offset of text to underline DEPRECATED
258
- dsl_property :width # desired width of text
259
- #dsl_accessor :wrap_length # wrap length of text, if applic UNUSED
260
365
 
261
366
  # next 3 to be checked if used or not. Copied from TK.
262
367
  dsl_property :select_foreground, :select_background # color init_pair
@@ -270,7 +375,7 @@ module RubyCurses
270
375
  dsl_property :attr # attribute bold, normal, reverse
271
376
  dsl_accessor :name # name to refr to or recall object by_name
272
377
  attr_accessor :id #, :zorder
273
- attr_accessor :curpos # cursor position inside object
378
+ attr_accessor :curpos # cursor position inside object - column, not row.
274
379
  attr_reader :config # COULD GET AXED SOON NOTE
275
380
  attr_accessor :form # made accessor 2008-11-27 22:32 so menu can set
276
381
  attr_accessor :state # normal, selected, highlighted
@@ -278,11 +383,31 @@ module RubyCurses
278
383
  dsl_property :visible # boolean # 2008-12-09 11:29
279
384
  #attr_accessor :modified # boolean, value modified or not (moved from field 2009-01-18 00:14 )
280
385
  dsl_accessor :help_text # added 2009-01-22 17:41 can be used for status/tooltips
386
+
387
+ dsl_property :preferred_width # added 2009-10-28 13:40 for splitpanes and better resizing
388
+ dsl_property :preferred_height # added 2009-10-28 13:40 for splitpanes and better resizing
389
+ dsl_property :min_width # added 2009-10-28 13:40 for splitpanes and better resizing
390
+ dsl_property :min_height # added 2009-10-28 13:40 for splitpanes and better resizing
391
+
392
+ ## 2010-01-05 13:27 create buffer conditionally, if enclosing component asks. Needs to be passed down
393
+ ##+ to further children or editor components. Default false.
394
+ attr_accessor :should_create_buffer # added 2010-01-05 13:16 BUFFERED, trying to create buffersonly where required.
281
395
 
396
+ ## I think parent_form was not a good idea since i can't add parent widget offsets
397
+ ##+ thus we should use parent_comp and push up.
398
+ attr_accessor :parent_component # added 2010-01-12 23:28 BUFFERED - to bubble up
399
+ # tired of getting the cursor wrong and guessing, i am now going to try to get absolute
400
+ # coordinates - 2010-02-07 20:17 this should be updated by parent.
401
+ attr_accessor :ext_col_offset, :ext_row_offset # 2010-02-07 20:16 to get abs position for cursor
402
+ #attr_accessor :manages_cursor # does this widget manage cursor, or should form handle it 2010-02-07 20:54
403
+ attr_accessor :rows_panned # moved from form, how many rows scrolled.panned 2010-02-11 15:26
404
+ attr_accessor :cols_panned # moved from form, how many cols scrolled.panned 2010-02-11 15:26
405
+
282
406
  def initialize form, aconfig={}, &block
283
407
  @form = form
284
408
  @bgcolor ||= "black" # 0
285
409
  @row_offset = @col_offset = 0
410
+ @ext_row_offset = @ext_col_offset = 0 # 2010-02-07 20:18
286
411
  @state = :NORMAL
287
412
  @color ||= "white" # $datacolor
288
413
  @attr = nil
@@ -296,6 +421,8 @@ module RubyCurses
296
421
  def init_vars
297
422
  # just in case anyone does a super. Not putting anything here
298
423
  # since i don't want anyone accidentally overriding
424
+ @buffer_modified = false
425
+ #@manages_cursor = false # form should manage it, I will pass row and col to it. XXX ?
299
426
  end
300
427
 
301
428
  # modified
@@ -330,6 +457,10 @@ module RubyCurses
330
457
  def on_leave
331
458
  fire_handler :LEAVE, self
332
459
  end
460
+ ##
461
+ # @return row and col of a widget where painting data actually starts
462
+ # row and col is where a widget starts. offsets usually take into account borders.
463
+ # the offsets typically are where the cursor should be positioned inside, upon on_enter.
333
464
  def rowcol
334
465
  # $log.debug "widgte rowcol : #{@row+@row_offset}, #{@col+@col_offset}"
335
466
  return @row+@row_offset, @col+@col_offset
@@ -356,18 +487,19 @@ module RubyCurses
356
487
  else
357
488
  acolor = $datacolor
358
489
  end
359
- @form.window.printstring r, c, "%-*s" % [len, value], acolor, @attr
490
+ @graphic.printstring r, c, "%-*s" % [len, value], acolor, @attr
360
491
  # next line should be in same color but only have @att so we can change att is nec
361
492
  #@form.window.mvchgat(y=r, x=c, max=len, Ncurses::A_NORMAL, @bgcolor, nil)
493
+ @buffer_modified = true # required for form to call buffer_to_screen
362
494
  end
363
495
 
364
496
  def destroy
365
- $log.debug "DESTROY : widget"
497
+ $log.debug "DESTROY : widget #{@name} "
366
498
  panel = @window.panel
367
499
  Ncurses::Panel.del_panel(panel) if !panel.nil?
368
500
  @window.delwin if !@window.nil?
369
501
  end
370
- # @ deprecated pls call windows method
502
+ # @deprecated pls call windows method
371
503
  def printstring(win, r,c,string, color, att = Ncurses::A_NORMAL)
372
504
 
373
505
  att = Ncurses::A_NORMAL if att.nil?
@@ -399,6 +531,14 @@ module RubyCurses
399
531
  raise "Form is nil in set_form" if form.nil?
400
532
  @form = form
401
533
  @id = form.add_widget(self) if !form.nil? and form.respond_to? :add_widget
534
+ # 2009-10-29 15:04 use form.window, unless buffer created
535
+ # should not use form.window so explicitly everywhere.
536
+ # added 2009-12-27 20:05 BUFFERED in case child object needs a form.
537
+ # We don;t wish to overwrite the graphic object
538
+ if @graphic.nil?
539
+ $log.debug " setting graphic to form window for #{self.class}, #{form} "
540
+ @graphic = form.window unless form.nil? # use screen for writing, not buffer
541
+ end
402
542
  end
403
543
  # puts cursor on correct row.
404
544
  def set_form_row
@@ -407,9 +547,13 @@ module RubyCurses
407
547
  @form.row = @row + 1
408
548
  end
409
549
  # set cursor on correct column, widget
410
- def set_form_col col=@curpos
411
- @curpos = col
412
- @form.col = @col + @col_offset + @curpos
550
+ # Ideally, this should be overriden, as it is not likely to be correct.
551
+ def set_form_col col1=@curpos
552
+ @curpos = col1 || 0 # 2010-01-14 21:02
553
+ #@form.col = @col + @col_offset + @curpos
554
+ c = @col + @col_offset + @curpos
555
+ $log.debug " #{@name} widget WARNING super set_form_col #{c}, #{@form} "
556
+ setrowcol nil, c
413
557
  end
414
558
  def hide
415
559
  @visible = false
@@ -446,11 +590,32 @@ module RubyCurses
446
590
  # or a field to be focussed on a key, or any other user defined action based on key
447
591
  # e.g. bind_key ?\C-x, object, block
448
592
  # added 2009-01-06 19:13 since widgets need to handle keys properly
449
- def bind_key keycode, *args, &blk
450
- $log.debug "called bind_key BIND #{keycode} #{keycode_tos(keycode)} #{args} "
593
+ # 2010-02-24 12:43 trying to take in multiple key bindings, TODO unbind
594
+ # TODO add symbol so easy to map from config file or mapping file
595
+ def OLDbind_key keycode, *args, &blk
451
596
  @key_handler ||= {}
597
+ if !block_given?
598
+ blk = args.pop
599
+ raise "If block not passed, last arg should be a method symbol" if !blk.is_a? Symbol
600
+ $log.debug " #{@name} bind_key received a symbol #{blk} "
601
+ end
602
+ case keycode
603
+ when String
604
+ $log.debug "Widg String called bind_key BIND #{keycode} #{keycode_tos(keycode)} "
605
+ keycode = keycode.getbyte(0) #if keycode.class==String ## 1.9 2009-10-05 19:40
606
+ @key_handler[keycode] = blk
607
+ when Array
608
+ # for starters lets try with 2 keys only
609
+ a0 = keycode[0]
610
+ a0 = keycode[0].getbyte(0) if keycode[0].class == String
611
+ a1 = keycode[1]
612
+ a1 = keycode[1].getbyte(0) if keycode[1].class == String
613
+ @key_handler[a0] ||= OrderedHash.new
614
+ @key_handler[a0][a1] = blk
615
+ else
616
+ @key_handler[keycode] = blk
617
+ end
452
618
  @key_args ||= {}
453
- @key_handler[keycode] = blk
454
619
  @key_args[keycode] = args
455
620
  end
456
621
  ##
@@ -468,13 +633,10 @@ module RubyCurses
468
633
  # added 2009-01-18 12:58 returns ret val of blk.call
469
634
  # so that if block does not handle, the key can still be handled
470
635
  # e.g. table last row, last col does not handle, so it will auto go to next field
636
+ # 2010-02-24 13:45 handles 2 key combinations, copied from Form, must be identical in logic
637
+ # except maybe for window pointer. TODO not tested
471
638
  def process_key keycode, object
472
- return :UNHANDLED if @key_handler.nil?
473
- blk = @key_handler[keycode]
474
- return :UNHANDLED if blk.nil?
475
- #$log.debug "called process_key #{object}, #{@key_args[keycode]}"
476
- return blk.call object, *@key_args[keycode]
477
- #0
639
+ return _process_key keycode, object, @graphic
478
640
  end
479
641
  ##
480
642
  # to be added at end of handle_key of widgets so instlalled actions can be checked
@@ -482,6 +644,346 @@ module RubyCurses
482
644
  ret = process_key ch, self
483
645
  return :UNHANDLED if ret == :UNHANDLED
484
646
  end
647
+ # @since 0.1.3
648
+ def get_preferred_size
649
+ return @preferred_height, @preferred_width
650
+ end
651
+ ##
652
+ # creates a buffer for the widget to write to.
653
+ # This is typically called in the constructor. Sometimes, the constructor
654
+ # does not have a height or width, since the object will be resized based on parents
655
+ # size, as in splitpane
656
+ # Please use this only if embedding this object in another object/s that would wish
657
+ # to crop this. Otherwise, you could have many pads in your app.
658
+ # Sets @graphic which can be used in place of @form.window
659
+ #
660
+ # @return [buffer] returns pad created
661
+ # @since 0.1.3
662
+ # NOTE: 2010-01-12 11:14 there are some parent widgets that may want this w to have a larger top and left.
663
+ # Setting it later, means that the first repaint can be off.
664
+
665
+ def create_buffer()
666
+ $log.debug " #{self.class} CB called with #{@should_create_buffer} H: #{@height} W #{@width} "
667
+ if @should_create_buffer
668
+ @height or $log.warn " CB height is nil, setting to 1. This may not be what you want"
669
+ mheight = @height || 1 # some widgets don't have height XXX
670
+ mwidth = @width || 30 # some widgets don't have width as yet
671
+ mrow = @row || 0
672
+ mcol = @col || 0
673
+ layout = { :height => mheight, :width => mwidth, :top => mrow, :left => mcol }
674
+ $log.debug " cb .. #{@name} create_buffer #{mrow} #{mcol} #{mheight} #{mwidth}"
675
+ @screen_buffer = VER::Pad.create_with_layout(layout)
676
+ @is_double_buffered = true # will be checked prior to blitting
677
+ @buffer_modified = true # set this in repaint
678
+ @repaint_all = true # added 2010-01-08 19:02
679
+ else
680
+ ## NOTE: if form has not been set, you could run into problems
681
+ ## Typically a form MUST be set by now, unless you are buffering, in which
682
+ ##+ case it should go in above block.
683
+ @screen_buffer = @form.window if @form
684
+ end
685
+
686
+ @graphic = @screen_buffer # use buffer for writing, not screen window
687
+ return @screen_buffer
688
+ end # create_buffer
689
+
690
+ ##
691
+ # checks if buffer not created already, and figures
692
+ # out dimensions.
693
+ # Preferable to use this instead of directly using create_buffer.
694
+ #
695
+ def safe_create_buffer
696
+ if @screen_buffer == nil
697
+ if @height == nil
698
+ @height = @preferred_height || @min_height
699
+ end
700
+ if @width == nil
701
+ @width = @preferred_width || @min_width
702
+ end
703
+ create_buffer
704
+ end
705
+ return @screen_buffer
706
+ end
707
+ ##
708
+ # Inform the system that the buffer has been modified
709
+ # and should be blitted over the screen or copied to parent.
710
+ def set_buffer_modified(tf=true)
711
+ @buffer_modified = tf
712
+ end
713
+
714
+
715
+ ##
716
+ # copy the buffer to the screen, or given window/pad.
717
+ # Relevant only in double_buffered case since pad has to be written
718
+ # to screen. Should be called only by outer form, not by widgets as a widget
719
+ # could be inside another widget.
720
+ # Somewhere we need to clear screen if scrolling.???
721
+ # aka b2s
722
+ # @param [Window, #get_window, nil] screen to write to, if nil then write to phys screen
723
+ # @return 0 - copy success, -1 copy failure, 1 - did nothing, usually since buffer_modified false
724
+
725
+ def buffer_to_screen(screen=nil, pminrow=0, pmincol=0)
726
+ raise "deprecated b2s "
727
+ return 1 unless @is_double_buffered and @buffer_modified
728
+ # screen is nil when form calls this to write to physical screen
729
+ $log.debug " screen inside buffer_to_screen b2s :#{screen} "
730
+ $log.error "ERROR !I have moved away fomr this method. Your program is broken and will not be working"
731
+ ## 2010-01-03 19:38 i think its wrong to put the pad onto the screen
732
+ ##+ since wrefreshing the window will cause this to be overwriting
733
+ ##+ so i put current window here.
734
+ if screen == nil
735
+ #$log.debug " XXX calling graphic.wrefresh 2010-01-03 12:27 (parent_buffer was nil) "
736
+ $log.debug " XXX 2010-01-03 20:47 now copying pad #{@graphic} onto form.window"
737
+ ret = @graphic.wrefresh
738
+ ## 2010-01-03 20:45 rather than writing to phys screen, i write to forms window
739
+ ##+ so later updates to that window do not overwrite this widget.
740
+ ## We need to check this out with scrollpane and splitpane.
741
+ #ret = @graphic.copywin(@form.window.get_window, 0, 0, @row, @col, @row+@height-1, @col+@width-1,0)
742
+ else
743
+ # screen is passed when a parent object calls this to copy child buffer to itself
744
+ @graphic.set_backing_window(screen)
745
+ $log.debug " #{@name} #{@graphic} calling copy pad to win COPY"
746
+ ret = @graphic.copy_pad_to_win
747
+ end
748
+ @buffer_modified = false
749
+ return ret
750
+ end # buffer_to_screen
751
+ ##
752
+ # returns screen_buffer or nil
753
+ #
754
+ # @return [screen_buffer, nil] screen_buffer earlier created
755
+ # @since 0.1.3
756
+
757
+ def get_buffer()
758
+ @screen_buffer
759
+ end # get_buffer
760
+
761
+ ##
762
+ # destroys screen_buffer if present
763
+ #
764
+ # @return
765
+ # @since 0.1.3
766
+
767
+ def destroy_buffer()
768
+ if @screen_buffer != nil
769
+ @screen_buffer.destroy # ???
770
+ end
771
+ end # destroy_buffer
772
+
773
+ ##
774
+ # Does the widget buffer its output in a pad
775
+ #
776
+ # @return [true, false] comment
777
+
778
+ def is_double_buffered?()
779
+ @is_double_buffered
780
+ end # is_double_buffered
781
+
782
+ ##
783
+ # getter and setter for width - 2009-10-29 22:45
784
+ # Using dsl_property style
785
+ #
786
+ # @param [val, nil] value to set
787
+ # @return [val] earlier value if nil param
788
+ # @since 0.1.3
789
+ #
790
+ def width(*val)
791
+ #$log.debug " inside XXX width() #{val[0]}"
792
+ if val.empty?
793
+ return @width
794
+ else
795
+ #$log.debug " inside XXX width()"
796
+ oldvalue = @width || 0 # is this default okay, else later nil cries
797
+ @width = val.size == 1 ? val[0] : val
798
+ newvalue = @width
799
+ @config["width"]=@width
800
+ if oldvalue != newvalue
801
+ fire_property_change("width", oldvalue, newvalue)
802
+ repaint_all(true) # added 2010-01-08 18:51 so widgets can redraw everything.
803
+ end
804
+ if is_double_buffered? and newvalue != oldvalue
805
+ $log.debug " #{@name} w calling resize of screen buffer with #{newvalue}. WARNING: does not change buffering_params"
806
+ @screen_buffer.resize(0, newvalue)
807
+ end
808
+ end
809
+ end
810
+ def width=val
811
+ width(val)
812
+ end
813
+ ##
814
+ # getter and setter for height - 2009-10-30 12:25
815
+ # Using dsl_property style
816
+ # SO WE've finally succumbed and added height to widget
817
+ # @param [val, nil] height to set
818
+ # @return [val] earlier height if nil param
819
+ # @since 0.1.3
820
+ #
821
+ def height(*val)
822
+ #$log.debug " inside XXX height() #{val[0]}"
823
+ if val.empty?
824
+ return @height
825
+ else
826
+ #$log.debug " inside #{@name} height()"
827
+ oldvalue = @height || 0 # is this default okay, else later nil cries
828
+ @height = val.size == 1 ? val[0] : val
829
+ newvalue = @height
830
+ @config["height"]=@height
831
+ if oldvalue != newvalue
832
+ fire_property_change("height", oldvalue, newvalue)
833
+ $log.debug " widget #{@name} setting repaint_all to true"
834
+ @repaint_all=true
835
+ end
836
+ # XXX this troubles me a lot. gets fired more than we would like
837
+ # XXX When both h and w change then happens 2 times.
838
+ if is_double_buffered? and newvalue != oldvalue
839
+ $log.debug " #{@name} h calling resize of screen buffer with #{newvalue}"
840
+ @screen_buffer.resize(newvalue, 0)
841
+ end
842
+ end
843
+ end
844
+ def height=val
845
+ height(val)
846
+ end
847
+ # to give simple access to other components, (eg, parent) to tell a comp to either
848
+ # paint its data, or to paint all - borders, headers, footers due to a big change (ht/width)
849
+ def repaint_required(tf=true)
850
+ @repaint_required = tf
851
+ end
852
+ def repaint_all(tf=true)
853
+ @repaint_all = tf
854
+ @repaint_required = tf
855
+ end
856
+
857
+ ##
858
+ # When an enclosing component creates a pad (buffer) and the child component
859
+ #+ should write onto the same pad, then the enclosing component should override
860
+ #+ the default graphic of child. This applies mainly to editor components in
861
+ #+ listboxes and tables.
862
+ # @param graphic graphic object to use for writing contents
863
+ # @see prepare_editor in rlistbox.
864
+ # added 2010-01-05 15:25
865
+ def override_graphic gr
866
+ @graphic = gr
867
+ end
868
+
869
+ ## passing a cursor up and adding col and row offsets
870
+ ## Added 2010-01-13 13:27 I am checking this out.
871
+ ## I would rather pass the value down and store it than do this recursive call
872
+ ##+ for each cursor display
873
+ # @see Form#setrowcol
874
+ def setformrowcol r, c
875
+ @form.row = r unless r.nil?
876
+ @form.col = c unless c.nil?
877
+ # this is stupid, going through this route i was losing windows top and left
878
+ # And this could get repeated if there are mult objects.
879
+ if !@parent_component.nil? and @parent_component != self
880
+ r+= @parent_component.form.window.top unless r.nil?
881
+ c+= @parent_component.form.window.left unless c.nil?
882
+ $log.debug " (#{@name}) calling parents setformrowcol #{r}, #{c} pa: #{@parent_component.name} self: #{name}, #{self.class}, poff #{@parent_component.row_offset}, #{@parent_component.col_offset}, top:#{@form.window.left} left:#{@form.window.left} "
883
+ @parent_component.setformrowcol r, c
884
+ else
885
+ # no more parents, now set form
886
+ $log.debug " name NO MORE parents setting #{r}, #{c} in #{@form} "
887
+ @form.setrowcol r, c
888
+ end
889
+ end
890
+ ## widget: i am putting one extra level of indirection so i can switch here
891
+ # between form#setrowcol and setformrowcol, since i am not convinced either
892
+ # are giving the accurate result. i am not sure what the issue is.
893
+ def setrowcol r, c
894
+ # 2010-02-07 21:32 is this where i should add ext_offsets
895
+ #$log.debug " #{@name} w.setrowcol #{r} + #{@ext_row_offset}, #{c} + #{@ext_col_offset} "
896
+ # commented off 2010-02-15 18:22
897
+ #r += @ext_row_offset unless r.nil?
898
+ #c += @ext_col_offset unless c.nil?
899
+ if @form
900
+ @form.setrowcol r, c
901
+ else
902
+ @parent_component.setrowcol r, c
903
+ end
904
+ #setformrowcol r,c
905
+ end
906
+
907
+ # move from TextView
908
+ # parameters relating to buffering - new 2010-02-12 12:09 RFED16
909
+ # I am merging so i can call multiple times
910
+ # WARNING NOTE : this does not set Pad's top and left since Pad may not be created yet, or at all
911
+ def set_buffering params
912
+ @buffer_params ||= {}
913
+ #@should_create_buffer = params[:should_create_buffer] || true
914
+ @target_window ||= params[:target_window]
915
+ # trying out, 2010-02-12 19:40 sometimes no form even with parent.
916
+ @form = params[:form] unless @form
917
+ ## XXX trying this out.
918
+ # what if container does not ask child to buffer, as in splitpane
919
+ # then graphic could be nil
920
+ if @graphic.nil? # and should_create_buffer not set or false XXX
921
+ @graphic = @target_window
922
+ end
923
+ $log.debug " set_buffering #{@name} got target window #{@target_window}, #{@graphic} - THIS DOES NOT UPDATE PAD ... sr:#{params[:screen_top]} sc:#{params[:screen_left]} top:#{params[:top]} left:#{params[:left]} bot:#{params[:bottom]} rt:#{params[:right]} "
924
+ # @top = params[:top]
925
+ # @left = params[:left]
926
+ # @bottom = params[:bottom]
927
+ # @right = params[:right]
928
+ # offsets ?
929
+ # warning, this does not touch @top and left of Pad, often pad will bot yet be created
930
+ @buffer_params.merge!(params)
931
+ if !@screen_buffer.nil?
932
+ # update Pad since some folks take from there such as print_border
933
+ @screen_buffer.top = params[:screen_top] if !params[:screen_top].nil?
934
+ @screen_buffer.left = params[:screen_left] if !params[:screen_left].nil?
935
+ end
936
+ end
937
+
938
+ # copy buffer onto window
939
+ # RFED16 added 2010-02-12 14:42 0 simpler buffer management
940
+ def buffer_to_window
941
+ if @is_double_buffered and @buffer_modified
942
+ raise " #{@name} @buffer_params not passed. Use set_buffering()" unless @buffer_params
943
+ # we are notchecking for TV's width exceedingg, could get -1 if TV exceeds parent/
944
+ $log.debug "RFED16 paint #{@name} calling b2s #{@graphic} "
945
+ # TODO need to call set_screen_row_col (top, left), set_pad_top_left (pminrow, pmaxrow), set_screen_max_row_col
946
+ if false
947
+ # boh these print the pad behind 0,0, later scroll etc cover it and show bars.
948
+ # adding window was giving error
949
+ ret = buffer_to_screen #@target_window.get_window
950
+ #ret = @graphic.wrefresh
951
+ else
952
+ # ext gives me parents offset. often the row would be zero, so we still need one extra
953
+ r1 = @ext_row_offset # XXX NO, i should use top and left
954
+ c1 = @ext_col_offset
955
+ r = @graphic.top # 2010-02-12 21:12 TRYING THIS.
956
+ c = @graphic.left
957
+ maxr = @buffer_params[:bottom]
958
+ maxc = @buffer_params[:right]
959
+ r = @buffer_params[:screen_top] || 0
960
+ c = @buffer_params[:screen_left] || 0
961
+ $log.debug " b2w #{r1} #{c1} , #{r} #{c} "
962
+ ## sadly this is bypassing the method that does this stuff in Pad. We need to assimilate it back, so not so much work here
963
+ pminr = @graphic.pminrow
964
+ pminc = @graphic.pmincol
965
+ border_width = 0 # 2 #XXX 2010-02-15 23:40 2 to 0
966
+ $log.debug " #{@name} ret = @graphic.copywin(@target_window.get_window, #{pminr}, #{pminc}, #{r}, #{c}, #{r}+#{maxr} - #{border_width}, #{c} + #{maxc} - #{border_width} ,0)"
967
+ # this print the view at 0,0, byt covers the scrllare, bars not shown.
968
+ # this can crash if textview is smaller than container dimension
969
+ # can crash/give -1 when panning, giong beyond pad size XXX
970
+ ret = @graphic.copywin(@target_window.get_window, pminr, pminc, r, c, r+maxr-border_width, c+maxc-border_width,0)
971
+ if ret == -1
972
+ $log.debug " copywin #{@name} h #{@height} w #{@width} "
973
+ if @height <= maxr-border_width
974
+ $log.warn " h #{@height} is <= :bottom #{maxr} "
975
+ end
976
+ if @width <= maxc-border_width
977
+ $log.warn " h #{@width} is <= :right #{maxc} "
978
+ end
979
+ $log.warn "ERROR !!! copywin returns -1 check Target: #{@target_window}, #{@target_window.get_window} " if ret == -1
980
+ end
981
+ end
982
+ $log.debug " copywin ret --> #{ret} "
983
+ #
984
+ end
985
+ end
986
+ ##
485
987
  ## ADD HERE WIDGET
486
988
  end
487
989
 
@@ -506,6 +1008,19 @@ module RubyCurses
506
1008
  attr_reader :by_name # hash containing widgets by name for retrieval
507
1009
  attr_reader :menu_bar
508
1010
  attr_accessor :navigation_policy # :CYCLICAL will cycle around. Needed to move to other tabs
1011
+ ## i need some way to move the cursor by telling the main form what the coords are
1012
+ ##+ perhaps this will work
1013
+ attr_accessor :parent_form # added 2009-12-28 23:01 BUFFERED - to bubble up row col changes
1014
+ # how many rows the component is panning embedded widget by
1015
+ attr_accessor :rows_panned # HACK added 2009-12-30 16:01 BUFFERED
1016
+ # how many cols the component is panning embedded widget by
1017
+ attr_accessor :cols_panned # HACK added 2009-12-30 16:01 BUFFERED
1018
+ ## next 2 added since tabbedpanes offset needs to be accounted by form inside it.
1019
+ attr_accessor :add_cols # 2010-01-26 20:23 additional columns due to being placed in some container
1020
+ attr_accessor :add_rows # 2010-01-26 20:23 additional columns due to being placed in some container
1021
+ attr_accessor :name # for debugging 2010-02-02 20:12
1022
+ attr_accessor :ext_col_offset, :ext_row_offset # 2010-02-07 20:16 to get abs position for cursor
1023
+ # attr_accessor :allow_alt_digits # catch Alt-1-9 as digit_argument
509
1024
  def initialize win, &block
510
1025
  @window = win
511
1026
  @widgets = []
@@ -513,12 +1028,26 @@ module RubyCurses
513
1028
  @active_index = -1
514
1029
  @padx = @pady = 0
515
1030
  @row = @col = -1
1031
+ @ext_row_offset = @ext_col_offset = 0 # 2010-02-07 20:18
1032
+ @add_cols = @add_rows = 0 # 2010-01-26 20:28
516
1033
  @handler = {}
517
1034
  @modified = false
518
1035
  @focusable = true
519
1036
  @navigation_policy ||= :CYCLICAL
520
1037
  instance_eval &block if block_given?
521
1038
  @firsttime = true # internal, don't touch
1039
+ ## I need some counter so a widget knows it has been panned and can send a correct
1040
+ ##+ cursor coordinate to system.
1041
+ @rows_panned = @cols_panned = 0 # how many rows were panned, typically at a higher level
1042
+ @firsttime = true; # added on 2010-01-02 19:21 to prevent scrolling crash !
1043
+ @name ||= ""
1044
+ $kill_ring ||= [] # 2010-03-09 22:42 so textarea and others can copy and paste
1045
+ $kill_ring_pointer = 0 # needs to be incremented with each append, moved with yank-pop
1046
+ $append_next_kill = false
1047
+ $kill_last_pop_size = 0 # size of last pop which has to be cleared
1048
+
1049
+ #@allow_alt_digits = true ; # capture Alt-1-9 as digit_args. Set to false if you wish to map
1050
+ # Alt-1-9 to buttons of tabs
522
1051
  end
523
1052
  ##
524
1053
  # set this menubar as the form's menu bar.
@@ -549,9 +1078,21 @@ module RubyCurses
549
1078
  @by_name[widget.name] = widget
550
1079
  end
551
1080
 
1081
+
1082
+ $log.debug " #{self} adding a widget #{@widgets.length} .. #{widget} "
552
1083
  @widgets << widget
1084
+ # add form offsets to widget's external offsets - 2010-02-07 20:22
1085
+ if widget.is_a? RubyCurses::Widget
1086
+ if @window # ext seems redundant
1087
+ widget.ext_col_offset += @window.left # just hope window aint undef!! XXX
1088
+ $log.debug " #{@name} add widget ( #{widget.name} ) ext_row #{widget.ext_row_offset} += #{@window.top} "
1089
+ widget.ext_row_offset += @window.top
1090
+ end
1091
+ end
553
1092
  return @widgets.length-1
554
- end
1093
+ end
1094
+ alias :add :add_widget
1095
+
555
1096
  # remove a widget
556
1097
  # added 2008-12-09 12:18
557
1098
  def remove_widget widget
@@ -564,31 +1105,69 @@ module RubyCurses
564
1105
  # form repaint
565
1106
  # to be called at some interval, such as after each keypress.
566
1107
  def repaint
1108
+ $log.debug " form repaint:#{self}, #{@name} , r #{@row} c #{@col} "
567
1109
  @widgets.each do |f|
568
1110
  next if f.visible == false # added 2008-12-09 12:17
569
1111
  f.repaint
1112
+ # added 2009-10-29 20:11 for double buffered widgets
1113
+ # this should only happen if painting actually happened
1114
+ #$log.debug " #{self} form repaint parent_buffer (#{@parent_buffer}) if #{f.is_double_buffered?} : #{f.name} "
1115
+ pb = @parent_buffer #|| @window
1116
+ # is next line used 2010-02-05 00:04 its wiping off top in scrollpane in tabbedpane
1117
+ # RFED16 - the next line should never execute now, since no outer object is buffered
1118
+ #+ only those within containers are.
1119
+ # Drat - this line is happeing since components inside a TP are double_buffered
1120
+ #x f.buffer_to_screen(pb) if f.is_double_buffered?
570
1121
  end
571
- @window.clear_error
1122
+ @window.clear_error # suddenly throwing up on a small pad 2010-03-02 15:22 TPNEW
572
1123
  @window.print_status_message $status_message unless $status_message.nil?
573
1124
  @window.print_error_message $error_message unless $error_message.nil?
574
1125
  $error_message = $status_message = nil
575
1126
  # this can bomb if someone sets row. We need a better way!
576
- if @row == -1 #or @firsttime == true
1127
+ if @row == -1 and @firsttime == true
577
1128
  #set_field_cursor 0
578
- $log.debug "form repaint calling select field 0"
1129
+ # this part caused an endless loop on 2010-01-02 19:20 when scrollpane scrolled up
1130
+ $log.debug "form repaint calling select field 0 SHOULD HAPPEN FIRST TIME ONLY"
579
1131
  #select_field 0
580
1132
  req_first_field
581
- #@firsttime = false
1133
+ @firsttime = false
582
1134
  end
583
1135
  setpos
584
- @window.wrefresh
1136
+ # XXX this creates a problem if window is a pad
1137
+ # although this does show cursor movement etc.
1138
+ ### XXX@window.wrefresh
1139
+ if @window.window_type == :WINDOW
1140
+ $log.debug " formrepaint #{@name} calling window.wrefresh #{@window} "
1141
+ @window.wrefresh
1142
+ else
1143
+ # UGLY HACK TO MAKE TABBEDPANES WORK !!
1144
+ # If the form is based on a Pad, then it would come here to write the Pad onto the parent_buffer
1145
+ # However, I've obviated the need to handle anything here by adding a display_form after handle_key
1146
+ # in TP.
1147
+ #x if @parent_buffer!=nil
1148
+ #x $log.debug " formrep coming to set backing window part #{@window} , type:#{@window.window_type}, #{@parent_buffer}, #{@parent_buffer.window_type} "
1149
+ # XXX RFED19 do we need at all 2010-02-19 15:26
1150
+ # this is required so that each key stroke registers on tabbedpane
1151
+ # for this to work both have to be pads
1152
+ #x @window.set_backing_window(@parent_buffer)
1153
+ #x @window.copy_pad_to_win
1154
+ #x @window.wrefresh #since the pads are writing onto window directly, i don't think we need this
1155
+ #x $log.debug " DO I NEED TO DO SOMETHING HERE FOR TABBEDPANES now ? WARN ?? YES, else keystrokes won't be updated "
1156
+ #x end
1157
+ end
585
1158
  end
586
1159
  ##
587
1160
  # move cursor to where the fields row and col are
588
1161
  # private
589
1162
  def setpos r=@row, c=@col
590
- # $log.debug "setpos : #{r} #{c}"
591
- @window.wmove r,c
1163
+ # next 2 lines added, only do cursor if current field doesn't manage it.
1164
+ #curr = get_current_field
1165
+ #return if curr.manages_cursor
1166
+ $log.debug "setpos : (#{self}) #{r} #{c}"
1167
+ ## adding just in case things are going out of bounds of a parent and no cursor to be shown
1168
+ return if r.nil? or c.nil? # added 2009-12-29 23:28 BUFFERED
1169
+ return if r<0 or c<0 # added 2010-01-02 18:49 stack too deep coming if goes above screen
1170
+ @window.wmove r,c
592
1171
  end
593
1172
  def get_current_field
594
1173
  select_next_field if @active_index == -1
@@ -628,17 +1207,23 @@ module RubyCurses
628
1207
  f.on_enter if f.respond_to? :on_enter
629
1208
  fire_handler :ENTER, f
630
1209
  end
1210
+ ## is a field focusable
1211
+ # Added a method here, so forms can extend this to avoid focussing on off-screen components
1212
+ def focusable?(f)
1213
+ return f.focusable
1214
+ end
631
1215
  ##
632
1216
  # puts focus on the given field/widget index
633
1217
  # XXX if called externally will not run a on_leave of previous field
634
1218
  def select_field ix0
635
- return if @widgets.nil? or @widgets.empty? or !@widgets[ix0].focusable
636
- # $log.debug "insdie select field : #{ix0} ai #{@active_index}"
1219
+ #return if @widgets.nil? or @widgets.empty? or !@widgets[ix0].focusable
1220
+ return if @widgets.nil? or @widgets.empty? or !focusable?(@widgets[ix0])
1221
+ #$log.debug "insdie select field : #{ix0} ai #{@active_index}"
637
1222
  f = @widgets[ix0]
638
- if f.focusable
1223
+ if focusable?(f)
639
1224
  @active_index = ix0
640
1225
  @row, @col = f.rowcol
641
- # $log.debug "insdie sele nxt field : ROW #{@row} COL #{@col} "
1226
+ #$log.debug " WMOVE insdie sele nxt field : ROW #{@row} COL #{@col} "
642
1227
  @window.wmove @row, @col
643
1228
  on_enter f
644
1229
  f.curpos = 0
@@ -669,7 +1254,7 @@ module RubyCurses
669
1254
  # in which case returns :NO_NEXT_FIELD.
670
1255
  def select_next_field
671
1256
  return if @widgets.nil? or @widgets.empty?
672
- #$log.debug "insdie sele nxt field : #{@active_index} WL:#{@widgets.length}"
1257
+ $log.debug "insdie sele nxt field : #{@active_index} WL:#{@widgets.length}"
673
1258
  if @active_index.nil?
674
1259
  @active_index = -1
675
1260
  else
@@ -687,7 +1272,8 @@ module RubyCurses
687
1272
  index = @active_index + 1
688
1273
  index.upto(@widgets.length-1) do |i|
689
1274
  f = @widgets[i]
690
- if f.focusable
1275
+ $log.debug "insdie sele nxt field : i #{i} #{index} WL:#{@widgets.length}, field #{f}"
1276
+ if focusable?(f)
691
1277
  select_field i
692
1278
  return
693
1279
  end
@@ -701,12 +1287,13 @@ module RubyCurses
701
1287
  #select_next_field
702
1288
  0.upto(index-1) do |i|
703
1289
  f = @widgets[i]
704
- if f.focusable
1290
+ if focusable?(f)
705
1291
  select_field i
706
1292
  return
707
1293
  end
708
1294
  end
709
1295
  end
1296
+ $log.debug "insdie sele nxt field : NO NEXT #{@active_index} WL:#{@widgets.length}"
710
1297
  return :NO_NEXT_FIELD
711
1298
  end
712
1299
  ##
@@ -732,7 +1319,7 @@ module RubyCurses
732
1319
  index = @active_index - 1
733
1320
  (index).downto(0) do |i|
734
1321
  f = @widgets[i]
735
- if f.focusable
1322
+ if focusable?(f)
736
1323
  select_field i
737
1324
  return
738
1325
  end
@@ -746,7 +1333,7 @@ module RubyCurses
746
1333
  total = @widgets.length-1
747
1334
  total.downto(index-1) do |i|
748
1335
  f = @widgets[i]
749
- if f.focusable
1336
+ if focusable?(f)
750
1337
  select_field i
751
1338
  return
752
1339
  end
@@ -757,61 +1344,231 @@ module RubyCurses
757
1344
  alias :req_next_field :select_next_field
758
1345
  alias :req_prev_field :select_prev_field
759
1346
  ##
760
- # move cursor by num columns
1347
+ # move cursor by num columns. Form
761
1348
  def addcol num
762
1349
  return if @col.nil? or @col == -1
763
1350
  @col += num
764
1351
  @window.wmove @row, @col
1352
+ ## 2010-01-30 23:45 exchange calling parent with calling this forms setrow
1353
+ # since in tabbedpane with table i am not gietting this forms offset.
1354
+ setrowcol nil, col
1355
+ # added on 2010-01-05 22:26 so component widgets like scrollpane can get the cursor
1356
+ #if !@parent_form.nil? and @parent_form != self #@form
1357
+ #$log.debug " #{@name} addcol calling parents setrowcol #{row}, #{col}: #{@parent_form} "
1358
+ #@parent_form.setrowcol nil, col
1359
+ #end
765
1360
  end
766
1361
  ##
767
1362
  # move cursor by given rows and columns, can be negative.
1363
+ # 2010-01-30 23:47 FIXME, if this is called we should call setrowcol like in addcol
768
1364
  def addrowcol row,col
769
- return if @col.nil? or @col == -1
1365
+ return if @col.nil? or @col == -1 # contradicts comment on top
770
1366
  return if @row.nil? or @row == -1
771
1367
  @col += col
772
1368
  @row += row
773
1369
  @window.wmove @row, @col
1370
+ # added on 2010-01-05 22:26 so component widgets like scrollpane can get the cursor
1371
+ if !@parent_form.nil? and @parent_form != @form
1372
+ $log.debug " #{@name} addrowcol calling parents setrowcol #{row}, #{col} "
1373
+ @parent_form.setrowcol row, col
1374
+ end
1375
+ end
1376
+
1377
+ ## added 2009-12-29 15:46 BUFFERED
1378
+ # Set forms row and col, so that the cursor can be displayed at that point.
1379
+ # Widgets should call this rather than touch row and col
1380
+ # directly. This should percolate the row and col
1381
+ # upwards to parent forms, after comparing to prevent
1382
+ # infinite recursion.
1383
+ # This is being done for embedded objects so that the cursor
1384
+ # can be maintained correctly.
1385
+ def OLDsetrowcol r, c
1386
+ @row = r unless r.nil?
1387
+ @col = c unless c.nil?
1388
+ r += @add_rows unless r.nil? # 2010-01-26 20:31
1389
+ c += @add_cols unless c.nil? # 2010-01-26 20:31
1390
+ $log.debug " addcols #{@add_cols} addrow #{@add_rows} : #{self} "
1391
+ if !@parent_form.nil? and @parent_form != self
1392
+ $log.debug " (#{@name}) calling parents setrowcol #{r}, #{c} : pare: #{@parent_form}; self: #{self}, #{self.class} "
1393
+ r += @parent_form.window.top unless r.nil?
1394
+ c += @parent_form.window.left unless c.nil?
1395
+ @parent_form.setrowcol r, c
1396
+ end
1397
+ end
1398
+ ## Form
1399
+ # New attempt at setting cursor using absolute coordinates
1400
+ # Also, trying NOT to go up. let this pad or window print cursor.
1401
+ def setrowcol r, c
1402
+ @row = r unless r.nil?
1403
+ @col = c unless c.nil?
1404
+ r += @add_rows unless r.nil? # 2010-01-26 20:31
1405
+ c += @add_cols unless c.nil? # 2010-01-26 20:31
1406
+ $log.debug " addcols #{@add_cols} addrow #{@add_rows} : #{self} "
1407
+ if !@parent_form.nil? and @parent_form != self
1408
+ $log.debug " (#{@name}) calling parents setrowcol #{r}, #{c} : pare: #{@parent_form}; self: #{self}, #{self.class} "
1409
+ #r += @parent_form.window.top unless r.nil?
1410
+ #c += @parent_form.window.left unless c.nil?
1411
+ @parent_form.setrowcol r, c
1412
+ end
774
1413
  end
775
1414
  ##
776
1415
  # bind an action to a key, required if you create a button which has a hotkey
777
1416
  # or a field to be focussed on a key, or any other user defined action based on key
778
1417
  # e.g. bind_key ?\C-x, object, block
779
- def bind_key keycode, *args, &blk
780
- $log.debug "called bind_key BIND #{keycode} #{keycode_tos(keycode)} #{args} "
781
- @key_handler ||= {}
782
- @key_args ||= {}
783
- @key_handler[keycode] = blk
784
- @key_args[keycode] = args
1418
+ # 1.9 if string passed then getbyte so user does not need to change much and
1419
+ # less chance of error 2009-10-04 16:08
1420
+ def OLDbind_key keycode, *args, &blk
1421
+ @key_handler ||= {}
1422
+ case keycode
1423
+ when String
1424
+ $log.debug "FORM String called bind_key BIND #{keycode} #{keycode_tos(keycode)} "
1425
+ keycode = keycode.getbyte(0) #if keycode.class==String ## 1.9 2009-10-05 19:40
1426
+ @key_handler[keycode] = blk
1427
+ when Array
1428
+ # for starters lets try with 2 keys only
1429
+ a0 = keycode[0]
1430
+ a0 = keycode[0].getbyte(0) if keycode[0].class == String
1431
+ a1 = keycode[1]
1432
+ a1 = keycode[1].getbyte(0) if keycode[1].class == String
1433
+ @key_handler[a0] ||= OrderedHash.new
1434
+ @key_handler[a0][a1] = blk
1435
+ else
1436
+ @key_handler[keycode] = blk
1437
+ end
1438
+ @key_args ||= {}
1439
+ @key_args[keycode] = args
785
1440
  end
786
1441
 
787
1442
  # e.g. process_key ch, self
788
1443
  # returns UNHANDLED if no block for it
789
1444
  # after form handles basic keys, it gives unhandled key to current field, if current field returns
790
1445
  # unhandled, then it checks this map.
1446
+ # Please update widget with any changes here. TODO: match regexes as in mapper
791
1447
  def process_key keycode, object
792
- return :UNHANDLED if @key_handler.nil?
793
- blk = @key_handler[keycode]
794
- return :UNHANDLED if blk.nil?
795
- $log.debug "called process_key #{object}, #{@key_args[keycode]}"
796
- blk.call object, *@key_args[keycode]
797
- 0
1448
+ return _process_key keycode, object, @window
1449
+ end
1450
+ #return :UNHANDLED if @key_handler.nil?
1451
+ #blk = @key_handler[keycode]
1452
+ #return :UNHANDLED if blk.nil?
1453
+ #if blk.is_a? OrderedHash
1454
+ ## Please note that this does not wait too long, you have to press next key fast
1455
+ ## since i have set halfdelay in ncurses.rb, test this with getchar to get more keys TODO
1456
+ #ch = @window.getch
1457
+ #if ch < 0 || ch > 255
1458
+ ##next
1459
+ #return nil
1460
+ #end
1461
+ #$log.debug " process_key: got #{keycode} , #{ch} "
1462
+ #yn = ch.chr
1463
+ #blk1 = blk[ch]
1464
+ #return nil if blk1.nil?
1465
+ #$log.debug " process_key: found block for #{keycode} , #{ch} "
1466
+ #blk = blk1
1467
+ #end
1468
+ #$log.debug "called process_key #{object}, kc: #{keycode}, args #{@key_args[keycode]}"
1469
+ ## return blk.call object, *@key_args[keycode]
1470
+ #blk.call object, *@key_args[keycode]
1471
+ #0
1472
+ #end
1473
+ # Defines how user can give numeric args to a command even in edit mode
1474
+ # User either presses universal_argument (C-u) which generates a series of 4 16 64.
1475
+ # Or he presses C-u and then types some numbers. Followed by the action.
1476
+ # @returns [0, :UNHANDLED] :UNHANDLED implies that last keystroke is still to evaluated
1477
+ # by system. ) implies only numeric args were obtained. This method updates $multiplier
1478
+ def universal_argument
1479
+ $multiplier = ( ($multiplier.nil? || $multiplier == 0) ? 4 : $multiplier *= 4)
1480
+ $log.debug " inside UNIV MULT0: #{$multiplier} "
1481
+ # See if user enters numerics. If so discard existing varaible and take only
1482
+ #+ entered values
1483
+ _m = 0
1484
+ while true
1485
+ ch = @window.getchar()
1486
+ case ch
1487
+ when -1
1488
+ next
1489
+ when ?0.getbyte(0)..?9.getbyte(0)
1490
+ _m *= 10 ; _m += (ch-48)
1491
+ $multiplier = _m
1492
+ $log.debug " inside UNIV MULT #{$multiplier} "
1493
+ when ?\C-u.getbyte(0)
1494
+ if _m == 0
1495
+ # user is incrementally hitting C-u
1496
+ $multiplier *= 4
1497
+ else
1498
+ # user is terminating some numbers so he can enter a numeric command next
1499
+ return 0
1500
+ end
1501
+ else
1502
+ $log.debug " inside UNIV MULT else got #{ch} "
1503
+ # here is some other key that is the function key to be repeated. we must honor this
1504
+ # and ensure it goes to the right widget
1505
+ return ch
1506
+ #return :UNHANDLED
1507
+ end
1508
+ end
1509
+ return 0
1510
+ end
1511
+ def digit_argument ch
1512
+ $multiplier = ch - ?\M-0.getbyte(0)
1513
+ $log.debug " inside UNIV MULT 0 #{$multiplier} "
1514
+ # See if user enters numerics. If so discard existing varaible and take only
1515
+ #+ entered values
1516
+ _m = $multiplier
1517
+ while true
1518
+ ch = @window.getchar()
1519
+ case ch
1520
+ when -1
1521
+ next
1522
+ when ?0.getbyte(0)..?9.getbyte(0)
1523
+ _m *= 10 ; _m += (ch-48)
1524
+ $multiplier = _m
1525
+ $log.debug " inside UNIV MULT 1 #{$multiplier} "
1526
+ when ?\M-0.getbyte(0)..?\M-9.getbyte(0)
1527
+ _m *= 10 ; _m += (ch-?\M-0.getbyte(0))
1528
+ $multiplier = _m
1529
+ $log.debug " inside UNIV MULT 2 #{$multiplier} "
1530
+ else
1531
+ $log.debug " inside UNIV MULT else got #{ch} "
1532
+ # here is some other key that is the function key to be repeated. we must honor this
1533
+ # and ensure it goes to the right widget
1534
+ return ch
1535
+ #return :UNHANDLED
1536
+ end
1537
+ end
1538
+ return 0
798
1539
  end
799
1540
  ## forms handle keys
800
1541
  # mainly traps tab and backtab to navigate between widgets.
801
1542
  # I know some widgets will want to use tab, e.g edit boxes for entering a tab
802
1543
  # or for completion.
803
1544
  def handle_key(ch)
1545
+ if ch == ?\C-u.getbyte(0)
1546
+ ret = universal_argument
1547
+ $log.debug " FORM set MULT to #{$multiplier}, ret = #{ret} "
1548
+ return 0 if ret == 0
1549
+ ch = ret # unhandled char
1550
+ elsif ch >= ?\M-1.getbyte(0) && ch <= ?\M-9.getbyte(0)
1551
+ if $catch_alt_digits
1552
+ ret = digit_argument ch
1553
+ $log.debug " FORM set MULT DA to #{$multiplier}, ret = #{ret} "
1554
+ return 0 if ret == 0 # don't see this happening
1555
+ ch = ret # unhandled char
1556
+ end
1557
+ end
1558
+
804
1559
  case ch
805
1560
  when -1
806
1561
  return
807
1562
  else
1563
+ keycode = keycode_tos(ch)
1564
+ $log.debug " form HK #{ch} #{self}, #{@name}, #{keycode} "
808
1565
  field = get_current_field
809
1566
  handled = :UNHANDLED
810
1567
  handled = field.handle_key ch unless field.nil? # no field focussable
811
1568
  # some widgets like textarea and list handle up and down
812
1569
  if handled == :UNHANDLED or handled == -1 or field.nil?
813
1570
  case ch
814
- when 9, ?\M-\C-i # tab and M-tab in case widget eats tab (such as Table)
1571
+ when 9, ?\M-\C-i.getbyte(0) # tab and M-tab in case widget eats tab (such as Table)
815
1572
  ret = select_next_field
816
1573
  return ret if ret == :NO_NEXT_FIELD
817
1574
  # alt-shift-tab or backtab (in case Table eats backtab)
@@ -822,14 +1579,31 @@ module RubyCurses
822
1579
  select_prev_field
823
1580
  when KEY_DOWN
824
1581
  select_next_field
1582
+ #when ?\M-L.getbyte(0)
1583
+ ### trying out these for fuun and testing splitpane 2010-01-10 20:32
1584
+ #$log.debug " field #{field.name} was #{field.width} "
1585
+ #field.width += 1
1586
+ #$log.debug " field #{field.name} now #{field.width} "
1587
+ #field.repaint_all
1588
+ #when ?\M-H.getbyte(0), ?\M-<.getbyte(0)
1589
+ #field.width -= 1
1590
+ #$log.debug " field #{field.name} now #{field.width} "
1591
+ #field.repaint_all
1592
+ #when ?\M-J.getbyte(0)
1593
+ #field.height += 1
1594
+ #when ?\M-K.getbyte(0)
1595
+ #field.height -= 1
825
1596
  else
826
1597
  ret = process_key ch, self
1598
+ $log.debug " process_key #{ch} got ret #{ret} in #{self} "
827
1599
  return :UNHANDLED if ret == :UNHANDLED
828
1600
  end
829
1601
  end
830
1602
  end
831
- $log.debug " form before repaint"
1603
+ $log.debug " form before repaint #{self} , #{@name}, ret #{ret}"
832
1604
  repaint
1605
+ #return handled # TRYNG 2010-03-01 23:30 since TP returns NO_NEXT_FIELD sometimes
1606
+ #$multiplier = 0
833
1607
  end
834
1608
  ##
835
1609
  # test program to dump data onto log
@@ -854,9 +1628,36 @@ module RubyCurses
854
1628
  end
855
1629
  $log.debug " END DUMPING DATA "
856
1630
  end
1631
+ ##
1632
+ # trying out for splitpane and others who have a sub-form
1633
+ def set_parent_buffer b
1634
+ @parent_buffer = b
1635
+ end
1636
+ # 2010-02-07 14:50 to aid in debugging and comparing log files.
1637
+ def to_s; @name || self; end
857
1638
 
858
1639
  ## ADD HERE FORM
859
1640
  end
1641
+ ## Created and sent to all listeners whenever a property is changed
1642
+ # @see fire_property_change
1643
+ # @see fire_handler
1644
+ # @since 1.0.5 added 2010-02-25 23:06
1645
+ class PropertyChangeEvent
1646
+ attr_accessor :source, :property_name, :oldvalue, :newvalue
1647
+ def initialize source, property_name, oldvalue, newvalue
1648
+ set source, property_name, oldvalue, newvalue
1649
+ end
1650
+ def set source, property_name, oldvalue, newvalue
1651
+ @source, @property_name, @oldvalue, @newvalue =
1652
+ source, property_name, oldvalue, newvalue
1653
+ end
1654
+ def to_s
1655
+ "PROPERTY_CHANGE name: #{property_name}, oldval: #{@oldvalue}, newvalue: #{@newvalue}, source: #{@source}"
1656
+ end
1657
+ def inspect
1658
+ to_s
1659
+ end
1660
+ end
860
1661
 
861
1662
  ##
862
1663
  # Text edit field
@@ -887,6 +1688,7 @@ module RubyCurses
887
1688
  #attr_reader :curpos # cursor position in buffer current, in WIDGET
888
1689
  attr_accessor :datatype # crrently set during set_buffer
889
1690
  attr_reader :original_value # value on entering field
1691
+ attr_accessor :overwrite_mode # true or false INSERT OVERWRITE MODE
890
1692
 
891
1693
  def initialize form, config={}, &block
892
1694
  @form = form
@@ -931,11 +1733,17 @@ module RubyCurses
931
1733
  end
932
1734
  end
933
1735
  def putch char
934
- return -1 if !@editable or @buffer.length >= @maxlen
1736
+ return -1 if !@editable
1737
+ return -1 if !@overwrite_mode and @buffer.length >= @maxlen
935
1738
  if @chars_allowed != nil
936
1739
  return if char.match(@chars_allowed).nil?
937
1740
  end
938
- @buffer.insert(@curpos, char)
1741
+ # added insert or overwrite mode 2010-03-17 20:11
1742
+ if @overwrite_mode
1743
+ @buffer[@curpos] = char
1744
+ else
1745
+ @buffer.insert(@curpos, char)
1746
+ end
939
1747
  @curpos += 1 if @curpos < @maxlen
940
1748
  @modified = true
941
1749
  $log.debug " FIELD FIRING CHANGE: #{char} at new #{@curpos}: bl:#{@buffer.length} buff:[#{@buffer}]"
@@ -997,8 +1805,13 @@ module RubyCurses
997
1805
  label.col @col-(label.name.length+1) if label.col == -1
998
1806
  label.label_for(self)
999
1807
  end
1808
+
1809
+ ## Note that some older widgets like Field repaint every time the form.repaint
1810
+ ##+ is called, whether updated or not. I can't remember why this is, but
1811
+ ##+ currently I've not implemented events with these widgets. 2010-01-03 15:00
1812
+
1000
1813
  def repaint
1001
- # $log.debug("FIELD: #{id}, #{zorder}, #{focusable}")
1814
+ $log.debug("repaint FIELD: #{id}, #{name}, #{focusable}")
1002
1815
  #return if display_length <= 0 # added 2009-02-17 00:17 sometimes editor comp has 0 and that
1003
1816
  # becomes negative below, no because editing still happens
1004
1817
  @display_length = 1 if display_length == 0
@@ -1017,11 +1830,12 @@ module RubyCurses
1017
1830
  else
1018
1831
  acolor = $datacolor
1019
1832
  end
1020
- @form.window.printstring row, col, sprintf("%-*s", display_length, printval), acolor, @attr
1833
+ @graphic = @form.window if @graphic.nil? ## cell editor listbox hack XXX fix in correct place
1834
+ $log.debug " Field g:#{@graphic}. r,c,displen:#{@row}, #{@col}, #{@display_length} "
1835
+ @graphic.printstring row, col, sprintf("%-*s", display_length, printval), acolor, @attr
1021
1836
  end
1022
1837
  def set_focusable(tf)
1023
1838
  @focusable = tf
1024
- # @form.regenerate_focusables
1025
1839
  end
1026
1840
 
1027
1841
  # field
@@ -1047,13 +1861,13 @@ module RubyCurses
1047
1861
  end
1048
1862
  when 330
1049
1863
  delete_curr_char if @editable
1050
- when ?\C-a
1864
+ when ?\C-a.getbyte(0)
1051
1865
  cursor_home
1052
- when ?\C-e
1866
+ when ?\C-e.getbyte(0)
1053
1867
  cursor_end
1054
- when ?\C-k
1868
+ when ?\C-k.getbyte(0)
1055
1869
  delete_eol if @editable
1056
- when ?\C-u
1870
+ when ?\C-_.getbyte(0) # changed on 2010-02-26 14:44 so C-u can be used as numeric arg
1057
1871
  @buffer.insert @curpos, @delete_buffer unless @delete_buffer.nil?
1058
1872
  when 32..126
1059
1873
  #$log.debug("FIELD: ch #{ch} ,at #{@curpos}, buffer:[#{@buffer}] bl: #{@buffer.to_s.length}")
@@ -1137,6 +1951,7 @@ module RubyCurses
1137
1951
  set_modified
1138
1952
  addcol -1
1139
1953
  end
1954
+ ## add a column to cursor position. Field
1140
1955
  def addcol num
1141
1956
  if num < 0
1142
1957
  if @form.col <= @col + @col_offset
@@ -1213,7 +2028,7 @@ module RubyCurses
1213
2028
  ##
1214
2029
  # install trigger to call whenever a value is updated
1215
2030
  def update_command *args, &block
1216
- $log.debug "Variable: update command set #{args}"
2031
+ $log.debug "Variable: update command set " # #{args}"
1217
2032
  @update_command << block
1218
2033
  @args << args
1219
2034
  end
@@ -1327,9 +2142,9 @@ module RubyCurses
1327
2142
  # for other widgets, attempt to change focus to that field
1328
2143
  def bind_hotkey
1329
2144
  if !@mnemonic.nil?
1330
- ch = @mnemonic.downcase()[0] ## FIXME 1.9
2145
+ ch = @mnemonic.downcase()[0].ord ## FIXME 1.9 DONE
1331
2146
  # meta key
1332
- mch = ?\M-a + (ch - ?a)
2147
+ mch = ?\M-a.getbyte(0) + (ch - ?a.getbyte(0)) ## FIXME 1.9
1333
2148
  if @label_for.is_a? RubyCurses::Button and @label_for.respond_to? :fire
1334
2149
  @form.bind_key(mch, @label_for) { |_form, _butt| _butt.fire }
1335
2150
  else
@@ -1365,8 +2180,10 @@ module RubyCurses
1365
2180
  str = @justify.to_sym == :right ? "%*s" : "%-*s" # added 2008-12-22 19:05
1366
2181
  # loop added for labels that are wrapped.
1367
2182
  # TODO clear separately since value can change in status like labels
2183
+ $log.debug " RWID 1595 #{self.class} value: #{value} form: #{form} "
2184
+ @graphic = @form.window if @graphic.nil? ## HACK messagebox givig this in repaint, 423 not working ??
1368
2185
  0.upto(_height-1) { |i|
1369
- @form.window.printstring r+i, c, " " * len , acolor,@attr
2186
+ @graphic.printstring r+i, c, " " * len , acolor,@attr
1370
2187
  }
1371
2188
  lablist.each_with_index do |_value, ix|
1372
2189
  break if ix >= _height
@@ -1374,12 +2191,12 @@ module RubyCurses
1374
2191
  padding = (@display_length - _value.length)/2
1375
2192
  _value = " "*padding + _value + " "*padding # so its cleared if we change it midway
1376
2193
  end
1377
- @form.window.printstring r, c, str % [len, _value], acolor,@attr
2194
+ @graphic.printstring r, c, str % [len, _value], acolor,@attr
1378
2195
  r += 1
1379
2196
  end
1380
2197
  if !@mnemonic.nil?
1381
2198
  ulindex = value.index(@mnemonic) || value.index(@mnemonic.swapcase)
1382
- @form.window.mvchgat(y=firstrow, x=c+ulindex, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, acolor, nil)
2199
+ @graphic.mvchgat(y=firstrow, x=c+ulindex, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, acolor, nil)
1383
2200
  end
1384
2201
  #@form.window.mvchgat(y=r, x=c, max=len, Ncurses::A_NORMAL, color, nil)
1385
2202
  @repaint_required = false
@@ -1396,7 +2213,6 @@ module RubyCurses
1396
2213
  def initialize form, config={}, &block
1397
2214
  @focusable = true
1398
2215
  @editable = false
1399
- #@command_block = nil
1400
2216
  @handler={} # event handler
1401
2217
  @event_args ||= {}
1402
2218
  super
@@ -1404,8 +2220,6 @@ module RubyCurses
1404
2220
  @color ||= $datacolor
1405
2221
  @surround_chars ||= ['[ ', ' ]']
1406
2222
  @col_offset = @surround_chars[0].length
1407
- #@text = @name if @text.nil?
1408
- #bind_hotkey # 2008-12-23 22:41 remarked
1409
2223
  end
1410
2224
  ##
1411
2225
  # set button based on Action
@@ -1433,13 +2247,15 @@ module RubyCurses
1433
2247
  end
1434
2248
  ##
1435
2249
  # FIXME this will not work in messageboxes since no form available
2250
+ # if already set mnemonic, then unbind_key, ??
2251
+
1436
2252
  def mnemonic char
1437
2253
  $log.error " #{self} COULD NOT SET MNEMONIC since form NIL" if @form.nil?
1438
2254
  return if @form.nil?
1439
2255
  @mnemonic = char
1440
- ch = char.downcase()[0] ## XXX 1.9
2256
+ ch = char.downcase()[0].ord ## XXX 1.9
1441
2257
  # meta key
1442
- mch = ?\M-a + (ch - ?a)
2258
+ mch = ?\M-a.getbyte(0) + (ch - ?a.getbyte(0))
1443
2259
  $log.debug " #{self} setting MNEMO to #{char} #{mch}"
1444
2260
  @form.bind_key(mch, self) { |_form, _butt| _butt.fire }
1445
2261
  end
@@ -1460,10 +2276,10 @@ module RubyCurses
1460
2276
  _value = @text || getvalue # hack for Togglebutton FIXME
1461
2277
  #_value = getvalue
1462
2278
  $log.debug " bind hot #{_value} #{@underline}"
1463
- ch = _value[@underline,1].downcase()[0] ## XXX 1.9
2279
+ ch = _value[@underline,1].downcase()[0].ord ## XXX 1.9 2009-10-05 18:55 TOTEST
1464
2280
  @mnemonic = _value[@underline,1]
1465
2281
  # meta key
1466
- mch = ?\M-a + (ch - ?a)
2282
+ mch = ?\M-a.getbyte(0) + (ch - ?a.getbyte(0))
1467
2283
  @form.bind_key(mch, self) { |_form, _butt| _butt.fire }
1468
2284
  end
1469
2285
  # 2009-01-17 01:48 removed so widgets can be called
@@ -1496,12 +2312,22 @@ module RubyCurses
1496
2312
  value = getvalue_for_paint
1497
2313
  #$log.debug("button repaint :#{self} r:#{r} c:#{c} col:#{color} bg #{bgcolor} v: #{value} ul #{@underline} mnem #{@mnemonic}")
1498
2314
  len = @display_length || value.length
1499
- @form.window.printstring r, c, "%-*s" % [len, value], color, @attr
2315
+ @graphic = @form.window if @graphic.nil? ## cell editor listbox hack XXX fix in correct place
2316
+ @graphic.printstring r, c, "%-*s" % [len, value], color, @attr
1500
2317
  # @form.window.mvchgat(y=r, x=c, max=len, Ncurses::A_NORMAL, bgcolor, nil)
1501
2318
  # in toggle buttons the underline can change as the text toggles
1502
2319
  if !@underline.nil? or !@mnemonic.nil?
1503
2320
  uline = @underline && (@underline + @text_offset) || value.index(@mnemonic) || value.index(@mnemonic.swapcase)
1504
- @form.window.mvchgat(y=r, x=c+uline, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, color, nil)
2321
+ $log.debug " mvchgat UNDERLI r= #{r} - #{@graphic.top} c #{c} c+x #{c+uline}- #{@graphic.left} #{@graphic} "
2322
+ #$log.debug " XXX HACK in next line related to UNDERLINES -graphic.top"
2323
+ y=r #-@graphic.top
2324
+ x=c+uline #-@graphic.left
2325
+ if @graphic.window_type == :PAD
2326
+ x -= @graphic.left
2327
+ y -= @graphic.top
2328
+ end
2329
+ raise "button underline location error #{x} , #{y} " if x < 0 or c < 0
2330
+ @graphic.mvchgat(y, x, max=1, Ncurses::A_BOLD|Ncurses::A_UNDERLINE, color, nil)
1505
2331
  end
1506
2332
  end
1507
2333
  ## command of button (invoked on press, hotkey, space)
@@ -1519,11 +2345,9 @@ module RubyCurses
1519
2345
  def handle_key ch
1520
2346
  case ch
1521
2347
  when KEY_LEFT, KEY_UP
1522
- $log.debug " from 2009-01-16 18:18 buttons return UNHANDLED on UP DOWN LEFT RIGHT"
1523
2348
  return :UNHANDLED
1524
2349
  # @form.select_prev_field
1525
2350
  when KEY_RIGHT, KEY_DOWN
1526
- $log.debug " from 2009-01-16 18:18 buttons return UNHANDLED on UP DOWN LEFT RIGHT"
1527
2351
  return :UNHANDLED
1528
2352
  # @form.select_next_field
1529
2353
  when KEY_ENTER, 10, 13, 32 # added space bar also