ncumbra 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,348 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: messagebox.rb
3
+ # Description: a small window with a list or message or fields and buttons which pops up.
4
+ # Author: j kepler http://github.com/mare-imbrium/canis/
5
+ # Date: 2018-04-13 - 23:10
6
+ # License: MIT
7
+ # Last update: 2018-04-21 14:44
8
+ # ----------------------------------------------------------------------------- #
9
+ # YFF Copyright (C) 2012-2018 j kepler
10
+ require 'umbra/window'
11
+ require 'umbra/form'
12
+ require 'umbra/widget'
13
+ require 'umbra/button'
14
+ require 'umbra/field'
15
+ require 'umbra/label'
16
+ require 'umbra/textbox'
17
+
18
+ module Umbra
19
+ class MessageBox
20
+
21
+ attr_reader :form
22
+ attr_reader :window
23
+ attr_accessor :title
24
+ attr_accessor :buttons # button labels. e.g. [Ok, Cancel]
25
+ #dsl_accessor :default_button
26
+ #
27
+ # a message to be printed, usually this will be the only thing supplied
28
+ # with an OK button. This should be a short string, a label will be used
29
+ # and input_config passed to it
30
+
31
+ #dsl_accessor :message
32
+ # you can also set button_orientation : :right, :left, :center
33
+ #
34
+ def initialize config={}, &block
35
+
36
+ h = config.fetch(:height, nil)
37
+ w = config.fetch(:width, nil)
38
+ t = config.fetch(:row, nil)
39
+ l = config.fetch(:col, nil)
40
+ if h && w && t && l
41
+ #@window = Window.new :height => h, :width => w, :top => t, :left => l
42
+ @window = Window.new h, w, t, l
43
+ # else window will be created in repaint, and form will pass it to widgets before their first
44
+ # repaint
45
+ end
46
+ @form = Form.new @window
47
+ @buttons = ["Ok", "Cancel"]
48
+
49
+ config.each_pair { |k,v| instance_variable_set("@#{k}",v) }
50
+ @config = config
51
+ @row = 0
52
+ @col = 0
53
+ @row_offset = 1
54
+ @col_offset = 2
55
+
56
+ #@color ||= :black
57
+ #@bgcolor ||= :white
58
+ @color_pair = CP_BLACK
59
+ # 2014-05-31 - 11:44 adding form color
60
+ # try not to set buttons color in this program, let button pick up user or form colors
61
+ #@form.color = @color
62
+ #@form.bgcolor = @bgcolor
63
+ #@form.color_pair = @color_pair
64
+ @maxrow = 3
65
+
66
+ instance_eval &block if block_given?
67
+ #yield_or_eval &block if block_given? TODO
68
+
69
+ end
70
+ def item widget
71
+ # # normalcolor gives a white on black stark title like links and elinks
72
+ # You can also do 'acolor' to give you a sober title that does not take attention away, like mc
73
+ # remove from existing form if set, problem with this is mnemonics -- rare situation.
74
+ #if widget.form
75
+ #f = widget.form
76
+ #f.remove_widget widget
77
+ #end
78
+ @maxrow ||= 3
79
+ #widget.set_form @form
80
+ @form.add_widget widget
81
+ widget.row ||= 0
82
+ widget.col ||= 0
83
+ if widget.row == 0
84
+ widget.row = [@maxrow+1, 3].max if widget.row == 0
85
+ else
86
+ widget.row += @row_offset
87
+ end
88
+ if widget.col == 0
89
+ widget.col = 5
90
+ else
91
+ # i don't know button_offset as yet
92
+ widget.col += @col_offset
93
+ end
94
+ # in most cases this override is okay, but what if user has set it
95
+ # The problem is that widget and field are doing a default setting so i don't know
96
+ # if user has set or widget has done a default setting. NOTE
97
+ # 2014-05-31 - 12:40 CANIS BUTTONCOLOR i have commented out since it should take from form
98
+ # to see effect
99
+ if false
100
+ widget.color ||= @color # we are overriding colors, how to avoid since widget sets it
101
+ widget.bgcolor ||= @bgcolor
102
+ widget.attr = @attr if @attr # we are overriding what user has put. DARN !
103
+ end
104
+ @maxrow = widget.row if widget.row > @maxrow
105
+ @suggested_h = @height || @maxrow+6
106
+ @suggested_w ||= 0
107
+ ww = widget.width || 5 # some widgets do no set a default width, and could be null
108
+ _w = [ww + 5, 15].max
109
+ @suggested_w = widget.col + _w if widget.col > @suggested_w
110
+ if ww >= @suggested_w
111
+ @suggested_w = ww + widget.col + 10
112
+ end
113
+ $log.debug " MESSAGEBOX add suggested_w #{@suggested_w} "
114
+ # if w's given col is > width then add to suggested_w or text.length
115
+ end
116
+ alias :add :item
117
+ # returns button index
118
+ # Call this after instantiating the window
119
+ def run
120
+ repaint
121
+ @form.pack # needs window
122
+ @form.repaint
123
+ @window.wrefresh
124
+ return handle_keys
125
+ end
126
+ def repaint
127
+ _create_window unless @window
128
+ #acolor = get_color $reverscolor, @color, @bgcolor
129
+ acolor = 0 # ??? FIXME
130
+ $log.debug " MESSAGE BOX bg:#{@bgcolor} , co:#{@color} , colorpair:#{acolor}"
131
+ @window.wbkgd(FFI::NCurses.COLOR_PAIR(acolor) | REVERSE);
132
+
133
+ @color_pair ||= CP_BLACK
134
+ bordercolor = @border_color || CP_BLACK
135
+ borderatt = @border_attrib || NORMAL
136
+ @window.wattron(FFI::NCurses.COLOR_PAIR(bordercolor) | (borderatt || FFI::NCurses::A_NORMAL))
137
+ print_border_mb @window, 1,2, @height, @width, nil, nil
138
+ @window.wattroff(FFI::NCurses.COLOR_PAIR(bordercolor) | (borderatt || FFI::NCurses::A_NORMAL))
139
+ @title ||= "+-+"
140
+ @title_color ||= CP_CYAN
141
+ @title_attr ||= REVERSE
142
+ title = " "+@title+" "
143
+ # normalcolor gives a white on black stark title like links and elinks
144
+ # You can also do 'acolor' to give you a sober title that does not take attention away, like mc
145
+ @window.printstring(@row=1,@col=(@width-title.length)/2,title, color=@title_color, @title_attr)
146
+ #print_message if @message
147
+ create_action_buttons(*@buttons) unless @action_buttons
148
+ end
149
+ def create_action_buttons *labels
150
+ @action_buttons = []
151
+ _row = @height-3
152
+ _col = (@width-(labels.count*8))/2
153
+ _col = 5 if _col < 1
154
+
155
+ labels.each_with_index do |l, ix|
156
+ b = Button.new text: l, row: _row, col: _col
157
+ _col += l.length+5
158
+ @action_buttons << b
159
+ @form.add_widget b
160
+ b.command do
161
+ @selected_index = ix
162
+ throw(:close, ix)
163
+ end
164
+ end
165
+ end
166
+ # CLEAN THIS UP TODO
167
+ # Pass a short message to be printed.
168
+ # This creates a label for a short message, and a field for a long one.
169
+ # @yield field created
170
+ # @param [String] text to display
171
+ def message message # yield label or field being used for display for further customization
172
+ @suggested_h = @height || 10
173
+ message = message.gsub(/[\n\r\t]/,' ') rescue message
174
+ message_col = 5
175
+ $log.debug " MESSAGE w: #{@width}, size: #{message.size} "
176
+ _pad = 5
177
+ @suggested_w = @width || [message.size + _pad + message_col , FFI::NCurses.COLS-2].min
178
+ r = 3
179
+ len = message.length
180
+ #@suggested_w = len + _pad + message_col if len < @suggested_w - _pad - message_col
181
+
182
+ display_length = @suggested_w-_pad
183
+ display_length -= message_col
184
+ message_height = 2
185
+ #clr = @color || :white
186
+ #bgclr = @bgcolor || :black
187
+
188
+ color_pair = CP_WHITE
189
+ # trying this out. sometimes very long labels get truncated, so i give a field in wchich user
190
+ # can use arrow key or C-a and C-e
191
+ if message.size > display_length
192
+ message_label = Field.new({:text => message, :name=>"message_label",
193
+ :row => r, :col => message_col, :width => display_length,
194
+ :color_pair => color_pair, :editable => false})
195
+ else
196
+ message_label = Label.new({:text => message, :name=>"message_label",
197
+ :row => r, :col => message_col, :width => display_length,
198
+ :height => message_height, :color_pair => color_pair})
199
+ end
200
+ @form.add_widget message_label
201
+ @maxrow = 3
202
+ yield message_label if block_given?
203
+ end
204
+ alias :message= :message
205
+
206
+ # This is for larger messages, or messages where the size is not known.
207
+ # A textview object is created and yielded.
208
+ #
209
+ def text message
210
+ @suggested_w = @width || (FFI::NCurses.COLS * 0.80).floor
211
+ @suggested_h = @height || (FFI::NCurses.LINES * 0.80).floor
212
+
213
+ message_col = 3
214
+ r = 2
215
+ display_length = @suggested_w-4
216
+ display_length -= message_col
217
+ #clr = @color || :white
218
+ #bgclr = @bgcolor || :black
219
+ color_pair = CP_WHITE
220
+
221
+ if message.is_a? Array
222
+ l = longest_in_list message
223
+ if l > @suggested_w
224
+ if l < FFI::NCurses.COLS
225
+ #@suggested_w = l
226
+ @suggested_w = FFI::NCurses.COLS-2
227
+ else
228
+ @suggested_w = FFI::NCurses.COLS-2
229
+ end
230
+ display_length = @suggested_w-6
231
+ end
232
+ # reduce width and height if you can based on array contents
233
+ else
234
+ message = wrap_text(message, display_length).split("\n")
235
+ end
236
+ # now that we have moved to textpad that +8 was causing black lines to remain after the text
237
+ message_height = message.size #+ 8
238
+ # reduce if possible if its not required.
239
+ #
240
+ r1 = (FFI::NCurses.LINES-@suggested_h)/2
241
+ r1 = r1.floor
242
+ w = @suggested_w
243
+ c1 = (FFI::NCurses.COLS-w)/2
244
+ c1 = c1.floor
245
+ @suggested_row = r1
246
+ @suggested_col = c1
247
+ brow = @button_row || @suggested_h-4
248
+ available_ht = brow - r + 1
249
+ message_height = [message_height, available_ht].min
250
+ # replaced 2014-04-14 - 23:51
251
+ message_label = Textbox.new({:name=>"message_label", :list => message,
252
+ :row => r, :col => message_col, :width => display_length,
253
+ :height => message_height, :color_pair => color_pair})
254
+ #message_label.set_content message
255
+ @form.add_widget message_label
256
+ yield message_label if block_given?
257
+
258
+ end
259
+ alias :text= :text
260
+ # returns length of longest
261
+ def longest_in_list list #:nodoc:
262
+ longest = list.inject(0) do |memo,word|
263
+ memo >= word.length ? memo : word.length
264
+ end
265
+ longest
266
+ end
267
+ def wrap_text(s, width=78) # {{{
268
+ s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n").split("\n")
269
+ end
270
+ def _create_window
271
+
272
+ $log.debug " MESSAGEBOX _create_window h:#{@height} w:#{@width} r:#{@row} c:#{@col} "
273
+ $log.debug " MESSAGEBOX _create_window h:#{@suggested_h} w:#{@suggested_w} "
274
+ @width ||= @suggested_w || 60
275
+ @height = @suggested_h || 10
276
+ $log.debug " MESSAGEBOX _create_window h:#{@height} w:#{@width} r:#{@row} c:#{@col} "
277
+ if @suggested_row
278
+ @row = @suggested_row
279
+ else
280
+ @row = ((FFI::NCurses.LINES-@height)/2).floor
281
+ end
282
+ if @suggested_col
283
+ @col = @suggested_col
284
+ else
285
+ w = @width
286
+ @col = ((FFI::NCurses.COLS-w)/2).floor
287
+ end
288
+ #@window = Window.new :height => @height, :width => @width, :top => @row, :left => @col
289
+ $log.debug " MESSAGEBOX _create_window h:#{@height} w:#{@width} r:#{@row} c:#{@col} "
290
+ @window = Window.new @height, @width, @row, @col
291
+ @graphic = @window
292
+ @form.window = @window
293
+ # in umbra, the widgets would not be having a window, if window was created after the widgets were added
294
+ end
295
+ def handle_keys
296
+ buttonindex = catch(:close) do
297
+ while((ch = @window.getch()) != FFI::NCurses::KEY_F10 )
298
+ break if ch == ?\C-q.getbyte(0) || ch == 2727 # added double esc
299
+ begin
300
+ # trying out repaint of window also if repaint all asked for. 12 is C-l
301
+ if ch == 1000 or ch == 12
302
+ repaint
303
+ end
304
+ @form.handle_key(ch)
305
+ @window.wrefresh
306
+ rescue => err
307
+ $log.debug( err) if err
308
+ $log.debug(err.backtrace.join("\n")) if err
309
+ #textdialog ["Error in Messagebox: #{err} ", *err.backtrace], :title => "Exception" # TODO
310
+ @window.refresh # otherwise the window keeps showing (new FFI-ncurses issue)
311
+ ensure
312
+ end
313
+
314
+ end # while loop
315
+ end # close
316
+ $log.debug "MESSAGEBOX: CALLING PROGRAM BEING RETURNED: #{buttonindex} "
317
+ @window.destroy
318
+ # added 2014-05-01 - 18:10 hopefully to refresh root_window.
319
+ #Window.refresh_all
320
+ return buttonindex
321
+ end
322
+ # this is identical to the border printed by dialogs.
323
+ # The border is printed not on the edge, but one row and column inside.
324
+ # This is purely cosmetic, otherwise windows.box should be used which prints a box
325
+ # on the edge.
326
+ private def print_border_mb window, row, col, height, width, color, attr # {{{
327
+ win = window.pointer
328
+ #att = attr
329
+ len = width
330
+ len = FFI::NCurses.COLS if len == 0
331
+ space_char = " ".codepoints.first
332
+ (row-1).upto(row+height-1) do |r|
333
+ # this loop clears the screen, printing spaces does not work since ncurses does not do anything
334
+ FFI::NCurses.mvwhline(win, r, col, space_char, len)
335
+ end
336
+
337
+ FFI::NCurses.mvwaddch win, row, col, FFI::NCurses::ACS_ULCORNER
338
+ FFI::NCurses.mvwhline( win, row, col+1, FFI::NCurses::ACS_HLINE, width-6)
339
+ FFI::NCurses.mvwaddch win, row, col+width-5, FFI::NCurses::ACS_URCORNER
340
+ FFI::NCurses.mvwvline( win, row+1, col, FFI::NCurses::ACS_VLINE, height-4)
341
+
342
+ FFI::NCurses.mvwaddch win, row+height-3, col, FFI::NCurses::ACS_LLCORNER
343
+ FFI::NCurses.mvwhline(win, row+height-3, col+1, FFI::NCurses::ACS_HLINE, width-6)
344
+ FFI::NCurses.mvwaddch win, row+height-3, col+width-5, FFI::NCurses::ACS_LRCORNER
345
+ FFI::NCurses.mvwvline( win, row+1, col+width-5, FFI::NCurses::ACS_VLINE, height-4)
346
+ end # }}}
347
+ end # class
348
+ end # module
data/lib/umbra/pad.rb ADDED
@@ -0,0 +1,340 @@
1
+ =begin
2
+ * Name: PadReader.rb
3
+ * Description : This is an independent file viewer that uses a Pad and traps keys
4
+ I am using only ffi-ncurses and not window.rb or any other support classes
5
+ so this can be used anywhere else.
6
+ * Author: jkepler
7
+ * Date: 2018-03-28 14:30
8
+ * License: MIT
9
+ * Last update: 2018-04-17 11:25
10
+
11
+ == CHANGES
12
+ == TODO
13
+ - should have option to wrap text
14
+ - / search ?
15
+ NOTE:
16
+ in this the cursor does not move down, it starts to scroll straight away.
17
+ So we need another version for lists and textviews in which the cursor moves with up and down.
18
+ The cursor should be invisible in this.
19
+ == -----------------------
20
+ =end
21
+ require 'ffi-ncurses'
22
+
23
+ class Pad
24
+
25
+ # You may pass height, width, row and col for creating a window otherwise a fullscreen window
26
+ # will be created. If you pass a window from caller then that window will be used.
27
+ # Some keys are trapped, jkhl space, pgup, pgdown, end, home, t b
28
+ # This is currently very minimal and was created to get me started to integrating
29
+ # pads into other classes such as textview.
30
+ def initialize config={}, &block
31
+
32
+ $log.debug " inside pad contructor"
33
+ @config = config
34
+ @rows = FFI::NCurses.LINES-1
35
+ @cols = FFI::NCurses.COLS-1
36
+ @prow = @pcol = 0 # show many cols we are panning
37
+ @startrow = 0
38
+ @startcol = 0
39
+
40
+ h = config.fetch(:height, 0)
41
+ w = config.fetch(:width, 0)
42
+ t = config.fetch(:row, 0)
43
+ l = config.fetch(:col, 0)
44
+ @color_pair = config.fetch(:color_pair, 14)
45
+ @attr = config.fetch(:attr, FFI::NCurses::A_BOLD)
46
+ @rows = h unless h == 0
47
+ @cols = w unless w == 0
48
+ @startrow = t unless t == 0
49
+ @startcol = l unless l == 0
50
+ @suppress_border = config[:suppress_border]
51
+ top = t
52
+ left = l
53
+ @height = h
54
+ @width = w
55
+ #@pointer, @panel = create_window(h, w, t, l)
56
+ @pointer, @panel = create_centered_window(h, w, @color_pair, @attr)
57
+
58
+ @startrow, @startcol = FFI::NCurses.getbegyx(@pointer)
59
+ unless @suppress_border
60
+ @startrow += 1
61
+ @startcol += 1
62
+ @rows -=3 # 3 is since print_border_only reduces one from width, to check whether this is correct
63
+ @cols -=3
64
+ end
65
+ $log.debug "top and left are: #{top} #{left} "
66
+ #@window.box # 2018-03-28 -
67
+ FFI::NCurses.box @pointer, 0, 0
68
+ title(config[:title])
69
+ FFI::NCurses.wbkgd(@pointer, FFI::NCurses.COLOR_PAIR(@color_pair) | @attr);
70
+ FFI::NCurses.curs_set 0 # cursor invisible
71
+ if config[:filename]
72
+ self.filename=(config[:filename])
73
+ elsif config[:list]
74
+ self.list=(config[:list])
75
+ end
76
+ end
77
+ # minimum window creator method, not using a class.
78
+ # However, some methods do require windows width and ht etc
79
+ def create_window h, w, t, l
80
+ pointer = FFI::NCurses.newwin(h, w, t, l)
81
+ panel = FFI::NCurses.new_panel(pointer)
82
+ FFI::NCurses.keypad(pointer, true)
83
+ return pointer, panel
84
+ end
85
+ def create_centered_window height, width, color_pair=14, attr=FFI::NCurses::A_BOLD
86
+ row = ((FFI::NCurses.LINES-height)/2).floor
87
+ col = ((FFI::NCurses.COLS-width)/2).floor
88
+ pointer = FFI::NCurses.newwin(height, width, row, col)
89
+ FFI::NCurses.wbkgd(pointer, FFI::NCurses.COLOR_PAIR(color_pair) | attr);
90
+ panel = FFI::NCurses.new_panel(pointer)
91
+ FFI::NCurses.keypad(pointer, true)
92
+ return pointer, panel
93
+ end
94
+ def destroy_window pointer, panel
95
+ FFI::NCurses.del_panel(panel) if panel
96
+ FFI::NCurses.delwin(pointer) if pointer
97
+ panel = pointer = nil # prevent call twice
98
+ end
99
+ def destroy_pad
100
+ if @pad
101
+ FFI::NCurses.delwin(@pad)
102
+ @pad = nil
103
+ end
104
+ end
105
+ # print a title over the box on zeroth row
106
+ def title stitle
107
+ return unless stitle
108
+ stitle = "| #{stitle} |"
109
+ col = (@width-stitle.size)/2
110
+ FFI::NCurses.mvwaddstr(@pointer, 0, col, stitle)
111
+ end
112
+ private def display_content content
113
+ @pad = create_pad content
114
+ FFI::NCurses.wrefresh(@pointer)
115
+ padrefresh
116
+ end
117
+
118
+ private def create_pad content
119
+ # destroy pad if exists
120
+ destroy_pad
121
+ @content_rows, @content_cols = content_dimensions(content)
122
+ pad = FFI::NCurses.newpad(@content_rows, @content_cols)
123
+ FFI::NCurses.wbkgd(pad, FFI::NCurses.COLOR_PAIR(@color_pair) | @attr);
124
+ FFI::NCurses.keypad(pad, true); # function and arrow keys
125
+
126
+ FFI::NCurses.update_panels
127
+ cp = @color_pair
128
+ #@attr = FFI::NCurses::A_BOLD
129
+ FFI::NCurses.wattron(pad, FFI::NCurses.COLOR_PAIR(cp) | @attr)
130
+ # WRITE
131
+ filler = " "*@content_cols
132
+ content.each_index { |ix|
133
+ #FFI::NCurses.mvwaddstr(pad,ix, 0, filler)
134
+ FFI::NCurses.mvwaddstr(pad,ix, 0, content[ix])
135
+ }
136
+ FFI::NCurses.wattroff(pad, FFI::NCurses.COLOR_PAIR(cp) | @attr)
137
+ return pad
138
+ end
139
+
140
+ # receive array as content source
141
+ #
142
+ def list=(content)
143
+ display_content content
144
+ end
145
+ # source of data is a filename
146
+ def filename=(filename)
147
+ content = File.open(filename,"r").read.split("\n")
148
+ display_content content
149
+ end
150
+ private def content_dimensions content
151
+ content_rows = content.count
152
+ content_cols = content_cols(content)
153
+ return content_rows, content_cols
154
+ end
155
+
156
+
157
+ # write pad onto window
158
+ private
159
+ def padrefresh
160
+ raise "padrefresh: Pad not created" unless @pad
161
+ FFI::NCurses.prefresh(@pad,@prow,@pcol, @startrow,@startcol, @rows + @startrow,@cols+@startcol);
162
+ end
163
+
164
+ # returns button index
165
+ # Call this after instantiating the window
166
+ public
167
+ def run
168
+ return handle_keys
169
+ end
170
+
171
+ # convenience method
172
+ private
173
+ def key x
174
+ x.getbyte(0)
175
+ end
176
+ def content_cols content
177
+ # FIXME bombs if content contains integer or nil.
178
+ #longest = content.max_by(&:length)
179
+ #longest.length
180
+ max = 1
181
+ content.each do |line|
182
+ next unless line
183
+ l = 1
184
+ case line
185
+ when String
186
+ l = line.length
187
+ else
188
+ l = line.to_s.length
189
+ end
190
+ max = l if l > max
191
+ end
192
+ return max
193
+ end
194
+ # returns length of longest
195
+ def longest_in_list list #:nodoc:
196
+ longest = list.inject(0) do |memo,word|
197
+ memo >= word.length ? memo : word.length
198
+ end
199
+ longest
200
+ end
201
+
202
+ # returns button index
203
+ private
204
+ def handle_keys
205
+ @height = FFI::NCurses.LINES-1 if @height == 0
206
+ ht = @rows
207
+ scroll_lines = @height/2
208
+ buttonindex = catch(:close) do
209
+ maxrow = @content_rows - @rows
210
+ maxcol = @content_cols - @cols
211
+ while ((ch = FFI::NCurses.wgetch(@pointer)) != FFI::NCurses::KEY_F10)
212
+ break if ch == ?\C-q.getbyte(0)
213
+ begin
214
+ case ch
215
+ when key(?g), 279 # home as per iterm2
216
+ @prow = 0
217
+ @pcol = 0
218
+ when key(?b), key(?G), 277 # end as per iterm2
219
+ @prow = maxrow-1
220
+ @pcol = 0
221
+ when key(?j), FFI::NCurses::KEY_DOWN
222
+ @prow += 1
223
+ when key(?k), FFI::NCurses::KEY_UP
224
+ @prow -= 1
225
+ when 32, 338 # Page Down abd Page Up as per iTerm2
226
+ @prow += 10
227
+ when key(?\C-d)
228
+ @prow += scroll_lines
229
+ when key(?\C-b)
230
+ @prow -= scroll_lines
231
+ when key(?\C-f)
232
+ @prow += ht
233
+ when key(?\C-u)
234
+ @prow -= ht
235
+ when 339
236
+ @prow -= 10
237
+ when key(?l), FFI::NCurses::KEY_RIGHT
238
+ @pcol += 1
239
+ when key(?$)
240
+ @pcol = maxcol - 1
241
+ when key(?h), FFI::NCurses::KEY_LEFT
242
+ @pcol -= 1
243
+ when key(?0)
244
+ @pcol = 0
245
+ when key(?q)
246
+ throw :close
247
+ else
248
+ #alert " #{ch} not mapped "
249
+ end
250
+ @prow = 0 if @prow < 0
251
+ @pcol = 0 if @pcol < 0
252
+ if @prow > maxrow-1
253
+ @prow = maxrow-1
254
+ end
255
+ if @pcol > maxcol-1
256
+ @pcol = maxcol-1
257
+ end
258
+ padrefresh
259
+ #FFI::NCurses::Panel.update_panels # 2018-03-28 - this bombs elsewhere
260
+ rescue => err
261
+ if $log
262
+ $log.debug err.to_s
263
+ $log.debug err.backtrace.join("\n")
264
+ end
265
+ FFI::NCurses.endwin
266
+ puts "INSIDE pad.rb"
267
+ puts err
268
+ puts err.backtrace.join("\n")
269
+ ensure
270
+ end
271
+
272
+ end # while loop
273
+ end # close
274
+ rescue => err
275
+ if $log
276
+ $log.debug err.to_s
277
+ $log.debug err.backtrace.join("\n")
278
+ end
279
+ FFI::NCurses.endwin
280
+ puts err
281
+ puts err.backtrace.join("\n")
282
+ ensure
283
+ #@window.destroy #unless @config[:window]
284
+ destroy_window @pointer, @panel
285
+ #FFI::NCurses.delwin(@pad) if @pad
286
+ destroy_pad
287
+ FFI::NCurses.curs_set 1 # cursor visible again
288
+ return buttonindex
289
+ end
290
+ end
291
+ if __FILE__ == $PROGRAM_NAME
292
+ def startup
293
+ require 'logger'
294
+ require 'date'
295
+
296
+ path = File.join(ENV["LOGDIR"] || "./" ,"v.log")
297
+ file = File.open(path, File::WRONLY|File::TRUNC|File::CREAT)
298
+ $log = Logger.new(path)
299
+ $log.level = Logger::DEBUG
300
+ today = Time.now.to_s
301
+ $log.info "Pad demo #{$0} started on #{today}"
302
+ end
303
+ def std_colors
304
+ FFI::NCurses.use_default_colors
305
+ # 2018-03-17 - changing it to ncurses defaults
306
+ FFI::NCurses.init_pair(0, FFI::NCurses::BLACK, -1)
307
+ FFI::NCurses.init_pair(1, FFI::NCurses::RED, -1)
308
+ FFI::NCurses.init_pair(2, FFI::NCurses::GREEN, -1)
309
+ FFI::NCurses.init_pair(3, FFI::NCurses::YELLOW, -1)
310
+ FFI::NCurses.init_pair(4, FFI::NCurses::BLUE, -1)
311
+ FFI::NCurses.init_pair(5, FFI::NCurses::MAGENTA, -1)
312
+ FFI::NCurses.init_pair(6, FFI::NCurses::CYAN, -1)
313
+ FFI::NCurses.init_pair(7, FFI::NCurses::WHITE, -1)
314
+ FFI::NCurses.init_pair(14, FFI::NCurses::WHITE, FFI::NCurses::CYAN)
315
+ end
316
+
317
+
318
+ FFI::NCurses.initscr
319
+ FFI::NCurses.curs_set 1
320
+ FFI::NCurses.raw
321
+ FFI::NCurses.noecho
322
+ FFI::NCurses.keypad FFI::NCurses.stdscr, true
323
+ FFI::NCurses.scrollok FFI::NCurses.stdscr, true
324
+ if FFI::NCurses.has_colors
325
+ FFI::NCurses.start_color
326
+ std_colors
327
+ end
328
+
329
+ startup
330
+ begin
331
+ file = ARGV[0] || $0
332
+ h = 20
333
+ w = 50
334
+ p = Pad.new :filename => "#{file}", :height => FFI::NCurses.LINES-1, :width => w, :row => 0, :col => 0, title: "pad.rb", color_pair: 14, attr: FFI::NCurses::A_BOLD
335
+ p.run
336
+ ensure
337
+ FFI::NCurses.endwin
338
+ FFI::NCurses.curs_set 1 # cursor visible again
339
+ end
340
+ end