rbhex-core 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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