cdk 0.9.0

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