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
@@ -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