rbcurse-core 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. data/README.md +69 -0
  2. data/VERSION +1 -0
  3. data/examples/abasiclist.rb +151 -0
  4. data/examples/alpmenu.rb +46 -0
  5. data/examples/app.sample +17 -0
  6. data/examples/atree.rb +100 -0
  7. data/examples/common/file.rb +45 -0
  8. data/examples/data/README.markdown +9 -0
  9. data/examples/data/brew.txt +38 -0
  10. data/examples/data/color.2 +37 -0
  11. data/examples/data/gemlist.txt +60 -0
  12. data/examples/data/lotr.txt +12 -0
  13. data/examples/data/ports.txt +136 -0
  14. data/examples/data/table.txt +37 -0
  15. data/examples/data/tasks.csv +88 -0
  16. data/examples/data/tasks.txt +27 -0
  17. data/examples/data/todo.txt +10 -0
  18. data/examples/data/todocsv.csv +28 -0
  19. data/examples/data/unix1.txt +21 -0
  20. data/examples/data/unix2.txt +11 -0
  21. data/examples/dbdemo.rb +487 -0
  22. data/examples/dirtree.rb +90 -0
  23. data/examples/newtabbedwindow.rb +100 -0
  24. data/examples/newtesttabp.rb +92 -0
  25. data/examples/tabular.rb +132 -0
  26. data/examples/tasks.rb +167 -0
  27. data/examples/term2.rb +83 -0
  28. data/examples/testkeypress.rb +72 -0
  29. data/examples/testlistbox.rb +158 -0
  30. data/examples/testmessagebox.rb +140 -0
  31. data/examples/testree.rb +106 -0
  32. data/examples/testwsshortcuts.rb +66 -0
  33. data/examples/testwsshortcuts2.rb +127 -0
  34. data/lib/rbcurse.rb +8 -0
  35. data/lib/rbcurse/core/docs/index.txt +73 -0
  36. data/lib/rbcurse/core/include/action.rb +40 -0
  37. data/lib/rbcurse/core/include/appmethods.rb +112 -0
  38. data/lib/rbcurse/core/include/bordertitle.rb +41 -0
  39. data/lib/rbcurse/core/include/chunk.rb +182 -0
  40. data/lib/rbcurse/core/include/io.rb +953 -0
  41. data/lib/rbcurse/core/include/listcellrenderer.rb +140 -0
  42. data/lib/rbcurse/core/include/listeditable.rb +317 -0
  43. data/lib/rbcurse/core/include/listscrollable.rb +590 -0
  44. data/lib/rbcurse/core/include/listselectable.rb +264 -0
  45. data/lib/rbcurse/core/include/multibuffer.rb +83 -0
  46. data/lib/rbcurse/core/include/orderedhash.rb +77 -0
  47. data/lib/rbcurse/core/include/ractionevent.rb +67 -0
  48. data/lib/rbcurse/core/include/rchangeevent.rb +27 -0
  49. data/lib/rbcurse/core/include/rhistory.rb +62 -0
  50. data/lib/rbcurse/core/include/rinputdataevent.rb +47 -0
  51. data/lib/rbcurse/core/include/vieditable.rb +170 -0
  52. data/lib/rbcurse/core/system/colormap.rb +163 -0
  53. data/lib/rbcurse/core/system/keyboard.rb +150 -0
  54. data/lib/rbcurse/core/system/keydefs.rb +30 -0
  55. data/lib/rbcurse/core/system/ncurses.rb +218 -0
  56. data/lib/rbcurse/core/system/panel.rb +162 -0
  57. data/lib/rbcurse/core/system/window.rb +901 -0
  58. data/lib/rbcurse/core/util/ansiparser.rb +117 -0
  59. data/lib/rbcurse/core/util/app.rb +1235 -0
  60. data/lib/rbcurse/core/util/basestack.rb +407 -0
  61. data/lib/rbcurse/core/util/bottomline.rb +1850 -0
  62. data/lib/rbcurse/core/util/colorparser.rb +71 -0
  63. data/lib/rbcurse/core/util/focusmanager.rb +31 -0
  64. data/lib/rbcurse/core/util/padreader.rb +189 -0
  65. data/lib/rbcurse/core/util/rcommandwindow.rb +587 -0
  66. data/lib/rbcurse/core/util/rdialogs.rb +619 -0
  67. data/lib/rbcurse/core/util/viewer.rb +149 -0
  68. data/lib/rbcurse/core/util/widgetshortcuts.rb +505 -0
  69. data/lib/rbcurse/core/widgets/applicationheader.rb +102 -0
  70. data/lib/rbcurse/core/widgets/box.rb +58 -0
  71. data/lib/rbcurse/core/widgets/divider.rb +310 -0
  72. data/lib/rbcurse/core/widgets/keylabelprinter.rb +178 -0
  73. data/lib/rbcurse/core/widgets/rcombo.rb +238 -0
  74. data/lib/rbcurse/core/widgets/rcontainer.rb +415 -0
  75. data/lib/rbcurse/core/widgets/rlink.rb +30 -0
  76. data/lib/rbcurse/core/widgets/rlist.rb +723 -0
  77. data/lib/rbcurse/core/widgets/rmenu.rb +939 -0
  78. data/lib/rbcurse/core/widgets/rmenulink.rb +22 -0
  79. data/lib/rbcurse/core/widgets/rmessagebox.rb +373 -0
  80. data/lib/rbcurse/core/widgets/rprogress.rb +118 -0
  81. data/lib/rbcurse/core/widgets/rtabbedpane.rb +615 -0
  82. data/lib/rbcurse/core/widgets/rtabbedwindow.rb +68 -0
  83. data/lib/rbcurse/core/widgets/rtextarea.rb +920 -0
  84. data/lib/rbcurse/core/widgets/rtextview.rb +780 -0
  85. data/lib/rbcurse/core/widgets/rtree.rb +787 -0
  86. data/lib/rbcurse/core/widgets/rwidget.rb +3040 -0
  87. data/lib/rbcurse/core/widgets/scrollbar.rb +143 -0
  88. data/lib/rbcurse/core/widgets/statusline.rb +94 -0
  89. data/lib/rbcurse/core/widgets/tabular.rb +264 -0
  90. data/lib/rbcurse/core/widgets/tabularwidget.rb +1211 -0
  91. data/lib/rbcurse/core/widgets/textpad.rb +516 -0
  92. data/lib/rbcurse/core/widgets/tree/treecellrenderer.rb +150 -0
  93. data/lib/rbcurse/core/widgets/tree/treemodel.rb +428 -0
  94. metadata +156 -0
@@ -0,0 +1,787 @@
1
+ =begin
2
+ * Name: rtree:
3
+ * Description : a Tree control
4
+ * Author: rkumar (arunachalesha)
5
+ * Date: 2010-09-18 12:02
6
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ * This file started on 2010-09-18 12:03 (copied from rlistbox)
8
+ TODO:
9
+ [x] load on tree will expand
10
+ [x] selected row on startup
11
+ [x] open up a node and make current on startup
12
+ [ ] find string
13
+ [/] expand all descendants
14
+ ++ +- and +?
15
+ =end
16
+ require 'rbcurse'
17
+ require 'rbcurse/core/widgets/tree/treemodel'
18
+ require 'rbcurse/core/widgets/tree/treecellrenderer'
19
+
20
+ TreeSelectionEvent = Struct.new(:node, :tree, :state, :previous_node, :row_first)
21
+
22
+ #include Ncurses # FFI 2011-09-8
23
+ module RubyCurses
24
+ extend self
25
+ # a representation of heirarchical data in outline form
26
+ # Currently supports only single selection, and does not allow editing.
27
+ # @events Events: SELECT, DESELECT, TREE_WILL_EXPAND_EVENT, TREE_COLLAPSED_EVENT
28
+ #
29
+ class Tree < Widget
30
+ require 'rbcurse/core/include/listscrollable'
31
+ # currently just use single selection
32
+ include ListScrollable
33
+ #extend Forwardable
34
+ dsl_accessor :height
35
+ dsl_accessor :title
36
+ dsl_property :title_attrib # bold, reverse, normal
37
+ dsl_accessor :border_attrib, :border_color # FIXME not used currently
38
+
39
+ attr_reader :toprow
40
+ # attr_reader :prow
41
+ # attr_reader :winrow
42
+ dsl_accessor :default_value # node to show as selected - what if user doesn't have it?
43
+ attr_accessor :current_index
44
+ dsl_accessor :selected_color, :selected_bgcolor, :selected_attr
45
+ dsl_accessor :max_visible_items # how many to display 2009-01-11 16:15
46
+ dsl_accessor :cell_editing_allowed # obsolete
47
+ dsl_accessor :suppress_borders
48
+ dsl_property :show_selector
49
+ dsl_property :row_selected_symbol # 2009-01-12 12:01 changed from selector to selected
50
+ dsl_property :row_unselected_symbol # added 2009-01-12 12:00
51
+ dsl_property :left_margin
52
+ dsl_accessor :sanitization_required # 2011-10-6
53
+ #dsl_accessor :valign # popup related
54
+ #
55
+ # will pressing a single key move to first matching row. setting it to false lets us use vim keys
56
+ attr_accessor :one_key_selection # will pressing a single key move to first matching row
57
+ # index of row selected, relates to internal representation, not tree. @see selected_row
58
+ attr_reader :selected_index # index of row that is selected. this relates to representation
59
+ attr_reader :treemodel # returns treemodel for further actions 2011-10-2
60
+
61
+ def initialize form, config={}, &block
62
+ @focusable = true
63
+ @editable = false
64
+ @row = 0
65
+ @col = 0
66
+ # array representation of tree
67
+ @list = nil
68
+ # any special attribs such as status to be printed in col1, or color (selection)
69
+ @list_attribs = {}
70
+ # hash containing nodes that are expanded or once expanded
71
+ # if value is true, then currently expanded, else once expanded
72
+ # TODO : will need purging under some situations
73
+ @expanded_state = {}
74
+ @suppress_borders = false
75
+ @row_offset = @col_offset = 1
76
+ @current_index = 0
77
+ @one_key_selection = false # use vim keys
78
+ super
79
+ #@selection_mode ||= :single # default is multiple, anything else given becomes single
80
+ @win = @graphic # 2010-01-04 12:36 BUFFERED replace form.window with graphic
81
+ @sanitization_required = true
82
+ @longest_line = 0
83
+
84
+
85
+ @win_left = 0
86
+ @win_top = 0
87
+ @_events.push(*[:ENTER_ROW, :LEAVE_ROW, :TREE_COLLAPSED_EVENT, :TREE_EXPANDED_EVENT, :TREE_SELECTION_EVENT, :TREE_WILL_COLLAPSE_EVENT, :TREE_WILL_EXPAND_EVENT])
88
+
89
+
90
+ bind(:PROPERTY_CHANGE){|e| @cell_renderer = nil } # will be recreated if anything changes 2011-09-28 V1.3.1
91
+ # FIXME the above is dangerous if user set his own renderer with some values XXX
92
+ init_vars
93
+
94
+ #if !@list.selected_index.nil?
95
+ #set_focus_on @list.selected_index # the new version
96
+ #end
97
+ @keys_mapped = false
98
+ end
99
+ def init_vars
100
+ @repaint_required = true
101
+ @toprow = @pcol = 0
102
+ if @show_selector
103
+ @row_selected_symbol ||= '>'
104
+ @row_unselected_symbol ||= ' '
105
+ @left_margin ||= @row_selected_symbol.length
106
+ end
107
+ @left_margin ||= 0
108
+ #@one_key_selection = true if @one_key_selection.nil?
109
+ @row_offset = @col_offset = 0 if @suppress_borders
110
+ @internal_width = 2 # taking into account borders accounting for 2 cols
111
+ @internal_width = 0 if @suppress_borders # should it be 0 ???
112
+
113
+ end
114
+ # maps keys to methods
115
+ # checks @key_map can be :emacs or :vim.
116
+ def map_keys
117
+ @keys_mapped = true
118
+ $log.debug " cam in XXXX map keys"
119
+ bind_key(32){ toggle_row_selection() }
120
+ bind_key(KEY_ENTER) { toggle_expanded_state() }
121
+ bind_key(?o) { toggle_expanded_state() }
122
+ bind_key(?f){ ask_selection_for_char() }
123
+ bind_key(?\M-v){ @one_key_selection = !@one_key_selection }
124
+ bind_key(KEY_DOWN){ next_row() }
125
+ bind_key(KEY_UP){ previous_row() }
126
+ bind_key(?O){ expand_children() }
127
+ bind_key(?X){ collapse_children() }
128
+ bind_key(?>, :scroll_right)
129
+ bind_key(?<, :scroll_left)
130
+ bind_key(?\M-l, :scroll_right)
131
+ bind_key(?\M-h, :scroll_left)
132
+ # TODO
133
+ bind_key(?x){ collapse_parent() }
134
+ bind_key(?p){ goto_parent() }
135
+ if $key_map == :emacs
136
+ $log.debug " EMACSam in XXXX map keys"
137
+ bind_key(?\C-v){ scroll_forward }
138
+ bind_key(?\M-v){ scroll_backward }
139
+ bind_key(?\C-s){ ask_search() }
140
+ bind_key(?\C-n){ next_row() }
141
+ bind_key(?\C-p){ previous_row() }
142
+ bind_key(?\M->){ goto_bottom() }
143
+ bind_key(?\M-<){ goto_top() }
144
+ else # :vim
145
+ $log.debug " VIM cam in XXXX map keys"
146
+ bind_key(?j){ next_row() }
147
+ bind_key(?k){ previous_row() }
148
+ bind_key(?\C-d){ scroll_forward }
149
+ bind_key(?\C-b){ scroll_backward }
150
+ bind_key(?G){ goto_bottom() }
151
+ bind_key([?g,?g]){ goto_top() }
152
+ bind_key(?/){ ask_search() }
153
+ end
154
+ end
155
+
156
+ ##
157
+ def row_count
158
+ return 0 if @list.nil?
159
+ @list.length
160
+ end
161
+ # at what row should scrolling begin
162
+ def scrollatrow
163
+ if @suppress_borders
164
+ return @height - 1
165
+ else
166
+ return @height - 3
167
+ end
168
+ end
169
+ #
170
+ # Sets the given node as root and returns treemodel.
171
+ # Returns root if no argument given.
172
+ # Now we return root if already set
173
+ # Made node nillable so we can return root.
174
+ #
175
+ # @raise ArgumentError if setting a root after its set
176
+ # or passing nil if its not been set.
177
+ def root node=nil, asks_allow_children=false, &block
178
+ if @treemodel
179
+ return @treemodel.root unless node
180
+ raise ArgumentError, "Root already set"
181
+ end
182
+
183
+ raise ArgumentError, "root: node cannot be nil" unless node
184
+ @treemodel = RubyCurses::DefaultTreeModel.new(node, asks_allow_children, &block)
185
+ end
186
+
187
+ # pass data to create this tree model
188
+ # used to be list
189
+ def data alist=nil
190
+
191
+ # if nothing passed, print an empty root, rather than crashing
192
+ alist = [] if alist.nil?
193
+ @data = alist # data given by user
194
+ case alist
195
+ when Array
196
+ @treemodel = RubyCurses::DefaultTreeModel.new("/")
197
+ @treemodel.root.add alist
198
+ when Hash
199
+ @treemodel = RubyCurses::DefaultTreeModel.new("/")
200
+ @treemodel.root.add alist
201
+ when TreeNode
202
+ # this is a root node
203
+ @treemodel = RubyCurses::DefaultTreeModel.new(alist)
204
+ when DefaultTreeModel
205
+ @treemodel = alist
206
+ else
207
+ if alist.is_a? DefaultTreeModel
208
+ @treemodel = alist
209
+ else
210
+ raise ArgumentError, "Tree does not know how to handle #{alist.class} "
211
+ end
212
+ end
213
+ # we now have a tree
214
+ raise "I still don't have a tree" unless @treemodel
215
+ set_expanded_state(@treemodel.root, true)
216
+ convert_to_list @treemodel
217
+
218
+ # added on 2009-01-13 23:19 since updates are not automatic now
219
+ #@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
220
+ #create_default_list_selection_model TODO
221
+ end
222
+ # private, for use by repaint
223
+ def _list
224
+ if @_structure_changed
225
+ @list = nil
226
+ @_structure_changed = false
227
+ end
228
+ unless @list
229
+ $log.debug " XXX recreating _list"
230
+ convert_to_list @treemodel
231
+ $log.debug " XXXX list: #{@list.size} : #{@list} "
232
+ end
233
+ return @list
234
+ end
235
+ def convert_to_list tree
236
+ @list = get_expanded_descendants(tree.root)
237
+ #$log.debug "XXX convert #{tree.root.children.size} "
238
+ #$log.debug " converted tree to list. #{@list.size} "
239
+ end
240
+ def traverse node, level=0, &block
241
+ raise "disuse"
242
+ #icon = node.is_leaf? ? "-" : "+"
243
+ #puts "%*s %s" % [ level+1, icon, node.user_object ]
244
+ yield node, level if block_given?
245
+ node.children.each do |e|
246
+ traverse e, level+1, &block
247
+ end
248
+ end
249
+ # return object under cursor
250
+ # Note: this should not be confused with selected row/s. User may not have selected this.
251
+ # This is only useful since in some demos we like to change a status bar as a user scrolls down
252
+ # @since 1.2.0 2010-09-06 14:33 making life easier for others.
253
+ def current_row
254
+ @list[@current_index]
255
+ end
256
+ alias :text :current_row # thanks to shoes, not sure how this will impact since widget has text.
257
+
258
+ # show default value as selected and fire handler for it
259
+ # This is called in repaint, so can raise an error if called on creation
260
+ # or before repaint. Just set @default_value, and let us handle the rest.
261
+ # Suggestions are welcome.
262
+ def select_default_values
263
+ return if @default_value.nil?
264
+ # NOTE list not yet created
265
+ raise "list has not yet been created" unless @list
266
+ index = node_to_row @default_value
267
+ raise "could not find node #{@default_value}, #{@list} " unless index
268
+ return unless index
269
+ @current_index = index
270
+ toggle_row_selection
271
+ @default_value = nil
272
+ end
273
+ def print_borders
274
+ width = @width
275
+ height = @height-1 # 2010-01-04 15:30 BUFFERED HEIGHT
276
+ window = @graphic # 2010-01-04 12:37 BUFFERED
277
+ startcol = @col
278
+ startrow = @row
279
+ @color_pair = get_color($datacolor)
280
+ # bordercolor = @border_color || $datacolor # changed 2011 dts
281
+ bordercolor = @border_color || @color_pair # 2011-09-28 V1.3.1
282
+ borderatt = @border_attrib || Ncurses::A_NORMAL
283
+
284
+ window.print_border startrow, startcol, height, width, bordercolor, borderatt
285
+ print_title
286
+ end
287
+ def print_title
288
+ return unless @title
289
+ _title = @title
290
+ if @title.length > @width - 2
291
+ _title = @title[0..@width-2]
292
+ end
293
+ @color_pair ||= get_color($datacolor)
294
+ @graphic.printstring( @row, @col+(@width-_title.length)/2, _title, @color_pair, @title_attrib) unless @title.nil?
295
+ end
296
+ ### START FOR scrollable ###
297
+ def get_content
298
+ #@list 2008-12-01 23:13
299
+ @list_variable && @list_variable.value || @list
300
+ # called by next_match in listscrollable
301
+ @list
302
+ end
303
+ def get_window
304
+ @graphic # 2010-01-04 12:37 BUFFERED
305
+ end
306
+ ### END FOR scrollable ###
307
+ # override widgets text
308
+ def getvalue
309
+ selected_row()
310
+ end
311
+ # Listbox
312
+ def handle_key(ch)
313
+ return if @list.nil? || @list.empty?
314
+ @current_index ||= 0
315
+ @toprow ||= 0
316
+ map_keys unless @keys_mapped
317
+ h = scrollatrow()
318
+ rc = row_count
319
+ $log.debug " tree got ch #{ch}"
320
+ case ch
321
+ when 27, ?\C-c.getbyte(0)
322
+ #editing_canceled @current_index if @cell_editing_allowed
323
+ #cancel_block # block
324
+ $multiplier = 0
325
+ return 0
326
+ #when ?\C-u.getbyte(0)
327
+ # multiplier. Series is 4 16 64
328
+ # TESTING @multiplier = (@multiplier == 0 ? 4 : @multiplier *= 4)
329
+ # return 0
330
+ when ?\C-c.getbyte(0)
331
+ @multiplier = 0
332
+ return 0
333
+ else
334
+ ret = :UNHANDLED
335
+ if ret == :UNHANDLED
336
+ # beware one-key eats up numbers. we'll be wondering why
337
+ if @one_key_selection
338
+ case ch
339
+ #when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0), ?0.getbyte(0)..?9.getbyte(0)
340
+ when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0)
341
+ # simple motion, key press defines motion
342
+ ret = set_selection_for_char ch.chr
343
+ else
344
+ ret = process_key ch, self
345
+ @multiplier = 0
346
+ return :UNHANDLED if ret == :UNHANDLED
347
+ end
348
+ else
349
+ # no motion on single key, we can freak out like in vim, pref f <char> for set_selection
350
+ case ch
351
+ when ?0.getbyte(0)..?9.getbyte(0)
352
+ $multiplier *= 10 ; $multiplier += (ch-48)
353
+ #$log.debug " setting mult to #{$multiplier} in list "
354
+ return 0
355
+ end
356
+ $log.debug " TREE before process key #{ch} "
357
+ ret = process_key ch, self
358
+ $log.debug " TREE after process key #{ch} #{ret} "
359
+ #$multiplier = 0 # 2010-09-02 22:35 this prevents parent from using mult
360
+ return :UNHANDLED if ret == :UNHANDLED
361
+ end
362
+ end
363
+ end
364
+ $multiplier = 0
365
+ end
366
+ # get a keystroke from user and go to first item starting with that key
367
+ def ask_selection_for_char
368
+ ch = @graphic.getch
369
+ if ch < 0 || ch > 255
370
+ return :UNHANDLED
371
+ end
372
+ ret = set_selection_for_char ch.chr
373
+ end
374
+ def ask_search_forward
375
+ regex = get_string("Enter regex to search")
376
+ ix = @list.find_match regex
377
+ if ix.nil?
378
+ alert("No matching data for: #{regex}")
379
+ else
380
+ set_focus_on(ix)
381
+ end
382
+ end
383
+ # gets string to search and calls data models find prev
384
+ def ask_search_backward
385
+ regex = get_string("Enter regex to search (backward)")
386
+ @last_regex = regex
387
+ ix = @list.find_prev regex, @current_index
388
+ if ix.nil?
389
+ alert("No matching data for: #{regex}")
390
+ else
391
+ set_focus_on(ix)
392
+ end
393
+ end
394
+ # please check for error before proceeding
395
+ # @return [Boolean] false if no data
396
+ def on_enter
397
+ if @list.size < 1
398
+ Ncurses.beep
399
+ return false
400
+ end
401
+ on_enter_row @current_index
402
+ set_form_row # added 2009-01-11 23:41
403
+ #$log.debug " ONE ENTER LIST #{@current_index}, #{@form.row}"
404
+ @repaint_required = true
405
+ super
406
+ #fire_handler :ENTER, self
407
+ true
408
+ end
409
+ def on_enter_row arow
410
+ #$log.debug " Listbox #{self} ENTER_ROW with curr #{@current_index}. row: #{arow} H: #{@handler.keys}"
411
+ #fire_handler :ENTER_ROW, arow
412
+ fire_handler :ENTER_ROW, self
413
+ #@list.on_enter_row self TODO
414
+ #edit_row_at arow
415
+ @repaint_required = true
416
+ end
417
+ ##
418
+ def on_leave_row arow
419
+ #$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: list[row]:#{@list[arow]}"
420
+ #$log.debug " Listbox #{self} leave with (cr: #{@current_index}) #{arow}: "
421
+ #fire_handler :LEAVE_ROW, arow
422
+ fire_handler :LEAVE_ROW, self
423
+ #editing_completed arow
424
+ end
425
+
426
+ ##
427
+ # getter and setter for cell_renderer
428
+ def cell_renderer(*val)
429
+ if val.empty?
430
+ @cell_renderer ||= create_default_cell_renderer
431
+ else
432
+ @cell_renderer = val[0]
433
+ end
434
+ end
435
+ def create_default_cell_renderer
436
+ return RubyCurses::TreeCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-@internal_width-@left_margin}
437
+ end
438
+ ##
439
+ # this method chops the data to length before giving it to the
440
+ # renderer, this can cause problems if the renderer does some
441
+ # processing. also, it pans the data horizontally giving the renderer
442
+ # a section of it.
443
+ # FIXME: tree may not be clearing till end see appdirtree after divider movement
444
+ def repaint
445
+ return unless @repaint_required
446
+ @height ||= 10
447
+ @width ||= 30
448
+
449
+ my_win = @form ? @form.window : @target_window
450
+ @graphic = my_win unless @graphic
451
+
452
+ raise " #{@name} neither form, nor target window given TV paint " unless my_win
453
+ raise " #{@name} NO GRAPHIC set as yet TV paint " unless @graphic
454
+ @win_left = my_win.left
455
+ @win_top = my_win.top
456
+
457
+ $log.debug "rtree repaint #{@name} graphic #{@graphic}"
458
+ print_borders unless @suppress_borders # do this once only, unless everything changes
459
+ maxlen = @maxlen || @width-@internal_width
460
+ maxlen -= @left_margin # 2011-10-6
461
+ tm = _list()
462
+ select_default_values
463
+ rc = row_count
464
+ tr = @toprow
465
+ acolor = get_color $datacolor
466
+ h = scrollatrow()
467
+ r,c = rowcol
468
+ @longest_line = @width #maxlen
469
+ 0.upto(h) do |hh|
470
+ crow = tr+hh
471
+ if crow < rc
472
+ _focussed = @current_index == crow ? true : false # row focussed ?
473
+ focus_type = _focussed
474
+ # added 2010-09-02 14:39 so inactive fields don't show a bright focussed line
475
+ #focussed = false if focussed && !@focussed
476
+ focus_type = :SOFT_FOCUS if _focussed && !@focussed
477
+ selected = row_selected? crow
478
+ content = tm[crow] # 2009-01-17 18:37 chomp giving error in some cases says frozen
479
+ if content.is_a? TreeNode
480
+ node = content
481
+ object = content
482
+ leaf = node.is_leaf?
483
+ # content passed is rejected by treecellrenderer 2011-10-6
484
+ content = node.user_object.to_s # may need to trim or truncate
485
+ expanded = row_expanded? crow
486
+ elsif content.is_a? String
487
+ $log.warn "Removed this entire block since i don't think it was used XXX "
488
+ # this block does not set object XXX
489
+ else
490
+ raise "repaint what is the class #{content.class} "
491
+ content = content.to_s
492
+ end
493
+ # this is redundant since data is taken by renderer directly
494
+ #sanitize content if @sanitization_required
495
+ #truncate value
496
+ ## set the selector symbol if requested
497
+ selection_symbol = ''
498
+ if @show_selector
499
+ if selected
500
+ selection_symbol = @row_selected_symbol
501
+ else
502
+ selection_symbol = @row_unselected_symbol
503
+ end
504
+ @graphic.printstring r+hh, c, selection_symbol, acolor,@attr
505
+ end
506
+
507
+ renderer = cell_renderer()
508
+ renderer.display_length(@width-@internal_width-@left_margin) # just in case resizing of listbox
509
+ renderer.pcol = @pcol
510
+ #renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, _focussed, selected
511
+ renderer.repaint @graphic, r+hh, c+@left_margin, crow, object, content, leaf, focus_type, selected, expanded
512
+ @longest_line = renderer.actual_length if renderer.actual_length > @longest_line
513
+ else
514
+ # clear rows
515
+ @graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
516
+ end
517
+ end
518
+ @table_changed = false
519
+ @repaint_required = false
520
+ end
521
+
522
+ def list_data_changed
523
+ if row_count == 0 # added on 2009-02-02 17:13 so cursor not hanging on last row which could be empty
524
+ init_vars
525
+ @current_index = 0
526
+ set_form_row
527
+ end
528
+ @repaint_required = true
529
+ end
530
+ def set_form_col col1=0
531
+ @cols_panned ||= 0 # RFED16 2010-02-17 23:40
532
+ win_col = 0
533
+ col2 = win_col + @col + @col_offset + col1 + @cols_panned + @left_margin
534
+ setrowcol nil, col2 # 2010-02-17 23:19 RFED16
535
+ end
536
+ def selected_row
537
+ @list[@selected_index].node
538
+ end
539
+
540
+ # An event is thrown when a row is selected or deselected.
541
+ # Please note that when a row is selected, another one is automatically deselected.
542
+ # An event is not thrown for that since your may not want to collapse that.
543
+ # Only clicking on a selected row, will send a DESELECT on it since you may want to collapse it.
544
+ # However, the previous selection is also present in the event object, so you can act upon it.
545
+ # This is not used for expanding or collapsing, only for application to show some data in another
546
+ # window or pane based on selection. Maybe there should not be a deselect for current row ?
547
+ def toggle_row_selection
548
+ node = @list[@current_index]
549
+ previous_node = nil
550
+ previous_node = @list[@selected_index] if @selected_index
551
+ if @selected_index == @current_index
552
+ @selected_index = nil
553
+ else
554
+ @selected_index = @current_index
555
+ end
556
+ state = @selected_index.nil? ? :DESELECTED : :SELECTED
557
+ #TreeSelectionEvent = Struct.new(:node, :tree, :state, :previous_node, :row_first)
558
+ @tree_selection_event = TreeSelectionEvent.new(node, self, state, previous_node, @current_index) #if @item_event.nil?
559
+ fire_handler :TREE_SELECTION_EVENT, @tree_selection_event # should the event itself be ITEM_EVENT
560
+ $log.debug " XXX tree selected #{@selected_index}/ #{@current_index} , #{state} "
561
+ @repaint_required = true
562
+ end
563
+ def toggle_expanded_state row=@current_index
564
+ state = row_expanded? row
565
+ node = row_to_node
566
+ if node.nil?
567
+ Ncurses.beep
568
+ $log.debug " No such node on row #{row} "
569
+ return
570
+ end
571
+ $log.debug " toggle XXX state #{state} #{node} "
572
+ if state
573
+ collapse_node node
574
+ else
575
+ expand_node node
576
+ end
577
+ end
578
+ def row_to_node row=@current_index
579
+ @list[row]
580
+ end
581
+ # convert a given node to row
582
+ def node_to_row node
583
+ crow = nil
584
+ @list.each_with_index { |e,i|
585
+ if e == node
586
+ crow = i
587
+ break
588
+ end
589
+ }
590
+ crow
591
+ end
592
+ # private
593
+ # related to index in representation, not tree
594
+ def row_selected? row
595
+ @selected_index == row
596
+ end
597
+ # @return [TreeNode, nil] returns selected node or nil
598
+
599
+ def row_expanded? row
600
+ node = @list[row]
601
+ node_expanded? node
602
+ end
603
+ def row_collapsed? row
604
+ !row_expanded? row
605
+ end
606
+ def set_expanded_state(node, state)
607
+ @expanded_state[node] = state
608
+ @repaint_required = true
609
+ _structure_changed true
610
+ end
611
+ def expand_node(node)
612
+ #$log.debug " expand called on #{node.user_object} "
613
+ state = true
614
+ fire_handler :TREE_WILL_EXPAND_EVENT, node
615
+ set_expanded_state(node, state)
616
+ fire_handler :TREE_EXPANDED_EVENT, node
617
+ end
618
+ def collapse_node(node)
619
+ $log.debug " collapse called on #{node.user_object} "
620
+ state = false
621
+ fire_handler :TREE_WILL_COLLAPSE_EVENT, node
622
+ set_expanded_state(node, state)
623
+ fire_handler :TREE_COLLAPSED_EVENT, node
624
+ end
625
+ # this is required to make a node visible, if you wish to start from a node that is not root
626
+ # e.g. you are loading app in a dir somewhere but want to show path from root down.
627
+ # NOTE this sucks since you have to click 2 times to expand it.
628
+ def mark_parents_expanded node
629
+ # i am setting parents as expanded, but NOT firing handlers - XXX separate this into expand_parents
630
+ _path = node.tree_path
631
+ _path.each do |e|
632
+ # if already expanded parent then break we should break
633
+ set_expanded_state(e, true)
634
+ end
635
+ end
636
+ # goes up to root of this node, and expands down to this node
637
+ # this is often required to make a specific node visible such
638
+ # as in a dir listing when current dir is deep in heirarchy.
639
+ def expand_parents node
640
+ _path = node.tree_path
641
+ _path.each do |e|
642
+ # if already expanded parent then break we should break
643
+ #set_expanded_state(e, true)
644
+ expand_node(e)
645
+ end
646
+ end
647
+ # this expands all the children of a node, recursively
648
+ # we can't use multiplier concept here since we are doing a preorder enumeration
649
+ # we need to do a breadth first enumeration to use a multiplier
650
+ #
651
+ def expand_children node=:current_index
652
+ $multiplier = 999 if !$multiplier || $multiplier == 0
653
+ node = row_to_node if node == :current_index
654
+ return if node.children.empty? # or node.is_leaf?
655
+ #node.children.each do |e|
656
+ #expand_node e # this will keep expanding parents
657
+ #expand_children e
658
+ #end
659
+ node.breadth_each($multiplier) do |e|
660
+ expand_node e
661
+ end
662
+ $multiplier = 0
663
+ _structure_changed true
664
+ end
665
+ def collapse_children node=:current_index
666
+ $multiplier = 999 if !$multiplier || $multiplier == 0
667
+ $log.debug " CCCC IINSIDE COLLLAPSE"
668
+ node = row_to_node if node == :current_index
669
+ return if node.children.empty? # or node.is_leaf?
670
+ #node.children.each do |e|
671
+ #expand_node e # this will keep expanding parents
672
+ #expand_children e
673
+ #end
674
+ node.breadth_each($multiplier) do |e|
675
+ $log.debug "CCC collapsing #{e.user_object} "
676
+ collapse_node e
677
+ end
678
+ $multiplier = 0
679
+ _structure_changed true
680
+ end
681
+ # collapse parent
682
+ # can use multiplier.
683
+ # # we need to move up also
684
+ def collapse_parent node=:current_index
685
+ node = row_to_node if node == :current_index
686
+ parent = node.parent
687
+ return if parent.nil?
688
+ goto_parent node
689
+ collapse_node parent
690
+ end
691
+ def goto_parent node=:current_index
692
+ node = row_to_node if node == :current_index
693
+ parent = node.parent
694
+ return if parent.nil?
695
+ crow = @current_index
696
+ @list.each_with_index { |e,i|
697
+ if e == parent
698
+ crow = i
699
+ break
700
+ end
701
+ }
702
+ @repaint_required = true
703
+ #set_form_row # will not work if off form
704
+ set_focus_on crow
705
+ end
706
+
707
+ def has_been_expanded node
708
+ @expanded_state.has_key? node
709
+ end
710
+ def node_expanded? node
711
+ @expanded_state[node] == true
712
+ end
713
+ def node_collapsed? node
714
+ !node_expanded?(node)
715
+ end
716
+ def get_expanded_descendants(node)
717
+ nodes = []
718
+ nodes << node
719
+ traverse_expanded node, nodes
720
+ $log.debug " def get_expanded_descendants(node) #{nodes.size} "
721
+ return nodes
722
+ end
723
+ def traverse_expanded node, nodes
724
+ return if !node_expanded? node
725
+ #nodes << node
726
+ node.children.each do |e|
727
+ nodes << e
728
+ if node_expanded? e
729
+ traverse_expanded e, nodes
730
+ else
731
+ next
732
+ end
733
+ end
734
+ end
735
+
736
+ #
737
+ # To retrieve the node corresponding to a path specified as an array or string
738
+ # Do not mention the root.
739
+ # e.g. "ruby/1.9.2/io/console"
740
+ # or %w[ ruby 1.9.3 io console ]
741
+ # @since 1.4.0 2011-10-2
742
+ def get_node_for_path(user_path)
743
+ case user_path
744
+ when String
745
+ user_path = user_path.split "/"
746
+ when Array
747
+ else
748
+ raise ArgumentError, "Should be Array or String delimited with /"
749
+ end
750
+ $log.debug "TREE #{user_path} " if $log.debug?
751
+ root = @treemodel.root
752
+ found = nil
753
+ user_path.each { |e|
754
+ success = false
755
+ root.children.each { |c|
756
+ if c.user_object == e
757
+ found = c
758
+ success = true
759
+ root = c
760
+ break
761
+ end
762
+ }
763
+ return false unless success
764
+
765
+ }
766
+ return found
767
+ end
768
+ # default block
769
+ # @since 1.5.0 2011-11-22
770
+ def command *args, &block
771
+ bind :TREE_WILL_EXPAND_EVENT, *args, &block
772
+ end
773
+ private
774
+ # please do not rely on this yet, name could change
775
+ def _structure_changed tf=true
776
+ @_structure_changed = tf
777
+ @repaint_required = true
778
+ #@list = nil
779
+ end
780
+
781
+
782
+
783
+ # ADD HERE
784
+ end # class tree
785
+
786
+
787
+ end # module