slithernix-cdk 0.0.1

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