slithernix-cdk 0.0.1

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