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.
@@ -0,0 +1,561 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class ALPHALIST < CDK::CDKOBJS
5
+ attr_reader :scroll_field, :entry_field, :list
6
+
7
+ def initialize(cdkscreen, xplace, yplace, height, width, title, label,
8
+ list, list_size, filler_char, highlight, box, shadow)
9
+ super()
10
+ parent_width = cdkscreen.window.maxx
11
+ parent_height = cdkscreen.window.maxy
12
+ box_width = width
13
+ box_height = height
14
+ label_len = 0
15
+ bindings = {
16
+ CDK::BACKCHAR => Curses::KEY_PPAGE,
17
+ CDK::FORCHAR => Curses::KEY_NPAGE,
18
+ }
19
+
20
+ if !self.createList(list, list_size)
21
+ self.destroy
22
+ return nil
23
+ end
24
+
25
+ self.setBox(box)
26
+
27
+ # If the height is a negative value, the height will be ROWS-height,
28
+ # otherwise the height will be the given height.
29
+ box_height = CDK.setWidgetDimension(parent_height, height, 0)
30
+
31
+ # If the width is a negative value, the width will be COLS-width,
32
+ # otherwise the width will be the given width.
33
+ box_width = CDK.setWidgetDimension(parent_width, width, 0)
34
+
35
+ # Translate the label string to a chtype array
36
+ if label.size > 0
37
+ lentmp = []
38
+ chtype_label = CDK.char2Chtype(label, lentmp, [])
39
+ label_len = lentmp[0]
40
+ end
41
+
42
+ # Rejustify the x and y positions if we need to.
43
+ xtmp = [xplace]
44
+ ytmp = [yplace]
45
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
46
+ xpos = xtmp[0]
47
+ ypos = ytmp[0]
48
+
49
+ # Make the file selector window.
50
+ @win = Curses::Window.new(box_height, box_width, ypos, xpos)
51
+
52
+ if @win.nil?
53
+ self.destroy
54
+ return nil
55
+ end
56
+ @win.keypad(true)
57
+
58
+ # Set some variables.
59
+ @screen = cdkscreen
60
+ @parent = cdkscreen.window
61
+ @highlight = highlight
62
+ @filler_char = filler_char
63
+ @box_height = box_height
64
+ @box_width = box_width
65
+ @shadow = shadow
66
+ @shadow_win = nil
67
+
68
+ # Do we want a shadow?
69
+ if shadow
70
+ @shadow_win = Curses::Window.new(box_height, box_width,
71
+ ypos + 1, xpos + 1)
72
+ end
73
+
74
+ # Create the entry field.
75
+ temp_width = if CDK::ALPHALIST.isFullWidth(width)
76
+ then CDK::FULL
77
+ else box_width - 2 - label_len
78
+ end
79
+ @entry_field = CDK::ENTRY.new(cdkscreen, @win.begx, @win.begy,
80
+ title, label, Curses::A_NORMAL, filler_char, :MIXED, temp_width,
81
+ 0, 512, box, false)
82
+ if @entry_field.nil?
83
+ self.destroy
84
+ return nil
85
+ end
86
+ @entry_field.setLLchar(CDK::ACS_LTEE)
87
+ @entry_field.setLRchar(CDK::ACS_RTEE)
88
+
89
+ # Callback functions
90
+ adjust_alphalist_cb = lambda do |object_type, object, alphalist, key|
91
+ scrollp = alphalist.scroll_field
92
+ entry = alphalist.entry_field
93
+
94
+ if scrollp.list_size > 0
95
+ # Adjust the scrolling list.
96
+ alphalist.injectMyScroller(key)
97
+
98
+ # Set the value in the entry field.
99
+ current = CDK.chtype2Char(scrollp.item[scrollp.current_item])
100
+ entry.setValue(current)
101
+ entry.draw(entry.box)
102
+ return true
103
+ end
104
+ CDK.Beep
105
+ return false
106
+ end
107
+
108
+ complete_word_cb = lambda do |object_type, object, alphalist, key|
109
+ entry = alphalist.entry_field
110
+ scrollp = nil
111
+ selected = -1
112
+ ret = 0
113
+ alt_words = []
114
+
115
+ if entry.info.size == 0
116
+ CDK.Beep
117
+ return true
118
+ end
119
+
120
+ # Look for a unique word match.
121
+ index = CDK.searchList(alphalist.list, alphalist.list.size, entry.info)
122
+
123
+ # if the index is less than zero, return we didn't find a match
124
+ if index < 0
125
+ CDK.Beep
126
+ return true
127
+ end
128
+
129
+ # Did we find the last word in the list?
130
+ if index == alphalist.list.size - 1
131
+ entry.setValue(alphalist.list[index])
132
+ entry.draw(entry.box)
133
+ return true
134
+ end
135
+
136
+ # Ok, we found a match, is the next item similar?
137
+ len = [entry.info.size, alphalist.list[index + 1].size].min
138
+ ret = alphalist.list[index + 1][0...len] <=> entry.info
139
+ if ret == 0
140
+ current_index = index
141
+ match = 0
142
+ selected = -1
143
+
144
+ # Start looking for alternate words
145
+ # FIXME(original): bsearch would be more suitable.
146
+ while current_index < alphalist.list.size &&
147
+ (alphalist.list[current_index][0...len] <=> entry.info) == 0
148
+ alt_words << alphalist.list[current_index]
149
+ current_index += 1
150
+ end
151
+
152
+ # Determine the height of the scrolling list.
153
+ height = if alt_words.size < 8 then alt_words.size + 3 else 11 end
154
+
155
+ # Create a scrolling list of close matches.
156
+ scrollp = CDK::SCROLL.new(entry.screen,
157
+ CDK::CENTER, CDK::CENTER, CDK::RIGHT, height, -30,
158
+ "<C></B/5>Possible Matches.", alt_words, alt_words.size,
159
+ true, Curses::A_REVERSE, true, false)
160
+
161
+ # Allow them to select a close match.
162
+ match = scrollp.activate([])
163
+ selected = scrollp.current_item
164
+
165
+ # Check how they exited the list.
166
+ if scrollp.exit_type == :ESCAPE_HIT
167
+ # Destroy the scrolling list.
168
+ scrollp.destroy
169
+
170
+ # Beep at the user.
171
+ CDK.Beep
172
+
173
+ # Redraw the alphalist and return.
174
+ alphalist.draw(alphalist.box)
175
+ return true
176
+ end
177
+
178
+ # Destroy the scrolling list.
179
+ scrollp.destroy
180
+
181
+ # Set the entry field to the selected value.
182
+ entry.set(alt_words[match], entry.min, entry.max, entry.box)
183
+
184
+ # Move the highlight bar down to the selected value.
185
+ (0...selected).each do |x|
186
+ alphalist.injectMyScroller(Curses::KEY_DOWN)
187
+ end
188
+
189
+ # Redraw the alphalist.
190
+ alphalist.draw(alphalist.box)
191
+ else
192
+ # Set the entry field with the found item.
193
+ entry.set(alphalist.list[index], entry.min, entry.max, entry.box)
194
+ entry.draw(entry.box)
195
+ end
196
+ return true
197
+ end
198
+
199
+ pre_process_entry_field = lambda do |cdktype, object, alphalist, input|
200
+ scrollp = alphalist.scroll_field
201
+ entry = alphalist.entry_field
202
+ info_len = entry.info.size
203
+ result = 1
204
+ empty = false
205
+
206
+ if alphalist.isBind(:ALPHALIST, input)
207
+ result = 1 # Don't try to use this key in editing
208
+ elsif (CDK.isChar(input) &&
209
+ input.chr.match(/^[[:alnum:][:punct:]]$/)) ||
210
+ [Curses::KEY_BACKSPACE, Curses::KEY_DC].include?(input)
211
+ index = 0
212
+ curr_pos = entry.screen_col + entry.left_char
213
+ pattern = entry.info.clone
214
+ if [Curses::KEY_BACKSPACE, Curses::KEY_DC].include?(input)
215
+ if input == Curses::KEY_BACKSPACE
216
+ curr_pos -= 1
217
+ end
218
+ if curr_pos >= 0
219
+ pattern.slice!(curr_pos)
220
+ end
221
+ else
222
+ front = (pattern[0...curr_pos] or '')
223
+ back = (pattern[curr_pos..-1] or '')
224
+ pattern = front + input.chr + back
225
+ end
226
+
227
+ if pattern.size == 0
228
+ empty = true
229
+ elsif (index = CDK.searchList(alphalist.list,
230
+ alphalist.list.size, pattern)) >= 0
231
+ # XXX: original uses n scroll downs/ups for <10 positions change
232
+ scrollp.setPosition(index)
233
+ alphalist.drawMyScroller
234
+ else
235
+ CDK.Beep
236
+ result = 0
237
+ end
238
+ end
239
+
240
+ if empty
241
+ scrollp.setPosition(0)
242
+ alphalist.drawMyScroller
243
+ end
244
+
245
+ return result
246
+ end
247
+
248
+ # Set the key bindings for the entry field.
249
+ @entry_field.bind(:ENTRY, Curses::KEY_UP, adjust_alphalist_cb, self)
250
+ @entry_field.bind(:ENTRY, Curses::KEY_DOWN, adjust_alphalist_cb, self)
251
+ @entry_field.bind(:ENTRY, Curses::KEY_NPAGE, adjust_alphalist_cb, self)
252
+ @entry_field.bind(:ENTRY, Curses::KEY_PPAGE, adjust_alphalist_cb, self)
253
+ @entry_field.bind(:ENTRY, CDK::KEY_TAB, complete_word_cb, self)
254
+
255
+ # Set up the post-process function for the entry field.
256
+ @entry_field.setPreProcess(pre_process_entry_field, self)
257
+
258
+ # Create the scrolling list. It overlaps the entry field by one line if
259
+ # we are using box-borders.
260
+ temp_height = @entry_field.win.maxy - @border_size
261
+ temp_width = if CDK::ALPHALIST.isFullWidth(width)
262
+ then CDK::FULL
263
+ else box_width - 1
264
+ end
265
+ @scroll_field = CDK::SCROLL.new(cdkscreen, @win.begx,
266
+ @entry_field.win.begy + temp_height, CDK::RIGHT,
267
+ box_height - temp_height, temp_width, '', list, list_size,
268
+ false, Curses::A_REVERSE, box, false)
269
+ @scroll_field.setULchar(CDK::ACS_LTEE)
270
+ @scroll_field.setURchar(CDK::ACS_RTEE)
271
+
272
+ # Setup the key bindings.
273
+ bindings.each do |from, to|
274
+ self.bind(:ALPHALIST, from, :getc, to)
275
+ end
276
+
277
+ cdkscreen.register(:ALPHALIST, self)
278
+ end
279
+
280
+ # This erases the alphalist from the screen.
281
+ def erase
282
+ if self.validCDKObject
283
+ @scroll_field.erase
284
+ @entry_field.erase
285
+
286
+ CDK.eraseCursesWindow(@shadow_win)
287
+ CDK.eraseCursesWindow(@win)
288
+ end
289
+ end
290
+
291
+ # This moves the alphalist field to the given location.
292
+ def move(xplace, yplace, relative, refresh_flag)
293
+ windows = [@win, @shadow_win]
294
+ subwidgets = [@entry_field, @scroll_field]
295
+ self.move_specific(xplace, yplace, relative, refresh_flag,
296
+ windows, subwidgets)
297
+ end
298
+
299
+ # The alphalist's focus resides in the entry widget. But the scroll widget
300
+ # will not draw items highlighted unless it has focus. Temporarily adjust
301
+ # the focus of the scroll widget when drawing on it to get the right
302
+ # highlighting.
303
+ def saveFocus
304
+ @save = @scroll_field.has_focus
305
+ @scroll_field.has_focus = @entry_field.has_focus
306
+ end
307
+
308
+ def restoreFocus
309
+ @scroll_field.has_focus = @save
310
+ end
311
+
312
+ def drawMyScroller
313
+ self.saveFocus
314
+ @scroll_field.draw(@scroll_field.box)
315
+ self.restoreFocus
316
+ end
317
+
318
+ def injectMyScroller(key)
319
+ self.saveFocus
320
+ @scroll_field.inject(key)
321
+ self.restoreFocus
322
+ end
323
+
324
+ # This draws the alphalist widget.
325
+ def draw(box)
326
+ # Does this widget have a shadow?
327
+ unless @shadow_win.nil?
328
+ Draw.drawShadow(@shadow_win)
329
+ end
330
+
331
+ # Draw in the entry field.
332
+ @entry_field.draw(@entry_field.box)
333
+
334
+ # Draw in the scroll field.
335
+ self.drawMyScroller
336
+ end
337
+
338
+ # This activates the alphalist
339
+ def activate(actions)
340
+ ret = 0
341
+
342
+ # Draw the widget.
343
+ self.draw(@box)
344
+
345
+ # Activate the widget.
346
+ ret = @entry_field.activate(actions)
347
+
348
+ # Copy the exit type from the entry field.
349
+ @exit_type = @entry_field.exit_type
350
+
351
+ # Determine the exit status.
352
+ if @exit_type != :EARLY_EXIT
353
+ return ret
354
+ end
355
+ return 0
356
+ end
357
+
358
+ # This injects a single character into the alphalist.
359
+ def inject(input)
360
+ ret = -1
361
+
362
+ # Draw the widget.
363
+ self.draw(@box)
364
+
365
+ # Inject a character into the widget.
366
+ ret = @entry_field.inject(input)
367
+
368
+ # Copy the eixt type from the entry field.
369
+ @exit_type = @entry_field.exit_type
370
+
371
+ # Determine the exit status.
372
+ if @exit_type == :EARLY_EXIT
373
+ ret = -1
374
+ end
375
+
376
+ @result_data = ret
377
+ return ret
378
+ end
379
+
380
+ # This sets multiple attributes of the widget.
381
+ def set(list, list_size, filler_char, highlight, box)
382
+ self.setContents(list, list_size)
383
+ self.setFillerChar(filler_char)
384
+ self.setHighlight(highlight)
385
+ self.setBox(box)
386
+ end
387
+
388
+ # This function sets the information inside the alphalist.
389
+ def setContents(list, list_size)
390
+ if !self.createList(list, list_size)
391
+ return
392
+ end
393
+
394
+ # Set the information in the scrolling list.
395
+ @scroll_field.set(@list, @list_size, false,
396
+ @scroll_field.highlight, @scroll_field.box)
397
+
398
+ # Clean out the entry field.
399
+ self.setCurrentItem(0)
400
+ @entry_field.clean
401
+
402
+ # Redraw the widget.
403
+ self.erase
404
+ self.draw(@box)
405
+ end
406
+
407
+ # This returns the contents of the widget.
408
+ def getContents(size)
409
+ size << @list_size
410
+ return @list
411
+ end
412
+
413
+ # Get/set the current position in the scroll widget.
414
+ def getCurrentItem
415
+ return @scroll_field.getCurrentItem
416
+ end
417
+
418
+ def setCurrentItem(item)
419
+ if @list_size != 0
420
+ @scroll_field.setCurrentItem(item)
421
+ @entry_field.setValue(@list[@scroll_field.getCurrentItem])
422
+ end
423
+ end
424
+
425
+ # This sets the filler character of the entry field of the alphalist.
426
+ def setFillerChar(filler_character)
427
+ @filler_char = filler_character
428
+ @entry_field.setFillerChar(filler_character)
429
+ end
430
+
431
+ def getFillerChar
432
+ return @filler_char
433
+ end
434
+
435
+ # This sets the highlgith bar attributes
436
+ def setHighlight(highlight)
437
+ @highlight = highlight
438
+ end
439
+
440
+ def getHighlight
441
+ @highlight
442
+ end
443
+
444
+ # These functions set the drawing characters of the widget.
445
+ def setMyULchar(character)
446
+ @entry_field.setULchar(character)
447
+ end
448
+
449
+ def setMyURchar(character)
450
+ @entry_field.setURchar(character)
451
+ end
452
+
453
+ def setMyLLchar(character)
454
+ @scroll_field.setLLchar(character)
455
+ end
456
+
457
+ def setMyLRchar(character)
458
+ @scroll_field.setLRchar(character)
459
+ end
460
+
461
+ def setMyVTchar(character)
462
+ @entry_field.setVTchar(character)
463
+ @scroll_field.setVTchar(character)
464
+ end
465
+
466
+ def setMyHZchar(character)
467
+ @entry_field.setHZchar(character)
468
+ @scroll_field.setHZchar(character)
469
+ end
470
+
471
+ def setMyBXattr(character)
472
+ @entry_field.setBXattr(character)
473
+ @scroll_field.setBXattr(character)
474
+ end
475
+
476
+ # This sets the background attribute of the widget.
477
+ def setBKattr(attrib)
478
+ @entry_field.setBKattr(attrib)
479
+ @scroll_field.setBKattr(attrib)
480
+ end
481
+
482
+ def destroyInfo
483
+ @list = ''
484
+ @list_size = 0
485
+ end
486
+
487
+ # This destroys the alpha list
488
+ def destroy
489
+ self.destroyInfo
490
+
491
+ # Clean the key bindings.
492
+ self.cleanBindings(:ALPHALIST)
493
+
494
+ @entry_field.destroy
495
+ @scroll_field.destroy
496
+
497
+ # Free up the window pointers.
498
+ CDK.deleteCursesWindow(@shadow_win)
499
+ CDK.deleteCursesWindow(@win)
500
+
501
+ # Unregister the object.
502
+ CDK::SCREEN.unregister(:ALPHALIST, self)
503
+ end
504
+
505
+ # This function sets the pre-process function.
506
+ def setPreProcess(callback, data)
507
+ @entry_field.setPreProcess(callback, data)
508
+ end
509
+
510
+ # This function sets the post-process function.
511
+ def setPostProcess(callback, data)
512
+ @entry_field.setPostProcess(callback, data)
513
+ end
514
+
515
+ def createList(list, list_size)
516
+ if list_size >= 0
517
+ newlist = []
518
+
519
+ # Copy in the new information.
520
+ status = true
521
+ (0...list_size).each do |x|
522
+ newlist << list[x]
523
+ if newlist[x] == 0
524
+ status = false
525
+ break
526
+ end
527
+ end
528
+ if status
529
+ self.destroyInfo
530
+ @list_size = list_size
531
+ @list = newlist
532
+ @list.sort!
533
+ end
534
+ else
535
+ self.destroyInfo
536
+ status = true
537
+ end
538
+ return status
539
+ end
540
+
541
+ def focus
542
+ self.entry_field.focus
543
+ end
544
+
545
+ def unfocus
546
+ self.entry_field.unfocus
547
+ end
548
+
549
+ def self.isFullWidth(width)
550
+ width == CDK::FULL || (Curses.cols != 0 && width >= Curses.cols)
551
+ end
552
+
553
+ def position
554
+ super(@win)
555
+ end
556
+
557
+ def object_type
558
+ :ALPHALIST
559
+ end
560
+ end
561
+ end