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,129 @@
1
+ require "ffi-ncurses"
2
+ module Ncurses # changed on 2011-09-8
3
+ # making minimal changes as per ffi-ncurses 0.4.0 which implements panels
4
+ #module Canis # too many places call Ncurses::Panel
5
+ class Panel #< Struct.new(:pointer)
6
+
7
+ def initialize(window)
8
+ if window.respond_to?(:pointer)
9
+ @pointer = FFI::NCurses.new_panel(window.pointer)
10
+ else
11
+ @pointer = FFI::NCurses.new_panel(window)
12
+ end
13
+ end
14
+ def pointer
15
+ @pointer
16
+ end
17
+
18
+ # Puts panel below all other panels.
19
+ def bottom_panel
20
+ FFI::NCurses.bottom_panel(@pointer)
21
+ end
22
+ alias bottom bottom_panel
23
+
24
+ # Put the visible panel on top of all other panels in the stack.
25
+ #
26
+ # To ensure compatibility across platforms, use this method instead of
27
+ # {show_panel} when the panel is shown.
28
+ def top_panel
29
+ FFI::NCurses.top_panel(@pointer)
30
+ end
31
+ alias top top_panel
32
+
33
+ # Makes hidden panel visible by placing it on the top of the stack.
34
+ #
35
+ # To ensure compatibility across platforms, use this method instead of
36
+ # {top_panel} when the panel is hidden.
37
+ def show_panel
38
+ FFI::NCurses.show_panel(@pointer)
39
+ end
40
+ alias show show_panel
41
+
42
+ # Removes the given panel from the panel stack and thus hides it from
43
+ # view.
44
+ # The PANEL structure is not lost, merely removed from the stack.
45
+ def hide_panel
46
+ FFI::NCurses.hide_panel(@pointer)
47
+ end
48
+ alias hide hide_panel
49
+
50
+ # Returns a pointer to the window of the given panel.
51
+ def panel_window
52
+ FFI::NCurses.panel_window(@pointer)
53
+ end
54
+ alias window panel_window
55
+
56
+ # Replace the window of the panel with the given window.
57
+ # Useful, for example, if you want to resize a panel.
58
+ # You can call {replace_panel} on the output of {wresize}.
59
+ # It does not change the position of the panel in the stack.
60
+ def replace_panel(window)
61
+ FFI::NCurses.replace_panel(@pointer, window)
62
+ end
63
+ alias replace replace_panel
64
+
65
+ # Move the panel window so that its upper-left corner is at
66
+ # (+starty+,+startx+).
67
+ # It does not change the position of the panel in the stack.
68
+ # Be sure to use this method instead of {mvwin}, to move a panel window.
69
+ def move_panel(starty = 0, startx = 0)
70
+ FFI::NCurses.move_panel(@pointer, starty, startx)
71
+ end
72
+ alias move move_panel
73
+
74
+ # Returns true if the panel is in the panel stack, false if not.
75
+ # Returns ERR if the panel pointer is a null pointer.
76
+ def panel_hidden
77
+ FFI::NCurses.panel_hidden(@pointer) == 0
78
+ end
79
+ alias hidden? panel_hidden
80
+
81
+ # Returns pointer to the panel above.
82
+ def panel_above
83
+ FFI::NCurses.panel_above(@pointer)
84
+ end
85
+ alias above panel_above
86
+
87
+ # Return a pointer to the panel just below panel.
88
+ # If the panel argument is a pointer to 0, it returns a pointer to the
89
+ # top panel in the stack.
90
+ def panel_below
91
+ FFI::NCurses.panel_below(@pointer)
92
+ end
93
+ alias below panel_below
94
+
95
+ # Returns the user pointer for a given panel.
96
+ def panel_userptr
97
+ FFI::NCurses.panel_userptr(@pointer)
98
+ end
99
+ alias userptr panel_userptr
100
+
101
+ # sets the panel's user pointer.
102
+ def set_panel_userptr(user_pointer)
103
+ FFI::NCurses.set_panel_userptr(@pointer, user_pointer)
104
+ end
105
+ alias userptr= set_panel_userptr
106
+
107
+ # Remove the panel from the stack and deallocate the PANEL structure.
108
+ # Doesn't remove the associated window.
109
+ def del_panel
110
+ FFI::NCurses.del_panel(@pointer)
111
+ end
112
+ alias del del_panel
113
+ alias delete del_panel
114
+
115
+ class << self
116
+ # these will be used when you say Ncurses::Panel.del_panel(@panel.pointer)
117
+ # You could directly say FFI:NCurses or even @panel.del_panel.
118
+ def update_panels
119
+ FFI::NCurses.update_panels
120
+ end
121
+ def method_missing(name, *args)
122
+ if (FFI::NCurses.respond_to?(name))
123
+ return FFI::NCurses.send(name, *args)
124
+ end
125
+ raise "Panel did not respond_to #{name} "
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,1081 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: window.rb
3
+ # Description: A wrapper over window
4
+ # Author: jkepler http://github.com/mare-imbrium/canis/
5
+ # Date: Around for a long time
6
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ # Last update: 2014-07-10 00:13
8
+ #
9
+ # == CHANGED
10
+ # removed dead or redudant code - 2014-04-22 - 12:53
11
+ # - replaced getchar with new simpler one - 2014-05-04
12
+ # - introduced key_tos to replace keycode_tos, moved to Util in rwidget.rb
13
+ # - reintroduced nedelay and reduced escdelay
14
+ #
15
+ # == TODO
16
+ # strip and remove cruft. Several methods marked as deprecated.
17
+ # ----------------------------------------------------------------------------- #
18
+ #
19
+ require 'canis/core/system/ncurses'
20
+ require 'canis/core/system/panel'
21
+ # this is since often windows are declared with 0 height or width and this causes
22
+ # crashes in the most unlikely places. This prevceents me from having to write ternary
23
+ # e.g.
24
+ # @layout[:width].ifzero(FFI::NCurses::LINES-2)
25
+ class Fixnum
26
+ def ifzero v
27
+ return self if self != 0
28
+ return v
29
+ end
30
+ end
31
+ # This class is to be extended so that it can be called by anyone wanting to implement
32
+ # chunks ot text with color and attributes. Chunkline consists of multiple chunks of colored text
33
+ # and should implement a +each_with_color+.
34
+ # The purpose of adding this is so that +chunk.rb+ does not need to be required if colored text
35
+ # is not being used by an application.
36
+ class AbstractChunkLine; end
37
+
38
+ module Canis
39
+ class Window
40
+ attr_reader :width, :height, :top, :left
41
+ attr_accessor :layout # hash containing hwtl
42
+ attr_reader :panel # reader requires so he can del it in end
43
+ attr_accessor :name # more for debugging log files. 2010-02-02 19:58
44
+ #attr_accessor :modified # has it been modified and may need a refresh 2014-04-22 - 10:23 CLEANUP
45
+ # for root windows we need to know the form so we can ask it to update when
46
+ # there are overlapping windows.
47
+ attr_accessor :form
48
+
49
+ # creation and layout related {{{
50
+ # @param [Array, Hash] window coordinates (ht, w, top, left)
51
+ # or
52
+ # @param [int, int, int, int] window coordinates (ht, w, top, left)
53
+ # 2011-09-21 allowing array, or 4 ints, in addition to hash @since 1.3.1
54
+ def initialize(*args)
55
+
56
+ case args.size
57
+ when 1
58
+ case args[0]
59
+ when Array, Hash
60
+ layout = args[0]
61
+ else
62
+ raise ArgumentError, "Window expects 4 ints, array of 4 ints, or Hash in constructor"
63
+ end
64
+ when 4
65
+ layout = { :height => args[0], :width => args[1], :top => args[2], :left => args[3] }
66
+ end
67
+
68
+ @visible = true
69
+ set_layout(layout)
70
+
71
+ #$log.debug "XXX:WINDOW got h #{@height}, w #{@width}, t #{@top}, l #{@left} "
72
+
73
+ @height = FFI::NCurses.LINES if @height == 0 # 2011-11-14 added since tired of checking for zero
74
+ @width = FFI::NCurses.COLS if @width == 0
75
+
76
+ @window = FFI::NCurses.newwin(@height, @width, @top, @left) # added FFI 2011-09-6
77
+ # trying out refreshing underlying window.
78
+ $global_windows ||= []
79
+ # this causes issues padrefresh failing when display_list does a resize.
80
+ #$global_windows << self
81
+ @panel = Ncurses::Panel.new(@window) # added FFI 2011-09-6
82
+ #$error_message_row = $status_message_row = Ncurses.LINES-1
83
+ $error_message_row ||= Ncurses.LINES-1
84
+ $error_message_col ||= 1 # ask (bottomline) uses 0 as default so you can have mismatch. XXX
85
+ $status_message ||= Canis::Variable.new # in case not an App
86
+
87
+ # 2014-05-07 - 12:29 CANIS earlier this was called $key_map but that suggests a map.
88
+ $key_map_type ||= :vim
89
+ $esc_esc = true; # gove me double esc as 2727 so i can map it.
90
+ init_vars
91
+
92
+ unless @key_reader
93
+ create_default_key_reader
94
+ end
95
+
96
+
97
+ end
98
+ def init_vars
99
+ Ncurses::keypad(@window, true)
100
+ # Added this so we can get Esc, and also C-c pressed in succession does not crash system
101
+ # 2011-12-20 half-delay crashes system as does cbreak
102
+ #This causes us to be unable to process gg qq since getch won't wait.
103
+ #FFI::NCurses::nodelay(@window, bf = true)
104
+ # wtimeout was causing RESIZE sigwinch to only happen after pressing a key
105
+ #Ncurses::wtimeout(@window, $ncurses_timeout || 500) # will wait a second on wgetch so we can get gg and qq
106
+ #@stack = [] # since we have moved to handler 2014-04-20 - 11:15
107
+ @name ||="#{self}"
108
+ @modified = true
109
+ $catch_alt_digits ||= false # is this where is should put globals ? 2010-03-14 14:00 XXX
110
+ end
111
+ ##
112
+ # this is an alternative constructor
113
+ def self.root_window(layout = { :height => 0, :width => 0, :top => 0, :left => 0 })
114
+
115
+ @layout = layout
116
+ @window = Window.new(@layout)
117
+ @window.name = "Window::ROOTW:#{$global_windows.count}"
118
+ @window.wrefresh
119
+ Ncurses::Panel.update_panels
120
+ # earlier we only put root window, now we may need to do all (bline - numbered menu - alert)
121
+ $global_windows << @window unless $global_windows.include? @window
122
+ return @window
123
+ end
124
+
125
+ # This refreshes the root window whenever overlapping windows are
126
+ # destroyed or moved.
127
+ # This works by asking the root window's form to repaint all its objects.
128
+ # This is now being called whenever a window is destroyed (and also resized).
129
+ # However, it must
130
+ # manually be called if you move a window.
131
+ # NOTE : if there are too many root windows, this could get expensive since we are updating all.
132
+ # We may need to have a way to specify which window to repaint.
133
+ # If there are non-root windows above, we may have manually refresh only the previous one.
134
+ #
135
+ def self.refresh_all current_win=nil
136
+ #Ncurses.touchwin(FFI::NCurses.stdscr)
137
+ # above blanks out entire screen
138
+ # in case of multiple root windows lets just do last otherwise too much refreshing.
139
+ gw = $global_windows
140
+ if current_win
141
+ gw = $global_windows.select {|e| e != current_win }
142
+ end
143
+ return unless gw.last
144
+ wins = [ gw.last ]
145
+ wins.each_with_index do |w,i|
146
+ $log.debug " REFRESH_ALL on #{w.name} (#{i}) sending 1000"
147
+ # NOTE 2014-05-01 - 20:25 although we have reached the root window from any level
148
+ # however, this is sending the hack to whoever is trapping the key, which in our current
149
+ # case happends to be Viewer, *not* the root form. We need to send to root form.
150
+ f = w.form
151
+ if f
152
+ # send hack to root windows form if passed.
153
+ f.handle_key 1000
154
+ end
155
+ #w.ungetch(1000)
156
+ # below blanks out entire screen too
157
+ #FFI::NCurses.touchwin(w.get_window)
158
+ #$log.debug "XXX: refreshall diong window "
159
+ #w.hide
160
+ #w.show
161
+ #Ncurses.refresh
162
+ #w.wrefresh
163
+ end
164
+ #Ncurses::Panel.update_panels
165
+ end
166
+ # 2009-10-13 12:24
167
+ # not used as yet
168
+ # this is an alternative constructor
169
+ # created if you don't want to create a hash first
170
+ # 2011-09-21 V1.3.1 You can now send an array to Window constructor
171
+ def self.create_window(h=0, w=0, t=0, l=0)
172
+ layout = { :height => h, :width => w, :top => t, :left => l }
173
+ @window = Window.new(layout)
174
+ return @window
175
+ end
176
+
177
+ def resize_with(layout)
178
+ #$log.debug " DARN ! This awready duz a resize!! if h or w or even top or left changed!!! XXX"
179
+ set_layout(layout)
180
+ $log.debug " resize after set_layout: #{@height} , #{@width} , #{@top} , #{@left}, "
181
+ wresize(height, width)
182
+ mvwin(top, left)
183
+ Window.refresh_all self
184
+ end
185
+
186
+ %w[width height top left].each do |side|
187
+ eval(
188
+ "def #{side}=(n)
189
+ return if n == #{side}
190
+ @layout[:#{side}] = n
191
+ resize_with @layout
192
+ end"
193
+ )
194
+ end
195
+
196
+ ##
197
+ # Creating variables case of array, we still create the hash
198
+ # @param array or hash containing h w t and l
199
+ def set_layout(layout)
200
+ case layout
201
+ when Array
202
+ $log.error "NIL in window constructor" if layout.include? nil
203
+ raise ArgumentError, "Nil in window constructor" if layout.include? nil
204
+ # NOTE this is just setting, and not replacing zero with max values
205
+ @height, @width, @top, @left = *layout
206
+ raise ArgumentError, "Nil in window constructor" if @top.nil? || @left.nil?
207
+
208
+ @layout = { :height => @height, :width => @width, :top => @top, :left => @left }
209
+ when Hash
210
+ @layout = layout
211
+
212
+ [:height, :width, :top, :left].each do |name|
213
+ instance_variable_set("@#{name}", @layout[name])
214
+ end
215
+ end
216
+ end
217
+ # --- layout and creation related }}}
218
+
219
+ # ADDED DUE TO FFI
220
+ def wrefresh
221
+ Ncurses.wrefresh(@window)
222
+ end
223
+ def delwin # 2011-09-7
224
+ Ncurses.delwin(@window)
225
+ end
226
+ def attron *args
227
+ FFI::NCurses.wattron @window, *args
228
+ end
229
+ def attroff *args
230
+ FFI::NCurses.wattroff @window, *args
231
+ end
232
+ #
233
+ # ## END FFI
234
+
235
+ def resize
236
+ resize_with(@layout)
237
+ end
238
+
239
+ # Ncurses
240
+
241
+
242
+ def x=(n) move(y, n) end
243
+ def y=(n) move(n, x) end
244
+
245
+ #def move(y, x)
246
+ #return unless @visible
247
+ ## Log.debug([y, x] => caller[0,4])
248
+ ##@window.wmove(y, x) # bombing since ffi-ncurses 0.4.0 (maybe it was never called
249
+ ##earlier. was crashing in appemail.rb testchoose.
250
+ #wmove y,x # can alias it
251
+ #end
252
+ # since include FFI is taking over, i need to force it here. not going into
253
+ # method_missing
254
+ def wmove y,x
255
+ #Ncurses.wmove @window, y, x
256
+ FFI::NCurses.wmove @window, y, x
257
+ end
258
+ alias :move :wmove
259
+
260
+ def method_missing(name, *args)
261
+ name = name.to_s
262
+ if (name[0,2] == "mv")
263
+ test_name = name.dup
264
+ test_name[2,0] = "w" # insert "w" after"mv"
265
+ if (FFI::NCurses.respond_to?(test_name))
266
+ return FFI::NCurses.send(test_name, @window, *args)
267
+ end
268
+ end
269
+ test_name = "w" + name
270
+ if (FFI::NCurses.respond_to?(test_name))
271
+ return FFI::NCurses.send(test_name, @window, *args)
272
+ end
273
+ FFI::NCurses.send(name, @window, *args)
274
+ end
275
+
276
+ def respond_to?(name)
277
+ name = name.to_s
278
+ if (name[0,2] == "mv" && FFI::NCurses.respond_to?("mvw" + name[2..-1]))
279
+ return true
280
+ end
281
+ FFI::NCurses.respond_to?("w" + name) || FFI::NCurses.respond_to?(name)
282
+ end
283
+
284
+ #--
285
+ # removing some methods that not used or used once
286
+ # leaving here so we not what to do to print in these cases
287
+ def print(string, width = width)
288
+ w = width == 0? Ncurses.COLS : width
289
+ waddnstr(string.to_s, w) # changed 2011 dts
290
+ end
291
+
292
+ #def print_yx(string, y = 0, x = 0)
293
+ #w = width == 0? Ncurses.COLS : width
294
+ #mvwaddnstr(y, x, string, w) # changed 2011 dts
295
+ #end
296
+ #++
297
+
298
+
299
+ # return the character to the keyboard buffer to be read again.
300
+ def ungetch(ch)
301
+ Ncurses.ungetch(ch)
302
+ end
303
+
304
+ # reads a character from keyboard and returns
305
+ # NOTE:
306
+ # if a function key is pressed, multiple such ints will be returned one after the other
307
+ # so the caller must decipher the same. See +getchar()+
308
+ #
309
+ # @return int
310
+ # @return -1 if no char read
311
+ # ORIGINALLY After esc there was a timeout, but after others there was notimeout, so it would wait
312
+ # indefinitely for a key
313
+ # NOTE : caller may set a timeout prior to calling, but not change setting after since this method
314
+ # maintains the default state in +ensure+. e.g. +widget.rb+ does a blocking get in +_process_key+
315
+ # Curses sets a timeout when ESCAPE is pressed, it is called ESCDELAY and is 1000 milliseconds.
316
+ # You may reduce it if you are not on some old slow telnet session. This returns faster from an esc
317
+ # although there are still some issues. ESC-ESC becomes an issue, but if i press ESC-ESC-1 then esc-esc comes
318
+ # together. otherwise there is a -1 between each esc.
319
+ #
320
+ def getch
321
+ #c = @window.getch
322
+ #FFI::NCurses::nodelay(@window, true)
323
+ #FFI::NCurses::wtimeout(@window, 0)
324
+ #$log.debug " #{Time.now.to_f} inside MAIN before getch "
325
+ c = FFI::NCurses.wgetch(@window)
326
+ # the only reason i am doing this is so ESC can be returned if no key is pressed
327
+ # after that, not sure how this effects everything. most likely I should just
328
+ # go back to using a wtimeout, and not worry about resize requiring a keystroke
329
+ if c == 27
330
+ $escstart = Time.now.to_f
331
+ # if ESC pressed don't wait too long for next key
332
+ Ncurses::wtimeout(@window, $ncurses_timeout || 500) # will wait n millisecond on wgetch so that we can return if no
333
+ else
334
+ FFI::NCurses.set_escdelay(100)
335
+ # this means keep waiting for a key.
336
+ Ncurses::nowtimeout(@window, true)
337
+ end
338
+ c
339
+
340
+ rescue SystemExit, Interrupt
341
+ #FFI::NCurses.flushinp
342
+ 3 # is C-c
343
+ rescue StandardError
344
+ -1 # is C-c
345
+ ensure
346
+ # whatever the default is, is to be set here in case caller changed it.
347
+ #FFI::NCurses::nodelay(@window, true)
348
+ end
349
+
350
+ # Earlier this was handled by window itself. Now we delegate to a reader
351
+ # @return int keycode, can be function key or meta or arrow key.
352
+ #
353
+ # NOTE:
354
+ # This is called by user programs in a loop.
355
+ # We are now moving from returning an int to returning a string similar to what
356
+ # user would get on commandline using C-v
357
+ #
358
+ def getchar
359
+ @key_reader.getchar
360
+ end
361
+
362
+
363
+ # setup and reset
364
+
365
+
366
+
367
+ # Ncurses panel
368
+
369
+ def hide
370
+ #return unless visible? # added 2011-10-14 these 2 are not behaving properly
371
+ Ncurses::Panel.hide_panel @panel.pointer
372
+ #Ncurses.refresh # wnoutrefresh
373
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
374
+ @visible = false
375
+ end
376
+
377
+ def show
378
+ #return if visible? # added 2011-10-14 these 2 are not behaving properly
379
+ Ncurses::Panel.show_panel @panel.pointer
380
+ #Ncurses.refresh # wnoutrefresh
381
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
382
+ @visible = true
383
+ end
384
+
385
+
386
+ def visible?
387
+ @visible
388
+ end
389
+
390
+ ##
391
+ # destroy window, panel and any pads that were requested
392
+ #
393
+ def destroy
394
+ # typically the ensure block should have this
395
+
396
+ #$log.debug "win destroy start"
397
+
398
+ $global_windows.delete self
399
+ Ncurses::Panel.del_panel(@panel.pointer) if @panel
400
+ delwin() if @window
401
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
402
+
403
+ # destroy any pads that were created by widgets using get_pad
404
+ @pads.each { |pad|
405
+ FFI::NCurses.delwin(pad) if pad
406
+ pad = nil
407
+ } if @pads
408
+ # added here to hopefully take care of this issue once and for all.
409
+ # Whenever any window is destroyed, the root window is repainted.
410
+ #
411
+ Window.refresh_all
412
+ #$log.debug "win destroy end"
413
+ end
414
+
415
+ #
416
+ # 2011-11-13 since 1.4.1
417
+ # Widgets can get window to create a pad for them. This way when the window
418
+ # is destroyed, it will delete all the pads. A widget wold not be able to do this.
419
+ # The destroy method of the widget will be called.
420
+ def get_pad content_rows, content_cols
421
+ pad = FFI::NCurses.newpad(content_rows, content_cols)
422
+ @pads ||= []
423
+ @pads << pad
424
+ ## added 2013-03-05 - 19:21 without next line how was pad being returned
425
+ return pad
426
+ end
427
+
428
+ # print and chunk related --- {{{
429
+ #
430
+ # Allows user to send data as normal string or chunks for printing
431
+ # An array is assumed to be a chunk containing color and attrib info
432
+ #
433
+ def printstring_or_chunks(r,c,content, color, att = Ncurses::A_NORMAL)
434
+ if content.is_a? String
435
+ printstring(r,c,content, color, att)
436
+ elsif content.is_a? AbstractChunkLine
437
+ #$log.debug "XXX: using chunkline" # 2011-12-10 12:40:13
438
+ wmove r, c
439
+ a = get_attrib att
440
+ # please add width to avoid overflow
441
+ show_colored_chunks content, color, a
442
+ elsif content.is_a? Array
443
+ # several chunks in one row - NOTE Very experimental may change
444
+ if content[0].is_a? Array
445
+ $log.warn "XXX: WARNING outdated should send in a chunkline"
446
+ wmove r, c
447
+ a = get_attrib att
448
+ # please add width to avoid overflow
449
+ show_colored_chunks content, color, a
450
+ else
451
+ # a single row chunk - NOTE Very experimental may change
452
+ text = content[1].dup
453
+ printstring r, c, text, content[0] || color, content[2] || att
454
+ end
455
+ end
456
+ end
457
+ #
458
+ # prints a string formatted in our new experimental coloring format
459
+ # taken from tmux. Currently, since i have chunks workings, i convert
460
+ # to chunks and use the existing print function. This could change.
461
+ # An example of a formatted string is:
462
+ # s="#[fg=green]testing chunks #[fg=yellow, bg=red, bold]yellow #[reverse] reverseme \
463
+ # #[normal]normal#[bg = black]just yellow#[fg=blue],blue now #[underline] underlined text"
464
+ # Ideally I should push and pop colors which the shell does not do with ansi terminal sequences.
465
+ # That way i can have a line in red,
466
+ # with some word in yellow, and then the line continues in red.
467
+ #
468
+ def printstring_formatted(r,c,content, color, att = Ncurses::A_NORMAL)
469
+ att = get_attrib att unless att.is_a? Fixnum
470
+ chunkline = convert_to_chunk(content, color, att)
471
+ printstring_or_chunks r,c, chunkline, color, att
472
+ end # print
473
+ #
474
+ # print a formatted line right aligned
475
+ # c (col) is ignored and calculated based on width and unformatted string length
476
+ #
477
+ def printstring_formatted_right(r,c,content, color, att = Ncurses::A_NORMAL)
478
+ clean = content.gsub /#\[[^\]]*\]/,'' # clean out all markup
479
+ #c = actual_width() - clean.length # actual width not working if resize
480
+ c = getmaxx() - clean.length
481
+ printstring_formatted(r,c,content, color, att )
482
+ end
483
+
484
+ private
485
+ def get_default_color_parser
486
+ require 'canis/core/util/defaultcolorparser'
487
+ @color_parser || DefaultColorParser.new
488
+ end
489
+ # supply with a color parser, if you supplied formatted text
490
+ public
491
+ def color_parser f
492
+ $log.debug "XXX: color_parser setting in window to #{f} "
493
+ require 'canis/core/include/colorparser'
494
+ if f == :tmux
495
+ @color_parser = get_default_color_parser()
496
+ else
497
+ @color_parser = f
498
+ end
499
+ end
500
+ #
501
+ # Takes a formatted string and converts the parsed parts to chunks.
502
+ #
503
+ # @param [String] takes the entire line or string and breaks into an array of chunks
504
+ # @yield chunk if block
505
+ # @return [ChunkLine] # [Array] array of chunks
506
+ public
507
+ def convert_to_chunk s, colorp=$datacolor, att=FFI::NCurses::A_NORMAL
508
+ unless @color_parser
509
+ require 'canis/core/include/colorparser'
510
+ @color_parser = get_default_color_parser()
511
+ @converter = Chunks::ColorParser.new @color_parser
512
+ # we need to know set the parent in colorparser. 2014-05-26 - 14:49
513
+ @converter.form = self.form
514
+ end
515
+ @converter.convert_to_chunk s, colorp, att
516
+ end
517
+
518
+ ##
519
+ # prints a string at row, col, with given color and attribute
520
+ # added by rk 2008-11-29 19:01
521
+ # I usually use this, not the others ones here
522
+ # @param r - row
523
+ # @param c - col
524
+ # @param string - text to print
525
+ # @param color - color pair
526
+ # @ param att - ncurses attribute: normal, bold, reverse, blink,
527
+ # underline
528
+ public
529
+ def printstring(r,c,string, color, att = Ncurses::A_NORMAL)
530
+
531
+ raise "printstring recvd nil row #{r} or col #{c} " if r.nil? || c.nil?
532
+ #$log.debug " #{@name} inside window printstring r #{r} c #{c} #{string} "
533
+ if att.nil?
534
+ att = Ncurses::A_NORMAL
535
+ else
536
+ att = get_attrib att
537
+ end
538
+
539
+ wattron(Ncurses.COLOR_PAIR(color) | att)
540
+ mvwprintw(r, c, "%s", :string, string);
541
+ wattroff(Ncurses.COLOR_PAIR(color) | att)
542
+ end
543
+ ##
544
+ # prints the border for message boxes
545
+ #
546
+ # NOTE : FOR MESSAGEBOXES ONLY !!!! Then why not move to messagebox FIXME
547
+ def print_border_mb row, col, height, width, color, attr
548
+ # the next is for xterm-256
549
+ att = get_attrib attr
550
+ len = width
551
+ len = Ncurses.COLS-0 if len == 0
552
+ # print a bar across the screen
553
+ #attron(Ncurses.COLOR_PAIR(color) | att)
554
+ # this works for newmessagebox but not for old one.
555
+ # Even now in some cases some black shows through, if the widget is printing spaces
556
+ # such as field or textview on a messagebox.
557
+ (row-1).upto(row+height-1) do |r|
558
+ mvwhline(r, col, 1, len)
559
+ end
560
+ #attroff(Ncurses.COLOR_PAIR(color) | att)
561
+
562
+ mvwaddch row, col, Ncurses::ACS_ULCORNER
563
+ mvwhline( row, col+1, Ncurses::ACS_HLINE, width-6)
564
+ mvwaddch row, col+width-5, Ncurses::ACS_URCORNER
565
+ mvwvline( row+1, col, Ncurses::ACS_VLINE, height-4)
566
+
567
+ mvwaddch row+height-3, col, Ncurses::ACS_LLCORNER
568
+ mvwhline(row+height-3, col+1, Ncurses::ACS_HLINE, width-6)
569
+ mvwaddch row+height-3, col+width-5, Ncurses::ACS_LRCORNER
570
+ mvwvline( row+1, col+width-5, Ncurses::ACS_VLINE, height-4)
571
+ end
572
+
573
+ ##
574
+ # prints a border around a widget, CLEARING the area.
575
+ # If calling with a pad, you would typically use 0,0, h-1, w-1.
576
+ # FIXME can this be moved to module Bordertitle ?
577
+ def print_border row, col, height, width, color, att=Ncurses::A_NORMAL
578
+ raise "height needs to be supplied." if height.nil?
579
+ raise "width needs to be supplied." if width.nil?
580
+ att ||= Ncurses::A_NORMAL
581
+
582
+ #$log.debug " inside window print_border r #{row} c #{col} h #{height} w #{width} "
583
+
584
+ # 2009-11-02 00:45 made att nil for blanking out
585
+ # FIXME - in tabbedpanes this clears one previous line ??? XXX when using a textarea/view
586
+ # when using a pad this calls pads printstring which again reduces top and left !!! 2010-01-26 23:53
587
+ ww=width-2
588
+ clr = " "*ww
589
+ (row+1).upto(row+height-1) do |r|
590
+ printstring( r, col+1, clr, color, att)
591
+ end
592
+ print_border_only row, col, height, width, color, att
593
+ end
594
+
595
+
596
+ ## print just the border, no cleanup
597
+ #+ Earlier, we would clean up. Now in some cases, i'd like
598
+ #+ to print border over what's been done.
599
+ # XXX this reduces 1 from width but not height !!! FIXME
600
+ # FIXME can this be moved to module Bordertitle ?
601
+ def print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
602
+ if att.nil?
603
+ att = Ncurses::A_NORMAL
604
+ else
605
+ att = get_attrib att
606
+ end
607
+ wattron(Ncurses.COLOR_PAIR(color) | att)
608
+ mvwaddch row, col, Ncurses::ACS_ULCORNER
609
+ mvwhline( row, col+1, Ncurses::ACS_HLINE, width-2)
610
+ mvwaddch row, col+width-1, Ncurses::ACS_URCORNER
611
+ mvwvline( row+1, col, Ncurses::ACS_VLINE, height-1)
612
+
613
+ mvwaddch row+height-0, col, Ncurses::ACS_LLCORNER
614
+ mvwhline(row+height-0, col+1, Ncurses::ACS_HLINE, width-2)
615
+ mvwaddch row+height-0, col+width-1, Ncurses::ACS_LRCORNER
616
+ mvwvline( row+1, col+width-1, Ncurses::ACS_VLINE, height-1)
617
+ wattroff(Ncurses.COLOR_PAIR(color) | att)
618
+ end
619
+
620
+ # Previously this printed a chunk as a full line, I've modified it to print on
621
+ # one line. This can be used for running text.
622
+ # NOTE 2013-03-08 - 17:02 added width so we don't overflow
623
+ # NOTE 2014-05-11 - textpad has its own version, so does not call this.
624
+ def show_colored_chunks(chunks, defcolor = nil, defattr = nil, wid = 999, pcol = 0)
625
+ return unless visible?
626
+ ww = 0
627
+ chunks.each_with_color do |text, color, attrib|
628
+
629
+ ## 2013-03-08 - 19:11 take care of scrolling by means of pcol
630
+ if pcol > 0
631
+ if pcol > text.length
632
+ # ignore entire chunk and reduce pcol
633
+ pcol -= text.length
634
+ next
635
+ else
636
+ # print portion of chunk and zero pcol
637
+ text = text[pcol..-1]
638
+ pcol = 0
639
+ end
640
+ end
641
+ oldw = ww
642
+ ww += text.length
643
+ if ww > wid
644
+ # if we are exceeding the width then by howmuch
645
+ rem = wid - oldw
646
+ if rem > 0
647
+ # take only as much as we are allowed
648
+ text = text[0,rem]
649
+ else
650
+ break
651
+ end
652
+ end
653
+
654
+ color ||= defcolor
655
+ attrib ||= defattr
656
+
657
+ cc, bg = ColorMap.get_colors_for_pair color
658
+ #$log.debug "XXX: CHUNK window #{text}, cp #{color} , attrib #{attrib}. #{cc}, #{bg} "
659
+ color_set(color,nil) if color
660
+ wattron(attrib) if attrib
661
+ #print(text)
662
+ waddnstr(text.to_s, @width) # changed 2014-04-22 - 11:59 to reduce a function
663
+ wattroff(attrib) if attrib
664
+ end
665
+ end
666
+ # ----- }}}
667
+
668
+ # This used to return an Ncurses window object, and you could call methods on it
669
+ # Now it returns a FFI::NCurses.window pointer which you cannot call methods on.
670
+ # You have to pass it to FFI::NCurses.<method>
671
+ def get_window; @window; end
672
+
673
+ # returns name of window or self (mostly for debugging)
674
+ def to_s; @name || self; end
675
+
676
+ # actions to perform when window closed.
677
+ # == Example
678
+ # @window.close_command do
679
+ # if confirm("Save tasks?", :default_button => 0)
680
+ # take some actions
681
+ # end
682
+ # end
683
+ def close_command *args, &block
684
+ @close_command ||= []
685
+ @close_args ||= []
686
+ @close_command << block
687
+ @close_args << args
688
+ end
689
+ alias :command :close_command
690
+
691
+ # set a single command to confirm whether window shoud close or not
692
+ # Block should return true or false for closing or not
693
+ # == Examples
694
+ #
695
+ # @window.confirm_close_command do
696
+ # confirm "Sure you wanna quit?", :default_button => 1
697
+ # end
698
+ #
699
+ def confirm_close_command *args, &block
700
+ @confirm_close_command = block
701
+ @confirm_close_args = args
702
+ end
703
+
704
+ # Called when window close is requested by user.
705
+ # Executes confirm_close block and if it succeeds then executes close commands
706
+ # called by util/app.rb
707
+ def fire_close_handler
708
+ if @confirm_close_command
709
+ comm = @confirm_close_command
710
+ ret = comm.call(self, *@confirm_close_args)
711
+ return ret unless ret # only return if false returned
712
+ end
713
+ if @close_command
714
+ @close_command.each_with_index do |comm, ix|
715
+ comm.call(self, *@close_args[ix]) if comm
716
+ end
717
+ end
718
+ @close_command = nil
719
+ @close_args = nil
720
+ return true
721
+ end
722
+
723
+ # creates a key reader unless overridden by application which should be rare.
724
+ def create_default_key_reader
725
+ @key_reader = DefaultKeyReader.new self
726
+ end
727
+
728
+
729
+ end # window
730
+
731
+ # created on 2014-04-20 - 00:19 so that user can install own handler
732
+ #
733
+ #
734
+ # A class that reads keys and handles function, shifted function, control, alt, and other
735
+ # extended keys.
736
+ # THis essentially consists of a method getchar which will be called by the application
737
+ # to get keys in a loop. Application may also call getchar to get one key in some situations.
738
+ #
739
+ # Originally, rbcurse returned an int, but we are movign to a string, so that user can use the exact
740
+ # control codes he gets on the terminal using C-v and map them here.
741
+ #
742
+ #
743
+ class DefaultKeyReader # --- {{{
744
+ def initialize win
745
+ @window = win
746
+ @stack = []
747
+ end
748
+
749
+ # return an int for the key read. this is just a single int, and is not interpreted
750
+ # for control or function keys. it also will return -1 when no action.
751
+ # You may re-implenent it or call the original one.
752
+ #
753
+ def getch
754
+ @window.getch
755
+ end
756
+
757
+
758
+ # A map of int keycodes associated with a string name which is defined in $kh
759
+ $kh_int ||= Hash.new {|hash, key| hash[key] = key.hash }
760
+ # these 4 for xterm-color which does not send 265 on F1
761
+ $kh_int["F1"] = 265
762
+ $kh_int["F2"] = 266
763
+ $kh_int["F3"] = 267
764
+ $kh_int["F4"] = 268
765
+ # testing out shift+Function. these are the codes my kb generates
766
+ if File.exists? File.expand_path("~/ncurses-keys.yml")
767
+ # a sample of this file should be available with this
768
+ # the file is a hash or mapping, but should not contrain control characters.
769
+ # Usually delete the control character and insert a "\e" in its place.
770
+ # "\e[1;3C": C-RIGHT
771
+ require 'yaml'
772
+ $kh = YAML::load( File.open( File.expand_path("~/ncurses-keys.yml" ) ))
773
+ else
774
+ # if we could not find any mappings then use some dummy ones that work on my laptop.
775
+ $kh=Hash.new
776
+ KEY_S_F1=''
777
+ $kh[KEY_S_F1]="S-F1"
778
+ $kh['']="S-F2"
779
+ $kh['']="S-F3"
780
+ $kh['']="S-F4"
781
+ $kh['[15;2~']="S-F5"
782
+
783
+ end
784
+ # this is for xterm-color which does not send 265 on F1
785
+ $kh['OP']="F1"
786
+ $kh['OQ']="F2"
787
+ $kh['OR']="F3"
788
+ $kh['OS']="F4"
789
+
790
+ # NOTE: This is a reworked and much simpler version of the original getchar which was taken from manveru's
791
+ # codebase. This also currently returns the keycode as int while placing the char version in a
792
+ # global $key_chr. Until we are ready to return a char, we use this.
793
+ #
794
+ # FIXME : I have tried very hard to revert to nodelay but it does not seem to have an effect when ESC is pressed.
795
+ # Somewhere, there is a delay when ESC is pressed. I not longer wish to provide the feature of pressing ESC
796
+ # and then a key to be evaluated as Meta-key. This slows down when a user just presses ESC.
797
+ #
798
+ # Read a char from the window (from user) and returns int code.
799
+ # In some cases, such as codes entered in the $kh hash, we do not yet have a keycode defined
800
+ # so we return 9999 and the user can access $key_chr.
801
+ #
802
+ # NOTE: Do not convert to string, that is doing two things. Allow user to convert if required using
803
+ # `key_tos`
804
+ def getchar
805
+ $key_chr = nil
806
+ c = nil
807
+ while true
808
+ c = self.getch
809
+ break if c != -1
810
+ end
811
+
812
+ cn = c
813
+ $key_int = c
814
+ # handle control codes 0 to 127 but not escape
815
+ if cn >= 0 && cn < 128 && cn != 27
816
+ #$key_chr = key_tos(c)
817
+ return c
818
+ end
819
+
820
+ # if escape then get into a loop and keep checking till -1 or another escape
821
+ #
822
+ if c == 27
823
+ buff=c.chr
824
+ # if there is another escape coming through then 2 keys were pressed so
825
+ # evaluate upon hitting an escape
826
+ # NOTE : i think only if ESc is followed by [ should be keep collectig
827
+ # otherwise the next char should evaluate. cases like F1 are already being sent in as high integer codes
828
+ while true
829
+ #$log.debug " #{Time.now.to_f} inside LOOP before getch "
830
+ # This getch seems to take enough time not to return a -1 for almost a second
831
+ # even if nodelay is true ??? XXX
832
+ FFI::NCurses.set_escdelay(5)
833
+ k = self.getch
834
+ #$log.debug "elapsed #{elapsed} millis inside LOOP AFTER getch #{k} (#{elapsed1})"
835
+ $log.debug "inside LOOP AFTER getch #{k} "
836
+
837
+ if k == 27
838
+ # seems like two Meta keys pressed in quick succession without chance for -1 to kick in
839
+ # but this still does not catch meta char followed by single char. M-za , it does.
840
+ if $esc_esc
841
+ if buff == 27.chr
842
+ $key_chr = "<ESC-ESC>"
843
+ return 2727
844
+ else
845
+ alert "buff is #{buff}"
846
+ end
847
+ end
848
+ $log.debug " 1251 before evaluate "
849
+ x = _evaluate_buff buff
850
+ # return ESC so it can be interpreted again.
851
+ @window.ungetch k
852
+ $key_chr = x if x
853
+ return $key_int if x
854
+ $log.warn "getchar: window.rb 1200 Found no mapping for #{buff} "
855
+ $key_chr = buff
856
+ return $key_int
857
+ #return buff # otherwise caught in loop ???
858
+ elsif k > -1
859
+ # FIXME next lne crashes if M-C-h pressed which gives 263
860
+ if k > 255
861
+ $log.warn "getchar: window.rb 1247 Found no mapping for #{buff} #{k} "
862
+ $key_int = k + 128
863
+ return $key_int
864
+ # this contains ESc followed by a high number
865
+ =begin
866
+ ka = key_tos(k)
867
+ if ka
868
+ $key_chr = "<M-" + ka[1..-1]
869
+ $key_int = k + 128
870
+ return $key_int
871
+ else
872
+ $key_chr = "UNKNOWN: Meta + #{k}"
873
+ return 9999
874
+ end
875
+ =end
876
+ end
877
+
878
+ buff += k.chr
879
+ # this is an alt/meta code. All other complex codes seem to have a [ after the escape
880
+ # so we will keep accumulating them.
881
+ # NOTE this still means that user can press Alt-[ and some letter in quick succession
882
+ # and it will accumulate rather than be interpreted as M-[.
883
+ #
884
+ if buff.length == 2 and k == 79
885
+ # this is Alt-O and can be a F key in some terms like xterm-color
886
+ elsif buff.length == 2 and k.chr != '['
887
+ x = _evaluate_buff buff
888
+
889
+ $key_chr = x
890
+ return $key_int if x
891
+ end
892
+ #$log.debug "XXX: getchar adding #{k}, #{k.chr} to buff #{buff} "
893
+ else
894
+ #$log.debug " GOT -1 in escape "
895
+ # it is -1 so evaluate
896
+ x = _evaluate_buff buff
897
+ $key_chr = x if x
898
+ return $key_int if x
899
+ $log.warn "getchar: window.rb 1256 Found no mapping for #{buff} "
900
+ $key_chr = buff
901
+ return $key_int
902
+ end
903
+ end
904
+ end
905
+
906
+ # what if keyname does not return anything
907
+ if c > 127
908
+ #$log.info "xxxgetchar: window.rb sending #{c} "
909
+ =begin
910
+ ch = FFI::NCurses::keyname(c)
911
+ # remove those ugly brackets around function keys
912
+ if ch && ch[-1]==')'
913
+ ch = ch.gsub(/[()]/,'')
914
+ end
915
+ if ch && ch.index("KEY_")
916
+ ch = ch.gsub(/KEY_/,'')
917
+ end
918
+ ch = "<#{ch}>" if ch
919
+ #return ch if ch
920
+ $key_chr = ch if ch
921
+ $key_chr = "UNKNOWN:#{c}" unless ch
922
+ $log.warn "getchar: window.rb 1234 Found no mapping for #{c} " unless ch
923
+ =end
924
+ #$key_chr = key_tos(ch)
925
+ return c
926
+ end
927
+ if c
928
+ #$key_chr = c.chr
929
+ return c
930
+ end
931
+ end
932
+
933
+
934
+ def getchar_as_char
935
+ $key_int = getchar
936
+ $key_chr = key_tos( $key_int )
937
+ return $key_chr
938
+ end
939
+
940
+
941
+ =begin
942
+ # NOTE I cannot use this since we are not ready to take a string, that is a big decision that
943
+ # requries a lot of work, and some decisions. We may bind using "<CR>" or "<C-d>" so
944
+ # maybe that's how we may need to send back
945
+ ## get a character from user and return as a string
946
+ # Adapted from:
947
+ #http://stackoverflow.com/questions/174933/how-to-get-a-single-character-without-pressing-enter/8274275#8274275
948
+ # Need to take complex keys and matc against a hash.
949
+ # We cannot use the cetus example as is since here $stdin.ready? does not work and more importantly
950
+ # we have keyboard set to true so function keys and arrow keys are not returned as multiple values but as
951
+ # one int in the 255 and above range. so that must be interpreted separately.
952
+ #
953
+ # If we wait for -1 then quick M-a can get concatenated. we need to take care
954
+ # a ESC means the previous one should be evaluated and not contactenated
955
+ # FIXME = ESCESC 2727 - can't do this as will clash with Esc, M-(n).
956
+ # this is a rework of the above but returns an int so that the existing programs can keep working.
957
+ # We will store the char codes/ in a global string so user can get esp if unknown.
958
+ # UNUSED since we are still using int codes.
959
+ def getchar_as_char # -- {{{
960
+ c = nil
961
+ while true
962
+ c = self.getch
963
+ break if c != -1
964
+ end
965
+
966
+ cn = c
967
+ #return FFI::NCurses::keyname(c) if [10,13,127,0,32,8].include? c
968
+ $key_int = c
969
+ if cn >= 0 && cn < 128 && cn != 27
970
+ $key_chr = key_tos(c)
971
+ return $key_chr
972
+ end
973
+
974
+ # if escape then get into a loop and keep checking till -1 or another escape
975
+ #
976
+ if c == 27
977
+ buff=c.chr
978
+ # if there is another escape coming through then 2 keys were pressed so
979
+ # evaluate upon hitting an escape
980
+ # NOTE : i think only if ESc is followed by [ should be keep collectig
981
+ # otherwise the next char should evaluate. cases like F1 are already being sent in as high integer codes
982
+ while true
983
+
984
+ k = self.getch
985
+
986
+ if k == 27
987
+ # seems like two Meta keys pressed in quick succession without chance for -1 to kick in
988
+ # but this still does not catch meta char followed by single char. M-za
989
+ x = _evaluate_buff buff
990
+ # return ESC so it can be interpreted again.
991
+ @window.ungetch k
992
+ return x if x
993
+ $log.warn "getchar: window.rb 1200 Found no mapping for #{buff} "
994
+ return buff # otherwise caught in loop ???
995
+ elsif k > -1
996
+ buff += k.chr
997
+ # this is an alt/meta code. All other complex codes seem to have a [ after the escape
998
+ # so we will keep accumulating them.
999
+ # NOTE this still means that user can press Alt-[ and some letter in quick succession
1000
+ # and it will accumulate rather than be interpreted as M-[.
1001
+ #
1002
+ if buff.length == 2 and k.chr != '['
1003
+ x = _evaluate_buff buff
1004
+ return x if x
1005
+ end
1006
+ #$log.debug "XXX: getchar adding #{k}, #{k.chr} to buff #{buff} "
1007
+ else
1008
+ # it is -1 so evaluate
1009
+ x = _evaluate_buff buff
1010
+ return x if x
1011
+ return buff
1012
+ end
1013
+ end
1014
+ end
1015
+
1016
+ # what if keyname does not return anything
1017
+ if c > 127
1018
+ #$log.info "xxxgetchar: window.rb sending #{c} "
1019
+ ch = FFI::NCurses::keyname(c)
1020
+ # remove those ugly brackets around function keys
1021
+ if ch && ch[-1]==')'
1022
+ ch = ch.gsub(/[()]/,'')
1023
+ end
1024
+ return ch if ch
1025
+ $log.warn "getchar: window.rb 1234 Found no mapping for #{c} "
1026
+ return c
1027
+ end
1028
+ return c.chr if c
1029
+ end # -- }}}
1030
+ =end
1031
+
1032
+ # Generate and return an int for a newkey which user has specified in yml file.
1033
+ # We use hash, which won't allow me to derive key string
1034
+ # in case loop user can do:
1035
+ # when KEY_ENTER
1036
+ # when 32
1037
+ # when $kh_int["S-F2"]
1038
+ def _get_int_for_newkey x
1039
+ # FIXME put the declaration somewhere else maybe in window cons ???
1040
+ y = $kh_int[x]
1041
+ # when i give user the hash, he can get the string back ???
1042
+ $kh_int[y] = x unless $kh_int.key? y
1043
+ return y
1044
+ end
1045
+ # check buffer if some key mapped in global kh for this
1046
+ # Otherwise if it is 2 keys then it is a Meta key
1047
+ # Can return nil if no mapping
1048
+ # @return [String] string code for key (since it is mostly from $kh. Also sets, $key_int
1049
+ private
1050
+ def _evaluate_buff buff
1051
+ if buff == 27.chr
1052
+ $key_int = 27
1053
+ #$escend = Time.now.to_f
1054
+ #elapsed = ($escend - $escstart)*1000
1055
+ #$log.debug " #{elapsed} evaluated to ESC"
1056
+ $key_chr = "<ESC>"
1057
+ return $key_chr
1058
+ end
1059
+ x=$kh[buff]
1060
+ if x
1061
+ $key_int = 9999
1062
+ $key_int = _get_int_for_newkey(x)
1063
+ $key_cache[$key_int] = x unless $key_cache.key? $key_int
1064
+ # FIXME currently 9999 signifies unknown key, but since this is derived from a user list
1065
+ # we could have some dummy number being passed or set by user too.
1066
+ return "<#{x}>"
1067
+ end
1068
+ #$log.debug "XXX: getchar returning with #{buff}"
1069
+ if buff.size == 2
1070
+ ## possibly a meta/alt char
1071
+ k = buff[-1]
1072
+ $key_int = 128 + k.ord
1073
+ return key_tos( $key_int )
1074
+ end
1075
+ $key_int = 99999
1076
+ nil
1077
+ end
1078
+
1079
+ end # class DefaultKeyReader -- }}}
1080
+
1081
+ end