rbcurse 1.4.0 → 1.4.1.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (142) hide show
  1. data/NOTES +68 -0
  2. data/README.markdown +73 -330
  3. data/TODO2.txt +2 -2
  4. data/VERSION +1 -1
  5. data/examples/abasiclist.rb +8 -2
  6. data/examples/alpmenu.rb +1 -1
  7. data/examples/app.rb +1 -24
  8. data/examples/appdirtree.rb +1 -1
  9. data/examples/appemail.rb +8 -14
  10. data/examples/appemaillb.rb +2 -2
  11. data/examples/appgcompose.rb +7 -5
  12. data/examples/common/file.rb +40 -0
  13. data/examples/{rmail.rb → common/rmail.rb} +0 -0
  14. data/examples/data/README.markdown +9 -0
  15. data/examples/data/brew.txt +38 -0
  16. data/examples/data/color.2 +37 -0
  17. data/examples/data/gemlist.txt +60 -0
  18. data/examples/data/lotr.txt +12 -0
  19. data/examples/data/ports.txt +136 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/{todocsv.csv → data/todocsv.csv} +0 -0
  22. data/examples/data/unix1.txt +21 -0
  23. data/examples/data/unix2.txt +11 -0
  24. data/examples/dbdemo.rb +49 -14
  25. data/examples/{appgmail.rb → deprecated/appgmail.rb} +1 -1
  26. data/examples/{splitp.rb → deprecated/splitp.rb} +0 -0
  27. data/examples/{testscrolllb.rb → deprecated/testscrolllb.rb} +0 -0
  28. data/examples/{testscrollp.rb → deprecated/testscrollp.rb} +0 -0
  29. data/examples/{testscrollta.rb → deprecated/testscrollta.rb} +0 -0
  30. data/examples/{testscrolltable.rb → deprecated/testscrolltable.rb} +0 -0
  31. data/examples/{testsplit.rb → deprecated/testsplit.rb} +0 -0
  32. data/examples/{testsplit2.rb → deprecated/testsplit2.rb} +0 -0
  33. data/examples/{testsplit3.rb → deprecated/testsplit3.rb} +0 -0
  34. data/examples/{testsplit3_1.rb → deprecated/testsplit3_1.rb} +0 -0
  35. data/examples/{testsplit3a.rb → deprecated/testsplit3a.rb} +0 -0
  36. data/examples/{testsplit3b.rb → deprecated/testsplit3b.rb} +0 -0
  37. data/examples/{testsplitta.rb → deprecated/testsplitta.rb} +0 -0
  38. data/examples/{testsplittv.rb → deprecated/testsplittv.rb} +0 -0
  39. data/examples/{testsplittvv.rb → deprecated/testsplittvv.rb} +0 -0
  40. data/examples/{testtpane.rb → deprecated/testtpane.rb} +0 -0
  41. data/examples/{testtpane2.rb → deprecated/testtpane2.rb} +0 -0
  42. data/examples/{testtpanetable.rb → deprecated/testtpanetable.rb} +0 -0
  43. data/examples/dirtree.rb +17 -7
  44. data/examples/experimental/resultsetdemo.rb +280 -0
  45. data/examples/experimental/testmform.rb +35 -0
  46. data/examples/{testscroller.rb → experimental/testscroller.rb} +1 -19
  47. data/examples/experimental/teststackflow.rb +111 -0
  48. data/examples/menu1.rb +1 -0
  49. data/examples/multispl.rb +4 -4
  50. data/examples/newmessagebox.rb +130 -0
  51. data/examples/newtabbedwindow.rb +100 -0
  52. data/examples/newtesttabp.rb +0 -3
  53. data/examples/qdfilechooser.rb +0 -3
  54. data/examples/rfe.rb +134 -18
  55. data/examples/rfe_renderer.rb +48 -2
  56. data/examples/sqlc.rb +2 -1
  57. data/examples/sqlm.rb +0 -2
  58. data/examples/table1.rb +1 -7
  59. data/examples/term2.rb +4 -1
  60. data/examples/test1.rb +0 -5
  61. data/examples/test2.rb +42 -31
  62. data/examples/testapp2.rb +8 -1
  63. data/examples/testchars.rb +0 -4
  64. data/examples/testcombo.rb +5 -9
  65. data/examples/testkeypress.rb +0 -3
  66. data/examples/testmenu.rb +0 -4
  67. data/examples/testmulticomp.rb +3 -5
  68. data/examples/testmulticontainer.rb +94 -0
  69. data/examples/testtable.rb +0 -5
  70. data/examples/testtabp.rb +16 -18
  71. data/examples/testtodo.rb +1 -5
  72. data/examples/testwsshortcuts.rb +64 -0
  73. data/examples/testwsshortcuts2.rb +126 -0
  74. data/examples/viewtodo.rb +1 -4
  75. data/lib/rbcurse.rb +1 -1
  76. data/lib/rbcurse/app.rb +92 -69
  77. data/lib/rbcurse/applicationheader.rb +46 -6
  78. data/lib/rbcurse/celleditor.rb +1 -9
  79. data/lib/rbcurse/comboboxcellrenderer.rb +0 -4
  80. data/lib/rbcurse/common/ansiparser.rb +117 -0
  81. data/{examples → lib/rbcurse/common}/appmethods.rb +25 -0
  82. data/lib/rbcurse/common/basestack.rb +407 -0
  83. data/lib/rbcurse/common/bordertitle.rb +41 -0
  84. data/lib/rbcurse/common/chunk.rb +177 -0
  85. data/lib/rbcurse/common/colorparser.rb +71 -0
  86. data/lib/rbcurse/common/keydefs.rb +30 -0
  87. data/lib/rbcurse/common/widgetshortcuts.rb +302 -0
  88. data/lib/rbcurse/deprecated/README.markdown +12 -0
  89. data/lib/rbcurse/{rscrollpane.rb → deprecated/rscrollpane.rb} +0 -0
  90. data/lib/rbcurse/{rsplitpane.rb → deprecated/rsplitpane.rb} +0 -0
  91. data/lib/rbcurse/{rsplitpane2.rb → deprecated/rsplitpane2.rb} +0 -0
  92. data/lib/rbcurse/{rviewport.rb → deprecated/rviewport.rb} +0 -0
  93. data/lib/rbcurse/experimental/README.markdown +14 -0
  94. data/lib/rbcurse/experimental/resultsettextview.rb +585 -0
  95. data/lib/rbcurse/experimental/stackflow.rb +478 -0
  96. data/lib/rbcurse/extras/bottomline.rb +85 -16
  97. data/lib/rbcurse/extras/box.rb +58 -0
  98. data/lib/rbcurse/extras/directorylist.rb +1 -1
  99. data/lib/rbcurse/extras/horizlist.rb +203 -0
  100. data/lib/rbcurse/extras/listselectable.rb +8 -0
  101. data/lib/rbcurse/extras/multiform.rb +330 -0
  102. data/lib/rbcurse/extras/multilinelabel.rb +142 -0
  103. data/lib/rbcurse/extras/newmessagebox.rb +328 -0
  104. data/lib/rbcurse/extras/newtabbedpane.rb +612 -0
  105. data/lib/rbcurse/extras/newtabbedwindow.rb +68 -0
  106. data/lib/rbcurse/extras/padreader.rb +189 -0
  107. data/lib/rbcurse/extras/rcomboedit.rb +256 -0
  108. data/lib/rbcurse/extras/resultsetbrowser.rb +281 -0
  109. data/lib/rbcurse/extras/statusline.rb +44 -6
  110. data/lib/rbcurse/extras/tabularwidget.rb +141 -104
  111. data/lib/rbcurse/extras/textpad.rb +516 -0
  112. data/lib/rbcurse/extras/viewer.rb +2 -0
  113. data/lib/rbcurse/io.rb +120 -3
  114. data/lib/rbcurse/listcellrenderer.rb +2 -1
  115. data/lib/rbcurse/listkeys.rb +1 -1
  116. data/lib/rbcurse/listscrollable.rb +72 -7
  117. data/lib/rbcurse/rbasiclistbox.rb +64 -12
  118. data/lib/rbcurse/rchangeevent.rb +0 -1
  119. data/lib/rbcurse/rcombo.rb +54 -59
  120. data/lib/rbcurse/rcommandwindow.rb +46 -10
  121. data/lib/rbcurse/rcontainer.rb +415 -0
  122. data/lib/rbcurse/rdialogs.rb +242 -165
  123. data/lib/rbcurse/rinputdataevent.rb +0 -1
  124. data/lib/rbcurse/rlistbox.rb +19 -9
  125. data/lib/rbcurse/rmessagebox.rb +1 -5
  126. data/lib/rbcurse/rmulticontainer.rb +64 -61
  127. data/lib/rbcurse/rmultitextview.rb +0 -2
  128. data/lib/rbcurse/rpopupmenu.rb +0 -2
  129. data/lib/rbcurse/rscrollform.rb +61 -12
  130. data/lib/rbcurse/rtabbedpane.rb +89 -73
  131. data/lib/rbcurse/rtabbedwindow.rb +53 -211
  132. data/lib/rbcurse/rtable.rb +0 -2
  133. data/lib/rbcurse/rtextarea.rb +14 -14
  134. data/lib/rbcurse/rtextview.rb +165 -50
  135. data/lib/rbcurse/rvimsplit.rb +15 -12
  136. data/lib/rbcurse/rwidget.rb +404 -150
  137. data/lib/rbcurse/table/tablecellrenderer.rb +1 -4
  138. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -3
  139. data/lib/ver/ncurses.rb +1 -9
  140. data/lib/ver/rpad.rb +375 -0
  141. data/lib/ver/window.rb +262 -440
  142. metadata +73 -31
@@ -0,0 +1,328 @@
1
+ # --------------------------------------------------------------------------------- #
2
+ # File: newmessagebox.rb
3
+ # Description: This is a cleaner attempt at messagebox on the lines of
4
+ # the new tabbedpane and window.
5
+ # Author: rkumar http://github.com/rkumar/rbcurse/
6
+ # Date: 03.11.11 - 22:15
7
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
8
+ # Last update: 03.11.11 - 23:47
9
+ # == CHANGES
10
+ # == TODO
11
+ # _ <ENTER> should result in OK being pressed if its default, ESC should result in cancel esp 2 time
12
+ # ensure that text and message are used in isolation and not with other things
13
+ #
14
+ # _ determine window size, but we are doing instance eval later.
15
+ #
16
+ # _ maybe have shortcuts for some widgets field, buttons, text and label and
17
+ # share with app and otherszz
18
+ #
19
+ # _ stack, flow and grid
20
+ #
21
+ # --------------------------------------------------------------------------------- #
22
+
23
+ require 'rbcurse'
24
+ require 'rbcurse/common/bordertitle'
25
+
26
+ include RubyCurses
27
+ class NewMessagebox
28
+ include BorderTitle
29
+ include RubyCurses::Utils
30
+
31
+ attr_reader :form
32
+ attr_reader :window
33
+ dsl_accessor :button_type
34
+ #
35
+ # a message to be printed, usually this will be the only thing supplied
36
+ # with an OK button. This should be a short string, a label will be used
37
+ # and input_config passed to it
38
+
39
+ #dsl_accessor :message
40
+ # you can also set button_orientation : :right, :left, :center
41
+ #
42
+ def initialize config={}, &block
43
+
44
+ h = config.fetch(:height, nil)
45
+ w = config.fetch(:width, nil)
46
+ t = config.fetch(:row, nil)
47
+ l = config.fetch(:col, nil)
48
+ if h && w && t && l
49
+ @window = VER::Window.new :height => h, :width => w, :top => t, :left => l
50
+ @graphic = @window
51
+ end
52
+ @form = Form.new @window
53
+
54
+ config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
55
+ @config = config
56
+ @row = 0
57
+ @col = 0
58
+ @row_offset = 1
59
+ @col_offset = 2
60
+ #bordertitle_init
61
+ @color ||= :black
62
+ @bgcolor ||= :white
63
+
64
+ instance_eval &block if block_given?
65
+
66
+ end
67
+ def item widget
68
+ # remove from existing form if set
69
+ widget.set_form @form
70
+ widget.row ||= 0
71
+ widget.col ||= 0
72
+ # i don't know button_offset as yet
73
+ widget.row += @row_offset
74
+ widget.col += @col_offset
75
+ # in most cases this override is okay, but what if user has set it
76
+ # The problem is that widget and field are doing a default setting so i don't know
77
+ # if user has set or widget has done a default setting. NOTE
78
+ widget.color @color # we are overriding colors, how to avoid since widget sets it
79
+ widget.bgcolor @bgcolor
80
+ widget.attr = @attr if @attr # we are overriding what user has put. DARN !
81
+ @maxrow ||= 0
82
+ @maxrow = widget.row if widget.row > @maxrow
83
+ @suggested_h = @height || @maxrow+6
84
+ end
85
+ alias :add :item
86
+ # returns button index
87
+ # Call this after instantiating the window
88
+ def run
89
+ repaint
90
+ @form.repaint
91
+ @window.wrefresh
92
+ return handle_keys
93
+ end
94
+ def repaint
95
+ _create_window unless @window
96
+ acolor = get_color $reverscolor # this implicitly uses color and bgcolor fooling me often
97
+ $log.debug " MESSAGE BOX bg:#{@bgcolor} , co:#{@color} , colorpair:#{acolor}"
98
+ @window.wbkgd(Ncurses.COLOR_PAIR(acolor)); # does not work on xterm-256color
99
+
100
+ #print_borders unless @suppress_borders # do this once only, unless everything changes
101
+ #@window.print_border_mb 1,2, @height, @width, $normalcolor, FFI::NCurses::A_REVERSE
102
+ @color_pair = get_color($datacolor)
103
+ bordercolor = @border_color || @color_pair
104
+ borderatt = @border_attrib || Ncurses::A_NORMAL
105
+ @window.wattron(Ncurses.COLOR_PAIR(bordercolor) | (borderatt || FFI::NCurses::A_NORMAL))
106
+ @window.print_border_mb 1,2, @height, @width, bordercolor, borderatt
107
+ @window.wattroff(Ncurses.COLOR_PAIR(bordercolor) | (borderatt || FFI::NCurses::A_NORMAL))
108
+ title = " "+@title+" "
109
+ @window.printstring(@row=1,@col=(@width-title.length)/2,title, color=$normalcolor)
110
+ #print_message if @message
111
+ create_action_buttons unless @action_buttons
112
+ end
113
+ # Pass a short message to be printed.
114
+ # This creates a label for a short message, and a field for a long one.
115
+ # @yield field created
116
+ # @param [String] text to display
117
+ def message message # yield label or field being used for display for further customization
118
+ @suggested_h = @height || 10
119
+ @suggested_w = @width || 60
120
+ message_col = 5
121
+ r = 3
122
+ len = message.length
123
+ @suggested_w = len + 8 + message_col if len < @suggested_w - 8 - message_col
124
+
125
+ display_length = @suggested_w-8
126
+ display_length -= message_col
127
+ message_height = 2
128
+ clr = @color || :white
129
+ bgclr = @bgcolor || :black
130
+
131
+ # trying this out. sometimes very long labels get truncated, so i give a field in wchich user
132
+ # can use arrow key or C-a and C-e
133
+ if message.size > display_length
134
+ message_label = RubyCurses::Field.new @form, {:text => message, :name=>"message_label",
135
+ :row => r, :col => message_col, :display_length => display_length,
136
+ :bgcolor => bgclr , :color => clr, :editable => false}
137
+ else
138
+ message_label = RubyCurses::Label.new @form, {:text => message, :name=>"message_label",
139
+ :row => r, :col => message_col, :display_length => display_length,
140
+ :height => message_height, :bgcolor => bgclr , :color => clr}
141
+ end
142
+ yield message_label if block_given?
143
+ end
144
+
145
+ # This is for larger messages, or messages where the size is not known.
146
+ # A textview object is created and yielded.
147
+ #
148
+ def text message
149
+ @suggested_w = @width || (FFI::NCurses.COLS * 0.70).floor
150
+ @suggested_h = @height || (FFI::NCurses.LINES * 0.70).floor
151
+
152
+ message_col = 3
153
+ r = 2
154
+ display_length = @suggested_w-4
155
+ display_length -= message_col
156
+ clr = @color || :white
157
+ bgclr = @bgcolor || :black
158
+
159
+ if message.is_a? Array
160
+ # reduce width and height if you can based on array contents
161
+ else
162
+ message = wrap_text(message, display_length).split("\n")
163
+ end
164
+ message_height = message.size
165
+ # reduce if possible if its not required.
166
+ #
167
+ r1 = (FFI::NCurses.LINES-@suggested_h)/2
168
+ r1 = r1.floor
169
+ w = @suggested_w
170
+ c1 = (FFI::NCurses.COLS-w)/2
171
+ c1 = c1.floor
172
+ @suggested_row = r1
173
+ @suggested_col = c1
174
+ brow = @button_row || @suggested_h-4
175
+ available_ht = brow - r + 1
176
+ message_height = [message_height, available_ht].min
177
+ require 'rbcurse/rtextview'
178
+ message_label = RubyCurses::TextView.new @form, {:name=>"message_label", :text => message,
179
+ :row => r, :col => message_col, :width => display_length, :suppress_borders => true,
180
+ :height => message_height, :bgcolor => bgclr , :color => clr}
181
+ #message_label.set_content message
182
+ yield message_label if block_given?
183
+
184
+ end
185
+ # returns button index
186
+ private
187
+ def handle_keys
188
+ buttonindex = catch(:close) do
189
+ while((ch = @window.getchar()) != FFI::NCurses::KEY_F10 )
190
+ break if ch == ?\C-q.getbyte(0)
191
+ begin
192
+ @form.handle_key(ch)
193
+ @window.wrefresh
194
+ rescue => err
195
+ $log.debug( err) if err
196
+ $log.debug(err.backtrace.join("\n")) if err
197
+ alert "Got an exception in NewMessagebox: #{err}. Check log"
198
+ $error_message.value = ""
199
+ ensure
200
+ end
201
+
202
+ end # while loop
203
+ end # close
204
+ $log.debug "XXX: CALLER GOT #{buttonindex} "
205
+ @window.destroy
206
+ return buttonindex
207
+ end
208
+ private
209
+ def create_action_buttons
210
+ return unless @button_type
211
+ case @button_type.to_s.downcase
212
+ when "ok"
213
+ make_buttons ["&OK"]
214
+ when "ok_cancel" #, "input", "list", "field_list"
215
+ make_buttons %w[&OK &Cancel]
216
+ when "ok_apply_cancel" #, "input", "list", "field_list"
217
+ make_buttons %w[&OK &Apply &Cancel]
218
+ when "yes_no"
219
+ make_buttons %w[&Yes &No]
220
+ when "yes_no_cancel"
221
+ make_buttons ["&Yes", "&No", "&Cancel"]
222
+ when "custom"
223
+ raise "Blank list of buttons passed to custom" if @buttons.nil? or @buttons.size == 0
224
+ make_buttons @buttons
225
+ else
226
+ $log.warn "No buttontype passed for creating tabbedpane. Not creating any"
227
+ #make_buttons ["&OK"]
228
+ end
229
+ end
230
+ private
231
+ def make_buttons names
232
+ @action_buttons = []
233
+ $log.debug "XXX: came to NTP make buttons FORM= #{@form.name} names #{names} "
234
+ total = names.inject(0) {|total, item| total + item.length + 4}
235
+ bcol = align_buttons total, @button_orientation
236
+
237
+ # this craps out when height is zero
238
+ brow = @row + @height-4
239
+ brow = FFI::NCurses.LINES-2 if brow < 0
240
+ @button_row = brow
241
+ #color_pair = get_color($normalcolor)
242
+ #@window.wattron(Ncurses.COLOR_PAIR(color_pair) | (@attrib || FFI::NCurses::A_NORMAL))
243
+ #@window.mvwhline( brow-1, @col+1, Ncurses::ACS_HLINE, @width-2)
244
+ #@window.wattroff(Ncurses.COLOR_PAIR(color_pair) | (@attrib || FFI::NCurses::A_NORMAL))
245
+ $log.debug "XXX: putting buttons :on #{brow} , #{bcol} : #{@row} , #{@height} "
246
+ button_ct=0
247
+ tpp = self
248
+ _color = @color
249
+ _bgcolor = @bgcolor
250
+ names.each_with_index do |bname, ix|
251
+ text = bname
252
+ #underline = @underlines[ix] if !@underlines.nil?
253
+
254
+ button = Button.new @form do
255
+ text text
256
+ name bname
257
+ row brow
258
+ col bcol
259
+ #underline underline
260
+ highlight_background $reversecolor
261
+ color _color
262
+ bgcolor _bgcolor
263
+ end
264
+ @action_buttons << button
265
+ button.form = @form
266
+ button.override_graphic @graphic
267
+ index = button_ct
268
+ tpp = self
269
+ button.command { |form| @selected_index = index; @stop = true;
270
+ # ActionEvent has source event and action_command
271
+ action = ActionEvent.new(tpp, index, button.text)
272
+ if @command
273
+ @command.call(action, @args)
274
+ else
275
+ # default action if you don't specify anything
276
+ throw(:close, @selected_index)
277
+ end
278
+ }
279
+ button_ct += 1
280
+ bcol += text.length+6
281
+ end
282
+ end
283
+ def _create_window
284
+
285
+ @width = @suggested_w || 60
286
+ @height = @suggested_h || 10
287
+ if @suggested_row
288
+ @row = @suggested_row
289
+ else
290
+ @row = ((FFI::NCurses.LINES-@height)/2).floor
291
+ end
292
+ if @suggested_col
293
+ @col = @suggested_col
294
+ else
295
+ w = @width
296
+ @col = ((FFI::NCurses.COLS-w)/2).floor
297
+ end
298
+ @window = VER::Window.new :height => @height, :width => @width, :top => @row, :left => @col
299
+ @graphic = @window
300
+ @form.window = @window
301
+ end
302
+ #
303
+ # specify a code block to be fired when an action button is pressed
304
+ # This will supply (to the code block) an ActionEvent followed by
305
+ # whatever args that were given.
306
+ # ActionEvent contains source, event, action_command which map to
307
+ # the messagebox, selected_index (base 0) and button title.
308
+ #
309
+ public
310
+ def command *args, &blk
311
+ @command = blk
312
+ @args = args
313
+ end
314
+
315
+ # returns starting column for buttons to start painting
316
+ # Not very correct in case of :right
317
+ private
318
+ def align_buttons textlen, orient=:center
319
+ case orient
320
+ when :left
321
+ return @col+@col_offset
322
+ when :right
323
+ return (@width-textlen)-5
324
+ else
325
+ return (@width-textlen)/2
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,612 @@
1
+ =begin
2
+ * Name: newtabbedpane.rb
3
+ * Description : This is radically simplified tabbedpane. The earlier version
4
+ was too complex with multiple forms and pads, and a scrollform. This uses
5
+ the external form and has some very simple programming to handle the whole thing.
6
+ * Author: rkumar (http://github.com/rkumar/rbcurse/)
7
+ * Date: 2011-10-20
8
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
9
+ * Last update: Sat Oct 22 18:40:49 IST 2011
10
+
11
+ == CHANGES
12
+ == TODO
13
+ on start first buttons bottom should not be lined.
14
+ Alt-1-9 to goto tabs
15
+ add remove tabs at any time - started, untested
16
+ events for tab add/remove/etc
17
+ =end
18
+ require 'rbcurse'
19
+ ##
20
+ module RubyCurses
21
+ class NewTabbedPane < Widget
22
+ dsl_property :title, :title_attrib
23
+ # what kind of buttons, if this is a window, :ok :ok_camcel :ok_apply_cancel
24
+ dsl_accessor :button_type
25
+ attr_reader :button_row
26
+ # index of tab that is currently open
27
+ attr_reader :current_tab
28
+
29
+
30
+ def initialize form=nil, config={}, &block
31
+ @_events ||= []
32
+ @_events.push(:PRESS)
33
+ @button_gap = 2
34
+ init_vars
35
+ super
36
+ @focusable = true
37
+ @editable = true
38
+ @col_offset = 2
39
+ raise ArgumentError, "NewTabbedPane : row or col not set: r: #{@row} c: #{@col} " unless @row && @col
40
+ end
41
+
42
+ # Add a tab
43
+ def tab title, config={}, &block
44
+ #@tab_components[title]=[]
45
+ #@tabs << Tab.new(title, self, config, &block)
46
+ insert_tab @tabs.count, title, config, &block
47
+ self
48
+ end
49
+ alias :add_tab :tab
50
+
51
+ # a shortcut for binding a command to a press of an action button
52
+ # The block will be passed
53
+ # This is only relevant if you have asked for buttons to be created, which is
54
+ # only relevant in a TabbedWindow
55
+ # ActionEvent has source event and action_command
56
+ def command *args, &block
57
+ bind :PRESS, *args, &block
58
+ end
59
+
60
+ # -------------- tab maintenance commands ------------------ #
61
+
62
+ def insert_tab index, title, config={}, &block
63
+ @tabs.insert(index, Tab.new(title, self, config, &block) )
64
+ end
65
+ def remove_tab tab
66
+ @tabs.delete tab
67
+ self
68
+ end
69
+ def remove_all
70
+ @tabs = []
71
+ self
72
+ end
73
+ def remove_tab_at index = @current_tab
74
+ @tabs.delete_at index
75
+ end
76
+
77
+ def repaint
78
+ @current_tab ||= 0
79
+ @button_row ||= @row + 2
80
+ @separator_row = @button_row + 1 # hope we have it by now, where to print separator
81
+ @separator_row0 = @button_row - 1 unless @button_row == @row + 1
82
+ @separator_row2 = @row + @height - 3 # hope we have it by now, where to print separator
83
+ #return unless @repaint_required
84
+ if @buttons.empty?
85
+ _create_buttons
86
+ @components = @buttons.dup
87
+ @components.push(*@tabs[@current_tab].items)
88
+ create_action_buttons
89
+ @components.push(*@action_buttons)
90
+ elsif @tab_changed
91
+ @components = @buttons.dup
92
+ @components.push(*@tabs[@current_tab].items)
93
+ @components.push(*@action_buttons)
94
+ @tab_changed = false
95
+ end
96
+ # if some major change has happened then repaint everything
97
+ if @repaint_required
98
+ $log.debug " NEWTAB repaint graphic #{@graphic} "
99
+ print_borders unless @suppress_borders # do this once only, unless everything changes
100
+ print_separator1
101
+ @components.each { |e| e.repaint_all(true); e.repaint }
102
+ else
103
+ @components.each { |e| e.repaint }
104
+ end # if repaint_required
105
+ print_border if (@suppress_borders == false && @repaint_all) # do this once only, unless everything changes
106
+ @repaint_required = false
107
+ end
108
+ def handle_key ch
109
+ $log.debug " NEWTABBED handle_key #{ch} "
110
+ return if @components.empty?
111
+ _multiplier = ($multiplier == 0 ? 1 : $multiplier )
112
+
113
+ # should this go here 2011-10-19
114
+ unless @_entered
115
+ $log.warn "WARN: calling ON_ENTER since in this situation it was not called"
116
+ on_enter
117
+ end
118
+ #if ch == KEY_TAB
119
+ #$log.debug "NEWTABBED GOTO NEXT"
120
+ #return goto_next_component
121
+ #elsif ch == KEY_BTAB
122
+ #return goto_prev_component
123
+ #end
124
+ comp = @current_component
125
+ $log.debug " NEWTABBED handle_key #{ch}: #{comp}"
126
+ if comp
127
+ ret = comp.handle_key(ch)
128
+ $log.debug " NEWTABBED handle_key#{ch}: #{comp} returned #{ret} "
129
+ if ret != :UNHANDLED
130
+ comp.repaint # NOTE: if we don;t do this, then it won't get repainted. I will have to repaint ALL
131
+ # in repaint of this.
132
+ return ret
133
+ end
134
+ $log.debug "XXX NEWTABBED key unhandled by comp #{comp.name} "
135
+ else
136
+ Ncurses.beep
137
+ $log.warn "XXX NEWTABBED key unhandled NULL comp"
138
+ end
139
+ case ch
140
+ when ?\C-c.getbyte(0)
141
+ $multiplier = 0
142
+ return 0
143
+ when ?0.getbyte(0)..?9.getbyte(0)
144
+ $log.debug " VIM coming here to set multiplier #{$multiplier} "
145
+ $multiplier *= 10 ; $multiplier += (ch-48)
146
+ return 0
147
+ end
148
+ ret = process_key ch, self
149
+ # allow user to map left and right if he wants
150
+ if ret == :UNHANDLED
151
+ case ch
152
+ when KEY_UP, KEY_BTAB
153
+ # form will pick this up and do needful
154
+ return goto_prev_component #unless on_first_component?
155
+ when KEY_LEFT
156
+ # if i don't check for first component, key will go back to form,
157
+ # but not be processes. so focussed remain here, but be false.
158
+ # In case of returnign an unhandled TAB, on_leave will happen and cursor will move to
159
+ # previous component outside of this.
160
+ return goto_prev_component unless on_first_component?
161
+ when KEY_RIGHT, KEY_TAB
162
+ return goto_next_component #unless on_last_component?
163
+ when KEY_DOWN
164
+ if on_a_button?
165
+ return goto_first_item
166
+ else
167
+ return goto_next_component #unless on_last_component?
168
+ end
169
+ else
170
+ #@_entered = false
171
+ return :UNHANDLED
172
+ end
173
+ end
174
+
175
+ $multiplier = 0
176
+ return 0
177
+ end
178
+ # on enter processing
179
+ # Very often the first may be a label !
180
+ def on_enter
181
+ # if BTAB, the last comp
182
+ if $current_key == KEY_BTAB
183
+ @current_component = @components.last
184
+ else
185
+ @current_component = @components.first
186
+ end
187
+ return unless @current_component
188
+ $log.debug " NEWTABBED came to ON_ENTER #{@current_component} "
189
+ set_form_row
190
+ @_entered = true
191
+ end
192
+ def on_leave
193
+ @_entered = false
194
+ super
195
+ end
196
+ # takes focus to first item (after buttons)
197
+ def goto_first_item
198
+ bc = @buttons.count
199
+ @components[bc..-1].each { |c|
200
+ if c.focusable
201
+ leave_current_component
202
+ @current_component = c
203
+ set_form_row
204
+ break
205
+ end
206
+ }
207
+ end
208
+ def goto_last_item
209
+ bc = @buttons.count
210
+ f = nil
211
+ @components[bc..-1].each { |c|
212
+ if c.focusable
213
+ f = c
214
+ end
215
+ }
216
+ if f
217
+ leave_current_component
218
+ @current_component = f
219
+ set_form_row
220
+ end
221
+ end
222
+ def goto_next_component
223
+ if @current_component != nil
224
+ leave_current_component
225
+ if on_last_component?
226
+ @_entered = false
227
+ return :UNHANDLED
228
+ end
229
+ @current_index = @components.index(@current_component)
230
+ index = @current_index + 1
231
+ index.upto(@components.length-1) do |i|
232
+ f = @components[i]
233
+ if f.focusable
234
+ @current_index = i
235
+ @current_component = f
236
+ return set_form_row
237
+ end
238
+ end
239
+ end
240
+ @_entered = false
241
+ return :UNHANDLED
242
+ end
243
+ def goto_prev_component
244
+ if @current_component != nil
245
+ leave_current_component
246
+ if on_first_component?
247
+ @_entered = false
248
+ return :UNHANDLED
249
+ end
250
+ @current_index = @components.index(@current_component)
251
+ index = @current_index -= 1
252
+ index.downto(0) do |i|
253
+ f = @components[i]
254
+ if f.focusable
255
+ @current_index = i
256
+ @current_component = f
257
+ return set_form_row
258
+ end
259
+ end
260
+ end
261
+ return :UNHANDLED
262
+ end
263
+ # private
264
+ def set_form_row
265
+ return :UNHANDLED if @current_component.nil?
266
+ $log.debug " NEWTABBED on enter sfr #{@current_component} "
267
+ @current_component.on_enter
268
+ @current_component.set_form_row # why was this missing in vimsplit. is it
269
+ # that on_enter does a set_form_row
270
+ @current_component.set_form_col # XXX
271
+ @current_component.repaint
272
+ # XXX compo should do set_form_row and col if it has that
273
+ end
274
+ # private
275
+ def set_form_col
276
+ return if @current_component.nil?
277
+ $log.debug " #{@name} NEWTABBED set_form_col calling sfc for #{@current_component.name} "
278
+ @current_component.set_form_col
279
+ end
280
+ # leave the component we are on.
281
+ # This should be followed by all containers, so that the on_leave action
282
+ # of earlier comp can be displayed, such as dimming components selections
283
+ def leave_current_component
284
+ @current_component.on_leave
285
+ # NOTE this is required, since repaint will just not happen otherwise
286
+ # Some components are erroneously repainting all, after setting this to true so it is
287
+ # working there.
288
+ @current_component.repaint_required true
289
+ $log.debug " after on_leave VIMS XXX #{@current_component.focussed} #{@current_component.name}"
290
+ @current_component.repaint
291
+ end
292
+
293
+ # is focus on first component
294
+ def on_first_component?
295
+ @current_component == @components.first
296
+ end
297
+ # is focus on last component
298
+ def on_last_component?
299
+ @current_component == @components.last
300
+ end
301
+ def on_a_button?
302
+ @components.index(@current_component) < @buttons.count
303
+ end
304
+ # set focus on given component
305
+ # Sometimes you have the handle to component, and you want to move focus to it
306
+ def goto_component comp
307
+ return if comp == @current_component
308
+ leave_current_component
309
+ @current_component = comp
310
+ set_form_row
311
+ end
312
+ def set_current_tab t
313
+ return if @current_tab == t
314
+ @current_tab = t
315
+ goto_component @components[t]
316
+ @tab_changed = true
317
+ @repaint_required = true
318
+ self
319
+ end
320
+
321
+ def DEPRECATED_handle_key ch # :nodoc
322
+ map_keys unless @keys_mapped
323
+ ret = process_key ch, self
324
+ @multiplier = 0
325
+ return :UNHANDLED if ret == :UNHANDLED
326
+ return 0
327
+ end
328
+
329
+ # Put all the housekeeping stuff at the end
330
+ private
331
+ def init_vars
332
+ @buttons = []
333
+ @tabs = []
334
+ #@tab_components = {}
335
+ @bottombuttons = []
336
+ #
337
+ # I'll keep current tabs comps in this to simplify
338
+ @components = []
339
+ @_entered = false
340
+ end
341
+
342
+ def map_keys
343
+ @keys_mapped = true
344
+ #bind_key(?q, :myproc)
345
+ #bind_key(32, :myproc)
346
+ end
347
+
348
+ def _create_buttons
349
+ $log.debug "XXX: INSIDE create_buttons col_offset #{@col_offset} "
350
+ v = Variable.new
351
+ r = @button_row # @row + 1
352
+ col = @col + @col_offset
353
+ @tabs.each_with_index { |t, i|
354
+ txt = t.text
355
+ @buttons << TabButton.new(nil) do
356
+ variable v
357
+ text txt
358
+ name txt
359
+ #value txt
360
+ row r
361
+ col col
362
+ surround_chars ['','']
363
+ selected_background 'green'
364
+ selected_foreground 'white'
365
+
366
+ end
367
+ b = @buttons.last
368
+ b.command do
369
+ set_current_tab i
370
+ end
371
+ b.form = @form
372
+ b.override_graphic @graphic
373
+ col += txt.length + @button_gap
374
+ }
375
+ end # _create_buttons
376
+ private
377
+ def print_borders
378
+ width = @width
379
+ height = @height-1
380
+ window = @graphic
381
+ startcol = @col
382
+ startrow = @row
383
+ @color_pair = get_color($datacolor)
384
+ $log.debug "NTP #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
385
+ window.print_border startrow, startcol, height, width, @color_pair, @attr
386
+ print_title
387
+ end
388
+ def print_separator1
389
+
390
+ width = @width
391
+ height = @height-1
392
+ window = @graphic
393
+ startcol = @col
394
+ startrow = @row
395
+ @color_pair = get_color($datacolor)
396
+ r = @separator_row
397
+ c = @col + @col_offset
398
+
399
+ urcorner = []
400
+ #window.printstring r, c, '-' * (@width-2), @color_pair, @attr
401
+ window.mvwhline( r, @col+1, Ncurses::ACS_HLINE, @width-2)
402
+ @buttons.each_with_index do |b, i|
403
+ l = b.text.length
404
+ if b.selected?
405
+ if false #c == @col + @col_offset
406
+ cc = c
407
+ ll = l+1
408
+ else
409
+ cc = c-1
410
+ ll = l+2
411
+ end
412
+ #rr = r -1 #unless rr == @col
413
+ window.printstring r, cc, " "*ll, @color_pair, @attr
414
+ #window.printstring r, cc-1, FFI::NCurses::ACS_HLINE.chr*ll, @color_pair, @attr
415
+ #window.printstring r-2, cc, FFI::NCurses::ACS_HLINE.chr*ll, @color_pair, @attr
416
+ window.mvwaddch r, cc-1, FFI::NCurses::ACS_LRCORNER unless cc-1 <= @col
417
+ window.mvwaddch r, c+l+1, FFI::NCurses::ACS_LLCORNER
418
+ else
419
+ window.mvwaddch r, c+l+1, FFI::NCurses::ACS_BTEE
420
+ end
421
+
422
+ #window.printstring r-2, c, FFI::NCurses::ACS_HLINE*l, @color_pair, @attr
423
+ #tt = b.text
424
+ #window.printstring r, c, tt, @color_pair, @attr
425
+ c += l + 1
426
+ #window.mvwaddch r, c, '+'.ord
427
+ window.mvwaddch r-1, c, FFI::NCurses::ACS_VLINE
428
+ window.mvwaddch r-2, c, FFI::NCurses::ACS_URCORNER #ACS_TTEE
429
+ #window.mvwaddch r-2, c+1, '/'.ord
430
+ urcorner << c
431
+ c+=@button_gap
432
+ end
433
+ window.mvwhline( @separator_row0, @col + 1, Ncurses::ACS_HLINE, c-@col-1-@button_gap)
434
+ window.mvwhline( @separator_row2, @col + 1, Ncurses::ACS_HLINE, @width-2)
435
+ urcorner.each do |c|
436
+ window.mvwaddch r-2, c, FFI::NCurses::ACS_URCORNER #ACS_TTEE
437
+ end
438
+ end
439
+ def print_title
440
+ return unless @title
441
+ _title = @title
442
+ if @title.length > @width - 2
443
+ _title = @title[0..@width-2]
444
+ end
445
+ @graphic.printstring( @row, @col+(@width-_title.length)/2, _title,
446
+ @color_pair, @title_attrib) unless @title.nil?
447
+ end
448
+
449
+ #
450
+ # create the buttons at the bottom OK/ APPLY/ CANCEL
451
+
452
+ def create_action_buttons
453
+ return unless @button_type
454
+ case @button_type.to_s.downcase
455
+ when "ok"
456
+ make_buttons ["&OK"]
457
+ when "ok_cancel" #, "input", "list", "field_list"
458
+ make_buttons %w[&OK &Cancel]
459
+ when "ok_apply_cancel" #, "input", "list", "field_list"
460
+ make_buttons %w[&OK &Apply &Cancel]
461
+ when "yes_no"
462
+ make_buttons %w[&Yes &No]
463
+ when "yes_no_cancel"
464
+ make_buttons ["&Yes", "&No", "&Cancel"]
465
+ when "custom"
466
+ raise "Blank list of buttons passed to custom" if @buttons.nil? or @buttons.size == 0
467
+ make_buttons @buttons
468
+ else
469
+ $log.warn "No buttontype passed for creating tabbedpane. Not creating any"
470
+ #make_buttons ["&OK"]
471
+ end
472
+ end
473
+ def make_buttons names
474
+ @action_buttons = []
475
+ $log.debug "XXX: came to NTP make buttons FORM= #{@form.name} names #{names} "
476
+ total = names.inject(0) {|total, item| total + item.length + 4}
477
+ bcol = center_column total
478
+
479
+ # this craps out when height is zero
480
+ brow = @row + @height-2
481
+ brow = FFI::NCurses.LINES-2 if brow < 0
482
+ $log.debug "XXX: putting buttons :on #{brow} : #{@row} , #{@height} "
483
+ button_ct=0
484
+ tpp = self
485
+ names.each_with_index do |bname, ix|
486
+ text = bname
487
+ #underline = @underlines[ix] if !@underlines.nil?
488
+
489
+ button = Button.new nil do
490
+ text text
491
+ name bname
492
+ row brow
493
+ col bcol
494
+ #underline underline
495
+ highlight_background $reversecolor
496
+ color $datacolor
497
+ bgcolor $datacolor
498
+ end
499
+ @action_buttons << button
500
+ button.form = @form
501
+ button.override_graphic @graphic
502
+ index = button_ct
503
+ tpp = self
504
+ button.command { |form| @selected_index = index; @stop = true;
505
+ # ActionEvent has source event and action_command
506
+ fire_handler :PRESS, ActionEvent.new(tpp, index, button.text)
507
+ #throw(:close, @selected_index)
508
+ }
509
+ button_ct += 1
510
+ bcol += text.length+6
511
+ end
512
+ end
513
+ def center_column textlen
514
+ width = @col + @width
515
+ return (width-textlen)/2
516
+ end
517
+
518
+ ## ADD ABOVE
519
+ end # class
520
+
521
+ class Tab
522
+ attr_accessor :text
523
+ attr_reader :config
524
+ attr_reader :items
525
+ attr_accessor :parent_component
526
+ attr_accessor :index
527
+ attr_accessor :button # so you can set an event on it 2011-10-4
528
+ attr_accessor :row_offset
529
+ attr_accessor :col_offset
530
+ def initialize text, parent_component, aconfig={}, &block
531
+ @text = text
532
+ @items = []
533
+ @config = aconfig
534
+ @parent_component = parent_component
535
+ @row_offset ||= 2
536
+ @col_offset ||= 2
537
+ @config.each_pair { |k,v| variable_set(k,v) }
538
+ instance_eval &block if block_given?
539
+ end
540
+ def item widget
541
+ widget.form = @parent_component.form
542
+ widget.override_graphic @parent_component.form.window
543
+ # these will fail if TP put inside some other container. NOTE
544
+ widget.row ||= 0
545
+ widget.col ||= 0
546
+ # If we knew it was only widget we could expand it
547
+ if widget.kind_of?(Container) #|| widget.respond_to?(:width)
548
+ widget.width ||= @parent_component.width-3
549
+ end
550
+ # Darn ! this was setting Label to fully height
551
+ if widget.kind_of?(Container) #|| widget.respond_to?(:height)
552
+ widget.height ||= @parent_component.height-3
553
+ end
554
+ # i don't know button_offset as yet
555
+ widget.row += @row_offset + @parent_component.row + 1
556
+ widget.col += @col_offset + @parent_component.col
557
+ @items << widget
558
+ end
559
+ end # class tab
560
+ class TabButton < RadioButton
561
+ attr_accessor :display_tab_on_traversal
562
+ def getvalue_for_paint
563
+ @text
564
+ end
565
+ def selected?
566
+ @variable.value == @value
567
+ end
568
+
569
+ end
570
+
571
+ end # module
572
+ if __FILE__ == $PROGRAM_NAME
573
+ require 'rbcurse/app'
574
+ require 'rbcurse/rcontainer'
575
+ App.new do
576
+ #r = Container.new nil, :row => 1, :col => 2, :width => 40, :height => 10, :title => "A container"
577
+ r = Container.new nil, :suppress_borders => true
578
+ f1 = field "name", :maxlen => 20, :display_length => 20, :bgcolor => :white,
579
+ :color => :black, :text => "abc", :label => ' Name: '
580
+ f2 = field "email", :display_length => 20, :bgcolor => :white,
581
+ :color => :blue, :text => "me@google.com", :label => 'Email: '
582
+ f3 = radio :group => :grp, :text => "red", :value => "RED", :color => :red
583
+ f4 = radio :group => :grp, :text => "blue", :value => "BLUE", :color => :blue
584
+ f5 = radio :group => :grp, :text => "green", :value => "GREEN", :color => :green
585
+ r.add(f1)
586
+ r.add(f2)
587
+ r.add(f3,f4,f5)
588
+ NewTabbedPane.new @form, :row => 3, :col => 5, :width => 60, :height => 20 do
589
+ title "User Setup"
590
+ button_type :ok_apply_cancel
591
+ tab "&Profile" do
592
+ item Field.new nil, :row => 2, :col => 2, :text => "enter your name", :label => ' Name: '
593
+ item Field.new nil, :row => 3, :col => 2, :text => "enter your email", :label => 'Email: '
594
+ end
595
+ tab "&Settings" do
596
+ item CheckBox.new nil, :row => 2, :col => 2, :text => "Use HTTPS", :mnemonic => 'u'
597
+ item CheckBox.new nil, :row => 3, :col => 2, :text => "Quit with confirm", :mnemonic => 'q'
598
+ end
599
+ tab "&Term" do
600
+ radio = Variable.new
601
+ item RadioButton.new nil, :row => 2, :col => 2, :text => "&xterm", :value => "xterm", :variable => radio
602
+ item RadioButton.new nil, :row => 3, :col => 2, :text => "sc&reen", :value => "screen", :variable => radio
603
+ radio.update_command() {|rb| ENV['TERM']=rb.value }
604
+ end
605
+ tab "Conta&iner" do
606
+ item r
607
+ end
608
+
609
+ end
610
+ end # app
611
+
612
+ end