cdk 0.9.0

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