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,619 @@
1
+ require_relative 'scroller'
2
+
3
+ module CDK
4
+ class SELECTION < CDK::SCROLLER
5
+ attr_reader :selections
6
+
7
+ def initialize(cdkscreen, xplace, yplace, splace, height, width, title,
8
+ list, list_size, choices, choice_count, highlight, box, shadow)
9
+ super()
10
+ widest_item = -1
11
+ parent_width = cdkscreen.window.maxx
12
+ parent_height = cdkscreen.window.maxy
13
+ box_width = width
14
+ bindings = {
15
+ CDK::BACKCHAR => Curses::KEY_PPAGE,
16
+ CDK::FORCHAR => Curses::KEY_NPAGE,
17
+ 'g' => Curses::KEY_HOME,
18
+ '1' => Curses::KEY_HOME,
19
+ 'G' => Curses::KEY_END,
20
+ '<' => Curses::KEY_HOME,
21
+ '>' => Curses::KEY_END,
22
+ }
23
+
24
+ if choice_count <= 0
25
+ self.destroy
26
+ return nil
27
+ end
28
+
29
+ @choice = []
30
+ @choicelen = []
31
+
32
+ self.setBox(box)
33
+
34
+ # If the height is a negative value, the height will be ROWS-height,
35
+ # otherwise the height will be the given height.
36
+ box_height = CDK.setWidgetDimension(parent_height, height, 0)
37
+
38
+ # If the width is a negative value, the width will be COLS-width,
39
+ # otherwise the width will be the given width
40
+ box_width = CDK.setWidgetDimension(parent_width, width, 0)
41
+ box_width = self.setTitle(title, box_width)
42
+
43
+ # Set the box height.
44
+ if @title_lines > box_height
45
+ box_height = @title_lines = [list_size, 8].min, + 2 * border_size
46
+ end
47
+
48
+ @maxchoicelen = 0
49
+
50
+ # Adjust the box width if there is a scroll bar.
51
+ if splace == CDK::LEFT || splace == CDK::RIGHT
52
+ box_width += 1
53
+ @scrollbar = true
54
+ else
55
+ @scrollbar = false
56
+ end
57
+
58
+ # Make sure we didn't extend beyond the dimensions of the window.
59
+ @box_width = [box_width, parent_width].min
60
+ @box_height = [box_height, parent_height].min
61
+
62
+ self.setViewSize(list_size)
63
+
64
+ # Rejustify the x and y positions if we need to.
65
+ xtmp = [xplace]
66
+ ytmp = [yplace]
67
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, @box_width, @box_height)
68
+ xpos = xtmp[0]
69
+ ypos = ytmp[0]
70
+
71
+ # Make the selection window.
72
+ @win = Curses::Window.new(@box_height, @box_width, ypos, xpos)
73
+
74
+ # Is the window nil?
75
+ if @win.nil?
76
+ self.destroy
77
+ return nil
78
+ end
79
+
80
+ # Turn the keypad on for this window.
81
+ @win.keypad(true)
82
+
83
+ # Create the scrollbar window.
84
+ if splace == CDK::RIGHT
85
+ @scrollbar_win = @win.subwin(self.maxViewSize, 1,
86
+ self.SCREEN_YPOS(ypos), xpos + @box_width - @border_size - 1)
87
+ elsif splace == CDK::LEFT
88
+ @scrollbar_win = @win.subwin(self.maxViewSize, 1,
89
+ self.SCREEN_YPOS(ypos), self.SCREEN_XPOS(ypos))
90
+ else
91
+ @scrollbar_win = nil
92
+ end
93
+
94
+ # Set the rest of the variables
95
+ @screen = cdkscreen
96
+ @parent = cdkscreen.window
97
+ @scrollbar_placement = splace
98
+ @max_left_char = 0
99
+ @left_char = 0
100
+ @highlight = highlight
101
+ @choice_count = choice_count
102
+ @accepts_focus = true
103
+ @input_window = @win
104
+ @shadow = shadow
105
+
106
+ self.setCurrentItem(0)
107
+
108
+ # Each choice has to be converted from string to chtype array
109
+ (0...choice_count).each do |j|
110
+ choicelen = []
111
+ @choice << CDK.char2Chtype(choices[j], choicelen, [])
112
+ @choicelen << choicelen[0]
113
+ @maxchoicelen = [@maxchoicelen, choicelen[0]].max
114
+ end
115
+
116
+ # Each item in the needs to be converted to chtype array
117
+ widest_item = self.createList(list, list_size)
118
+ if widest_item > 0
119
+ self.updateViewWidth(widest_item)
120
+ elsif list_size > 0
121
+ self.destroy
122
+ return nil
123
+ end
124
+
125
+ # Do we need to create a shadow.
126
+ if shadow
127
+ @shadow_win = Curses::Window.new(box_height, box_width,
128
+ ypos + 1, xpos + 1)
129
+ end
130
+
131
+ # Setup the key bindings
132
+ bindings.each do |from, to|
133
+ self.bind(:SELECTION, from, :getc, to)
134
+ end
135
+
136
+ # Register this baby.
137
+ cdkscreen.register(:SELECTION, self)
138
+ end
139
+
140
+ # Put the cursor on the currently-selected item.
141
+ def fixCursorPosition
142
+ scrollbar_adj = if @scrollbar_placement == CDK::LEFT
143
+ then 1
144
+ else 0
145
+ end
146
+ ypos = self.SCREEN_YPOS(@current_item - @current_top)
147
+ xpos = self.SCREEN_XPOS(0) + scrollbar_adj
148
+
149
+ @input_window.move(ypos, xpos)
150
+ @input_window.refresh
151
+ end
152
+
153
+ # This actually manages the selection widget
154
+ def activate(actions)
155
+ # Draw the selection list
156
+ self.draw(@box)
157
+
158
+ if actions.nil? || actions.size == 0
159
+ while true
160
+ self.fixCursorPosition
161
+ input = self.getch([])
162
+
163
+ # Inject the character into the widget.
164
+ ret = self.inject(input)
165
+ if @exit_type != :EARLY_EXIT
166
+ return ret
167
+ end
168
+ end
169
+ else
170
+ # Inject each character one at a time.
171
+ actions.each do |action|
172
+ ret = self.inject(action)
173
+ if @exit_type != :EARLY_EXIT
174
+ return ret
175
+ end
176
+ end
177
+ end
178
+
179
+ # Set the exit type and return.
180
+ self.setExitType(0)
181
+ return 0
182
+ end
183
+
184
+ # This injects a single characer into the widget.
185
+ def inject(input)
186
+ pp_return = 1
187
+ ret = -1
188
+ complete = false
189
+
190
+ # Set the exit type
191
+ self.setExitType(0)
192
+
193
+ # Draw the widget list.
194
+ self.drawList(@box)
195
+
196
+ # Check if there is a pre-process function to be called.
197
+ unless @pre_process_func.nil?
198
+ pp_return = @pre_process_func.call(:SELECTION, self,
199
+ @pre_process_data, input)
200
+ end
201
+
202
+ # Should we continue?
203
+ if pp_return != 0
204
+ # Check for a predefined binding.
205
+ if self.checkBind(:SELECTION, input)
206
+ complete = true
207
+ else
208
+ case input
209
+ when Curses::KEY_UP
210
+ self.KEY_UP
211
+ when Curses::KEY_DOWN
212
+ self.KEY_DOWN
213
+ when Curses::KEY_RIGHT
214
+ self.KEY_RIGHT
215
+ when Curses::KEY_LEFT
216
+ self.KEY_LEFT
217
+ when Curses::KEY_PPAGE
218
+ self.KEY_PPAGE
219
+ when Curses::KEY_NPAGE
220
+ self.KEY_NPAGE
221
+ when Curses::KEY_HOME
222
+ self.KEY_HOME
223
+ when Curses::KEY_END
224
+ self.KEY_END
225
+ when '$'
226
+ @left_char = @max_left_char
227
+ when '|'
228
+ @left_char = 0
229
+ when ' '
230
+ if @mode[@current_item] == 0
231
+ if @selections[@current_item] == @choice_count - 1
232
+ @selections[@current_item] = 0
233
+ else
234
+ @selections[@current_item] += 1
235
+ end
236
+ else
237
+ CDK.Beep
238
+ end
239
+ when CDK::KEY_ESC
240
+ self.setExitType(input)
241
+ complete = true
242
+ when Curses::Error
243
+ self.setExitType(input)
244
+ complete = true
245
+ when Curses::KEY_ENTER, CDK::KEY_TAB, CDK::KEY_RETURN
246
+ self.setExitType(input)
247
+ ret = 1
248
+ complete = true
249
+ when CDK::REFRESH
250
+ @screen.erase
251
+ @screen.refresh
252
+ end
253
+ end
254
+
255
+ # Should we call a post-process?
256
+ if !complete && !(@post_process_func.nil?)
257
+ @post_process_func.call(:SELECTION, self, @post_process_data, input)
258
+ end
259
+ end
260
+
261
+ unless complete
262
+ self.drawList(@box)
263
+ self.setExitType(0)
264
+ end
265
+
266
+ @result_data = ret
267
+ self.fixCursorPosition
268
+ return ret
269
+ end
270
+
271
+ # This moves the selection field to the given location.
272
+ def move(xplace, yplace, relative, refresh_flag)
273
+ windows = [@win, @scrollbar_win, @shadow_win]
274
+ self.move_specific(xplace, yplace, relative, refresh_flag,
275
+ windows, [])
276
+ end
277
+
278
+ # This function draws the selection list.
279
+ def draw(box)
280
+ # Draw in the shadow if we need to.
281
+ unless @shadow_win.nil?
282
+ Draw.drawShadow(@shadow_win)
283
+ end
284
+
285
+ self.drawTitle(@win)
286
+
287
+ # Redraw the list
288
+ self.drawList(box)
289
+ end
290
+
291
+ # This function draws the selection list window.
292
+ def drawList(box)
293
+ scrollbar_adj = if @scrollbar_placement == LEFT then 1 else 0 end
294
+ screen_pos = 0
295
+ sel_item = -1
296
+
297
+ # If there is to be a highlight, assign it now
298
+ if @has_focus
299
+ sel_item = @current_item
300
+ end
301
+
302
+ # draw the list...
303
+ j = 0
304
+ while j < @view_size && (j + @current_top) < @list_size
305
+ k = j + @current_top
306
+ if k < @list_size
307
+ screen_pos = self.SCREENPOS(k, scrollbar_adj)
308
+ ypos = self.SCREEN_YPOS(j)
309
+ xpos = self.SCREEN_XPOS(0)
310
+
311
+ # Draw the empty line.
312
+ Draw.writeBlanks(@win, xpos, ypos, CDK::HORIZONTAL, 0, @win.maxx)
313
+
314
+ # Draw the selection item.
315
+ Draw.writeChtypeAttrib(@win,
316
+ if screen_pos >= 0 then screen_pos else 1 end,
317
+ ypos, @item[k],
318
+ if k == sel_item then @highlight else Curses::A_NORMAL end,
319
+ CDK::HORIZONTAL,
320
+ if screen_pos >= 0 then 0 else 1 - screen_pos end,
321
+ @item_len[k])
322
+
323
+ # Draw the choice value
324
+ Draw.writeChtype(@win, xpos + scrollbar_adj, ypos,
325
+ @choice[@selections[k]], CDK::HORIZONTAL, 0,
326
+ @choicelen[@selections[k]])
327
+ end
328
+ j += 1
329
+ end
330
+
331
+ # Determine where the toggle is supposed to be.
332
+ if @scrollbar
333
+ @toggle_pos = (@current_item * @step).floor
334
+ @toggle_pos = [@toggle_pos, @scrollbar_win.maxy - 1].min
335
+
336
+ @scrollbar_win.mvwvline(0, 0, CDK::ACS_CKBOARD,
337
+ @scrollbar_win.maxy)
338
+ @scrollbar_win.mvwvline(@toggle_pos, 0,
339
+ ' '.ord | Curses::A_REVERSE, @toggle_size)
340
+ end
341
+
342
+ # Box it if needed
343
+ if @box
344
+ Draw.drawObjBox(@win, self)
345
+ end
346
+
347
+ self.fixCursorPosition
348
+ end
349
+
350
+ # This sets the background attribute of the widget.
351
+ def setBKattr(attrib)
352
+ @win.wbkgd(attrib)
353
+ unless @scrollbar_win.nil?
354
+ @scrollbar_win.wbkgd(attrib)
355
+ end
356
+ end
357
+
358
+ def destroyInfo
359
+ @item = []
360
+ end
361
+
362
+ # This function destroys the selection list.
363
+ def destroy
364
+ self.cleanTitle
365
+ self.destroyInfo
366
+
367
+ # Clean up the windows.
368
+ CDK.deleteCursesWindow(@scrollbar_win)
369
+ CDK.deleteCursesWindow(@shadow_win)
370
+ CDK.deleteCursesWindow(@win)
371
+
372
+ # Clean up the key bindings
373
+ self.cleanBindings(:SELECTION)
374
+
375
+ # Unregister this object.
376
+ CDK::SCREEN.unregister(:SELECTION, self)
377
+ end
378
+
379
+ # This function erases the selection list from the screen.
380
+ def erase
381
+ if self.validCDKObject
382
+ CDK.eraseCursesWindow(@win)
383
+ CDK.eraseCursesWindow(@shadow_win)
384
+ end
385
+ end
386
+
387
+ # This function sets a couple of the selection list attributes
388
+ def set(highlight, choices, box)
389
+ self.setChoices(choices)
390
+ self.setHighlight(highlight)
391
+ self.setBox(box)
392
+ end
393
+
394
+ # This sets the selection list items.
395
+ def setItems(list, list_size)
396
+ widest_item = self.createList(list, list_size)
397
+ if widest_item <= 0
398
+ return
399
+ end
400
+
401
+ # Clean up the display
402
+ (0...@view_size).each do |j|
403
+ Draw.writeBlanks(@win, self.SCREEN_XPOS(0), self.SCREEN_YPOS(j),
404
+ CDK::HORIZONTAL, 0, @win.maxx)
405
+ end
406
+
407
+ self.setViewSize(list_size)
408
+ self.setCurrentItem(0)
409
+
410
+ self.updateViewWidth(widest_item)
411
+ end
412
+
413
+ def getItems(list)
414
+ @item.each do |item|
415
+ list << CDK.chtype2Char(item)
416
+ end
417
+ return @list_size
418
+ end
419
+
420
+ def setSelectionTitle(title)
421
+ # Make sure the title isn't nil
422
+ if title.nil?
423
+ return
424
+ end
425
+
426
+ self.setTitle(title, -(@box_width + 1))
427
+
428
+ self.setViewSize(@list_size)
429
+ end
430
+
431
+ def getTitle
432
+ return CDK.chtype2Char(@title)
433
+ end
434
+
435
+ # This sets the highlight bar.
436
+ def setHighlight(highlight)
437
+ @highlight = highlight
438
+ end
439
+
440
+ def getHighlight
441
+ @highlight
442
+ end
443
+
444
+ # This sets the default choices for the selection list.
445
+ def setChoices(choices)
446
+ # Set the choice values in the selection list.
447
+ (0...@list_size).each do |j|
448
+ if choices[j] < 0
449
+ @selections[j] = 0
450
+ elsif choices[j] > @choice_count
451
+ @selections[j] = @choice_count - 1
452
+ else
453
+ @selections[j] = choices[j]
454
+ end
455
+ end
456
+ end
457
+
458
+ def getChoices
459
+ @selections
460
+ end
461
+
462
+ # This sets a single item's choice value.
463
+ def setChoice(index, choice)
464
+ correct_choice = choice
465
+ correct_index = index
466
+
467
+ # Verify that the choice value is in range.
468
+ if choice < 0
469
+ correct_choice = 0
470
+ elsif choice > @choice_count
471
+ correct_choice = @choice_count - 1
472
+ end
473
+
474
+ # make sure the index isn't out of range.
475
+ if index < 0
476
+ correct_index = 0
477
+ elsif index > @list_size
478
+ correct_index = @list_size - 1
479
+ end
480
+
481
+ # Set the choice value.
482
+ @selections[correct_index] = correct_choice
483
+ end
484
+
485
+ def getChoice(index)
486
+ # Make sure the index isn't out of range.
487
+ if index < 0
488
+ return @selections[0]
489
+ elsif index > list_size
490
+ return @selections[@list_size - 1]
491
+ else
492
+ return @selections[index]
493
+ end
494
+ end
495
+
496
+ # This sets the modes of the items in the selection list. Currently
497
+ # there are only two: editable=0 and read-only=1
498
+ def setModes(modes)
499
+ # set the modes
500
+ (0...@list_size).each do |j|
501
+ @mode[j] = modes[j]
502
+ end
503
+ end
504
+
505
+ def getModes
506
+ return @mode
507
+ end
508
+
509
+ # This sets a single mode of an item in the selection list.
510
+ def setMode(index, mode)
511
+ # Make sure the index isn't out of range.
512
+ if index < 0
513
+ @mode[0] = mode
514
+ elsif index > @list_size
515
+ @mode[@list_size - 1] = mode
516
+ else
517
+ @mode[index] = mode
518
+ end
519
+ end
520
+
521
+ def getMode(index)
522
+ # Make sure the index isn't out of range
523
+ if index < 0
524
+ return @mode[0]
525
+ elsif index > list_size
526
+ return @mode[@list_size - 1]
527
+ else
528
+ return @mode[index]
529
+ end
530
+ end
531
+
532
+ def getCurrent
533
+ return @current_item
534
+ end
535
+
536
+ # methods for generic type methods
537
+ def focus
538
+ self.drawList(@box)
539
+ end
540
+
541
+ def unfocus
542
+ self.drawList(@box)
543
+ end
544
+
545
+ def createList(list, list_size)
546
+ status = 0
547
+ widest_item = 0
548
+
549
+ if list_size >= 0
550
+ new_list = []
551
+ new_len = []
552
+ new_pos = []
553
+
554
+ box_width = self.AvailableWidth
555
+ adjust = @maxchoicelen + @border_size
556
+
557
+ status = 1
558
+ (0...list_size).each do |j|
559
+ lentmp = []
560
+ postmp = []
561
+ new_list << CDK.char2Chtype(list[j], lentmp, postmp)
562
+ new_len << lentmp[0]
563
+ new_pos << postmp[0]
564
+ #if new_list[j].size == 0
565
+ if new_list[j].nil?
566
+ status = 0
567
+ break
568
+ end
569
+ new_pos[j] =
570
+ CDK.justifyString(box_width, new_len[j], new_pos[j]) + adjust
571
+ widest_item = [widest_item, new_len[j]].max
572
+ end
573
+
574
+ if status
575
+ self.destroyInfo
576
+
577
+ @item = new_list
578
+ @item_pos = new_pos
579
+ @item_len = new_len
580
+ @selections = [0] * list_size
581
+ @mode = [0] * list_size
582
+ end
583
+ else
584
+ self.destroyInfo
585
+ end
586
+
587
+ return (if status then widest_item else 0 end)
588
+ end
589
+
590
+ # Determine how many characters we can shift to the right
591
+ # before all the items have been scrolled off the screen.
592
+ def AvailableWidth
593
+ @box_width - 2 * @border_size - @maxchoicelen
594
+ end
595
+
596
+ def updateViewWidth(widest)
597
+ @max_left_char = if @box_width > widest
598
+ then 0
599
+ else widest - self.AvailableWidth
600
+ end
601
+ end
602
+
603
+ def WidestItem
604
+ @max_left_char + self.AvailableWidth
605
+ end
606
+
607
+ def SCREENPOS(n, scrollbar_adj)
608
+ @item_pos[n] - @left_char + scrollbar_adj
609
+ end
610
+
611
+ def position
612
+ super(@win)
613
+ end
614
+
615
+ def object_type
616
+ :SELECTION
617
+ end
618
+ end
619
+ end