slithernix-cdk 0.0.1

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.
data/lib/cdk.rb ADDED
@@ -0,0 +1,1015 @@
1
+ # cdk.rb
2
+
3
+ # Copyright (c) 2013, Chris Sauro
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions are met:
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above copyright
11
+ # notice, this list of conditions and the following disclaimer in the
12
+ # documentation and/or other materials provided with the distribution.
13
+ # * Neither the name of Chris Sauro nor the
14
+ # names of its contributors may be used to endorse or promote products
15
+ # derived from this software without specific prior written permission.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18
+ # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ # DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
21
+ # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
+ # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ #trace = TracePoint.new(:call) do |tp|
29
+ # File.open('/tmp/execution_trace.log', 'a') do |f|
30
+ # f.puts "Called method '#{tp.method_id}' at #{tp.path}:#{tp.lineno}"
31
+ # end
32
+ #end
33
+ #
34
+ #trace.enable
35
+
36
+ require 'curses'
37
+
38
+ require_relative 'cdk/draw'
39
+ require_relative 'cdk/display'
40
+ require_relative 'cdk/traverse'
41
+
42
+ require_relative 'cdk/screen'
43
+
44
+ require_relative 'cdk/alphalist'
45
+ require_relative 'cdk/buttonbox'
46
+ require_relative 'cdk/calendar'
47
+ require_relative 'cdk/dialog'
48
+ require_relative 'cdk/dscale'
49
+ require_relative 'cdk/entry'
50
+ require_relative 'cdk/fscale'
51
+ require_relative 'cdk/fslider'
52
+ require_relative 'cdk/fselect'
53
+ require_relative 'cdk/histogram'
54
+ require_relative 'cdk/itemlist'
55
+ require_relative 'cdk/label'
56
+ require_relative 'cdk/marquee'
57
+ require_relative 'cdk/matrix'
58
+ require_relative 'cdk/mentry'
59
+ require_relative 'cdk/menu'
60
+ require_relative 'cdk/radio'
61
+ require_relative 'cdk/scale'
62
+ require_relative 'cdk/scroll'
63
+ require_relative 'cdk/selection'
64
+ require_relative 'cdk/slider'
65
+ require_relative 'cdk/swindow'
66
+ require_relative 'cdk/template'
67
+ require_relative 'cdk/uscale'
68
+ require_relative 'cdk/uslider'
69
+ require_relative 'cdk/viewer'
70
+
71
+ # I hate this but, whatever
72
+ module Curses
73
+ def self.napms(ms)
74
+ sleep(ms / 1000.0)
75
+ end
76
+
77
+ def self.unctrl(ch)
78
+ raise Curses::Error, 'Input is not an Integer' unless ch.is_a?(Integer)
79
+ raise Curses::Error, 'Input is out of ASCII range' if ch < 0 || ch > 127
80
+
81
+ if (32..126).include?(ch)
82
+ ch.chr
83
+ elsif ch == 127
84
+ '^?'
85
+ else
86
+ "^#{(ch + 64).chr}"
87
+ end
88
+ rescue => e
89
+ raise Curses::Error, "Error in unctrl: #{e.message}"
90
+ end
91
+
92
+ class Window
93
+ def mvwvline(y, x, ch, n)
94
+ n.times do |i|
95
+ self.setpos(y + i, x)
96
+ self.addch(ch)
97
+ end
98
+ end
99
+
100
+ def mvwhline(y, x, ch, n)
101
+ self.setpos(y, x)
102
+
103
+ n.times do |i|
104
+ self.addch(ch)
105
+ end
106
+ end
107
+
108
+ def mvwaddch(y, x, ch)
109
+ self.setpos(y, x)
110
+ self.addch(ch)
111
+ end
112
+
113
+ def mvwdelch(y, x)
114
+ self.setpos(y, x)
115
+ self.delch()
116
+ end
117
+
118
+ def mvwinsch(y, x, ch)
119
+ self.setpos(y, x)
120
+ self.insch(ch)
121
+ end
122
+
123
+ def mvinch(y, x)
124
+ self.setpos(y, x)
125
+ self.inch
126
+ end
127
+ end
128
+ end
129
+
130
+ module CDK
131
+ # some useful global values
132
+ # but these aren't global variables? -- snake 2024
133
+
134
+ def CDK.CTRL(c)
135
+ c.ord & 0x1f
136
+ end
137
+
138
+ VERSION_MAJOR = 0
139
+ VERSION_MINOR = 0
140
+ VERSION_PATCH = 1
141
+
142
+ CDK_PATHMAX = 256
143
+
144
+ L_MARKER = '<'
145
+ R_MARKER = '>'
146
+
147
+ LEFT = 9000
148
+ RIGHT = 9001
149
+ CENTER = 9002
150
+ TOP = 9003
151
+ BOTTOM = 9004
152
+ HORIZONTAL = 9005
153
+ VERTICAL = 9006
154
+ FULL = 9007
155
+
156
+ NONE = 0
157
+ ROW = 1
158
+ COL = 2
159
+
160
+ MAX_BINDINGS = 300
161
+ MAX_ITEMS = 2000
162
+ MAX_BUTTONS = 200
163
+
164
+ REFRESH = CDK.CTRL('L')
165
+ PASTE = CDK.CTRL('V')
166
+ COPY = CDK.CTRL('Y')
167
+ ERASE = CDK.CTRL('U')
168
+ CUT = CDK.CTRL('X')
169
+ BEGOFLINE = CDK.CTRL('A')
170
+ ENDOFLINE = CDK.CTRL('E')
171
+ BACKCHAR = CDK.CTRL('B')
172
+ FORCHAR = CDK.CTRL('F')
173
+ TRANSPOSE = CDK.CTRL('T')
174
+ NEXT = CDK.CTRL('N')
175
+ PREV = CDK.CTRL('P')
176
+ DELETE = "\177".ord
177
+ KEY_ESC = "\033".ord
178
+ KEY_RETURN = "\012".ord
179
+ KEY_TAB = "\t".ord
180
+
181
+ ALL_SCREENS = []
182
+ ALL_OBJECTS = []
183
+
184
+ # ACS constants have been removed from ruby curses, putting them here
185
+ # note that this is garbage and likely to break all over the place.
186
+ ACS_BLOCK = 0x30 | Curses::A_ALTCHARSET
187
+ ACS_BOARD = 0x65 | Curses::A_ALTCHARSET
188
+ ACS_BTEE = 0x76 | Curses::A_ALTCHARSET
189
+ ACS_BULLET = 0x7e | Curses::A_ALTCHARSET
190
+ ACS_CKBOARD = 0x61 | Curses::A_ALTCHARSET
191
+ ACS_DARROW = 0x2e | Curses::A_ALTCHARSET
192
+ ACS_DEGREE = 0x66 | Curses::A_ALTCHARSET
193
+ ACS_DIAMOND = 0x60 | Curses::A_ALTCHARSET
194
+ ACS_GEQUAL = 0x7a | Curses::A_ALTCHARSET
195
+ ACS_HLINE = 0x71 | Curses::A_ALTCHARSET
196
+ ACS_LANTERN = 0x69 | Curses::A_ALTCHARSET
197
+ ACS_LARROW = 0x2c | Curses::A_ALTCHARSET
198
+ ACS_LEQUAL = 0x79 | Curses::A_ALTCHARSET
199
+ ACS_LLCORNER = 0x6d | Curses::A_ALTCHARSET
200
+ ACS_LRCORNER = 0x6a | Curses::A_ALTCHARSET
201
+ ACS_LTEE = 0x74 | Curses::A_ALTCHARSET
202
+ ACS_NEQUAL = 0x7c | Curses::A_ALTCHARSET
203
+ ACS_PI = 0x7b | Curses::A_ALTCHARSET
204
+ ACS_PLMINUS = 0x67 | Curses::A_ALTCHARSET
205
+ ACS_PLUS = 0x6e | Curses::A_ALTCHARSET
206
+ ACS_RARROW = 0x2b | Curses::A_ALTCHARSET
207
+ ACS_RTEE = 0x75 | Curses::A_ALTCHARSET
208
+ ACS_S1 = 0x6f | Curses::A_ALTCHARSET
209
+ ACS_S3 = 0x70 | Curses::A_ALTCHARSET
210
+ ACS_S5 = 0x71 | Curses::A_ALTCHARSET
211
+ ACS_S7 = 0x72 | Curses::A_ALTCHARSET
212
+ ACS_S9 = 0x73 | Curses::A_ALTCHARSET
213
+ ACS_STERLING = 0x7d | Curses::A_ALTCHARSET
214
+ ACS_TTEE = 0x77 | Curses::A_ALTCHARSET
215
+ ACS_UARROW = 0x2d | Curses::A_ALTCHARSET
216
+ ACS_ULCORNER = 0x6c | Curses::A_ALTCHARSET
217
+ ACS_URCORNER = 0x6b | Curses::A_ALTCHARSET
218
+ ACS_VLINE = 0x78 | Curses::A_ALTCHARSET
219
+
220
+ # This beeps then flushes the stdout stream
221
+ def CDK.Beep
222
+ Curses.beep
223
+ $stdout.flush
224
+ end
225
+
226
+ # This sets a blank string to be len of the given characer.
227
+ def CDK.cleanChar(s, len, character)
228
+ s << character * len
229
+ end
230
+
231
+ def CDK.cleanChtype(s, len, character)
232
+ s.concat(character * len)
233
+ end
234
+
235
+ # This takes an x and y position and realigns the values iff they sent in
236
+ # values like CENTER, LEFT, RIGHT
237
+ #
238
+ # window is an Curses::WINDOW object
239
+ # xpos, ypos is an array with exactly one value, an integer
240
+ # box_width, box_height is an integer
241
+ def CDK.alignxy (window, xpos, ypos, box_width, box_height)
242
+ first = window.begx
243
+ last = window.maxx
244
+ if (gap = (last - box_width)) < 0
245
+ gap = 0
246
+ end
247
+ last = first + gap
248
+
249
+ case xpos[0]
250
+ when LEFT
251
+ xpos[0] = first
252
+ when RIGHT
253
+ xpos[0] = first + gap
254
+ when CENTER
255
+ xpos[0] = first + (gap / 2)
256
+ else
257
+ if xpos[0] > last
258
+ xpos[0] = last
259
+ elsif xpos[0] < first
260
+ xpos[0] = first
261
+ end
262
+ end
263
+
264
+ first = window.begy
265
+ last = window.maxy
266
+ if (gap = (last - box_height)) < 0
267
+ gap = 0
268
+ end
269
+ last = first + gap
270
+
271
+ case ypos[0]
272
+ when TOP
273
+ ypos[0] = first
274
+ when BOTTOM
275
+ ypos[0] = first + gap
276
+ when CENTER
277
+ ypos[0] = first + (gap / 2)
278
+ else
279
+ if ypos[0] > last
280
+ ypos[0] = last
281
+ elsif ypos[0] < first
282
+ ypos[0] = first
283
+ end
284
+ end
285
+ end
286
+
287
+ # This takes a string, a field width, and a justification type
288
+ # and returns the adjustment to make, to fill the justification
289
+ # requirement
290
+ def CDK.justifyString (box_width, mesg_length, justify)
291
+
292
+ # make sure the message isn't longer than the width
293
+ # if it is, return 0
294
+ if mesg_length >= box_width
295
+ return 0
296
+ end
297
+
298
+ # try to justify the message
299
+ case justify
300
+ when LEFT
301
+ 0
302
+ when RIGHT
303
+ box_width - mesg_length
304
+ when CENTER
305
+ (box_width - mesg_length) / 2
306
+ else
307
+ justify
308
+ end
309
+ end
310
+
311
+ # This reads a file and sticks it into the list provided.
312
+ def CDK.readFile(filename, array)
313
+ begin
314
+ fd = File.new(filename, "r")
315
+ rescue
316
+ return -1
317
+ end
318
+
319
+ lines = fd.readlines.map do |line|
320
+ if line.size > 0 && line[-1] == "\n"
321
+ line[0...-1]
322
+ else
323
+ line
324
+ end
325
+ end
326
+ array.concat(lines)
327
+ fd.close
328
+ array.size
329
+ end
330
+
331
+ def CDK.encodeAttribute (string, from, mask)
332
+ mask << 0
333
+ case string[from + 1]
334
+ when 'B' then mask[0] = Curses::A_BOLD
335
+ when 'D' then mask[0] = Curses::A_DIM
336
+ when 'K' then mask[0] = Curses::A_BLINK
337
+ when 'R' then mask[0] = Curses::A_REVERSE
338
+ when 'S' then mask[0] = Curses::A_STANDOUT
339
+ when 'U' then mask[0] = Curses::A_UNDERLINE
340
+ end
341
+
342
+ if mask[0] != 0
343
+ from += 1
344
+ elsif CDK.digit?(string[from+1]) and CDK.digit?(string[from + 2])
345
+ mask[0] = Curses.A_BOLD
346
+
347
+ if Curses.has_colors?
348
+ # XXX: Only checks if terminal has colours not if colours are started
349
+ pair = string[from + 1..from + 2].to_i
350
+ mask[0] = Curses.color_pair(pair)
351
+ end
352
+
353
+ from += 2
354
+ elsif CDK.digit?(string[from + 1])
355
+ if Curses.has_colors?
356
+ # XXX: Only checks if terminal has colours not if colours are started
357
+ pair = string[from + 1].to_i
358
+ mask[0] = Curses.color_pair(pair)
359
+ else
360
+ mask[0] = Curses.A_BOLD
361
+ end
362
+
363
+ from += 1
364
+ end
365
+
366
+ return from
367
+ end
368
+
369
+ # The reverse of encodeAttribute
370
+ # Well, almost. If attributes such as bold and underline are combined in the
371
+ # same string, we do not necessarily reconstruct them in the same order.
372
+ # Also, alignment markers and tabs are lost.
373
+
374
+ def CDK.decodeAttribute (string, from, oldattr, newattr)
375
+ table = {
376
+ 'B' => Curses::A_BOLD,
377
+ 'D' => Curses::A_DIM,
378
+ 'K' => Curses::A_BLINK,
379
+ 'R' => Curses::A_REVERSE,
380
+ 'S' => Curses::A_STANDOUT,
381
+ 'U' => Curses::A_UNDERLINE
382
+ }
383
+
384
+ result = if string.nil? then '' else string end
385
+ base_len = result.size
386
+ tmpattr = oldattr & Curses::A_ATTRIBUTES
387
+
388
+ newattr &= Curses::A_ATTRIBUTES
389
+ if tmpattr != newattr
390
+ while tmpattr != newattr
391
+ found = false
392
+ table.keys.each do |key|
393
+ if (table[key] & tmpattr) != (table[key] & newattr)
394
+ found = true
395
+ result << CDK::L_MARKER
396
+ if (table[key] & tmpattr).nonzero?
397
+ result << '!'
398
+ tmpattr &= ~(table[key])
399
+ else
400
+ result << '/'
401
+ tmpattr |= table[key]
402
+ end
403
+ result << key
404
+ break
405
+ end
406
+ end
407
+ # XXX: Only checks if terminal has colours not if colours are started
408
+ if Curses.has_colors?
409
+ if (tmpattr & Curses::A_COLOR) != (newattr & Curses::A_COLOR)
410
+ oldpair = Curses.PAIR_NUMBER(tmpattr)
411
+ newpair = Curses.PAIR_NUMBER(newattr)
412
+ if !found
413
+ found = true
414
+ result << CDK::L_MARKER
415
+ end
416
+ if newpair.zero?
417
+ result << '!'
418
+ result << oldpair.to_s
419
+ else
420
+ result << '/'
421
+ result << newpair.to_s
422
+ end
423
+ tmpattr &= ~(Curses::A_COLOR)
424
+ newattr &= ~(Curses::A_COLOR)
425
+ end
426
+ end
427
+
428
+ if found
429
+ result << CDK::R_MARKER
430
+ else
431
+ break
432
+ end
433
+ end
434
+ end
435
+
436
+ return from + result.size - base_len
437
+ end
438
+
439
+ # This function takes a string, full of format markers and translates
440
+ # them into a chtype array. This is better suited to curses because
441
+ # curses uses chtype almost exclusively
442
+ def CDK.char2Chtype (string, to, align)
443
+ to << 0
444
+ align << LEFT
445
+ result = []
446
+
447
+ if string.size > 0
448
+ used = 0
449
+
450
+ # The original code makes two passes since it has to pre-allocate space but
451
+ # we should be able to make do with one since we can dynamically size it
452
+ adjust = 0
453
+ attrib = Curses::A_NORMAL
454
+ last_char = 0
455
+ start = 0
456
+ used = 0
457
+ x = 3
458
+
459
+ # Look for an alignment marker.
460
+ if string[0] == L_MARKER
461
+ if string[1] == 'C' && string[2] == R_MARKER
462
+ align[0] = CENTER
463
+ start = 3
464
+ elsif string[1] == 'R' && string[2] == R_MARKER
465
+ align[0] = RIGHT
466
+ start = 3
467
+ elsif string[1] == 'L' && string[2] == R_MARKER
468
+ start = 3
469
+ elsif string[1] == 'B' && string[2] == '='
470
+ # Set the item index value in the string.
471
+ result = [' '.ord, ' '.ord, ' '.ord]
472
+
473
+ # Pull out the bullet marker.
474
+ while x < string.size and string[x] != R_MARKER
475
+ result << (string[x].ord | Curses::A_BOLD)
476
+ x += 1
477
+ end
478
+ adjust = 1
479
+
480
+ # Set the alignment variables
481
+ start = x
482
+ used = x
483
+ elsif string[1] == 'I' && string[2] == '='
484
+ from = 3
485
+ x = 0
486
+
487
+ while from < string.size && string[from] != Curses.R_MARKER
488
+ if CDK.digit?(string[from])
489
+ adjust = adjust * 10 + string[from].to_i
490
+ x += 1
491
+ end
492
+ from += 1
493
+ end
494
+
495
+ start = x + 4
496
+ end
497
+ end
498
+
499
+ while adjust > 0
500
+ adjust -= 1
501
+ result << ' '
502
+ used += 1
503
+ end
504
+
505
+ # Set the format marker boolean to false
506
+ inside_marker = false
507
+
508
+ # Start parsing the character string.
509
+ from = start
510
+ while from < string.size
511
+ # Are we inside a format marker?
512
+ if !inside_marker
513
+ if string[from] == L_MARKER &&
514
+ ['/', '!', '#'].include?(string[from + 1])
515
+ inside_marker = true
516
+ elsif string[from] == "\\" && string[from + 1] == L_MARKER
517
+ from += 1
518
+ result << (string[from].ord | attrib)
519
+ used += 1
520
+ from += 1
521
+ elsif string[from] == "\t"
522
+ begin
523
+ result << ' '
524
+ used += 1
525
+ end while (used & 7).nonzero?
526
+ else
527
+ result << (string[from].ord | attrib)
528
+ used += 1
529
+ end
530
+ else
531
+ case string[from]
532
+ when R_MARKER
533
+ inside_marker = false
534
+ when '#'
535
+ last_char = 0
536
+ case string[from + 2]
537
+ when 'L'
538
+ case string[from + 1]
539
+ when 'L'
540
+ last_char = CDK::ACS_LLCORNER
541
+ when 'U'
542
+ last_char = CDK::ACS_ULCORNER
543
+ when 'H'
544
+ last_char = CDK::ACS_HLINE
545
+ when 'V'
546
+ last_char = CDK::ACS_VLINE
547
+ when 'P'
548
+ last_char = CDK::ACS_PLUS
549
+ end
550
+ when 'R'
551
+ case string[from + 1]
552
+ when 'L'
553
+ last_char = CDK::ACS_LRCORNER
554
+ when 'U'
555
+ last_char = CDK::ACS_URCORNER
556
+ end
557
+ when 'T'
558
+ case string[from + 1]
559
+ when 'T'
560
+ last_char = CDK::ACS_TTEE
561
+ when 'R'
562
+ last_char = CDK::ACS_RTEE
563
+ when 'L'
564
+ last_char = CDK::ACS_LTEE
565
+ when 'B'
566
+ last_char = CDK::ACS_BTEE
567
+ end
568
+ when 'A'
569
+ case string[from + 1]
570
+ when 'L'
571
+ last_char = CDK::ACS_LARROW
572
+ when 'R'
573
+ last_char = CDK::ACS_RARROW
574
+ when 'U'
575
+ last_char = CDK::ACS_UARROW
576
+ when 'D'
577
+ last_char = CDK::ACS_DARROW
578
+ end
579
+ else
580
+ case [string[from + 1], string[from + 2]]
581
+ when ['D', 'I']
582
+ last_char = CDK::ACS_DIAMOND
583
+ when ['C', 'B']
584
+ last_char = CDK::ACS_CKBOARD
585
+ when ['D', 'G']
586
+ last_char = CDK::ACS_DEGREE
587
+ when ['P', 'M']
588
+ last_char = CDK::ACS_PLMINUS
589
+ when ['B', 'U']
590
+ last_char = CDK::ACS_BULLET
591
+ when ['S', '1']
592
+ last_char = CDK::ACS_S1
593
+ when ['S', '9']
594
+ last_char = CDK::ACS_S9
595
+ end
596
+ end
597
+
598
+ if last_char.nonzero?
599
+ adjust = 1
600
+ from += 2
601
+
602
+ if string[from + 1] == '('
603
+ # check for a possible numeric modifier
604
+ from += 2
605
+ adjust = 0
606
+
607
+ while from < string.size && string[from] != ')'
608
+ if CDK.digit?(string[from])
609
+ adjust = (adjust * 10) + string[from].to_i
610
+ end
611
+ from += 1
612
+ end
613
+ end
614
+ end
615
+ (0...adjust).each do |x|
616
+ result << (last_char | attrib)
617
+ used += 1
618
+ end
619
+ when '/'
620
+ mask = []
621
+ from = CDK.encodeAttribute(string, from, mask)
622
+ attrib |= mask[0]
623
+ when '!'
624
+ mask = []
625
+ from = CDK.encodeAttribute(string, from, mask)
626
+ attrib &= ~(mask[0])
627
+ end
628
+ end
629
+ from += 1
630
+ end
631
+
632
+ if result.size == 0
633
+ result << attrib
634
+ end
635
+ to[0] = used
636
+ else
637
+ result = []
638
+ end
639
+ return result
640
+ end
641
+
642
+ # Compare a regular string to a chtype string
643
+ def CDK.cmpStrChstr (str, chstr)
644
+ i = 0
645
+ r = 0
646
+
647
+ if str.nil? && chstr.nil?
648
+ return 0
649
+ elsif str.nil?
650
+ return 1
651
+ elsif chstr.nil?
652
+ return -1
653
+ end
654
+
655
+ while i < str.size && i < chstr.size
656
+ if str[r].ord < chstr[r]
657
+ return -1
658
+ elsif str[r].ord > chstr[r]
659
+ return 1
660
+ end
661
+ i += 1
662
+ end
663
+
664
+ if str.size < chstr.size
665
+ return -1
666
+ elsif str.size > chstr.size
667
+ return 1
668
+ else
669
+ return 0
670
+ end
671
+ end
672
+
673
+ def CDK.CharOf(chtype)
674
+ (chtype.ord & 255).chr
675
+ end
676
+
677
+ # This returns a string from a chtype array
678
+ # Formatting codes are omitted.
679
+ def CDK.chtype2Char(string)
680
+ newstring = ''
681
+
682
+ unless string.nil?
683
+ string.each do |char|
684
+ newstring << CDK.CharOf(char)
685
+ end
686
+ end
687
+
688
+ return newstring
689
+ end
690
+
691
+ # This returns a string from a chtype array
692
+ # Formatting codes are embedded
693
+ def CDK.chtype2String(string)
694
+ newstring = ''
695
+ unless string.nil?
696
+ need = 0
697
+ (0...string.size).each do |x|
698
+ need = CDK.decodeAttribute(newstring, need,
699
+ x > 0 ? string[x - 1] : 0, string[x])
700
+ newstring << string[x]
701
+ end
702
+ end
703
+
704
+ return newstring
705
+ end
706
+
707
+ # This returns the length of the integer.
708
+ #
709
+ # Currently a wrapper maintained for easy of porting.
710
+ def CDK.intlen (value)
711
+ value.to_str.size
712
+ end
713
+
714
+ # This opens the current directory and reads the contents.
715
+ def CDK.getDirectoryContents(directory, list)
716
+ counter = 0
717
+
718
+ # Open the directory.
719
+ Dir.foreach(directory) do |filename|
720
+ next if filename == '.'
721
+ list << filename
722
+ end
723
+
724
+ list.sort!
725
+ return list.size
726
+ end
727
+
728
+ # This looks for a subset of a word in the given list
729
+ def CDK.searchList(list, list_size, pattern)
730
+ index = -1
731
+
732
+ if pattern.size > 0
733
+ (0...list_size).each do |x|
734
+ len = [list[x].size, pattern.size].min
735
+ ret = (list[x][0...len] <=> pattern)
736
+
737
+ # If 'ret' is less than 0 then the current word is alphabetically
738
+ # less than the provided word. At this point we will set the index
739
+ # to the current position. If 'ret' is greater than 0, then the
740
+ # current word is alphabetically greater than the given word. We
741
+ # should return with index, which might contain the last best match.
742
+ # If they are equal then we've found it.
743
+ if ret < 0
744
+ index = ret
745
+ else
746
+ if ret == 0
747
+ index = x
748
+ end
749
+ break
750
+ end
751
+ end
752
+ end
753
+ return index
754
+ end
755
+
756
+ # This function checks to see if a link has been requested
757
+ def CDK.checkForLink (line, filename)
758
+ f_pos = 0
759
+ x = 3
760
+ if line.nil?
761
+ return -1
762
+ end
763
+
764
+ # Strip out the filename.
765
+ if line[0] == L_MARKER && line[1] == 'F' && line[2] == '='
766
+ while x < line.size
767
+ if line[x] == R_MARKER
768
+ break
769
+ end
770
+ if f_pos < CDK_PATHMAX
771
+ filename << line[x]
772
+ f_pos += 1
773
+ end
774
+ x += 1
775
+ end
776
+ end
777
+ return f_pos != 0
778
+ end
779
+
780
+ # Returns the filename portion of the given pathname, i.e. after the last
781
+ # slash
782
+ # For now this function is just a wrapper for File.basename kept for ease of
783
+ # porting and will be completely replaced in the future
784
+ def CDK.baseName (pathname)
785
+ File.basename(pathname)
786
+ end
787
+
788
+ # Returns the directory for the given pathname, i.e. the part before the
789
+ # last slash
790
+ # For now this function is just a wrapper for File.dirname kept for ease of
791
+ # porting and will be completely replaced in the future
792
+ def CDK.dirName (pathname)
793
+ File.dirname(pathname)
794
+ end
795
+
796
+ # If the dimension is a negative value, the dimension will be the full
797
+ # height/width of the parent window - the value of the dimension. Otherwise,
798
+ # the dimension will be the given value.
799
+ def CDK.setWidgetDimension (parent_dim, proposed_dim, adjustment)
800
+ # If the user passed in FULL, return the parents size
801
+ if proposed_dim == FULL or proposed_dim == 0
802
+ parent_dim
803
+ elsif proposed_dim >= 0
804
+ # if they gave a positive value, return it
805
+
806
+ if proposed_dim >= parent_dim
807
+ parent_dim
808
+ else
809
+ proposed_dim + adjustment
810
+ end
811
+ else
812
+ # if they gave a negative value then return the dimension
813
+ # of the parent plus the value given
814
+ #
815
+ if parent_dim + proposed_dim < 0
816
+ parent_dim
817
+ else
818
+ parent_dim + proposed_dim
819
+ end
820
+ end
821
+ end
822
+
823
+ # This safely erases a given window
824
+ def CDK.eraseCursesWindow (window)
825
+ return if window.nil?
826
+
827
+ window.erase
828
+ window.refresh
829
+ end
830
+
831
+ # This safely deletes a given window.
832
+ def CDK.deleteCursesWindow (window)
833
+ return if window.nil?
834
+
835
+ CDK.eraseCursesWindow(window)
836
+ window.close
837
+ end
838
+
839
+ # This moves a given window (if we're able to set the window's beginning).
840
+ # We do not use mvwin(), because it does not (usually) move subwindows.
841
+ def CDK.moveCursesWindow (window, xdiff, ydiff)
842
+ return if window.nil?
843
+
844
+ xpos = window.begx + xdiff
845
+ ypos = window.begy + ydiff
846
+
847
+ old_window = window
848
+ begin
849
+ window = Curses::Window.new(old_window.begy, old_window.begx, ypos, xpos)
850
+ old_window.erase
851
+ window
852
+ rescue
853
+ CDK.Beep
854
+ end
855
+ end
856
+
857
+ def CDK.digit?(character)
858
+ !(character.match(/^[[:digit:]]$/).nil?)
859
+ end
860
+
861
+ def CDK.alpha?(character)
862
+ !(character.match(/^[[:alpha:]]$/).nil?)
863
+ end
864
+
865
+ def CDK.isChar(c)
866
+ c.ord >= 0 && c.ord < Curses::KEY_MIN
867
+ end
868
+
869
+ def CDK.KEY_F(n)
870
+ 264 + n
871
+ end
872
+
873
+ def CDK.Version
874
+ return "%d.%d - %d" % [
875
+ CDK::VERSION_MAJOR,
876
+ CDK::VERSION_MINOR,
877
+ CDK::VERSION_PATCH,
878
+ ]
879
+ end
880
+
881
+ def CDK.getString(screen, title, label, init_value)
882
+ # Create the widget.
883
+ widget = CDK::ENTRY.new(screen, CDK::CENTER, CDK::CENTER, title, label,
884
+ Curses::A_NORMAL, '.', :MIXED, 40, 0, 5000, true, false)
885
+
886
+ # Set the default value.
887
+ widget.setValue(init_value)
888
+
889
+ # Get the string.
890
+ value = widget.activate([])
891
+
892
+ # Make sure they exited normally.
893
+ if widget.exit_type != :NORMAL
894
+ widget.destroy
895
+ return nil
896
+ end
897
+
898
+ # Return a copy of the string typed in.
899
+ value = entry.getValue.clone
900
+ widget.destroy
901
+ return value
902
+ end
903
+
904
+ # This allows a person to select a file.
905
+ def CDK.selectFile(screen, title)
906
+ # Create the file selector.
907
+ fselect = CDK::FSELECT.new(screen, CDK::CENTER, CDK::CENTER, -4, -20,
908
+ title, 'File: ', Curses::A_NORMAL, '_', Curses::A_REVERSE,
909
+ '</5>', '</48>', '</N>', '</N>', true, false)
910
+
911
+ # Let the user play.
912
+ filename = fselect.activate([])
913
+
914
+ # Check the way the user exited the selector.
915
+ if fselect.exit_type != :NORMAL
916
+ fselect.destroy
917
+ screen.refresh
918
+ return nil
919
+ end
920
+
921
+ # Otherwise...
922
+ fselect.destroy
923
+ screen.refresh
924
+ return filename
925
+ end
926
+
927
+ # This returns a selected value in a list
928
+ def CDK.getListindex(screen, title, list, list_size, numbers)
929
+ selected = -1
930
+ height = 10
931
+ width = -1
932
+ len = 0
933
+
934
+ # Determine the height of the list.
935
+ if list_size < 10
936
+ height = list_size + if title.size == 0 then 2 else 3 end
937
+ end
938
+
939
+ # Determine the width of the list.
940
+ list.each do |item|
941
+ width = [width, item.size + 10].max
942
+ end
943
+
944
+ width = [width, title.size].max
945
+ width += 5
946
+
947
+ # Create the scrolling list.
948
+ scrollp = CDK::SCROLL.new(screen, CDK::CENTER, CDK::CENTER, CDK::RIGHT,
949
+ height, width, title, list, list_size, numbers, Curses::A_REVERSE,
950
+ true, false)
951
+
952
+ # Check if we made the lsit.
953
+ if scrollp.nil?
954
+ screen.refresh
955
+ return -1
956
+ end
957
+
958
+ # Let the user play.
959
+ selected = scrollp.activate([])
960
+
961
+ # Check how they exited.
962
+ if scrollp.exit_type != :NORMAL
963
+ selected = -1
964
+ end
965
+
966
+ # Clean up.
967
+ scrollp.destroy
968
+ screen.refresh
969
+ return selected
970
+ end
971
+
972
+ # This allows the user to view information.
973
+ def CDK.viewInfo(screen, title, info, count, buttons, button_count,
974
+ interpret)
975
+ selected = -1
976
+
977
+ # Create the file viewer to view the file selected.
978
+ viewer = CDK::VIEWER.new(screen, CDK::CENTER, CDK::CENTER, -6, -16,
979
+ buttons, button_count, Curses::A_REVERSE, true, true)
980
+
981
+ # Set up the viewer title, and the contents to the widget.
982
+ viewer.set(title, info, count, Curses::A_REVERSE, interpret, true, true)
983
+
984
+ # Activate the viewer widget.
985
+ selected = viewer.activate([])
986
+
987
+ # Make sure they exited normally.
988
+ if viewer.exit_type != :NORMAL
989
+ viewer.destroy
990
+ return -1
991
+ end
992
+
993
+ # Clean up and return the button index selected
994
+ viewer.destroy
995
+ return selected
996
+ end
997
+
998
+ # This allows the user to view a file.
999
+ def CDK.viewFile(screen, title, filename, buttons, button_count)
1000
+ info = []
1001
+ result = 0
1002
+
1003
+ # Open the file and read the contents.
1004
+ lines = CDK.readFile(filename, info)
1005
+
1006
+ # If we couldn't read the file, return an error.
1007
+ if lines == -1
1008
+ result = lines
1009
+ else
1010
+ result = CDK.viewInfo(screen, title, info, lines, buttons,
1011
+ button_count, true)
1012
+ end
1013
+ return result
1014
+ end
1015
+ end