rbcurse 1.1.5 → 1.2.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (123) hide show
  1. data/CHANGELOG +45 -0
  2. data/Makefile +1 -1
  3. data/Manifest.txt +91 -0
  4. data/NOTES +349 -2
  5. data/README.markdown +12 -0
  6. data/VERSION +1 -1
  7. data/examples/abasiclist.rb +25 -0
  8. data/examples/alpmenu.rb +42 -0
  9. data/examples/app.rb +883 -0
  10. data/examples/appcombo.rb +17 -0
  11. data/examples/appdirtree.rb +73 -0
  12. data/examples/appemail.rb +164 -0
  13. data/examples/appemaillb.rb +308 -0
  14. data/examples/appgcompose.rb +303 -0
  15. data/examples/appgmail.rb +951 -0
  16. data/examples/atree.rb +56 -0
  17. data/examples/dirtree.rb +78 -0
  18. data/examples/focusmanager.rb +31 -0
  19. data/examples/imap.rb +48 -0
  20. data/examples/menu1.rb +79 -0
  21. data/examples/multispl.rb +86 -0
  22. data/examples/rfe.rb +3 -4
  23. data/examples/rmail.rb +188 -0
  24. data/examples/s.rb +10 -0
  25. data/examples/scrollbar.rb +104 -0
  26. data/examples/splitp.rb +56 -0
  27. data/examples/table1.rb +30 -0
  28. data/examples/term.rb +48 -0
  29. data/examples/term2.rb +54 -0
  30. data/examples/test1.rb +4 -2
  31. data/examples/test2.rb +9 -9
  32. data/examples/testapp.rb +44 -0
  33. data/examples/testapp2.rb +51 -0
  34. data/examples/testcombo.rb +2 -2
  35. data/examples/testgmail.rb +46 -0
  36. data/examples/testlistbox.rb +0 -1
  37. data/examples/testmultispl.rb +199 -0
  38. data/examples/testree.rb +127 -0
  39. data/examples/testscroller.rb +0 -1
  40. data/examples/testscrolllb.rb +1 -1
  41. data/examples/testscrollp.rb +2 -1
  42. data/examples/testscrollta.rb +1 -1
  43. data/examples/testscrolltable.rb +1 -2
  44. data/examples/testsplit.rb +1 -1
  45. data/examples/testsplit2.rb +1 -1
  46. data/examples/testsplit3.rb +1 -1
  47. data/examples/testsplit3_1.rb +1 -1
  48. data/examples/testsplit3a.rb +1 -1
  49. data/examples/testsplit3b.rb +1 -1
  50. data/examples/testsplitta.rb +1 -1
  51. data/examples/testsplittv.rb +1 -1
  52. data/examples/testsplittvv.rb +1 -1
  53. data/examples/testtodo.rb +491 -488
  54. data/examples/testvimsplit.rb +111 -0
  55. data/examples/todo.db +0 -0
  56. data/examples/todocsv.csv +28 -0
  57. data/examples/viewtodo.rb +408 -403
  58. data/lib/rbcurse/action.rb +1 -0
  59. data/lib/rbcurse/app.rb +1294 -0
  60. data/lib/rbcurse/applicationheader.rb +7 -2
  61. data/lib/rbcurse/checkboxcellrenderer.rb +0 -12
  62. data/lib/rbcurse/colormap.rb +34 -8
  63. data/lib/rbcurse/comboboxcellrenderer.rb +0 -11
  64. data/lib/rbcurse/defaultlistselectionmodel.rb +23 -7
  65. data/lib/rbcurse/extras/bottomline.rb +1681 -0
  66. data/lib/rbcurse/extras/directorylist.rb +445 -0
  67. data/lib/rbcurse/extras/directorytree.rb +69 -0
  68. data/lib/rbcurse/extras/divider.rb +310 -0
  69. data/lib/rbcurse/extras/focusmanager.rb +31 -0
  70. data/lib/rbcurse/extras/listselectable.rb +222 -0
  71. data/lib/rbcurse/extras/masterdetail.rb +164 -0
  72. data/lib/rbcurse/extras/menutree.rb +63 -0
  73. data/lib/rbcurse/extras/rlink.rb +27 -0
  74. data/lib/rbcurse/extras/rmenulink.rb +21 -0
  75. data/lib/rbcurse/extras/scrollbar.rb +134 -0
  76. data/lib/rbcurse/extras/stdscrwindow.rb +247 -0
  77. data/lib/rbcurse/extras/tabular.rb +258 -0
  78. data/lib/rbcurse/extras/tabularwidget.rb +1070 -0
  79. data/lib/rbcurse/extras/viewer.rb +106 -0
  80. data/lib/rbcurse/io.rb +137 -80
  81. data/lib/rbcurse/keylabelprinter.rb +4 -0
  82. data/lib/rbcurse/listcellrenderer.rb +91 -59
  83. data/lib/rbcurse/listscrollable.rb +93 -95
  84. data/lib/rbcurse/listselectable.rb +60 -7
  85. data/lib/rbcurse/ractionevent.rb +67 -0
  86. data/lib/rbcurse/rbasiclistbox.rb +688 -0
  87. data/lib/rbcurse/rcombo.rb +5 -5
  88. data/lib/rbcurse/rcommandwindow.rb +555 -0
  89. data/lib/rbcurse/rinputdataevent.rb +12 -0
  90. data/lib/rbcurse/rlistbox.rb +305 -124
  91. data/lib/rbcurse/rmenu.rb +99 -46
  92. data/lib/rbcurse/rmessagebox.rb +13 -6
  93. data/lib/rbcurse/rmulticontainer.rb +54 -93
  94. data/lib/rbcurse/rmultisplit.rb +731 -0
  95. data/lib/rbcurse/rmultitextview.rb +3 -2
  96. data/lib/rbcurse/rpopupmenu.rb +0 -1
  97. data/lib/rbcurse/rprogress.rb +117 -0
  98. data/lib/rbcurse/rscrollpane.rb +2 -1
  99. data/lib/rbcurse/rsplitpane.rb +94 -20
  100. data/lib/rbcurse/rsplitpane2.rb +1009 -0
  101. data/lib/rbcurse/rtabbedpane.rb +3 -2
  102. data/lib/rbcurse/rtabbedwindow.rb +0 -1
  103. data/lib/rbcurse/rtable.rb +92 -64
  104. data/lib/rbcurse/rtextarea.rb +91 -57
  105. data/lib/rbcurse/rtextview.rb +223 -70
  106. data/lib/rbcurse/rtree.rb +723 -0
  107. data/lib/rbcurse/rviewport.rb +2 -1
  108. data/lib/rbcurse/rvimsplit.rb +768 -0
  109. data/lib/rbcurse/rwidget.rb +524 -325
  110. data/lib/rbcurse/table/tablecellrenderer.rb +1 -1
  111. data/lib/rbcurse/table/tabledatecellrenderer.rb +0 -1
  112. data/lib/rbcurse/tree/treecellrenderer.rb +137 -0
  113. data/lib/rbcurse/tree/treemodel.rb +428 -0
  114. data/lib/rbcurse/vieditable.rb +14 -13
  115. data/lib/ver/ncurses.rb +6 -0
  116. data/lib/ver/window.rb +67 -32
  117. metadata +99 -23
  118. data/bin/rbcurse +0 -0
  119. data/examples/rvimsplit.rb +0 -376
  120. data/examples/todo.rb +0 -1
  121. data/lib/rbcurse/rform.rb +0 -845
  122. data/lib/rbcurse/selectable.rb +0 -94
  123. data/rbcurse.gemspec +0 -188
@@ -8,6 +8,10 @@
8
8
  * License:
9
9
  Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
10
 
11
+ NOTE: this is how we used to write code in the Java days. Anyone reading this source,
12
+ this is NOT how to code in rubyland. Please see this link, for how to code such as class:
13
+ http://blog.grayproductions.net/articles/all_about_struct
14
+
11
15
  =end
12
16
  require 'rubygems'
13
17
 
@@ -24,6 +28,8 @@ module RubyCurses
24
28
  @row = row
25
29
  @text = text
26
30
  end
31
+ # until now to_s was returning inspect, but to make it easy for users let us return the value
32
+ # they most expect which is the text that was changed
27
33
  def to_s
28
34
  inspect
29
35
  end
@@ -32,5 +38,11 @@ module RubyCurses
32
38
  #"#{@type.to_s}, #{@source}, ind0:#{@index0}, ind1:#{@index1}, row:#{@row}, text:#{@text}"
33
39
  "#{@type.to_s}, ind0:#{@index0}, ind1:#{@index1}, row:#{@row}, text:#{@text}"
34
40
  end
41
+ # this is so that earlier applications were getting source in the block, not an event. they
42
+ # were doing a fld.getvalue, so we must keep those apps running
43
+ # @since 1.2.0 added 2010-09-11 12:25
44
+ def getvalue
45
+ @source.getvalue
46
+ end
35
47
  end
36
48
  end
@@ -5,14 +5,17 @@
5
5
  * Date: 2008-11-19 12:49
6
6
  * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
7
  * This file started on 2009-01-13 22:18 (broken off rwidgets.rb)
8
+ NOTE: listbox now traps RETURN/ENTER/13 so if you are trapping it, please use bind :PRESS
8
9
  TODO
10
+ Perhaps keep printed data created by convert_value_to_text cached, and used for searching
11
+ cursor movement and other functions.
12
+ [ ] XXX Can we separate editing out. Make a ReadonlyList, and extend it as EditableList. This way the usual
13
+ use case remains cleaner.
9
14
  =end
10
- require 'rubygems'
11
- require 'ncurses'
12
- require 'logger'
13
15
  require 'rbcurse'
14
16
  require 'rbcurse/listcellrenderer'
15
17
  require 'rbcurse/listkeys'
18
+ require 'forwardable'
16
19
 
17
20
 
18
21
  include Ncurses
@@ -46,8 +49,9 @@ module RubyCurses
46
49
  attr_accessor :selected_index
47
50
  attr_reader :last_regex # should i really keep here as public or maintain in listbox
48
51
 
49
- def initialize anarray
52
+ def initialize anarray=[]
50
53
  @list = anarray.dup
54
+ @_events = [:LIST_DATA_EVENT, :ENTER_ROW]
51
55
  end
52
56
  # changd on 2009-01-14 12:28 based on ..
53
57
  # http://www.ruby-forum.com/topic/175637#769030
@@ -95,8 +99,10 @@ module RubyCurses
95
99
  return ret
96
100
  end
97
101
  def remove_all
102
+ return if @list.nil? || @list.empty? # 2010-09-21 13:25
98
103
  lde = ListDataEvent.new(0, @list.size, self, :INTERVAL_REMOVED)
99
104
  @list = []
105
+ @current_index = 0
100
106
  fire_handler :LIST_DATA_EVENT, lde
101
107
  end
102
108
  def delete obj
@@ -110,11 +116,14 @@ module RubyCurses
110
116
  def include?(obj)
111
117
  return @list.include?(obj)
112
118
  end
119
+ # returns a `dup()` of the list
113
120
  def values
114
121
  @list.dup
115
122
  end
123
+ # why do we have this here in data, we should remove this
124
+ # @deprecated this was just eye candy for some demo
116
125
  def on_enter_row object
117
- #$log.debug " XXX on_enter_row of list_data"
126
+ $log.debug " XXX on_enter_row of list_data"
118
127
  fire_handler :ENTER_ROW, object
119
128
  end
120
129
  # ##
@@ -186,10 +195,10 @@ module RubyCurses
186
195
  #
187
196
 
188
197
  ##
198
+ # TODO CAN WE MOVE THIS OUT TO ANOTHER FILE as confusing me
189
199
  # pops up a list of values for selection
190
200
  # 2008-12-10
191
201
  class PopupList
192
- include DSL
193
202
  include RubyCurses::EventHandler
194
203
  dsl_accessor :title
195
204
  dsl_accessor :row, :col, :height, :width
@@ -246,12 +255,15 @@ module RubyCurses
246
255
  @window.wrefresh
247
256
  handle_keys
248
257
  end
258
+ # class popup
249
259
  def list alist=nil
250
260
  return @list if alist.nil?
251
261
  @list = ListDataModel.new(alist)
262
+ @repaint_required = true
252
263
  # will we need this ? listbox made each time so data should be fresh
253
264
  #@list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
254
265
  end
266
+ # class popup
255
267
  def list_data_model ldm
256
268
  raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
257
269
  @list = ldm
@@ -301,7 +313,7 @@ module RubyCurses
301
313
  # $log.debug "popup ENTER : #{field.name}" if !field.nil?
302
314
  @stop = true
303
315
  return
304
- when 9
316
+ when KEY_TAB
305
317
  @form.select_next_field
306
318
  else
307
319
  # fields must return unhandled else we will miss hotkeys.
@@ -372,9 +384,17 @@ module RubyCurses
372
384
  end
373
385
  end # class PopupList
374
386
  ##
375
- # this is the new LISTBOX, based on new scrollable.
387
+ # A control for displaying a list of data or values.
388
+ # The list will be editable if @cell_editing_allowed
389
+ # is set to true when creating. By default, multiple selection is allowed, but may be set to :single.
390
+ # TODO: were we not going to force creation of datamodel and listener on startup by putting a blank
391
+ # :list in config, if no list or list_variable or model is there ?
392
+ # Or at end of constructor check, if no listdatamodel then create default.
393
+ # TODO : perhaps when datamodel created, attach listener to it, so we can fire to callers when
394
+ # they want to be informed of changes. As we did with selection listeners.
376
395
  #
377
396
  class Listbox < Widget
397
+
378
398
  require 'rbcurse/listscrollable'
379
399
  require 'rbcurse/listselectable'
380
400
  require 'rbcurse/defaultlistselectionmodel'
@@ -382,6 +402,7 @@ module RubyCurses
382
402
  include ListScrollable
383
403
  include ListSelectable
384
404
  include RubyCurses::ListKeys
405
+ extend Forwardable
385
406
  dsl_accessor :height
386
407
  dsl_accessor :title
387
408
  dsl_property :title_attrib # bold, reverse, normal
@@ -404,29 +425,36 @@ module RubyCurses
404
425
  dsl_property :left_margin
405
426
  # please set these in he constructor block. Settin them later will have no effect
406
427
  # since i would have bound them to actions
407
- dsl_accessor :KEY_ROW_SELECTOR
408
- dsl_accessor :KEY_GOTO_TOP
409
- dsl_accessor :KEY_GOTO_BOTTOM
410
- dsl_accessor :KEY_CLEAR_SELECTION
411
- dsl_accessor :KEY_NEXT_SELECTION
412
- dsl_accessor :KEY_PREV_SELECTION
413
- dsl_accessor :valign # 2009-01-17 18:32
428
+ dsl_accessor :KEY_ROW_SELECTOR # this is going to go XXX TODO
429
+ dsl_accessor :KEY_GOTO_TOP # this is going to go
430
+ dsl_accessor :KEY_GOTO_BOTTOM # this is going to go
431
+ dsl_accessor :KEY_CLEAR_SELECTION # this is going to go
432
+ dsl_accessor :KEY_NEXT_SELECTION # this is going to go
433
+ dsl_accessor :KEY_PREV_SELECTION # this is going to go
434
+ dsl_accessor :valign # 2009-01-17 18:32 vertical alignment used in combos
435
+ dsl_accessor :justify # 2010-09-27 12:41 used by renderer
414
436
  attr_accessor :one_key_selection # will pressing a single key select or not
437
+ dsl_accessor :border_attrib, :border_color #
438
+ dsl_accessor :sanitization_required
439
+ dsl_accessor :suppress_borders #to_print_borders
440
+
415
441
 
416
442
  def initialize form, config={}, &block
417
443
  @focusable = true
418
444
  @editable = false
445
+ @sanitization_required = true
419
446
  @row = 0
420
447
  @col = 0
421
- # data of listbox
422
- @list = []
448
+ # data of listbox this is not an array, its a pointer to the listdatamodel
449
+ @list = nil
423
450
  # any special attribs such as status to be printed in col1, or color (selection)
424
451
  @list_attribs = {}
452
+ @suppress_borders = false
453
+ @row_offset = @col_offset = 1 # for borders
425
454
  super
455
+ @_events.push(*[:ENTER_ROW, :LEAVE_ROW, :LIST_SELECTION_EVENT, :PRESS])
426
456
  @current_index ||= 0
427
- @row_offset = @col_offset = 1
428
- @content_rows = @list.length
429
- @selection_mode ||= 'multiple'
457
+ @selection_mode ||= :multiple # default is multiple, anything else given becomes single
430
458
  @win = @graphic # 2010-01-04 12:36 BUFFERED replace form.window with graphic
431
459
  # moving down to repaint so that scrollpane can set should_buffered
432
460
  # added 2010-02-17 23:05 RFED16 so we don't need a form.
@@ -443,21 +471,32 @@ module RubyCurses
443
471
  init_vars
444
472
  install_list_keys
445
473
 
446
- if !@list.selected_index.nil?
474
+ if @list && !@list.selected_index.nil?
447
475
  set_focus_on @list.selected_index # the new version
448
476
  end
449
477
  end
478
+ # this is called several times, from constructor
479
+ # and when list data changed, so only put relevant resets here.
480
+ # why can't current_index be set to 0 here
450
481
  def init_vars
451
- @to_print_borders ||= 1
452
482
  @repaint_required = true
453
483
  @toprow = @pcol = 0
484
+
485
+ @row_offset = @col_offset = 0 if @suppress_borders
454
486
  if @show_selector
455
487
  @row_selected_symbol ||= '>'
456
488
  @row_unselected_symbol ||= ' '
457
489
  @left_margin ||= @row_selected_symbol.length
458
490
  end
459
491
  @left_margin ||= 0
460
- @one_key_selection ||= true
492
+ @one_key_selection = true if @one_key_selection.nil?
493
+ # we reduce internal_width from width while printing
494
+ @internal_width = 2 # taking into account borders accounting for 2 cols
495
+ @internal_width = 0 if @suppress_borders # should it be 0 ???
496
+
497
+ end
498
+ def map_keys
499
+ return if @keys_mapped
461
500
  bind_key(?f){ ask_selection_for_char() }
462
501
  bind_key(?\M-v){ @one_key_selection = false }
463
502
  bind_key(?j){ next_row() }
@@ -466,38 +505,96 @@ module RubyCurses
466
505
  bind_key([?g,?g]){ goto_top() }
467
506
  bind_key(?/){ ask_search() }
468
507
  bind_key(?n){ find_more() }
469
-
470
- end
471
- def install_bindings
472
-
508
+ bind_key(32){ toggle_row_selection() }
509
+ bind_key(10){ fire_action_event }
510
+ bind_key(13){ fire_action_event }
511
+ @keys_mapped = true
473
512
  end
474
513
 
475
514
  ##
476
515
  # getter and setter for selection_mode
477
516
  # Must be called after creating model, so no duplicate. Since one may set in model directly.
478
517
  def selection_mode(*val)
479
- raise "ListSelectionModel not yet created!" if @list_selection_model.nil?
518
+ #raise "ListSelectionModel not yet created!" if @list_selection_model.nil?
519
+
480
520
  if val.empty?
481
- @list_selection_model.selection_mode
521
+ if @list_selection_model
522
+ return @list_selection_model.selection_mode
523
+ else
524
+ return @tmp_selection_mode
525
+ end
482
526
  else
483
- @list_selection_model.selection_mode = val[0]
527
+ if @list_selection_model.nil?
528
+ @tmp_selection_mode = val[0]
529
+ else
530
+ @list_selection_model.selection_mode = val[0].to_sym
531
+ end
484
532
  end
485
533
  end
486
534
  def row_count
535
+ return 0 if @list.nil?
487
536
  @list.length
488
537
  end
489
538
  # added 2009-01-07 13:05 so new scrollable can use
490
539
  def scrollatrow
491
- #@height - 2
492
- @height - 3 # 2010-01-04 15:30 BUFFERED HEIGHT
540
+ if @suppress_borders
541
+ return @height - 1
542
+ else
543
+ return @height - 3
544
+ end
493
545
  end
494
- def list alist=nil
495
- return @list if alist.nil?
496
- @list = RubyCurses::ListDataModel.new(alist)
546
+ # provide data to List in the form of an Array or Variable or
547
+ # ListDataModel. This will create a default ListSelectionModel.
548
+ #
549
+ # CHANGE as on 2010-09-21 12:53:
550
+ # If explicit nil passed then dummy datamodel and selection model created
551
+ # From now on, constructor will call this, so this can always
552
+ # happen.
553
+ #
554
+ # NOTE: sometimes this can be added much after its painted.
555
+ # Do not expect this to be called from constructor, although that
556
+ # is the usual case. it can be dependent on some other list or tree.
557
+ # @param [Array, Variable, ListDataModel] data to populate list with
558
+ # @return [ListDataModel] just created or assigned
559
+
560
+ def list *val
561
+ return @list if val.empty?
562
+ clear_selection if @list # clear previous selections if any
563
+ @default_values = nil if @list # clear previous selections if any
564
+ alist = val[0]
565
+ case alist
566
+ when Array
567
+ if @list
568
+ @list.remove_all
569
+ @list.insert 0, *alist
570
+ @current_index = 0
571
+ else
572
+ @list = RubyCurses::ListDataModel.new(alist)
573
+ end
574
+ when NilClass
575
+ if @list
576
+ @list.remove_all
577
+ else
578
+ @list = RubyCurses::ListDataModel.new(alist)
579
+ end
580
+ when Variable
581
+ @list = RubyCurses::ListDataModel.new(alist.value)
582
+ when RubyCurses::ListDataModel
583
+ @list = alist
584
+ else
585
+ raise ArgumentError, "Listbox list(): do not know how to handle #{alist.class} "
586
+ end
497
587
  # added on 2009-01-13 23:19 since updates are not automatic now
498
588
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
499
589
  create_default_list_selection_model
590
+ @list_selection_model.selection_mode = @tmp_selection_mode if @tmp_selection_mode
591
+ @repaint_required = true
592
+ @list
500
593
  end
594
+ # populate using a Variable which should contain a list
595
+ # NOTE: This explicilty overwrites any existing datamodel such as the
596
+ # default one. You may lose any events you have bound to the listbox
597
+ # prior to this call.
501
598
  def list_variable alist=nil
502
599
  return @list if alist.nil?
503
600
  @list = RubyCurses::ListDataModel.new(alist.value)
@@ -505,6 +602,11 @@ module RubyCurses
505
602
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
506
603
  create_default_list_selection_model
507
604
  end
605
+ # populate using a custom data model
606
+ # NOTE: This explicilty overwrites any existing datamodel such as the
607
+ # default one. You may lose any events you have bound to the listbox
608
+ # prior to this call.
609
+
508
610
  def list_data_model ldm=nil
509
611
  return @list if ldm.nil?
510
612
  raise "Expecting list_data_model" unless ldm.is_a? RubyCurses::ListDataModel
@@ -513,12 +615,41 @@ module RubyCurses
513
615
  @list.bind(:LIST_DATA_EVENT) { |e| list_data_changed() }
514
616
  create_default_list_selection_model
515
617
  end
618
+ # create a default list selection model and set it
619
+ # NOTE: I am now checking if one is not already created, since
620
+ # a second creation would wipe out any listeners on it.
621
+ # @see ListSelectable
622
+ # @see DefaultListSelectionModel
623
+ def create_default_list_selection_model
624
+ if @list_selection_model.nil?
625
+ list_selection_model DefaultListSelectionModel.new(self)
626
+ end
627
+ end
628
+ # added 2010-09-15 00:11 to make life easier
629
+ def_delegators :@list, :insert, :remove_all, :delete_at, :include?
630
+ # get element at
631
+ # @param [Fixnum] index for element
632
+ # @return [Object] element
633
+ # @since 1.2.0 2010-09-06 14:33 making life easier for others.
634
+ def [](off0)
635
+ @list[off0]
636
+ end
637
+ # return object under cursor
638
+ # Note: this should not be confused with selected row/s. User may not have selected this.
639
+ # This is only useful since in some demos we like to change a status bar as a user scrolls down
640
+ # @since 1.2.0 2010-09-06 14:33 making life easier for others.
641
+ def current_value
642
+ @list[@current_index]
643
+ end
644
+ # avoid using "row", i'd rather stick with "index" and "value".
645
+ alias :current_row :current_value
646
+ alias :text :current_value # thanks to shoes, not sure how this will impact since widget has text.
516
647
 
648
+ # XXX can this not be done at repaint
517
649
  def select_default_values
518
650
  return if @default_values.nil?
519
651
  @default_values.each do |val|
520
652
  row = @list.index val
521
- #do_select(row) unless row.nil?
522
653
  add_row_selection_interval row, row unless row.nil?
523
654
  end
524
655
  end
@@ -529,17 +660,22 @@ module RubyCurses
529
660
  startcol = @col
530
661
  startrow = @row
531
662
  @color_pair = get_color($datacolor)
532
- #$log.debug "rlistb: window.print_border #{startrow}, #{startcol} , #{height} , #{width} , @color_pair, @attr "
533
- window.print_border startrow, startcol, height, width, @color_pair, @attr
663
+ bordercolor = @border_color || $datacolor
664
+ borderatt = @border_attrib || Ncurses::A_NORMAL
665
+
666
+ #$log.debug "rlistb #{name}: window.print_border #{startrow}, #{startcol} , h:#{height}, w:#{width} , @color_pair, @attr "
667
+ window.print_border startrow, startcol, height, width, bordercolor, borderatt
534
668
  print_title
535
669
  end
536
670
  def print_title
537
- #printstring(@graphic, @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
538
- # 2010-01-04 15:53 BUFFERED
539
- # I notice that the old version would print a title that was longer than width,
540
- #+ but the new version won't print anything if it exceeds width.
541
- # TODO check title.length and truncate if exceeds width
542
- @graphic.printstring( @row, @col+(@width-@title.length)/2, @title, @color_pair, @title_attrib) unless @title.nil?
671
+ @color_pair ||= get_color($datacolor)
672
+ # check title.length and truncate if exceeds width
673
+ return unless @title
674
+ _title = @title
675
+ if @title.length > @width - 2
676
+ _title = @title[0..@width-2]
677
+ end
678
+ @graphic.printstring( @row, @col+(@width-_title.length)/2, _title, @color_pair, @title_attrib) unless @title.nil?
543
679
  end
544
680
  ### START FOR scrollable ###
545
681
  def get_content
@@ -551,24 +687,26 @@ module RubyCurses
551
687
  end
552
688
  ### END FOR scrollable ###
553
689
  # override widgets text
690
+ # returns indices of selected rows
554
691
  def getvalue
555
692
  selected_rows
556
693
  end
557
694
  # Listbox
558
695
  def handle_key(ch)
696
+ map_keys unless @keys_mapped
559
697
  @current_index ||= 0
560
698
  @toprow ||= 0
561
699
  h = scrollatrow()
562
700
  rc = row_count
563
- $log.debug " listbxo got ch #{ch}"
701
+ $log.debug " listbox got ch #{ch}"
564
702
  #$log.debug " when kps #{@KEY_PREV_SELECTION} "
565
703
  case ch
566
704
  when KEY_UP # show previous value
567
- previous_row
705
+ return previous_row
568
706
  when KEY_DOWN # show previous value
569
- next_row
707
+ return next_row
570
708
  when @KEY_ROW_SELECTOR # 32
571
- return if is_popup and @selection_mode == 'single' # not allowing select this way since there will be a difference
709
+ return if is_popup && @selection_mode != :multiple # not allowing select this way since there will be a difference
572
710
  toggle_row_selection @current_index #, @current_index
573
711
  @repaint_required = true
574
712
  when @KEY_SCROLL_FORWARD # ?\C-n
@@ -583,15 +721,15 @@ module RubyCurses
583
721
  when @KEY_NEXT_SELECTION # ?'
584
722
  $log.debug "insdie next selection"
585
723
  @oldrow = @current_index
586
- do_next_selection #if @select_mode == 'multiple'
724
+ do_next_selection
587
725
  bounds_check
588
726
  when @KEY_PREV_SELECTION # ?"
589
727
  @oldrow = @current_index
590
728
  $log.debug "insdie prev selection"
591
- do_prev_selection #if @select_mode == 'multiple'
729
+ do_prev_selection
592
730
  bounds_check
593
731
  when @KEY_CLEAR_SELECTION
594
- clear_selection #if @select_mode == 'multiple'
732
+ clear_selection
595
733
  @repaint_required = true
596
734
  when 27, ?\C-c.getbyte(0)
597
735
  editing_canceled @current_index if @cell_editing_allowed
@@ -632,9 +770,11 @@ module RubyCurses
632
770
  end
633
771
  end
634
772
  if ret == :UNHANDLED
773
+ # beware one-key eats up numbers. we'll be wondering why
635
774
  if @one_key_selection
636
775
  case ch
637
- when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0), ?0.getbyte(0)..?9.getbyte(0)
776
+ #when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0), ?0.getbyte(0)..?9.getbyte(0)
777
+ when ?A.getbyte(0)..?Z.getbyte(0), ?a.getbyte(0)..?z.getbyte(0)
638
778
  # simple motion, key press defines motion
639
779
  ret = set_selection_for_char ch.chr
640
780
  else
@@ -651,13 +791,24 @@ module RubyCurses
651
791
  return 0
652
792
  end
653
793
  ret = process_key ch, self
654
- $multiplier = 0
655
794
  return :UNHANDLED if ret == :UNHANDLED
656
795
  end
657
796
  end
658
797
  end
659
798
  $multiplier = 0
660
799
  end
800
+ # fire handler when user presses ENTER/RETURN
801
+ # @since 1.2.0
802
+ # listbox now traps ENTER key and fires action event
803
+ # to trap please bind :PRESS
804
+ #
805
+ def fire_action_event
806
+ # this does not select the row ???? FIXME ??
807
+ require 'rbcurse/ractionevent'
808
+ # should have been callled :ACTION_EVENT !!!
809
+ fire_handler :PRESS, ActionEvent.new(self, :PRESS, text)
810
+ end
811
+ # get a keystroke from user and go to first item starting with that key
661
812
  def ask_selection_for_char
662
813
  ch = @graphic.getch
663
814
  if ch < 0 || ch > 255
@@ -685,40 +836,26 @@ module RubyCurses
685
836
  set_focus_on(ix)
686
837
  end
687
838
  end
688
- ## listbox find_prev
689
- def OLDfind_prev
690
- ix = @list.find_prev
691
- regex = @last_regex
692
- if ix.nil?
693
- alert("No previous matching data for: #{regex}")
694
- else
695
- @oldrow = @current_index
696
- @current_index = ix
697
- bounds_check
698
- end
699
- end
700
- # table find_next
701
- def OLDfind_next
702
- ix = @list.find_next
703
- regex = @last_regex
704
- if ix.nil?
705
- alert("No more matching data for: #{regex}")
706
- else
707
- set_focus_on(ix) unless ix.nil?
708
- end
709
- end
839
+ # please check for error before proceeding
840
+ # @return [Boolean] false if no data
710
841
  def on_enter
842
+ if @list.nil? || @list.size == 0
843
+ Ncurses.beep
844
+ return :UNHANDLED
845
+ end
711
846
  on_enter_row @current_index
712
847
  set_form_row # added 2009-01-11 23:41
713
848
  #$log.debug " ONE ENTER LIST #{@current_index}, #{@form.row}"
714
849
  @repaint_required = true
715
- fire_handler :ENTER, self
850
+ super
851
+ #fire_handler :ENTER, self
852
+ true
716
853
  end
717
854
  def on_enter_row arow
718
855
  #$log.debug " Listbox #{self} ENTER_ROW with curr #{@current_index}. row: #{arow} H: #{@handler.keys}"
719
856
  #fire_handler :ENTER_ROW, arow
720
857
  fire_handler :ENTER_ROW, self
721
- @list.on_enter_row self
858
+ @list.on_enter_row self ## XXX WHY THIS ???
722
859
  edit_row_at arow
723
860
  @repaint_required = true
724
861
  end
@@ -787,7 +924,7 @@ module RubyCurses
787
924
  end
788
925
  end
789
926
  def create_default_cell_editor
790
- return RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> @width-2-@left_margin}
927
+ return RubyCurses::CellEditor.new RubyCurses::Field.new nil, {"focusable"=>false, "visible"=>false, "display_length"=> @width-@internal_width-@left_margin}
791
928
  end
792
929
  ##
793
930
  # getter and setter for cell_renderer
@@ -799,7 +936,7 @@ module RubyCurses
799
936
  end
800
937
  end
801
938
  def create_default_cell_renderer
802
- return RubyCurses::ListCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-2-@left_margin}
939
+ return RubyCurses::ListCellRenderer.new "", {"color"=>@color, "bgcolor"=>@bgcolor, "parent" => self, "display_length"=> @width-@internal_width-@left_margin}
803
940
  end
804
941
  ##
805
942
  # this method chops the data to length before giving it to the
@@ -812,42 +949,41 @@ module RubyCurses
812
949
  # not sure where to put this, once for all or repeat 2010-02-17 23:07 RFED16
813
950
  my_win = @form ? @form.window : @target_window
814
951
  @graphic = my_win unless @graphic
815
- #$log.warn "neither form not target window given!!! TV paint 368" unless my_win
816
- raise " #{@name} neither form, nor target window given TV paint " unless my_win
817
- raise " #{@name} NO GRAPHIC set as yet TV paint " unless @graphic
952
+ raise " #{@name} neither form, nor target window given LB paint " unless my_win
953
+ raise " #{@name} NO GRAPHIC set as yet LB paint " unless @graphic
818
954
  @win_left = my_win.left
819
955
  @win_top = my_win.top
820
956
 
821
- $log.debug " rlistbox repaint graphic #{@graphic} "
822
- print_borders if @to_print_borders == 1 # do this once only, unless everything changes
823
- rc = row_count
824
- maxlen = @maxlen ||= @width-2
957
+ #$log.debug "rlistbox repaint #{@name} r,c, #{@row} #{@col} , width: #{@width} "
958
+ print_borders unless @suppress_borders # do this once only, unless everything changes
959
+ #maxlen = @maxlen || @width-@internal_width
960
+ renderer = cell_renderer()
961
+ renderer.display_length(@width-@internal_width-@left_margin) # just in case resizing of listbox
825
962
  tm = list()
826
- tr = @toprow
827
- acolor = get_color $datacolor
828
- h = scrollatrow()
829
- r,c = rowcol
830
- 0.upto(h) do |hh|
831
- crow = tr+hh
832
- if crow < rc
833
- focussed = @current_index == crow ? true : false
963
+ rc = row_count
964
+ @longest_line = @width
965
+ $log.debug " rlistbox #{row_count} "
966
+ if rc > 0 # just added in case no data passed
967
+ tr = @toprow
968
+ acolor = get_color $datacolor
969
+ h = scrollatrow()
970
+ r,c = rowcol
971
+ 0.upto(h) do |hh|
972
+ crow = tr+hh
973
+ if crow < rc
974
+ _focussed = @current_index == crow ? true : false # row focussed ?
975
+ focus_type = _focussed
976
+ # added 2010-09-02 14:39 so inactive fields don't show a bright focussed line
977
+ #focussed = false if focussed && !@focussed
978
+ focus_type = :SOFT_FOCUS if _focussed && !@focussed
834
979
  selected = is_row_selected crow
835
980
  content = tm[crow] # 2009-01-17 18:37 chomp giving error in some cases says frozen
981
+ content = convert_value_to_text content, crow # 2010-09-23 20:12
982
+ # by now it has to be a String
836
983
  if content.is_a? String
837
984
  content = content.dup
838
- content.chomp!
839
- content.gsub!(/\t/, ' ') # don't display tab
840
- content.gsub!(/[^[:print:]]/, '') # don't display non print characters
841
- if !content.nil?
842
- if content.length > maxlen # only show maxlen
843
- content = content[@pcol..@pcol+maxlen-1]
844
- else
845
- content = content[@pcol..-1]
846
- end
847
- end
848
- elsif content.is_a? TrueClass or content.is_a? FalseClass
849
- else
850
- content = content.to_s
985
+ sanitize content if @sanitization_required
986
+ truncate content
851
987
  end
852
988
  ## set the selector symbol if requested
853
989
  selection_symbol = ''
@@ -860,38 +996,83 @@ module RubyCurses
860
996
  @graphic.printstring r+hh, c, selection_symbol, acolor,@attr
861
997
  end
862
998
  #renderer = get_default_cell_renderer_for_class content.class.to_s
863
- renderer = cell_renderer()
864
- #renderer.show_selector @show_selector
865
- #renderer.row_selected_symbol @row_selected_symbol
866
- #renderer.left_margin @left_margin
867
- #renderer.repaint @graphic, r+hh, c+(colix*11), content, focussed, selected
868
- ## added crow on 2009-02-06 23:03
869
- # since data is being truncated and renderer may need index
870
- renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, focussed, selected
871
- else
872
- # clear rows
873
- @graphic.printstring r+hh, c, " " * (@width-2), acolor,@attr
999
+ renderer.repaint @graphic, r+hh, c+@left_margin, crow, content, focus_type, selected
1000
+ else
1001
+ # clear rows
1002
+ @graphic.printstring r+hh, c, " " * (@width-@internal_width), acolor,@attr
1003
+ end
874
1004
  end
875
- end
876
- if @cell_editing_allowed
877
- @cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
878
- end
879
- @table_changed = false
1005
+ if @cell_editing_allowed
1006
+ @cell_editor.component.repaint unless @cell_editor.nil? or @cell_editor.component.form.nil?
1007
+ end
1008
+ end # rc == 0
1009
+ #@table_changed = false
880
1010
  @repaint_required = false
881
1011
  @buffer_modified = true # required by form to call buffer_to_screen BUFFERED
882
1012
  buffer_to_window # RFED16 2010-02-17 23:16
883
1013
  end
1014
+ # the idea here is to allow users who subclass Listbox to easily override parts of the cumbersome repaint
1015
+ # method. This assumes your List has some data, but you print a lot more. Now you don't need to
1016
+ # change the data in the renderer, or keep formatted data in the list itself.
1017
+ # e.g. @list contains file names, or File objects, and this converts to a long listing.
1018
+ # If the renderer did that, the truncation would be on wrong data.
1019
+ # @since 1.2.0
1020
+ def convert_value_to_text value, crow
1021
+ case value
1022
+ when TrueClass, FalseClass
1023
+ value
1024
+ else
1025
+ value.to_s if value
1026
+ end
1027
+ end
1028
+ # takes a block, this way anyone extending this class can just pass a block to do his job
1029
+ # This modifies the string
1030
+ def sanitize content
1031
+ if content.is_a? String
1032
+ content.chomp!
1033
+ content.gsub!(/\t/, ' ') # don't display tab
1034
+ content.gsub!(/[^[:print:]]/, '') # don't display non print characters
1035
+ else
1036
+ content
1037
+ end
1038
+ end
1039
+ # returns only the visible portion of string taking into account display length
1040
+ # and horizontal scrolling. MODIFIES STRING
1041
+ # if you;ve truncated the data, it could stay truncated even if lb is increased. be careful
1042
+ def truncate content
1043
+ _maxlen = @maxlen || @width-@internal_width
1044
+ _maxlen = @width-@internal_width if _maxlen > @width-@internal_width
1045
+ #$log.debug "TRUNCATE: listbox maxlen #{@maxlen}, #{_maxlen} width #{@width}: #{content} "
1046
+ if !content.nil?
1047
+ if content.length > _maxlen # only show maxlen
1048
+ @longest_line = content.length if content.length > @longest_line
1049
+ #content = content[@pcol..@pcol+maxlen-1]
1050
+ content.replace content[@pcol..@pcol+_maxlen-1]
1051
+ else
1052
+ # can this be avoided if pcol is 0 XXX
1053
+ content.replace content[@pcol..-1] if @pcol > 0
1054
+ end
1055
+ end
1056
+ #$log.debug " content: #{content}"
1057
+ content
1058
+ end
1059
+
884
1060
  def list_data_changed
885
1061
  if row_count == 0 # added on 2009-02-02 17:13 so cursor not hanging on last row which could be empty
886
1062
  init_vars
887
1063
  @current_index = 0
888
- set_form_row
1064
+ # I had placed this at some time to get cursor correct. But if this listbox is updated
1065
+ # during entry in another field, then this steals the row. e.g. test1.rb 5
1066
+ #set_form_row
889
1067
  end
890
1068
  @repaint_required = true
891
1069
  end
1070
+ # set cursor column position
1071
+ # if i set col1 to @curpos, i can move around left right if key mapped
892
1072
  def set_form_col col1=0
893
1073
  # TODO BUFFERED use setrowcol @form.row, col
894
1074
  # TODO BUFFERED use cols_panned
1075
+ #col1 ||= 0
895
1076
  @cols_panned ||= 0 # RFED16 2010-02-17 23:40
896
1077
  # editable listboxes will involve changing cursor and the form issue
897
1078
  ## added win_col on 2010-01-04 23:28 for embedded forms BUFFERED TRYING OUT