canis 0.0.4

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 (134) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +45 -0
  3. data/CHANGES +52 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +24 -0
  7. data/Rakefile +2 -0
  8. data/canis.gemspec +25 -0
  9. data/examples/alpmenu.rb +46 -0
  10. data/examples/app.sample +19 -0
  11. data/examples/appemail.rb +191 -0
  12. data/examples/atree.rb +105 -0
  13. data/examples/bline.rb +181 -0
  14. data/examples/common/devel.rb +319 -0
  15. data/examples/common/file.rb +93 -0
  16. data/examples/data/README.markdown +9 -0
  17. data/examples/data/brew.txt +38 -0
  18. data/examples/data/color.2 +37 -0
  19. data/examples/data/gemlist.txt +59 -0
  20. data/examples/data/lotr.txt +12 -0
  21. data/examples/data/ports.txt +136 -0
  22. data/examples/data/table.txt +37 -0
  23. data/examples/data/tasks.csv +88 -0
  24. data/examples/data/tasks.txt +27 -0
  25. data/examples/data/todo.txt +16 -0
  26. data/examples/data/todocsv.csv +28 -0
  27. data/examples/data/unix1.txt +21 -0
  28. data/examples/data/unix2.txt +11 -0
  29. data/examples/dbdemo.rb +506 -0
  30. data/examples/dirtree.rb +177 -0
  31. data/examples/newtabbedwindow.rb +100 -0
  32. data/examples/newtesttabp.rb +92 -0
  33. data/examples/tabular.rb +212 -0
  34. data/examples/tasks.rb +179 -0
  35. data/examples/term2.rb +88 -0
  36. data/examples/testbuttons.rb +307 -0
  37. data/examples/testcombo.rb +102 -0
  38. data/examples/testdb.rb +182 -0
  39. data/examples/testfields.rb +208 -0
  40. data/examples/testflowlayout.rb +43 -0
  41. data/examples/testkeypress.rb +98 -0
  42. data/examples/testlistbox.rb +187 -0
  43. data/examples/testlistbox1.rb +199 -0
  44. data/examples/testmessagebox.rb +144 -0
  45. data/examples/testprogress.rb +116 -0
  46. data/examples/testree.rb +107 -0
  47. data/examples/testsplitlayout.rb +53 -0
  48. data/examples/testsplitlayout1.rb +49 -0
  49. data/examples/teststacklayout.rb +48 -0
  50. data/examples/testwsshortcuts.rb +68 -0
  51. data/examples/testwsshortcuts2.rb +129 -0
  52. data/lib/canis.rb +16 -0
  53. data/lib/canis/core/docs/index.txt +104 -0
  54. data/lib/canis/core/docs/list.txt +16 -0
  55. data/lib/canis/core/docs/style_help.yml +34 -0
  56. data/lib/canis/core/docs/tabbedpane.txt +15 -0
  57. data/lib/canis/core/docs/table.txt +31 -0
  58. data/lib/canis/core/docs/textpad.txt +48 -0
  59. data/lib/canis/core/docs/tree.txt +23 -0
  60. data/lib/canis/core/include/.DS_Store +0 -0
  61. data/lib/canis/core/include/action.rb +83 -0
  62. data/lib/canis/core/include/actionmanager.rb +49 -0
  63. data/lib/canis/core/include/appmethods.rb +179 -0
  64. data/lib/canis/core/include/bordertitle.rb +49 -0
  65. data/lib/canis/core/include/canisparser.rb +100 -0
  66. data/lib/canis/core/include/colorparser.rb +437 -0
  67. data/lib/canis/core/include/defaultfilerenderer.rb +64 -0
  68. data/lib/canis/core/include/io.rb +320 -0
  69. data/lib/canis/core/include/layouts/SplitLayout.rb +161 -0
  70. data/lib/canis/core/include/layouts/abstractlayout.rb +213 -0
  71. data/lib/canis/core/include/layouts/flowlayout.rb +104 -0
  72. data/lib/canis/core/include/layouts/stacklayout.rb +109 -0
  73. data/lib/canis/core/include/listbindings.rb +89 -0
  74. data/lib/canis/core/include/listeditable.rb +319 -0
  75. data/lib/canis/core/include/listoperations.rb +61 -0
  76. data/lib/canis/core/include/listselectionmodel.rb +388 -0
  77. data/lib/canis/core/include/multibuffer.rb +173 -0
  78. data/lib/canis/core/include/ractionevent.rb +73 -0
  79. data/lib/canis/core/include/rchangeevent.rb +27 -0
  80. data/lib/canis/core/include/rhistory.rb +95 -0
  81. data/lib/canis/core/include/rinputdataevent.rb +47 -0
  82. data/lib/canis/core/include/textdocument.rb +111 -0
  83. data/lib/canis/core/include/vieditable.rb +175 -0
  84. data/lib/canis/core/include/widgetmenu.rb +66 -0
  85. data/lib/canis/core/system/colormap.rb +165 -0
  86. data/lib/canis/core/system/keydefs.rb +32 -0
  87. data/lib/canis/core/system/ncurses.rb +237 -0
  88. data/lib/canis/core/system/panel.rb +129 -0
  89. data/lib/canis/core/system/window.rb +1081 -0
  90. data/lib/canis/core/util/ansiparser.rb +119 -0
  91. data/lib/canis/core/util/app.rb +696 -0
  92. data/lib/canis/core/util/basestack.rb +412 -0
  93. data/lib/canis/core/util/defaultcolorparser.rb +84 -0
  94. data/lib/canis/core/util/extras/README +5 -0
  95. data/lib/canis/core/util/extras/bottomline.rb +1815 -0
  96. data/lib/canis/core/util/extras/padreader.rb +192 -0
  97. data/lib/canis/core/util/focusmanager.rb +31 -0
  98. data/lib/canis/core/util/helpmanager.rb +160 -0
  99. data/lib/canis/core/util/oldwidgetshortcuts.rb +304 -0
  100. data/lib/canis/core/util/promptmenu.rb +235 -0
  101. data/lib/canis/core/util/rcommandwindow.rb +933 -0
  102. data/lib/canis/core/util/rdialogs.rb +520 -0
  103. data/lib/canis/core/util/textutils.rb +74 -0
  104. data/lib/canis/core/util/viewer.rb +238 -0
  105. data/lib/canis/core/util/widgetshortcuts.rb +508 -0
  106. data/lib/canis/core/widgets/applicationheader.rb +103 -0
  107. data/lib/canis/core/widgets/box.rb +58 -0
  108. data/lib/canis/core/widgets/divider.rb +310 -0
  109. data/lib/canis/core/widgets/extras/README.md +12 -0
  110. data/lib/canis/core/widgets/extras/rtextarea.rb +960 -0
  111. data/lib/canis/core/widgets/extras/stackflow.rb +474 -0
  112. data/lib/canis/core/widgets/keylabelprinter.rb +194 -0
  113. data/lib/canis/core/widgets/listbox.rb +326 -0
  114. data/lib/canis/core/widgets/listfooter.rb +86 -0
  115. data/lib/canis/core/widgets/rcombo.rb +210 -0
  116. data/lib/canis/core/widgets/rcontainer.rb +415 -0
  117. data/lib/canis/core/widgets/rlink.rb +30 -0
  118. data/lib/canis/core/widgets/rmenu.rb +970 -0
  119. data/lib/canis/core/widgets/rmenulink.rb +30 -0
  120. data/lib/canis/core/widgets/rmessagebox.rb +400 -0
  121. data/lib/canis/core/widgets/rprogress.rb +118 -0
  122. data/lib/canis/core/widgets/rtabbedpane.rb +631 -0
  123. data/lib/canis/core/widgets/rtabbedwindow.rb +70 -0
  124. data/lib/canis/core/widgets/rwidget.rb +3634 -0
  125. data/lib/canis/core/widgets/scrollbar.rb +147 -0
  126. data/lib/canis/core/widgets/statusline.rb +113 -0
  127. data/lib/canis/core/widgets/table.rb +1072 -0
  128. data/lib/canis/core/widgets/tabular.rb +264 -0
  129. data/lib/canis/core/widgets/textpad.rb +1674 -0
  130. data/lib/canis/core/widgets/tree.rb +690 -0
  131. data/lib/canis/core/widgets/tree/treecellrenderer.rb +150 -0
  132. data/lib/canis/core/widgets/tree/treemodel.rb +432 -0
  133. data/lib/canis/version.rb +3 -0
  134. metadata +229 -0
@@ -0,0 +1,235 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: promptmenu.rb
3
+ # Description: a simple 'most' type menu at bottom of screen.
4
+ # Moved from io.rb
5
+ # Author: j kepler http://github.com/mare-imbrium/canis/
6
+ # Date: 2014-04-25 - 12:32
7
+ # License: MIT
8
+ # Last update: 2014-04-27 00:10
9
+ # ----------------------------------------------------------------------------- #
10
+ # promptmenu.rb Copyright (C) 2012-2014 j kepler
11
+ # Depends on rcommandwindow for display_menu
12
+
13
+ module Canis
14
+
15
+ ## A *simple* way of creating menus that will appear in a single row.
16
+ # This copies the menu at the bottom of "most" upon pressing ":".
17
+ # hotkey is the key to invoke an item (a single digit letter)
18
+ #
19
+ # label is an action name
20
+ #
21
+ # desc is a description displayed after an item is chosen. Usually, its like:
22
+ #+ "Folding has been enabled" or "Searches will now be case sensitive"
23
+ #
24
+ # action may be a Proc or a symbol which will be called if item selected
25
+ #+ action may be another menu, so recursive menus can be built, but each
26
+ #+ should fit in a line, its a simple system.
27
+
28
+ CMenuItem = Struct.new( :hotkey, :label, :desc, :action )
29
+
30
+
31
+ ## An encapsulated form of yesterday's Most Menu
32
+ # It keeps the internals away from the user.
33
+ # Its not really OOP in the sense that the PromptMenu is not a MenuItem. That's how it is in
34
+ # our Menu system, and that led to a lot of painful coding (at least for me). This is quite
35
+ # simple. A submenu contains a PromptMenu in its action object and is evaluated in a switch.
36
+ # A recursive loop handles submenus.
37
+ #
38
+ # Prompting of menu options with suboptions etc.
39
+ # A block of code or symbol or proc is executed for any leaf node
40
+ # This allows us to define different menus for different objects on the screen, and not have to map
41
+ # all kinds of control keys for operations, and have the user remember them. Only one key invokes the menu
42
+ # and the rest are ordinary characters.
43
+ #
44
+ # == Example
45
+ # menu = PromptMenu.new self do
46
+ # item :s, :goto_start
47
+ # item :b, :goto_bottom
48
+ # item :r, :scroll_backward
49
+ # item :l, :scroll_forward
50
+ # submenu :m, "submenu" do
51
+ # item :p, :goto_last_position
52
+ # item :r, :scroll_backward
53
+ # item :l, :scroll_forward
54
+ # end
55
+ # end
56
+ # menu.display_new :title => 'window title', :prompt => "Choose:"
57
+
58
+ class PromptMenu
59
+ include Io
60
+ attr_reader :text
61
+ attr_reader :options
62
+ def initialize caller, text="Choose:", &block
63
+ @caller = caller
64
+ @text = text
65
+ @options = []
66
+ yield_or_eval &block if block_given?
67
+ end
68
+ def add *menuitem
69
+ item = nil
70
+ case menuitem.first
71
+ when CMenuItem
72
+ item = menuitem.first
73
+ @options << item
74
+ else
75
+ case menuitem.size
76
+ when 4
77
+ item = CMenuItem.new(*menuitem.flatten)
78
+ when 2
79
+ # if user only sends key and symbol
80
+ menuitem[3] = menuitem[1]
81
+ item = CMenuItem.new(*menuitem.flatten)
82
+ when 1
83
+ if menuitem.first.is_a? Action
84
+ item = menuitem.first
85
+ else
86
+ raise ArgumentError, "Don't know how to handle #{menuitem.size} : #{menuitem} "
87
+ end
88
+ else
89
+ raise ArgumentError, "Don't know how to handle #{menuitem.size} : #{menuitem} "
90
+ end
91
+ @options << item
92
+ end
93
+ return item
94
+ end
95
+ alias :item :add
96
+ def create_mitem *args
97
+ item = CMenuItem.new(*args.flatten)
98
+ end
99
+ # Added this, since actually it could have been like this 2011-12-22
100
+ def self.create_menuitem *args
101
+ item = CMenuItem.new(*args.flatten)
102
+ end
103
+ # create the whole thing using a MenuTree which has minimal information.
104
+ # It uses a hotkey and a code only. We are supposed to resolve the display text
105
+ # and actual proc from the caller using this code.
106
+ def menu_tree mt, pm = self
107
+ mt.each_pair { |ch, code|
108
+ if code.is_a? Canis::MenuTree
109
+ item = pm.add(ch, code.value, "")
110
+ current = PromptMenu.new @caller, code.value
111
+ item.action = current
112
+ menu_tree code, current
113
+ else
114
+ item = pm.add(ch, code.to_s, "", code)
115
+ end
116
+ }
117
+ end
118
+ #
119
+ # To allow a more rubyesque way of defining menus and submenus
120
+ def submenu key, label, &block
121
+ item = CMenuItem.new(key, label)
122
+ @options << item
123
+ item.action = PromptMenu.new @caller, label, &block
124
+ end
125
+ #
126
+ # Display prompt_menu in columns using commandwindow
127
+ # This is an improved way of showing the "most" like menu. The earlier
128
+ # format would only print in one row.
129
+ #
130
+ def display_columns config={}
131
+ prompt = config[:prompt] || "Choose: "
132
+ require 'canis/core/util/rcommandwindow'
133
+ layout = { :height => 5, :width => Ncurses.COLS-0, :top => Ncurses.LINES-6, :left => 0 }
134
+ rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title] || "Menu"
135
+ w = rc.window
136
+ r = 4
137
+ c = 1
138
+ color = $datacolor
139
+ begin
140
+ menu = @options
141
+ $log.debug " DISP MENU "
142
+ ret = 0
143
+ len = 80
144
+ while true
145
+ h = {}
146
+ valid = []
147
+ labels = []
148
+ menu.each{ |item|
149
+ if item.respond_to? :hotkey
150
+ hk = item.hotkey.to_s
151
+ else
152
+ raise ArgumentError, "Promptmenu needs hotkey or mnemonic"
153
+ end
154
+ # 187compat 2013-03-20 - 19:00 throws up
155
+ labels << "%c. %s " % [ hk.getbyte(0), item.label ]
156
+ h[hk] = item
157
+ valid << hk
158
+ }
159
+ #$log.debug " valid are #{valid} "
160
+ color = $datacolor
161
+ #print_this(win, str, color, r, c)
162
+ rc.display_menu labels, :indexing => :custom
163
+ ch=w.getchar()
164
+ rc.clear
165
+ #$log.debug " got ch #{ch} "
166
+ next if ch < 0 or ch > 255
167
+ if ch == 3 || ch == ?\C-g.getbyte(0)
168
+ clear_this w, r, c, color, len
169
+ print_this(w, "Aborted.", color, r,c)
170
+ break
171
+ end
172
+ ch = ch.chr
173
+ index = valid.index ch
174
+ if index.nil?
175
+ clear_this w, r, c, color, len
176
+ print_this(w, "Not valid. Valid are #{valid}. C-c/C-g to abort.", color, r,c)
177
+ sleep 1
178
+ next
179
+ end
180
+ #$log.debug " index is #{index} "
181
+ item = h[ch]
182
+ # I don;t think this even shows now, its useless
183
+ if item.respond_to? :desc
184
+ desc = item.desc
185
+ #desc ||= "Could not find desc for #{ch} "
186
+ desc ||= ""
187
+ clear_this w, r, c, color, len
188
+ print_this(w, desc, color, r,c)
189
+ end
190
+ action = item.action
191
+ case action
192
+ #when Array
193
+ when PromptMenu
194
+ # submenu
195
+ menu = action.options
196
+ title = rc.title
197
+ rc.title title +" => " + action.text # set title of window to submenu
198
+ when Proc
199
+ #rc.destroy
200
+ ##bottom needs to be refreshed somehow
201
+ #FFI::NCurses.ungetch ?j
202
+ rc.hide
203
+ ret = action.call
204
+ break
205
+ when Symbol
206
+ if @caller.respond_to?(action, true)
207
+ rc.hide
208
+ $log.debug "XXX: IO caller responds to action #{action} "
209
+ ret = @caller.send(action)
210
+ elsif @caller.respond_to?(:execute_this, true)
211
+ rc.hide
212
+ ret = @caller.send(:execute_this, action)
213
+ else
214
+ alert "PromptMenu: unidentified action #{action} for #{@caller.class} "
215
+ raise "PromptMenu: unidentified action #{action} for #{@caller.class} "
216
+ end
217
+
218
+ break
219
+ else
220
+ $log.debug " Unidentified flying class #{action.class} "
221
+ break
222
+ end
223
+ end # while
224
+ ensure
225
+ rc.destroy
226
+ rc = nil
227
+ end
228
+ end
229
+ alias :display_new :display_columns
230
+ alias :display :display_columns
231
+
232
+ end # class PromptMenu
233
+
234
+
235
+ end # module
@@ -0,0 +1,933 @@
1
+ =begin
2
+ * Name: rcommandwindow: pops up a status message at bottom of screen
3
+ creating a new window, so we don't have to worry about having window
4
+ handle.
5
+
6
+ * Description
7
+ * Author: jkepler (ABCD)
8
+ * Date: 2008-11-19 12:49
9
+ * License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
10
+ * file separated on 2009-01-13 22:39
11
+
12
+ == Changes
13
+ Have removed a lot of stuff that was either a duplication of other stuff, or that was not really
14
+ adding any value.
15
+
16
+ == Issues
17
+
18
+ Only display_menu is of any use, and can be taken out and placed in some util or extra.
19
+ It is called by numbered_menu which can be retained.
20
+ display_menu is used by Promptmenu too.
21
+
22
+ =end
23
+ require 'canis'
24
+
25
+ module Canis
26
+
27
+ # this is taken from view and replaces the call to view, since we were
28
+ # modifying view a bit too much to fit it into the needs here.
29
+ #
30
+ def command_list content, config={}, &block #:yield: textpad
31
+ wt = 0 # top margin
32
+ wl = 0 # left margin
33
+ wh = Ncurses.LINES-wt # height, goes to bottom of screen
34
+ ww = Ncurses.COLS-wl # width, goes to right end
35
+ layout = { :height => wh, :width => ww, :top => wt, :left => wl }
36
+ if config.has_key? :layout
37
+ layout = config[:layout]
38
+ case layout
39
+ when Array
40
+ wh, ww, wt, wl = layout
41
+ layout = { :height => wh, :width => ww, :top => wt, :left => wl }
42
+ when Hash
43
+ # okay
44
+ end
45
+ end
46
+
47
+ fp = config[:title] || ""
48
+ pf = config.fetch(:print_footer, false)
49
+ ta = config.fetch(:title_attrib, 'bold')
50
+ fa = config.fetch(:footer_attrib, 'bold')
51
+ b_ah = config[:app_header]
52
+ type = config[:content_type]
53
+
54
+ v_window = Canis::Window.new(layout)
55
+ v_form = Canis::Form.new v_window
56
+ v_window.name = "command-list"
57
+ colors = Ncurses.COLORS
58
+ back = :blue
59
+ back = 235 if colors >= 256
60
+ blue_white = get_color($datacolor, :white, back)
61
+
62
+ tprow = 0
63
+ ah = nil
64
+ if b_ah
65
+ ah = ApplicationHeader.new v_form, "", :text_center => fp
66
+ tprow += 1
67
+ end
68
+
69
+ textview = TextPad.new v_form do
70
+ name "CommandList"
71
+ row tprow
72
+ col 0
73
+ width ww
74
+ height wh-tprow # earlier 2 but seems to be leaving space.
75
+ title fp
76
+ title_attrib ta
77
+ print_footer pf
78
+ footer_attrib fa
79
+ #border_attrib :reverse
80
+ border_color blue_white
81
+ end
82
+
83
+ t = textview
84
+ items = {:header => ah}
85
+ begin
86
+ textview.set_content content, :content_type => type
87
+ if block_given?
88
+ if block.arity > 0
89
+ yield textview, items
90
+ else
91
+ textview.instance_eval(&block)
92
+ end
93
+ end
94
+ v_form.repaint
95
+ v_window.wrefresh
96
+ Ncurses::Panel.update_panels
97
+ retval = ""
98
+ # allow closing using q and Ctrl-q in addition to any key specified
99
+ # user should not need to specify key, since that becomes inconsistent across usages
100
+ # NOTE: no longer can we close with just a q since often apps using this trap char keys
101
+ # NOTE: 2727 is no longer operational, so putting just ESC
102
+ while((ch = v_window.getchar()) != ?\C-q.getbyte(0) )
103
+ # ideally we should be throwing a close rather than this since called will need keys.
104
+ retval = textview.current_value() if ch == config[:close_key]
105
+ break if ch == config[:close_key] || ch == 3|| ch == 27 # removed double esc 2014-05-04 - 17:30
106
+ # if you've asked for ENTER then i also check for 10 and 13
107
+ retval = textview.current_value() if (ch == 10 || ch == 13) && config[:close_key] == KEY_ENTER
108
+ break if (ch == 10 || ch == 13) && config[:close_key] == KEY_ENTER
109
+ v_form.handle_key ch
110
+ v_form.repaint
111
+ end
112
+ rescue => err
113
+ $log.error " command-list ERROR #{err} "
114
+ $log.debug(err.backtrace.join("\n"))
115
+ alert "#{err}"
116
+ #textdialog ["Error in command-list: #{err} ", *err.backtrace], :title => "Exception"
117
+ ensure
118
+ v_window.destroy if !v_window.nil?
119
+ end
120
+ return retval
121
+ end
122
+ ##
123
+ # Creates a window at the bottom of the screen for some operations.
124
+ # Used for some operations such as:
125
+ # - display a menu
126
+ # - display some interactive text
127
+ # - display some text
128
+ #
129
+ class CommandWindow
130
+ include Canis::Utils
131
+ dsl_accessor :box
132
+ dsl_accessor :title
133
+ attr_reader :config
134
+ attr_reader :layout
135
+ attr_reader :window # required for keyboard or printing
136
+ dsl_accessor :height, :width, :top, :left # 2009-01-06 00:05 after removing meth missing
137
+
138
+ def initialize form=nil, aconfig={}, &block # --- {{{
139
+ @config = aconfig
140
+ @config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
141
+ instance_eval &block if block_given?
142
+ if @layout.nil?
143
+ set_layout(1,Ncurses.COLS, -1, 0)
144
+ end
145
+ @height = @layout[:height]
146
+ @width = @layout[:width]
147
+ @window = Canis::Window.new(@layout)
148
+ @start = 0 # row for display of text with paging
149
+ @list = []
150
+ draw_box
151
+ @window.wrefresh
152
+ @panel = @window.panel
153
+ Ncurses::Panel.update_panels
154
+ @window.wrefresh
155
+ @row_offset = 0
156
+ if @box
157
+ @row_offset = 1
158
+ end
159
+ end # --- }}}
160
+
161
+ # draw the box, needed to redo this upon clear since clearing of windows
162
+ # was removing the top border 2014-05-04 - 20:14
163
+ def draw_box
164
+ if @box == :border
165
+ @window.box 0,0
166
+ elsif @box
167
+ @window.attron(Ncurses.COLOR_PAIR($normalcolor) | Ncurses::A_REVERSE)
168
+ @window.mvhline 0,0,1,@width
169
+ @window.printstring 0,0,@title, $normalcolor #, 'normal' if @title
170
+ @window.attroff(Ncurses.COLOR_PAIR($normalcolor) | Ncurses::A_REVERSE)
171
+ else
172
+ #@window.printstring 0,0,@title, $normalcolor, 'reverse' if @title
173
+ title @title
174
+ end
175
+ end
176
+
177
+ # not sure if this is really required. print_string is just fine.
178
+ # print a string.
179
+ # config can be :x :y :color_pair
180
+ def print_str text, config={}
181
+ win = config.fetch(:window, @window) # assuming its in App
182
+ x = config.fetch :x, 0
183
+ y = config.fetch :y, 0
184
+ color = config[:color_pair] || $datacolor
185
+ raise "no window for ask print in #{self.class} name: #{name} " unless win
186
+ color=Ncurses.COLOR_PAIR(color);
187
+ win.attron(color);
188
+ win.mvprintw(x, y, "%s" % text);
189
+ win.attroff(color);
190
+ win.refresh
191
+ end
192
+
193
+
194
+
195
+ # ---- windowing functions {{{
196
+ ##
197
+ ## message box
198
+ def stopping?
199
+ @stop
200
+ end
201
+
202
+ # todo handle mappings, so user can map keys TODO
203
+ def handle_keys
204
+ begin
205
+ while((ch = @window.getchar()) != 999 )
206
+ case ch
207
+ when -1
208
+ next
209
+ else
210
+ press ch
211
+ break if @stop
212
+ yield ch if block_given?
213
+ end
214
+ end
215
+ ensure
216
+ destroy
217
+ end
218
+ return #@selected_index
219
+ end
220
+
221
+ # handles a key, commandline
222
+ def press ch
223
+ ch = ch.getbyte(0) if ch.class==String ## 1.9
224
+ $log.debug " XXX press #{ch} " if $log.debug?
225
+ case ch
226
+ when -1
227
+ return
228
+ when KEY_F1, 27, ?\C-q.getbyte(0)
229
+ @stop = true
230
+ return
231
+ when KEY_ENTER, 10, 13
232
+ #$log.debug "popup ENTER : #{@selected_index} "
233
+ #$log.debug "popup ENTER : #{field.name}" if !field.nil?
234
+ @stop = true
235
+ return
236
+ when ?\C-d.getbyte(0)
237
+ @start += @height-1
238
+ bounds_check
239
+ when KEY_UP
240
+ @start -= 1
241
+ @start = 0 if @start < 0
242
+ when KEY_DOWN
243
+ @start += 1
244
+ bounds_check
245
+ when ?\C-b.getbyte(0)
246
+ @start -= @height-1
247
+ @start = 0 if @start < 0
248
+ when 0
249
+ @start = 0
250
+ end
251
+ Ncurses::Panel.update_panels();
252
+ Ncurses.doupdate();
253
+ @window.wrefresh
254
+ end
255
+
256
+ # might as well add more keys for paging.
257
+ def configure(*val , &block)
258
+ case val.size
259
+ when 1
260
+ return @config[val[0]]
261
+ when 2
262
+ @config[val[0]] = val[1]
263
+ instance_variable_set("@#{val[0]}", val[1])
264
+ end
265
+ instance_eval &block if block_given?
266
+ end
267
+ def cget param
268
+ @config[param]
269
+ end
270
+
271
+ def set_layout(height=0, width=0, top=0, left=0)
272
+ # negative means top should be n rows from last line. -1 is last line
273
+ if top < 0
274
+ top = Ncurses.LINES-top
275
+ end
276
+ @layout = { :height => height, :width => width, :top => top, :left => left }
277
+ @height = height
278
+ @width = width
279
+ end
280
+ def show
281
+ @window.show
282
+ end
283
+ # this really helps if we are creating another window over this and we find the lower window
284
+ # still showing through. destroy does not often work so this clears current window.
285
+ # However, lower window may still have a black region. FIXME
286
+ def hide
287
+ @window.hide
288
+ Window.refresh_all
289
+ end
290
+ def destroy
291
+ @window.destroy
292
+ end
293
+ def OLDdestroy
294
+ $log.debug "DESTROY : rcommandwindow"
295
+ if @window
296
+ begin
297
+ panel = @window.panel
298
+ Ncurses::Panel.del_panel(panel.pointer) if panel
299
+ @window.delwin
300
+ rescue => exc
301
+ end
302
+ end
303
+ end
304
+ # refresh whatevers painted onto the window
305
+ def refresh
306
+ Ncurses::Panel.update_panels();
307
+ Ncurses.doupdate();
308
+ @window.wrefresh
309
+ end
310
+ # clears the window, leaving the title line as is, from row 1 onwards
311
+ def clear
312
+ @window.wmove 1,1
313
+ @window.wclrtobot
314
+ #@window.box 0,0 if @box == :border
315
+ draw_box
316
+ # lower line of border will get erased currently since we are writing to
317
+ # last line FIXME
318
+ end
319
+ # ---- windowing functions }}}
320
+
321
+ # modify the window title, or get it if no params passed.
322
+ def title t=nil # --- {{{
323
+ return @title unless t
324
+ @title = t
325
+ @window.printstring 0,0,@title, $normalcolor, 'reverse' if @title
326
+ end # --- }}}
327
+
328
+ #
329
+ # Displays list in a window at bottom of screen, if large then 2 or 3 columns.
330
+ # @param [Array] list of string to be displayed
331
+ # @param [Hash] configuration options: indexing and indexcolor
332
+ # indexing - can be letter or number. Anything else will be ignored, however
333
+ # it will result in first letter being highlighted in indexcolor
334
+ # indexcolor - color of mnemonic, default green
335
+ def display_menu list, options={} # --- {{{
336
+ indexing = options[:indexing]
337
+ #indexcolor = options[:indexcolor] || get_color($normalcolor, :yellow, :black)
338
+ indexcolor = $datacolor || 7 # XXX above line crashing on choose()
339
+ indexatt = Ncurses::A_BOLD
340
+ #
341
+ # the index to start from (used when scrolling a long menu such as file list)
342
+ startindex = options[:startindex] || 0
343
+
344
+ max_cols = 3 # maximum no of columns, we will reduce based on data size
345
+ l_succ = "`"
346
+ act_height = @height
347
+ if @box
348
+ act_height = @height - 2
349
+ end
350
+ lh = list.size
351
+ if lh < act_height
352
+ $log.debug "DDD inside one window" if $log.debug?
353
+ list.each_with_index { |e, i|
354
+ text = e
355
+ case e
356
+ when Array
357
+ text = e.first + " ..."
358
+ end
359
+ if indexing == :number
360
+ mnem = i+1
361
+ text = "%d. %s" % [i+1, text]
362
+ elsif indexing == :letter
363
+ mnem = l_succ.succ!
364
+ text = "%s. %s" % [mnem, text]
365
+ end
366
+ @window.printstring i+@row_offset, 1, text, $normalcolor
367
+ if indexing
368
+ @window.mvchgat(y=i+@row_offset, x=1, max=1, indexatt, indexcolor, nil)
369
+ end
370
+ }
371
+ else
372
+ $log.debug "DDD inside two window" if $log.debug?
373
+ row = 0
374
+ h = act_height
375
+ cols = (lh*1.0 / h).ceil
376
+ cols = max_cols if cols > max_cols
377
+ # sometimes elements are large like directory paths, so check size
378
+ datasize = list.first.length
379
+ if datasize > @width/3 # keep safety margin since checking only first row
380
+ cols = 1
381
+ elsif datasize > @width/2
382
+ cols = [2,cols].min
383
+ end
384
+ adv = (@width/cols).to_i
385
+ colct = 0
386
+ col = 1
387
+ $log.debug "DDDcols #{cols}, adv #{adv} size: #{lh} h: #{act_height} w #{@width} " if $log.debug?
388
+ list.each_with_index { |e, i|
389
+ text = e
390
+ # signify that there's a deeper level
391
+ case e
392
+ when Array
393
+ text = e.first + "..."
394
+ end
395
+ if indexing == :number
396
+ mnem = i+1
397
+ text = "%d. %s" % [mnem, text]
398
+ elsif indexing == :letter
399
+ mnem = l_succ.succ!
400
+ text = "%s. %s" % [mnem, text]
401
+ end
402
+ # print only within range and window height
403
+ #if i >= startindex && row < @window.actual_height
404
+ if i >= startindex && row < @window.height
405
+ $log.debug "XXX: MENU #{i} > #{startindex} row #{row} col #{col} "
406
+ @window.printstring row+@row_offset, col, text, $normalcolor
407
+ if indexing
408
+ @window.mvchgat(y=row+@row_offset, x=col, max=1, indexatt, indexcolor, nil)
409
+ end
410
+ colct += 1
411
+ if colct == cols
412
+ col = 1
413
+ row += 1
414
+ colct = 0
415
+ else
416
+ col += adv
417
+ end
418
+ end # startindex
419
+ }
420
+ end
421
+ Ncurses::Panel.update_panels();
422
+ Ncurses.doupdate();
423
+ @window.wrefresh
424
+ end # --- }}}
425
+
426
+
427
+ end # class CommandWindow
428
+
429
+ # a generic key dispatcher that can be used in various classes for handling keys,
430
+ # setting key_map and processing keys.
431
+ module KeyDispatcher # --- {{{
432
+
433
+ # key handler of Controlphandler
434
+ # This sets +@keyint+ with the value read by window.
435
+ # This sets +@keychr+ with the +chr+ value of +ch+ if ch between 32 and 127 exclusive.
436
+ # @param [Fixnum] ch is key read by window.
437
+ def handle_key ch
438
+ $log.debug " KeyDispatcher GOT KEY #{ch} "
439
+ @keyint = ch
440
+ @keychr = nil
441
+ chr = nil
442
+ chr = ch.chr if ch > 32 and ch < 127
443
+ @keychr = chr
444
+
445
+ ret = process_key ch
446
+ # revert to the basic handling of key_map and refreshing pad.
447
+ #####
448
+ # NOTE
449
+ # this is being done where we are creating some kind of front for a textpad by using +view+
450
+ # so we steal some keys, and pass the rest to +view+. Otherwise, next line is not needed.
451
+ # +@source+ typically would be handle to textpad yielded by +view+.
452
+ #####
453
+ @source._handle_key(ch) if ret == :UNHANDLED and @source
454
+ end
455
+
456
+ # checks the key against +@key_map+ if its set
457
+ # @param [Fixnum] ch character read by +Window+
458
+ # @return [0, :UNHANDLED] 0 if processed, :UNHANDLED if not processed so higher level can process
459
+ def process_key ch
460
+ chr = nil
461
+ if ch > 0 and ch < 256
462
+ chr = ch.chr
463
+ end
464
+ return :UNHANDLED unless @key_map
465
+ @key_map.each_pair do |k,p|
466
+ #$log.debug "KKK: processing key #{ch} #{chr} "
467
+ if (k == ch || k == chr)
468
+ #$log.debug "KKK: checking match == #{k}: #{ch} #{chr} "
469
+ # compare both int key and chr
470
+ #$log.debug "KKK: found match 1 #{ch} #{chr} "
471
+ p.call(self, ch)
472
+ return 0
473
+ elsif k.respond_to? :include?
474
+ #$log.debug "KKK: checking match include #{k}: #{ch} #{chr} "
475
+ # this bombs if its a String and we check for include of a ch.
476
+ if !k.is_a?( String ) && (k.include?( ch ) || k.include?(chr))
477
+ #$log.debug "KKK: found match include #{ch} #{chr} "
478
+ p.call(self, ch)
479
+ return 0
480
+ end
481
+ elsif k.is_a? Regexp
482
+ if k.match(chr)
483
+ #$log.debug "KKK: found match regex #{ch} #{chr} "
484
+ p.call(self, ch)
485
+ return 0
486
+ end
487
+ end
488
+ end
489
+ return :UNHANDLED
490
+ end
491
+ # setting up some keys
492
+ # This is currently an insertion key map, if you want a String named +@buffer+ updated.
493
+ # Expects buffer_changed and set_buffer to exist as well as +buffer()+.
494
+ # TODO add left and right arrow keys for changing insertion point. And other keys.
495
+ # XXX Why are we trying to duplicate a Field here ??
496
+ def default_string_key_map
497
+ require 'canis/core/include/action'
498
+ @key_map ||= {}
499
+ @key_map[ Regexp.new('[a-zA-Z0-9_\.\/]') ] = Action.new("Append to pattern") { |obj, ch|
500
+ obj.buffer << ch.chr
501
+ obj.buffer_changed
502
+ }
503
+ @key_map[ [127, ?\C-h.getbyte(0)] ] = Action.new("Delete Prev Char") { |obj, ch|
504
+ # backspace
505
+ buff = obj.buffer
506
+ buff = buff[0..-2] unless buff == ""
507
+ obj.set_buffer buff
508
+ }
509
+ end
510
+
511
+ # convenience method to bind a key or array /range of keys, or regex to a block
512
+ # @param [int, String, #include?, Regexp] keycode If the user presses this key, then execute given block
513
+ # @param [String, Action] descr is either a textual description of the key
514
+ # or an Action object
515
+ # @param [block] unless an Action object has been passed, a block is passed for execution
516
+ #
517
+ # @example
518
+ # bind_key '%', 'Do something' {|obj, ch| actions ... }
519
+ # bind_key [?\C-h.getbyte(0), 127], 'Delete something' {|obj, ch| actions ... }
520
+ # bind_key Regexp.new('[a-zA-Z_\.]'), 'Append char' {|obj, ch| actions ... }
521
+ # TODO test this
522
+ def bind_key keycode, descr, &block
523
+ if descr.is_a? Action
524
+ @key_map[keycode] = descr
525
+ else
526
+ @key_map[keycode] = Action.new(descr), block
527
+ end
528
+ end
529
+ end # module }}}
530
+
531
+ # presents given list in numbered format in a window above last line
532
+ # and accepts input on last line
533
+ # The list is a list of strings. e.g.
534
+ # %w{ ruby perl python haskell }
535
+ # Multiple levels can be given as:
536
+ # list = %w{ ruby perl python haskell }
537
+ # list[0] = %w{ ruby ruby1.9 ruby 1.8 rubinius jruby }
538
+ # In this case, "ruby" is the first level option. The others are used
539
+ # in the second level. This might make it clearer. first3 has 2 choices under it.
540
+ # [ "first1" , "first2", ["first3", "second1", "second2"], "first4"]
541
+ #
542
+ # Currently, we return an array containing each selected level
543
+ #
544
+ # @return [Array] selected option/s from list
545
+ def numbered_menu list1, config={}
546
+ if list1.nil? || list1.empty?
547
+ #say_with_pause "empty list passed to numbered_menu" # 2014-04-25
548
+ # remove bottomline
549
+ print_error_message "Empty list passed to numbered_menu"
550
+ return nil
551
+ end
552
+ prompt = config[:prompt] || "Select one: "
553
+ require 'canis/core/util/rcommandwindow'
554
+ layout = { :height => 5, :width => Ncurses.COLS-1, :top => Ncurses.LINES-6, :left => 0 }
555
+ rc = CommandWindow.new nil, :layout => layout, :box => true, :title => config[:title]
556
+ w = rc.window
557
+ # should we yield rc, so user can bind keys or whatever
558
+ # attempt a loop so we do levels.
559
+ retval = []
560
+ begin
561
+ while true
562
+ rc.display_menu list1, :indexing => :number
563
+ #ret = ask(prompt, Integer ) { |q| q.in = 1..list1.size }
564
+ # if class is specifited then update type in Field
565
+ ret = rb_gets(prompt) {|f| f.datatype = 1.class ; f.type :integer; f.valid_range(1..list1.size)}
566
+ val = list1[ret-1]
567
+ if val.is_a? Array
568
+ retval << val[0]
569
+ $log.debug "NL: #{retval} "
570
+ list1 = val[1..-1]
571
+ rc.clear
572
+ else
573
+ retval << val
574
+ $log.debug "NL1: #{retval} "
575
+ break
576
+ end
577
+ end
578
+ ensure
579
+ rc.destroy
580
+ rc = nil
581
+ end
582
+ #list1[ret-1]
583
+ $log.debug "NL2: #{retval} , #{retval.class} "
584
+ retval
585
+ end
586
+
587
+ # This is a variation of display_list which is more for selecting a file, or dir.
588
+ # It maps some keys to go up to parent dir, and to step into directory under cursor
589
+ # if you are in directory mode.
590
+ # @param [String] (optional) glob is a glob to apply when creating a listing
591
+ # @param [Hash] config options for configuring the listing
592
+ # @option config [Boolean] :recursive Should listing recurse, default true
593
+ # @option config [Boolean] :dirs Should list directories only, default false
594
+ # @option config [String] :startdir Directory to use as current
595
+ # You may also add other config pairs to be passed to textpad such as title
596
+ #
597
+ # NOTE: if you pass a glob, then :recursive will not apply. You must specify
598
+ # recursive by prepending "**/" or inserting it in the appropriate place such
599
+ # as "a/b/c/**/*rb". We would not know where to place the "**/".
600
+ #
601
+ # @example list directories recursively
602
+ #
603
+ # str = choose_file :title => "Select a file",
604
+ # :recursive => true,
605
+ # :dirs => true,
606
+ #
607
+ def choose_file glob, config={}
608
+ if glob.is_a? Hash
609
+ config = glob
610
+ glob = nil
611
+ end
612
+ frec = true
613
+ frec = config.delete :recursive if config.key? :recursive
614
+ fdir = config.delete :dirs
615
+ if glob.nil?
616
+ glob = "*"
617
+ if frec
618
+ glob = "**/*"
619
+ end
620
+ if fdir
621
+ glob << "/"
622
+ end
623
+ end
624
+ maxh = 15
625
+ # i am not going through that route, since going up and down a dir will be difficult in a generic
626
+ # case the glob has the dir in it, or i pass directory to the handler.
627
+ # why not Dir.pwd in next line ?? XXX
628
+ #directory = Pathname.new(File.expand_path(File.dirname($0)))
629
+ #_d = config.delete :directory
630
+ #if _d
631
+ #directory = Pathname.new(File.expand_path(_d))
632
+ #end
633
+ directory = config.delete :directory
634
+ # this keeps going up with each invocation if I send ".."
635
+ Dir.chdir(directory) if directory
636
+ command = config.delete(:command)
637
+ text = Dir.glob(glob)
638
+ #text = Dir[File.join(directory.to_s, glob)]
639
+ if !text or text.empty?
640
+ text = ["No entries"]
641
+ end
642
+ if text.size < maxh
643
+ config[:height] = text.size + 1
644
+ end
645
+ # calc window coords
646
+ _update_default_settings config
647
+ default_layout = config[:layout]
648
+ config[:close_key] = 1001
649
+ command_list(text, config) do |t, hash|
650
+ t.suppress_borders true
651
+ t.print_footer false
652
+ #t.fixed_bounds config.delete(:fixed_bounds)
653
+ t.key_handler = ControlPHandler.new(t)
654
+ t.key_handler.maxht = maxh
655
+ t.key_handler.default_layout = default_layout
656
+ t.key_handler.header = hash[:header]
657
+ t.key_handler.recursive_search(glob)
658
+ t.key_handler.directory_key_map
659
+ end
660
+ end
661
+
662
+ # NOTE: moved from bottomline since it used commandwindow but now we;ve
663
+ # moved away from ListObject to view. and i wonder what this really
664
+ # gives ?
665
+ # WARNING: if you actually use this, please copy it to your app, since
666
+ # it may be removed. I don;t see what real purpose it achieves. It is
667
+ # now a wrapper over Canis::Viewer.view
668
+ #
669
+ # Displays text at the bottom of the screen, close the screen with
670
+ # ENTER.
671
+ # @param text can be file name or Array of Strings.
672
+ # @param config is a Hash. :height which will be from bottom of screen
673
+ # and defaults to 15.
674
+ # All other config elements are passed to +view+. See viewer.rb.
675
+ def display_text text, config={}
676
+ _update_default_settings config
677
+ if text.is_a? String
678
+ if File.exists? text
679
+ text = File.open(text, 'r').read.split("\n")
680
+ end
681
+ end
682
+ command_list(text, config) do |t|
683
+ t.suppress_borders true
684
+ t.print_footer false
685
+ end
686
+ end
687
+ # update given hash with layout, close_key and app_header
688
+ # so this is shared across various methods.
689
+ # The layout created is weighted to the bottom, so it is always ending at the second last row
690
+ def _update_default_settings config={}
691
+ ht = config[:height] || 15
692
+ sh = Ncurses.LINES-1
693
+ sc = Ncurses.COLS-0
694
+ layout = [ ht, sc, sh-ht ,0]
695
+ config[:layout] = layout
696
+ config[:close_key] = KEY_ENTER
697
+ config[:app_header] = true
698
+ # repeated resetting seems to play with left and other things.
699
+ # let's say we know that my window will always have certain settings, then let me do a check for them in padrefresh
700
+ # in this window the only thing changing is the top (based on rows). all else is same.
701
+ #config[:fixed_bounds] = [nil, 0, sh, sc]
702
+ end
703
+ # create a new layout array based on size of data given
704
+ # The layout created is weighted to the bottom, so it is always ending at the second last row
705
+ # The +top+ keeps changing based on height.
706
+ def _new_layout size
707
+ ht = size
708
+ #ht = 15
709
+ sh = Ncurses.LINES-1
710
+ sc = Ncurses.COLS-0
711
+ layout = [ ht, sc, sh-ht ,0]
712
+ end
713
+ # return a blank if user quits list, or the value
714
+ # can we prevent a user from quitting, he must select ?
715
+ #
716
+ # Display a list of valies given in +text+ and allows user to shrink the list based on keys entered
717
+ # much like control-p.
718
+ #
719
+ # @param [Array<String>] array of Strings to print
720
+ # @param [ Hash ] config hash passed to Viewer.view
721
+ # May contain +:command+ which is a Proc that replenishes the list everytime user
722
+ # enters a key (updates the search string). The proc is supplied user-entered string.
723
+ # The following example is a proc that matches all the files returned by Dir.glob
724
+ # with the user entered string.
725
+ #
726
+ # Proc.new {|str| Dir.glob("**/*").select do |p| p.index str; end }
727
+ #
728
+ # @return [ String ] text of line user pressed ENTER on, or "" if user pressed ESC or C-c
729
+ # x show keys entered
730
+ # x shrink the pad based on results
731
+ # x if no results show somethinf like "No entries". or don't change
732
+ # TODO take left and right arrow key and adjust insert point
733
+ # TODO have some proc so user can keep querying, rather than passing a list. this way if the
734
+ # list is really long, all values don't need to be passed.
735
+ def display_list text, config={}
736
+ maxh = 15
737
+ _update_default_settings config
738
+ default_layout = config[:layout].dup
739
+ command = config.delete(:command)
740
+ command_list(text, config) do |t, hash|
741
+ t.suppress_borders true
742
+ t.print_footer false
743
+ #t.fixed_bounds config.delete(:fixed_bounds)
744
+ t.key_handler = ControlPHandler.new(t)
745
+ t.key_handler.maxht = maxh
746
+ t.key_handler.default_layout = default_layout
747
+ t.key_handler.header = hash[:header]
748
+ t.key_handler.command = command if command
749
+ end
750
+ end
751
+
752
+ # This is a keyhandler that traps some keys, much like control-p which filters down a list
753
+ # based on some alpha numeric chars. The rest, such as arrow-keys, are passed to the key_map
754
+ #
755
+ class ControlPHandler # --- {{{
756
+ require 'canis/core/include/action'
757
+ include Canis::KeyDispatcher
758
+
759
+ attr_accessor :maxht
760
+ attr_accessor :default_layout
761
+ # string the user is currently entering in (pattern to filter on)
762
+ attr_accessor :buffer
763
+ # application_header object whose text can be changed
764
+ attr_accessor :header
765
+ attr_accessor :key_map
766
+ # specify command to requery data
767
+ attr_accessor :command
768
+ attr_reader :source
769
+ attr_reader :keyint
770
+ attr_reader :keychr
771
+ def initialize source
772
+ @source = source
773
+ @list = source.text
774
+ # backup of data to refilter from if no command given to update
775
+ @__list = @list
776
+ @buffer = ""
777
+ @maxht ||=15
778
+ default_string_key_map
779
+ default_key_map
780
+ @no_match = false
781
+ end
782
+
783
+ # a default proc to requery data based on glob supplied and the pattern user enters
784
+ def recursive_search glob="**/*"
785
+ @command = Proc.new {|str| Dir.glob(glob).select do |p| p.index str; end }
786
+ end
787
+
788
+ # specify command to requery data
789
+ #def command &block
790
+ #@command = block
791
+ #end
792
+ #alias :command= :command
793
+
794
+ # signal that the data has changed and should be redisplayed
795
+ # with window resizing etc.
796
+ def data_changed list
797
+ sz = list.size
798
+ @source.text(list)
799
+ wh = @source.form.window.height
800
+ @source.form.window.hide
801
+ th = @source.height
802
+ sh = Ncurses.LINES-1
803
+ if sz < @maxht
804
+ # rows is less than tp size so reduce tp and window
805
+ @source.height = sz
806
+ nl = _new_layout sz+1
807
+ $log.debug "XXX: adjust ht to #{sz} layout is #{nl} size is #{sz}"
808
+ @source.form.window.resize_with(nl)
809
+ #Window.refresh_all
810
+ else
811
+ # expand the window ht to maxht
812
+ tt = @maxht-1
813
+ @source.height = tt
814
+ nl = _new_layout tt+1
815
+ $log.debug "XXX: increase ht to #{tt} def layout is #{nl} size is #{sz}"
816
+ @source.form.window.resize_with(nl)
817
+ end
818
+
819
+ @source.fire_dimension_changed
820
+
821
+ @source.init_vars # do if rows is less than current_index.
822
+ @source.set_form_row
823
+ @source.form.window.show
824
+
825
+ #Window.refresh_all
826
+ @source.form.window.wrefresh
827
+ Ncurses::Panel.update_panels();
828
+ Ncurses.doupdate();
829
+ end
830
+ # modify the pattern (used if some procs are trying to change using handle to self)
831
+ def set_buffer str
832
+ @buffer = str
833
+ buffer_changed
834
+ end
835
+ # signal that the user has added or deleted a char from the pattern
836
+ # and data should be requeried, etc
837
+ #
838
+ def buffer_changed
839
+ # display the pattern on the header
840
+ @header.text1(">>>#{@buffer}_") if @header
841
+ @header.text_right(Dir.pwd) if @header
842
+ @no_match = false
843
+
844
+ if @command
845
+ @list = @command.call(@buffer)
846
+ else
847
+ @list = @__list.select do |line|
848
+ line.index @buffer
849
+ end
850
+ end
851
+ sz = @list.size
852
+ if sz == 0
853
+ Ncurses.beep
854
+ #return 1
855
+ #this should make ENTER and arrow keys unusable except for BS or Esc,
856
+ @list = ["No entries"]
857
+ @no_match = true
858
+ end
859
+ data_changed @list
860
+ 0
861
+ end
862
+
863
+ # key handler of Controlphandler which overrides KeyDispatcher since we need to
864
+ # intercept KEY_ENTER
865
+ # @param [Fixnum] ch is key read by window.
866
+ # WARNING: Please note that if this is used in +Viewer.view+, that +view+
867
+ # has already trapped CLOSE_KEY which is KEY_ENTER/13 for closing, so we won't get 13
868
+ # anywhere
869
+ def handle_key ch
870
+ $log.debug " HANDLER GOT KEY #{ch} "
871
+ @keyint = ch
872
+ @keychr = nil
873
+ # accumulate keys in a string
874
+ # need to track insertion point if user uses left and right arrow
875
+ @buffer ||= ""
876
+ chr = nil
877
+ chr = ch.chr if ch > 47 and ch < 127
878
+ @keychr = chr
879
+ # Don't let user hit enter or keys if no match
880
+ if [13,10, KEY_ENTER, KEY_UP, KEY_DOWN].include? ch
881
+ if @no_match
882
+ $log.warn "XXX: KEY GOT WAS #{ch}, #{chr} "
883
+ # viewer has already blocked KEY_ENTER !
884
+ return 0 if [13,10, KEY_ENTER, KEY_UP, KEY_DOWN].include? ch
885
+ else
886
+ if [13,10, KEY_ENTER].include? ch
887
+ @source.form.window.ungetch(1001)
888
+ return 0
889
+ end
890
+ end
891
+ end
892
+ ret = process_key ch
893
+ # revert to the basic handling of key_map and refreshing pad.
894
+ # but this will rerun the keys and may once again run a mapping.
895
+ @source._handle_key(ch) if ret == :UNHANDLED
896
+ end
897
+
898
+ # setting up some keys
899
+ def default_key_map
900
+ tp = source
901
+ source.bind_key(?\M-n.getbyte(0), 'goto_end'){ tp.goto_end }
902
+ source.bind_key(?\M-p.getbyte(0), 'goto_start'){ tp.goto_start }
903
+ end
904
+
905
+ # specific actions for directory listers
906
+ # currently for stepping into directory under cursor
907
+ # and going to parent dir.
908
+ def directory_key_map
909
+ @key_map["<"] = Action.new("Goto Parent Dir") { |obj|
910
+ # go to parent dir
911
+ $log.debug "KKK: called proc for <"
912
+ Dir.chdir("..")
913
+ obj.buffer_changed
914
+ }
915
+ @key_map[">"] = Action.new("Change Dir"){ |obj|
916
+ $log.debug "KKK: called proc for > : #{obj.current_value} "
917
+ # step into directory
918
+
919
+ dir = obj.current_value
920
+ if File.directory? dir
921
+ Dir.chdir dir
922
+ obj.buffer_changed
923
+ end
924
+ }
925
+ end
926
+
927
+ # the line on which focus is.
928
+ def current_value
929
+ @source.current_value
930
+ end
931
+ end # -- class }}}
932
+
933
+ end # module