rbhex-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +20 -0
  3. data/CHANGELOG +2000 -0
  4. data/LICENSE +56 -0
  5. data/README.md +44 -0
  6. data/examples/abasiclist.rb +179 -0
  7. data/examples/alpmenu.rb +50 -0
  8. data/examples/app.sample +19 -0
  9. data/examples/atree.rb +100 -0
  10. data/examples/bline.rb +136 -0
  11. data/examples/common/file.rb +45 -0
  12. data/examples/data/README.markdown +9 -0
  13. data/examples/data/brew.txt +38 -0
  14. data/examples/data/color.2 +37 -0
  15. data/examples/data/gemlist.txt +60 -0
  16. data/examples/data/lotr.txt +12 -0
  17. data/examples/data/ports.txt +136 -0
  18. data/examples/data/table.txt +37 -0
  19. data/examples/data/tasks.csv +88 -0
  20. data/examples/data/tasks.txt +27 -0
  21. data/examples/data/todo.txt +10 -0
  22. data/examples/data/todo.txt.bak +10 -0
  23. data/examples/data/todocsv.csv +28 -0
  24. data/examples/data/unix1.txt +21 -0
  25. data/examples/data/unix2.txt +11 -0
  26. data/examples/dbdemo.rb +502 -0
  27. data/examples/dirtree.rb +94 -0
  28. data/examples/newtabbedwindow.rb +100 -0
  29. data/examples/newtesttabp.rb +92 -0
  30. data/examples/tabular.rb +146 -0
  31. data/examples/tasks.rb +178 -0
  32. data/examples/term2.rb +84 -0
  33. data/examples/testbuttons.rb +296 -0
  34. data/examples/testcombo.rb +102 -0
  35. data/examples/testfields.rb +195 -0
  36. data/examples/testkeypress.rb +72 -0
  37. data/examples/testlistbox.rb +170 -0
  38. data/examples/testmessagebox.rb +140 -0
  39. data/examples/testprogress.rb +116 -0
  40. data/examples/testree.rb +106 -0
  41. data/examples/testwsshortcuts.rb +66 -0
  42. data/examples/testwsshortcuts2.rb +128 -0
  43. data/lib/rbhex.rb +6 -0
  44. data/lib/rbhex/core/docs/index.txt +73 -0
  45. data/lib/rbhex/core/include/action.rb +80 -0
  46. data/lib/rbhex/core/include/actionmanager.rb +49 -0
  47. data/lib/rbhex/core/include/appmethods.rb +214 -0
  48. data/lib/rbhex/core/include/bordertitle.rb +48 -0
  49. data/lib/rbhex/core/include/chunk.rb +203 -0
  50. data/lib/rbhex/core/include/io.rb +553 -0
  51. data/lib/rbhex/core/include/listbindings.rb +74 -0
  52. data/lib/rbhex/core/include/listcellrenderer.rb +140 -0
  53. data/lib/rbhex/core/include/listeditable.rb +317 -0
  54. data/lib/rbhex/core/include/listscrollable.rb +663 -0
  55. data/lib/rbhex/core/include/listselectable.rb +271 -0
  56. data/lib/rbhex/core/include/multibuffer.rb +83 -0
  57. data/lib/rbhex/core/include/orderedhash.rb +77 -0
  58. data/lib/rbhex/core/include/ractionevent.rb +73 -0
  59. data/lib/rbhex/core/include/rchangeevent.rb +27 -0
  60. data/lib/rbhex/core/include/rhistory.rb +95 -0
  61. data/lib/rbhex/core/include/rinputdataevent.rb +47 -0
  62. data/lib/rbhex/core/include/vieditable.rb +172 -0
  63. data/lib/rbhex/core/include/widgetmenu.rb +66 -0
  64. data/lib/rbhex/core/system/colormap.rb +165 -0
  65. data/lib/rbhex/core/system/keyboard.rb +150 -0
  66. data/lib/rbhex/core/system/keydefs.rb +30 -0
  67. data/lib/rbhex/core/system/ncurses.rb +236 -0
  68. data/lib/rbhex/core/system/panel.rb +162 -0
  69. data/lib/rbhex/core/system/window.rb +913 -0
  70. data/lib/rbhex/core/util/ansiparser.rb +119 -0
  71. data/lib/rbhex/core/util/app.rb +1228 -0
  72. data/lib/rbhex/core/util/basestack.rb +410 -0
  73. data/lib/rbhex/core/util/bottomline.rb +1859 -0
  74. data/lib/rbhex/core/util/colorparser.rb +77 -0
  75. data/lib/rbhex/core/util/focusmanager.rb +31 -0
  76. data/lib/rbhex/core/util/padreader.rb +192 -0
  77. data/lib/rbhex/core/util/rcommandwindow.rb +604 -0
  78. data/lib/rbhex/core/util/rdialogs.rb +574 -0
  79. data/lib/rbhex/core/util/viewer.rb +149 -0
  80. data/lib/rbhex/core/util/widgetshortcuts.rb +506 -0
  81. data/lib/rbhex/core/version.rb +5 -0
  82. data/lib/rbhex/core/widgets/applicationheader.rb +103 -0
  83. data/lib/rbhex/core/widgets/box.rb +58 -0
  84. data/lib/rbhex/core/widgets/divider.rb +310 -0
  85. data/lib/rbhex/core/widgets/keylabelprinter.rb +194 -0
  86. data/lib/rbhex/core/widgets/rcombo.rb +253 -0
  87. data/lib/rbhex/core/widgets/rcontainer.rb +415 -0
  88. data/lib/rbhex/core/widgets/rlink.rb +30 -0
  89. data/lib/rbhex/core/widgets/rlist.rb +696 -0
  90. data/lib/rbhex/core/widgets/rmenu.rb +958 -0
  91. data/lib/rbhex/core/widgets/rmenulink.rb +22 -0
  92. data/lib/rbhex/core/widgets/rmessagebox.rb +387 -0
  93. data/lib/rbhex/core/widgets/rprogress.rb +118 -0
  94. data/lib/rbhex/core/widgets/rtabbedpane.rb +634 -0
  95. data/lib/rbhex/core/widgets/rtabbedwindow.rb +70 -0
  96. data/lib/rbhex/core/widgets/rtextarea.rb +960 -0
  97. data/lib/rbhex/core/widgets/rtextview.rb +739 -0
  98. data/lib/rbhex/core/widgets/rtree.rb +768 -0
  99. data/lib/rbhex/core/widgets/rwidget.rb +3277 -0
  100. data/lib/rbhex/core/widgets/scrollbar.rb +143 -0
  101. data/lib/rbhex/core/widgets/statusline.rb +113 -0
  102. data/lib/rbhex/core/widgets/tabular.rb +264 -0
  103. data/lib/rbhex/core/widgets/tabularwidget.rb +1142 -0
  104. data/lib/rbhex/core/widgets/textpad.rb +995 -0
  105. data/lib/rbhex/core/widgets/tree/treecellrenderer.rb +150 -0
  106. data/lib/rbhex/core/widgets/tree/treemodel.rb +428 -0
  107. data/rbhex-core.gemspec +32 -0
  108. metadata +172 -0
@@ -0,0 +1,913 @@
1
+ # ----------------------------------------------------------------------------- #
2
+ # File: window.rb
3
+ # Description: A wrapper over window
4
+ # Author: rkumar http://github.com/rkumar/rbcurse/
5
+ # Date: Around for a long time
6
+ # License: Same as Ruby's License (http://www.ruby-lang.org/LICENSE.txt)
7
+ # Last update: 2013-03-25 12:38
8
+ #
9
+ # == CHANGED
10
+ # removed Pad and Subwin to lib/ver/rpad.rb - hopefully I've seen the last of both
11
+ #
12
+ # == TODO
13
+ # strip and remove cruft. Now that I've stopped using pad, can we remove
14
+ # the prv_printstring nonsense.
15
+ # ----------------------------------------------------------------------------- #
16
+ #
17
+ require 'rbhex/core/system/ncurses'
18
+ require 'rbhex/core/system/panel'
19
+ require 'rbhex/core/include/chunk'
20
+ # this is since often windows are declared with 0 height or width and this causes
21
+ # crashes in the most unlikely places. This prevceents me from having to write ternary
22
+ # e.g.
23
+ # @layout[:width].ifzero(FFI::NCurses::LINES-2)
24
+ class Fixnum
25
+ def ifzero v
26
+ return self if self != 0
27
+ return v
28
+ end
29
+ end
30
+
31
+ module VER
32
+ class Window
33
+ attr_reader :width, :height, :top, :left
34
+ attr_accessor :layout # hash containing hwtl
35
+ attr_reader :panel # reader requires so he can del it in end
36
+ attr_reader :window_type # window or pad to distinguish 2009-11-02 23:11
37
+ attr_accessor :name # more for debugging log files. 2010-02-02 19:58
38
+ attr_accessor :modified # has it been modified and may need a refresh
39
+
40
+ # @param [Array, Hash] window coordinates (ht, w, top, left)
41
+ # or
42
+ # @param [int, int, int, int] window coordinates (ht, w, top, left)
43
+ # 2011-09-21 allowing array, or 4 ints, in addition to hash @since 1.3.1
44
+ def initialize(*args)
45
+
46
+ case args.size
47
+ when 1
48
+ case args[0]
49
+ when Array, Hash
50
+ layout = args[0]
51
+ else
52
+ raise ArgumentError, "Window expects 4 ints, array of 4 ints, or Hash in constructor"
53
+ end
54
+ when 4
55
+ layout = { :height => args[0], :width => args[1], :top => args[2], :left => args[3] }
56
+ end
57
+
58
+ @visible = true
59
+ reset_layout(layout)
60
+
61
+ #$log.debug "XXX:WINDOW got h #{@height}, w #{@width}, t #{@top}, l #{@left} "
62
+
63
+ @height = FFI::NCurses.LINES if @height == 0 # 2011-11-14 added since tired of checking for zero
64
+ @width = FFI::NCurses.COLS if @width == 0
65
+
66
+ @window = FFI::NCurses.newwin(@height, @width, @top, @left) # added FFI 2011-09-6
67
+ @panel = Ncurses::Panel.new(@window) # added FFI 2011-09-6
68
+ #$error_message_row = $status_message_row = Ncurses.LINES-1
69
+ $error_message_row ||= Ncurses.LINES-1
70
+ $error_message_col ||= 1 # ask (bottomline) uses 0 as default so you can have mismatch. XXX
71
+ $status_message ||= RubyCurses::Variable.new # in case not an App
72
+
73
+ $key_map ||= :vim
74
+ $esc_esc = true; # gove me double esc as 2727 so i can map it.
75
+ init_vars
76
+
77
+
78
+ end
79
+ def init_vars
80
+ @window_type = :WINDOW
81
+ Ncurses::keypad(@window, true)
82
+ # Added this so we can get Esc, and also C-c pressed in succession does not crash system
83
+ # 2011-12-20 half-delay crashes system as does cbreak
84
+ #This causes us to be unable to process gg qq since getch won't wait.
85
+ #Ncurses::nodelay(@window, bf = true)
86
+ # wtimeout was causing RESIZE sigwinch to only happen after pressing a key
87
+ #Ncurses::wtimeout(@window, $ncurses_timeout || 500) # will wait a second on wgetch so we can get gg and qq
88
+ @stack = []
89
+ @name ||="#{self}"
90
+ @modified = true
91
+ $catch_alt_digits ||= false # is this where is should put globals ? 2010-03-14 14:00 XXX
92
+ end
93
+ ##
94
+ # this is an alternative constructor
95
+ def self.root_window(layout = { :height => 0, :width => 0, :top => 0, :left => 0 })
96
+ #VER::start_ncurses
97
+ @layout = layout
98
+ @window = Window.new(@layout)
99
+ @window.name = "Window::ROOTW"
100
+ @window.wrefresh
101
+ Ncurses::Panel.update_panels
102
+ return @window
103
+ end
104
+ # 2009-10-13 12:24
105
+ # not used as yet
106
+ # this is an alternative constructor
107
+ # created if you don't want to create a hash first
108
+ # 2011-09-21 V1.3.1 You can now send an array to Window constructor
109
+ def self.create_window(h=0, w=0, t=0, l=0)
110
+ layout = { :height => h, :width => w, :top => t, :left => l }
111
+ @window = Window.new(layout)
112
+ return @window
113
+ end
114
+
115
+ def resize_with(layout)
116
+ $log.debug " DARN ! This awready duz a resize!! if h or w or even top or left changed!!! XXX"
117
+ reset_layout(layout)
118
+ #@window.wresize(height, width)
119
+ wresize(height, width)
120
+ #FFI::NCurses.wresize(@window,height, width)
121
+ # this is dicey since we often change top and left in pads only for panning !! XXX
122
+ #@window.mvwin(top, left)
123
+ mvwin(top, left)
124
+ #FFI::NCurses.mvwin(@window, top, left)
125
+ end
126
+
127
+ %w[width height top left].each do |side|
128
+ eval(
129
+ "def #{side}=(n)
130
+ return if n == #{side}
131
+ @layout[:#{side}] = n
132
+ resize_with @layout
133
+ end"
134
+ )
135
+ end
136
+ # ADDED DUE TO FFI
137
+ def wrefresh
138
+ Ncurses.wrefresh(@window)
139
+ end
140
+ def delwin # 2011-09-7
141
+ Ncurses.delwin(@window)
142
+ end
143
+ def attron *args
144
+ FFI::NCurses.wattron @window, *args
145
+ end
146
+ def attroff *args
147
+ FFI::NCurses.wattroff @window, *args
148
+ end
149
+ #
150
+ # ## END FFI
151
+
152
+ def resize
153
+ resize_with(@layout)
154
+ end
155
+
156
+ # Ncurses
157
+
158
+ def pos
159
+ return y, x
160
+ end
161
+
162
+ def y
163
+ Ncurses.getcury(@window)
164
+ end
165
+
166
+ def x
167
+ Ncurses.getcurx(@window)
168
+ end
169
+
170
+ def x=(n) move(y, n) end
171
+ def y=(n) move(n, x) end
172
+
173
+ #def move(y, x)
174
+ #return unless @visible
175
+ ## Log.debug([y, x] => caller[0,4])
176
+ ##@window.wmove(y, x) # bombing since ffi-ncurses 0.4.0 (maybe it was never called
177
+ ##earlier. was crashing in appemail.rb testchoose.
178
+ #wmove y,x # can alias it
179
+ #end
180
+ # since include FFI is taking over, i need to force it here. not going into
181
+ # method_missing
182
+ def wmove y,x
183
+ #Ncurses.wmove @window, y, x
184
+ FFI::NCurses.wmove @window, y, x
185
+ end
186
+ alias :move :wmove
187
+
188
+ # while moving from ncurses-ruby to FFI need to pass window pointer
189
+ # for w methods as well as mvw - NOT COMING HERE due to include FFI
190
+ def OLDmethod_missing(meth, *args)
191
+ $log.debug " WWWW method missing #{meth} "
192
+ if meth[0,1]=="w" || meth[0,3] == "mvw"
193
+ $log.debug " WWWW method missing #{meth} adding window in call "
194
+ #return @window.send(meth, @window, *args)
195
+ return FFI::NCurses.send(meth, @window, *args)
196
+ else
197
+ end
198
+ if @window
199
+ if @window.respond_to? meth
200
+ @window.send(meth, *args)
201
+ else
202
+ FFI::NCurses.send( meth, *args)
203
+ end
204
+ else
205
+ FFI::NCurses.send( meth, *args)
206
+ end
207
+ end
208
+
209
+ def method_missing(name, *args)
210
+ name = name.to_s
211
+ if (name[0,2] == "mv")
212
+ test_name = name.dup
213
+ test_name[2,0] = "w" # insert "w" after"mv"
214
+ if (FFI::NCurses.respond_to?(test_name))
215
+ return FFI::NCurses.send(test_name, @window, *args)
216
+ end
217
+ end
218
+ test_name = "w" + name
219
+ if (FFI::NCurses.respond_to?(test_name))
220
+ return FFI::NCurses.send(test_name, @window, *args)
221
+ end
222
+ FFI::NCurses.send(name, @window, *args)
223
+ end
224
+ def respond_to?(name)
225
+ name = name.to_s
226
+ if (name[0,2] == "mv" && FFI::NCurses.respond_to?("mvw" + name[2..-1]))
227
+ return true
228
+ end
229
+ FFI::NCurses.respond_to?("w" + name) || FFI::NCurses.respond_to?(name)
230
+ end
231
+
232
+ # NOTE: many of these methods using width will not work since root windows width
233
+ # is 0
234
+ def print(string, width = width)
235
+ return unless visible?
236
+ w = width == 0? Ncurses.COLS : width
237
+ waddnstr(string.to_s, w) # changed 2011 dts
238
+ end
239
+
240
+ # NOTE: many of these methods using width will not work since root windows width
241
+ # is 0
242
+ def print_yx(string, y = 0, x = 0)
243
+ w = width == 0? Ncurses.COLS : width
244
+ mvwaddnstr(y, x, string, w) # changed 2011 dts
245
+ end
246
+
247
+ # NOTE: many of these methods using width will not work since root windows width
248
+ # is 0
249
+ def print_empty_line
250
+ return unless visible?
251
+ w = getmaxx == 0? Ncurses.COLS : getmaxx
252
+ printw(' ' * w)
253
+ end
254
+
255
+ # NOTE: many of these methods using width will not work since root windows width
256
+ # is 0
257
+ def print_line(string)
258
+ w = getmaxx == 0? Ncurses.COLS : getmaxx
259
+ print(string.ljust(w))
260
+ end
261
+
262
+ # returns the actual width in case you've used a root window
263
+ # which returns a 0 for wid and ht
264
+ # NOTE: this does not work when resize , use getmaxx instead
265
+ #
266
+ def actual_width
267
+ width == 0? Ncurses.COLS : width
268
+ end
269
+
270
+ #
271
+ # returns the actual ht in case you've used a root window
272
+ # which returns a 0 for wid and ht
273
+ #
274
+ def actual_height
275
+ height == 0? Ncurses.LINES : height
276
+ end
277
+
278
+ # NOTE: many of these methods using width will not work since root windows width
279
+ # is 0
280
+ # Previously this printed a chunk as a full line, I've modified it to print on
281
+ # one line. This can be used for running text.
282
+ # NOTE 2013-03-08 - 17:02 added width so we don't overflow
283
+ def show_colored_chunks(chunks, defcolor = nil, defattr = nil, wid = 999, pcol = 0)
284
+ return unless visible?
285
+ ww = 0
286
+ chunks.each do |chunk| #|color, chunk, attrib|
287
+ case chunk
288
+ when Chunks::Chunk
289
+ color = chunk.color
290
+ attrib = chunk.attrib
291
+ text = chunk.text
292
+
293
+ ## 2013-03-08 - 19:11 take care of scrolling by means of pcol
294
+ if pcol > 0
295
+ if pcol > text.length
296
+ # ignore entire chunk and reduce pcol
297
+ pcol -= text.length
298
+ next
299
+ else
300
+ # print portion of chunk and zero pcol
301
+ text = text[pcol..-1]
302
+ pcol = 0
303
+ end
304
+ end
305
+ oldw = ww
306
+ ww += text.length
307
+ if ww > wid
308
+ # if we are exceeding the width then by howmuch
309
+ rem = wid - oldw
310
+ if rem > 0
311
+ # take only as much as we are allowed
312
+ text = text[0,rem]
313
+ else
314
+ break
315
+ end
316
+ end
317
+ when Array
318
+ # for earlier demos that used an array
319
+ color = chunk[0]
320
+ attrib = chunk[2]
321
+ text = chunk[1]
322
+ end
323
+
324
+ color ||= defcolor
325
+ attrib ||= defattr
326
+
327
+ cc, bg = ColorMap.get_colors_for_pair color
328
+ #$log.debug "XXX: CHUNK window #{text}, cp #{color} , attrib #{attrib}. #{cc}, #{bg} "
329
+ color_set(color,nil) if color
330
+ wattron(attrib) if attrib
331
+ print(text)
332
+ wattroff(attrib) if attrib
333
+ end
334
+ end
335
+
336
+ def puts(*strings)
337
+ print(strings.join("\n") << "\n")
338
+ end
339
+
340
+ def _refresh
341
+ return unless visible?
342
+ @window.refresh
343
+ end
344
+
345
+ def wnoutrefresh
346
+ return unless visible?
347
+ @window.wnoutrefresh
348
+ end
349
+
350
+ def color=(color)
351
+ @color = color
352
+ @window.color_set(color, nil)
353
+ end
354
+
355
+ def highlight_line(color, y, x, max)
356
+ @window.mvchgat(y, x, max, Ncurses::A_NORMAL, color, nil)
357
+ end
358
+
359
+ def ungetch(ch)
360
+ Ncurses.ungetch(ch)
361
+ end
362
+
363
+ def getch
364
+ #c = @window.getch
365
+ c = FFI::NCurses.wgetch(@window)
366
+ # the only reason i am doing this is so ESC can be returned if no key is pressed
367
+ # after that, not sure how this effects everything. most likely I should just
368
+ # go back to using a wtimeout, and not worry about resize requiring a keystroke
369
+ if c == 27
370
+ Ncurses::wtimeout(@window, $ncurses_timeout || 500) # will wait a second on wgetch so we can get gg and qq
371
+ else
372
+ Ncurses::nowtimeout(@window, true)
373
+ end
374
+ c
375
+ # 2011-12-20 - i am trying setting a timer on wgetch, see timeout
376
+ #c = FFI::NCurses.getch # this will keep waiting, nodelay won't be used on it, since
377
+ # we've put nodelay on window
378
+ #if c == Ncurses::KEY_RESIZE
379
+
380
+ rescue SystemExit, Interrupt
381
+ #FFI::NCurses.flushinp
382
+ 3 # is C-c
383
+ rescue StandardError
384
+ -1 # is C-c
385
+ end
386
+
387
+ # 2011-09-23 @since 1.3.1
388
+ # Added more combinations here. These 2 are just indicative
389
+ SPECIAL_KEYS = {
390
+ [27, 79, 50, 81] => 20014, # 'F14',
391
+ [27, 79, 50, 82] => 20015 # 'F15',
392
+ }
393
+
394
+ # returns control, alt, alt+ctrl, alt+control+shift, F1 .. etc
395
+ # ALT combinations also send a 27 before the actual key
396
+ # Please test with above combinations before using on your terminal
397
+ # added by rkumar 2008-12-12 23:07
398
+ # 2011-09-23 Redone Control-left, right, and Shift-F5..F10.
399
+ # Checking for quick press of Alt-Sh-O followed by Alt or printable char
400
+ # Checking for quick press of Alt-[ followed by Alt or printable char
401
+ # I attempted keeping a hash of combination arrays but it fails in the above
402
+ # 2 cases, so abandoned.
403
+ def getchar
404
+ while 1
405
+ ch = self.getch
406
+ #$log.debug "window getchar() GOT: #{ch}" if ch != -1
407
+ sf = @stack.first
408
+ if ch == -1
409
+ # the returns escape 27 if no key followed it, so its SLOW if you want only esc
410
+ if @stack.first == 27
411
+ #$log.debug " -1 stack sizze #{@stack.size}: #{@stack.inspect}, ch #{ch}"
412
+ case @stack.size
413
+ when 1
414
+ @stack.clear
415
+ return 27
416
+ when 2 # basically a ALT-O, or alt-[ (79 or 91) this will be really slow since it waits for -1
417
+ ch = 128 + @stack.last
418
+ $log.warn "XXX: WARN #{ch} CLEARING stack #{@stack} "
419
+ @stack.clear
420
+ return ch
421
+ else
422
+ # check up a hash of special keys
423
+ ret = SPECIAL_KEYS(@stack)
424
+ return ret if ret
425
+ $log.warn "INVALID UNKNOWN KEY: SHOULD NOT COME HERE getchar():#{@stack}"
426
+ end
427
+ end
428
+ # possibly a 49 left over from M3-1
429
+ unless @stack.empty?
430
+ if @stack.size == 1
431
+ @stack.clear
432
+ return sf
433
+ end
434
+ $log.warn "something on stack getchar(): #{@stack} "
435
+ end
436
+ # comemnt after testing keys since this will be called a lot, even stack.clear is called a lot
437
+ $log.warn "ERROR CLEARING STACK WITH STUFF ON IT getchar():#{@stack}" if ($log.debug? && !@stack.empty?)
438
+ @stack.clear
439
+ next
440
+ end # -1
441
+ # this is the ALT combination
442
+ if @stack.first == 27
443
+ # experimental. 2 escapes in quick succession to make exit faster
444
+ if @stack.size == 1 && ch == 27
445
+ @stack.clear
446
+ return 2727 if $esc_esc # this is double-esc if you wanna trap it, trying out
447
+ return 27
448
+ end
449
+ # possible F1..F3 on xterm-color
450
+ if ch == 79 || ch == 91
451
+ #$log.debug " got 27, #{ch}, waiting for one more"
452
+ @stack << ch
453
+ next
454
+ end
455
+ #$log.debug "stack SIZE #{@stack.size}, #{@stack.inspect}, ch: #{ch}"
456
+ if @stack == [27,79]
457
+ # xterm-color
458
+ case ch
459
+ when 80
460
+ ch = FFI::NCurses::KEY_F1
461
+ when 81
462
+ ch = FFI::NCurses::KEY_F2
463
+ when 82
464
+ ch = FFI::NCurses::KEY_F3
465
+ when 83
466
+ ch = FFI::NCurses::KEY_F4
467
+ #when 27 # another alt-char following Alt-Sh-O
468
+ else
469
+ ## iterm2 uses these for HOME END num keyboard keys
470
+ @stack.clear
471
+ #@stack << ch # earlier we pushed this but it could be of use
472
+ #return 128 + 79
473
+ return 128 + 79 + ch
474
+
475
+ end
476
+ @stack.clear
477
+ return ch
478
+ elsif @stack == [27, 91]
479
+ # XXX 27, 91 also is Alt-[
480
+ if ch == 90
481
+ @stack.clear
482
+ return KEY_BTAB # backtab
483
+ elsif ch == 53 || ch == 50 || ch == 51
484
+ # control left, right and shift function
485
+ @stack << ch
486
+ next
487
+ elsif ch == 27 # another alt-char immediately after Alt-[
488
+ $log.debug "getchar in 27, will return 128+91 " if $log.debug?
489
+ @stack.clear
490
+ @stack << ch
491
+ return 128 + 91
492
+ else
493
+ $log.debug "getchar in other, will return 128+91: #{ch} " if $log.debug?
494
+ # other cases Alt-[ followed by some char or key - merge with previous
495
+ @stack.clear
496
+ @stack << ch
497
+ return 128 + 91
498
+ end
499
+ elsif @stack == [27, 91, 53]
500
+ if ch == 68
501
+ @stack.clear
502
+ return C_LEFT # control-left
503
+ elsif ch == 67
504
+ @stack.clear
505
+ return C_RIGHT # -control-rt
506
+ end
507
+ elsif @stack == [27, 91, 51]
508
+ if ch == 49 && getch()== 126
509
+ @stack.clear
510
+ return 20009 # sh_f9
511
+ end
512
+ elsif @stack == [27, 91, 50]
513
+ if ch == 50 && getch()== 126
514
+ @stack.clear
515
+ return 20010 # sh-F10
516
+ end
517
+ if ch == 57 && getch()== 126
518
+ @stack.clear
519
+ return 20008 # sh-F8
520
+ elsif ch == 56 && getch()== 126
521
+ @stack.clear
522
+ return 20007 # sh-F7
523
+ elsif ch == 54 && getch()== 126
524
+ @stack.clear
525
+ return 20006 # sh-F6
526
+ elsif ch == 53 && getch()== 126
527
+ @stack.clear
528
+ return 20005 # sh-F5
529
+ end
530
+ end
531
+ # the usual Meta combos. (alt) - this is screwing it up, just return it in some way
532
+ ch = 128 + ch
533
+ @stack.clear
534
+ return ch
535
+ end # stack.first == 27
536
+ # append a 27 to stack, actually one can use a flag too
537
+ if ch == 27
538
+ @stack << 27
539
+ next
540
+ end
541
+ return ch
542
+ end # while
543
+ end # def
544
+
545
+ # doesn't seem to work, clears first line, not both
546
+ def clear
547
+ # return unless visible?
548
+ move 0, 0
549
+ puts *Array.new(height){ ' ' * (width - 1) }
550
+ end
551
+
552
+ # setup and reset
553
+
554
+ ## allow user to send an array
555
+ # I am tired of the hash layout (taken from ver).
556
+ def reset_layout(layout)
557
+ case layout
558
+ when Array
559
+ $log.error "NIL in window constructor" if layout.include? nil
560
+ raise ArgumentError, "Nil in window constructor" if layout.include? nil
561
+ @height, @width, @top, @left = *layout
562
+ raise ArgumentError, "Nil in window constructor" if @top.nil? || @left.nil?
563
+
564
+ @layout = { :height => @height, :width => @width, :top => @top, :left => @top }
565
+ when Hash
566
+ @layout = layout
567
+
568
+ [:height, :width, :top, :left].each do |name|
569
+ instance_variable_set("@#{name}", layout_value(name))
570
+ end
571
+ end
572
+ end
573
+
574
+ # removed ref to default_for since giving error in FFI 2011-09-8
575
+ def layout_value(name)
576
+ value = @layout[name]
577
+ default = default_for(name)
578
+
579
+ value = value.call(default) if value.respond_to?(:call)
580
+ return (value || default).to_i
581
+ end
582
+
583
+ # this gives error since stdscr is only a pointer at this time
584
+ def default_for(name)
585
+ case name
586
+ when :height, :top
587
+ #Ncurses.stdscr.getmaxy(stdscr)
588
+ FFI::NCurses.LINES
589
+ when :width, :left
590
+ #Ncurses.stdscr.getmaxx(stdscr)
591
+ FFI::NCurses.COLS
592
+ else
593
+ 0
594
+ end
595
+ end
596
+
597
+ # Ncurses panel
598
+
599
+ def hide
600
+ #return unless visible? # added 2011-10-14 these 2 are not behaving properly
601
+ Ncurses::Panel.hide_panel @panel.pointer
602
+ #Ncurses.refresh # wnoutrefresh
603
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
604
+ @visible = false
605
+ end
606
+
607
+ def show
608
+ #return if visible? # added 2011-10-14 these 2 are not behaving properly
609
+ Ncurses::Panel.show_panel @panel.pointer
610
+ #Ncurses.refresh # wnoutrefresh
611
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
612
+ @visible = true
613
+ end
614
+
615
+ def on_top
616
+ Ncurses::Panel.top_panel @panel.pointer
617
+ wnoutrefresh
618
+ end
619
+
620
+ def visible?
621
+ @visible
622
+ end
623
+
624
+ ##
625
+ # destroy window, panel and any pads that were requested
626
+ #
627
+ def destroy
628
+ # typically the ensure block should have this
629
+
630
+ #$log.debug "win destroy start"
631
+
632
+ Ncurses::Panel.del_panel(@panel.pointer) if @panel
633
+ delwin() if @window
634
+ Ncurses::Panel.update_panels # added so below window does not need to do this 2011-10-1
635
+
636
+ # destroy any pads that were created by widgets using get_pad
637
+ @pads.each { |pad|
638
+ FFI::NCurses.delwin(pad) if pad
639
+ pad = nil
640
+ } if @pads
641
+ #$log.debug "win destroy end"
642
+ end
643
+
644
+ #
645
+ # 2011-11-13 since 1.4.1
646
+ # Widgets can get window to create a pad for them. This way when the window
647
+ # is destroyed, it will delete all the pads. A widget wold not be able to do this.
648
+ # The destroy method of the widget will be called.
649
+ def get_pad content_rows, content_cols
650
+ pad = FFI::NCurses.newpad(content_rows, content_cols)
651
+ @pads ||= []
652
+ @pads << pad
653
+ ## added 2013-03-05 - 19:21 without next line how was pad being returned
654
+ return pad
655
+ end
656
+
657
+ #
658
+ # Allows user to send data as normal string or chunks for printing
659
+ # An array is assumed to be a chunk containing color and attrib info
660
+ #
661
+ def printstring_or_chunks(r,c,content, color, att = Ncurses::A_NORMAL)
662
+ if content.is_a? String
663
+ printstring(r,c,content, color, att)
664
+ elsif content.is_a? Chunks::ChunkLine
665
+ #$log.debug "XXX: using chunkline" # 2011-12-10 12:40:13
666
+ wmove r, c
667
+ a = get_attrib att
668
+ # please add width to avoid overflow
669
+ show_colored_chunks content, color, a
670
+ elsif content.is_a? Array
671
+ # several chunks in one row - NOTE Very experimental may change
672
+ if content[0].is_a? Array
673
+ $log.warn "XXX: WARNING outdated should send in a chunkline"
674
+ wmove r, c
675
+ a = get_attrib att
676
+ # please add width to avoid overflow
677
+ show_colored_chunks content, color, a
678
+ else
679
+ # a single row chunk - NOTE Very experimental may change
680
+ text = content[1].dup
681
+ printstring r, c, text, content[0] || color, content[2] || att
682
+ end
683
+ end
684
+ end
685
+ #
686
+ # prints a string formatted in our new experimental coloring format
687
+ # taken from tmux. Currently, since i have chunks workings, i convert
688
+ # to chunks and use the existing print function. This could change.
689
+ # An example of a formatted string is:
690
+ # s="#[fg=green]testing chunks #[fg=yellow, bg=red, bold]yellow #[reverse] reverseme \
691
+ # #[normal]normal#[bg = black]just yellow#[fg=blue],blue now #[underline] underlined text"
692
+ # Ideally I should push and pop colors which the shell does not do with ansi terminal sequences.
693
+ # That way i can have a line in red,
694
+ # with some word in yellow, and then the line continues in red.
695
+ #
696
+ def printstring_formatted(r,c,content, color, att = Ncurses::A_NORMAL)
697
+ att = get_attrib att unless att.is_a? Fixnum
698
+ chunkline = convert_to_chunk(content, color, att)
699
+ printstring_or_chunks r,c, chunkline, color, att
700
+ end # print
701
+ #
702
+ # print a formatted line right aligned
703
+ # c (col) is ignored and calculated based on width and unformatted string length
704
+ #
705
+ def printstring_formatted_right(r,c,content, color, att = Ncurses::A_NORMAL)
706
+ clean = content.gsub /#\[[^\]]*\]/,'' # clean out all markup
707
+ #c = actual_width() - clean.length # actual width not working if resize
708
+ c = getmaxx() - clean.length
709
+ printstring_formatted(r,c,content, color, att )
710
+ end
711
+
712
+ private
713
+ def get_default_color_parser
714
+ require 'rbhex/core/util/colorparser'
715
+ @color_parser || DefaultColorParser.new
716
+ end
717
+ # supply with a color parser, if you supplied formatted text
718
+ public
719
+ def color_parser f
720
+ $log.debug "XXX: color_parser setting in window to #{f} "
721
+ if f == :tmux
722
+ @color_parser = get_default_color_parser()
723
+ else
724
+ @color_parser = f
725
+ end
726
+ end
727
+ #
728
+ # Takes a formatted string and converts the parsed parts to chunks.
729
+ #
730
+ # @param [String] takes the entire line or string and breaks into an array of chunks
731
+ # @yield chunk if block
732
+ # @return [ChunkLine] # [Array] array of chunks
733
+ # @since 1.4.1 2011-11-3 experimental, can change
734
+ public
735
+ def convert_to_chunk s, colorp=$datacolor, att=FFI::NCurses::A_NORMAL
736
+ unless @color_parser
737
+ @color_parser = get_default_color_parser()
738
+ @converter = Chunks::ColorParser.new @color_parser
739
+ end
740
+ @converter.convert_to_chunk s, colorp, att
741
+ end
742
+
743
+ ##
744
+ # prints a string at row, col, with given color and attribute
745
+ # added by rk 2008-11-29 19:01
746
+ # I usually use this, not the others ones here
747
+ # @param r - row
748
+ # @param c - col
749
+ # @param string - text to print
750
+ # @param color - color pair
751
+ # @ param att - ncurses attribute: normal, bold, reverse, blink,
752
+ # underline
753
+ public
754
+ def printstring(r,c,string, color, att = Ncurses::A_NORMAL)
755
+ raise "Nil passed to peintstring row:#{r}, col:#{c}, #{color} " if r.nil? || c.nil? || color.nil?
756
+ #raise "Zero or less passed to printstring row:#{r}, col:#{c} " if $log.debug? && (r <=0 || c <=0)
757
+ prv_printstring(r,c,string, color, att )
758
+ end
759
+
760
+ ## name changed from printstring to prv_prinstring
761
+ def prv_printstring(r,c,string, color, att = Ncurses::A_NORMAL)
762
+
763
+ #$log.debug " #{@name} inside window printstring r #{r} c #{c} #{string} "
764
+ if att.nil?
765
+ att = Ncurses::A_NORMAL
766
+ else
767
+ att = get_attrib att
768
+ end
769
+ #att = att.downcase.to_sym if att.is_a? String
770
+ #case att
771
+ #when :normal
772
+ #att = Ncurses::A_NORMAL
773
+ #when :underline
774
+ #att = Ncurses::A_UNDERLINE
775
+ #when :bold
776
+ #att = Ncurses::A_BOLD
777
+ #when :reverse
778
+ #att = Ncurses::A_REVERSE
779
+ #when :dim
780
+ #att = Ncurses::A_DIM
781
+ #when :blink
782
+ #att = Ncurses::A_BLINK # unlikely to work
783
+ #end
784
+
785
+ wattron(Ncurses.COLOR_PAIR(color) | att)
786
+ mvwprintw(r, c, "%s", :string, string);
787
+ wattroff(Ncurses.COLOR_PAIR(color) | att)
788
+ end
789
+ ##
790
+ # NOTE : FOR MESSAGEBOXES ONLY !!!!
791
+ def print_border_mb row, col, height, width, color, attr
792
+ # the next is for xterm-256
793
+ att = get_attrib attr
794
+ len = width
795
+ len = Ncurses.COLS-0 if len == 0
796
+ # print a bar across the screen
797
+ #attron(Ncurses.COLOR_PAIR(color) | att)
798
+ # this works for newmessagebox but not for old one.
799
+ # Even now in some cases some black shows through, if the widget is printing spaces
800
+ # such as field or textview on a messagebox.
801
+ (row-1).upto(row+height-1) do |r|
802
+ mvwhline(r, col, 1, len)
803
+ end
804
+ #attroff(Ncurses.COLOR_PAIR(color) | att)
805
+
806
+ mvwaddch row, col, Ncurses::ACS_ULCORNER
807
+ mvwhline( row, col+1, Ncurses::ACS_HLINE, width-6)
808
+ mvwaddch row, col+width-5, Ncurses::ACS_URCORNER
809
+ mvwvline( row+1, col, Ncurses::ACS_VLINE, height-4)
810
+
811
+ mvwaddch row+height-3, col, Ncurses::ACS_LLCORNER
812
+ mvwhline(row+height-3, col+1, Ncurses::ACS_HLINE, width-6)
813
+ mvwaddch row+height-3, col+width-5, Ncurses::ACS_LRCORNER
814
+ mvwvline( row+1, col+width-5, Ncurses::ACS_VLINE, height-4)
815
+ end
816
+ ##
817
+ # prints a border around a widget, CLEARING the area.
818
+ # If calling with a pad, you would typically use 0,0, h-1, w-1.
819
+ def print_border row, col, height, width, color, att=Ncurses::A_NORMAL
820
+ raise "height needs to be supplied." if height.nil?
821
+ raise "width needs to be supplied." if width.nil?
822
+ att ||= Ncurses::A_NORMAL
823
+
824
+ #$log.debug " inside window print_border r #{row} c #{col} h #{height} w #{width} "
825
+
826
+ # 2009-11-02 00:45 made att nil for blanking out
827
+ # FIXME - in tabbedpanes this clears one previous line ??? XXX when using a textarea/view
828
+ # when using a pad this calls pads printstring which again reduces top and left !!! 2010-01-26 23:53
829
+ ww=width-2
830
+ (row+1).upto(row+height-1) do |r|
831
+ prv_printstring( r, col+1," "*ww , color, att)
832
+ end
833
+ prv_print_border_only row, col, height, width, color, att
834
+ end
835
+ def print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
836
+ prv_print_border_only row, col, height, width, color, att
837
+ end
838
+
839
+
840
+ ## print just the border, no cleanup
841
+ #+ Earlier, we would clean up. Now in some cases, i'd like
842
+ #+ to print border over what's been done.
843
+ # XXX this reduces 1 from width but not height !!! FIXME
844
+ def prv_print_border_only row, col, height, width, color, att=Ncurses::A_NORMAL
845
+ if att.nil?
846
+ att = Ncurses::A_NORMAL
847
+ else
848
+ att = get_attrib att
849
+ end
850
+ wattron(Ncurses.COLOR_PAIR(color) | att)
851
+ mvwaddch row, col, Ncurses::ACS_ULCORNER
852
+ mvwhline( row, col+1, Ncurses::ACS_HLINE, width-2)
853
+ mvwaddch row, col+width-1, Ncurses::ACS_URCORNER
854
+ mvwvline( row+1, col, Ncurses::ACS_VLINE, height-1)
855
+
856
+ mvwaddch row+height-0, col, Ncurses::ACS_LLCORNER
857
+ mvwhline(row+height-0, col+1, Ncurses::ACS_HLINE, width-2)
858
+ mvwaddch row+height-0, col+width-1, Ncurses::ACS_LRCORNER
859
+ mvwvline( row+1, col+width-1, Ncurses::ACS_VLINE, height-1)
860
+ wattroff(Ncurses.COLOR_PAIR(color) | att)
861
+ end
862
+ # This used to return an Ncurses window object, and you could call methods on it
863
+ # Now it returns a FFI::NCurses.window pointer which you cannot call methods on.
864
+ # You have to pass it to FFI::NCurses.<method>
865
+ def get_window; @window; end
866
+ def to_s; @name || self; end
867
+ # use in place of mvwhline if your widget could be using a pad or window
868
+ def rb_mvwhline row, col, char, width
869
+ mvwhline row, col, char, width
870
+ end
871
+ # use in place of mvwvline if your widget could be using a pad or window
872
+ def rb_mvwvline row, col, char, width
873
+ mvwvline row, col, char, width
874
+ end
875
+ # use in place of mvaddch if your widget could be using a pad or window
876
+ def rb_mvaddch row, col, char
877
+ mvaddch row, col, char
878
+ end
879
+ def close_command *args, &block
880
+ @close_command ||= []
881
+ @close_args ||= []
882
+ @close_command << block
883
+ @close_args << args
884
+ end
885
+ alias :command :close_command
886
+
887
+ # set a single command to confirm whether window shoud close or not
888
+ # Block should return true or false for closing or not
889
+ def confirm_close_command *args, &block
890
+ @confirm_close_command = block
891
+ @confirm_close_args = args
892
+ end
893
+
894
+ # need a way of lettign user decide whether he wishes to close
895
+ # in which case we return false. However, there could be several commands
896
+ # mapped. how do we know which is the one that has this authority
897
+ def fire_close_handler
898
+ if @confirm_close_command
899
+ comm = @confirm_close_command
900
+ ret = comm.call(self, *@confirm_close_args)
901
+ return ret unless ret # only return if false returned
902
+ end
903
+ if @close_command
904
+ @close_command.each_with_index do |comm, ix|
905
+ comm.call(self, *@close_args[ix]) if comm
906
+ end
907
+ end
908
+ @close_command = nil
909
+ @close_args = nil
910
+ return true
911
+ end
912
+ end
913
+ end