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.
data/lib/cdk/slider.rb ADDED
@@ -0,0 +1,542 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class SLIDER < CDK::CDKOBJS
5
+ def initialize(cdkscreen, xplace, yplace, title, label, filler,
6
+ field_width, start, low, high, inc, fast_inc, box, shadow)
7
+ super()
8
+ parent_width = cdkscreen.window.maxx
9
+ parent_height = cdkscreen.window.maxy
10
+ bindings = {
11
+ 'u' => Curses::KEY_UP,
12
+ 'U' => Curses::KEY_PPAGE,
13
+ CDK::BACKCHAR => Curses::KEY_PPAGE,
14
+ CDK::FORCHAR => Curses::KEY_NPAGE,
15
+ 'g' => Curses::KEY_HOME,
16
+ '^' => Curses::KEY_HOME,
17
+ 'G' => Curses::KEY_END,
18
+ '$' => Curses::KEY_END,
19
+ }
20
+ self.setBox(box)
21
+ box_height = @border_size * 2 + 1
22
+
23
+ # Set some basic values of the widget's data field.
24
+ @label = []
25
+ @label_len = 0
26
+ @label_win = nil
27
+ high_value_len = self.formattedSize(high)
28
+
29
+ # If the field_width is a negative will be COLS-field_width,
30
+ # otherwise field_width will be the given width.
31
+ field_width = CDK.setWidgetDimension(parent_width, field_width, 0)
32
+
33
+ # Translate the label string to a chtype array.
34
+ if !(label.nil?) && label.size > 0
35
+ label_len = []
36
+ @label = CDK.char2Chtype(label, label_len, [])
37
+ @label_len = label_len[0]
38
+ box_width = @label_len + field_width +
39
+ high_value_len + 2 * @border_size
40
+ else
41
+ box_width = field_width + high_value_len + 2 * @border_size
42
+ end
43
+
44
+ old_width = box_width
45
+ box_width = self.setTitle(title, box_width)
46
+ horizontal_adjust = (box_width - old_width) / 2
47
+
48
+ box_height += @title_lines
49
+
50
+ # Make sure we didn't extend beyond the dimensions of the window.
51
+ box_width = [box_width, parent_width].min
52
+ box_height = [box_height, parent_height].min
53
+ field_width = [field_width,
54
+ box_width - @label_len - high_value_len - 1].min
55
+
56
+ # Rejustify the x and y positions if we need to.
57
+ xtmp = [xplace]
58
+ ytmp = [yplace]
59
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
60
+ xpos = xtmp[0]
61
+ ypos = ytmp[0]
62
+
63
+ # Make the widget's window.
64
+ @win = Curses::Window.new(box_height, box_width, ypos, xpos)
65
+
66
+ # Is the main window nil?
67
+ if @win.nil?
68
+ self.destroy
69
+ return nil
70
+ end
71
+
72
+ # Create the widget's label window.
73
+ if @label.size > 0
74
+ @label_win = @win.subwin(1, @label_len,
75
+ ypos + @title_lines + @border_size,
76
+ xpos + horizontal_adjust + @border_size)
77
+ if @label_win.nil?
78
+ self.destroy
79
+ return nil
80
+ end
81
+ end
82
+
83
+ # Create the widget's data field window.
84
+ @field_win = @win.subwin(1, field_width + high_value_len - 1,
85
+ ypos + @title_lines + @border_size,
86
+ xpos + @label_len + horizontal_adjust + @border_size)
87
+
88
+ if @field_win.nil?
89
+ self.destroy
90
+ return nil
91
+ end
92
+ @field_win.keypad(true)
93
+ @win.keypad(true)
94
+
95
+ # Create the widget's data field.
96
+ @screen = cdkscreen
97
+ @window = cdkscreen.window
98
+ @shadow_win = nil
99
+ @box_width = box_width
100
+ @box_height = box_height
101
+ @field_width = field_width - 1
102
+ @filler = filler
103
+ @low = low
104
+ @high = high
105
+ @current = start
106
+ @inc = inc
107
+ @fastinc = fast_inc
108
+ @accepts_focus = true
109
+ @input_window = @win
110
+ @shadow = shadow
111
+ @field_edit = 0
112
+
113
+ # Set the start value.
114
+ if start < low
115
+ @current = low
116
+ end
117
+
118
+ # Do we want a shadow?
119
+ if shadow
120
+ @shadow_win = Curses::Window.new(box_height, box_width,
121
+ ypos + 1, xpos + 1)
122
+ if @shadow_win.nil?
123
+ self.destroy
124
+ return nil
125
+ end
126
+ end
127
+
128
+ # Setup the key bindings.
129
+ bindings.each do |from, to|
130
+ self.bind(:SLIDER, from, :getc, to)
131
+ end
132
+
133
+ cdkscreen.register(:SLIDER, self)
134
+ end
135
+
136
+ # This allows the person to use the widget's data field.
137
+ def activate(actions)
138
+ # Draw the widget.
139
+ self.draw(@box)
140
+
141
+ if actions.nil? || actions.size == 0
142
+ while true
143
+ input = self.getch([])
144
+
145
+ # Inject the character into the widget.
146
+ ret = self.inject(input)
147
+ if @exit_type != :EARLY_EXIT
148
+ return ret
149
+ end
150
+ end
151
+ else
152
+ # Inject each character one at a time.
153
+ actions.each do |action|
154
+ ret = self.inject(action)
155
+ if @exit_type != :EARLY_EXIT
156
+ return ret
157
+ end
158
+ end
159
+ end
160
+
161
+ # Set the exit type and return.
162
+ self.setExitType(0)
163
+ return -1
164
+ end
165
+
166
+ # Check if the value lies outside the low/high range. If so, force it in.
167
+ def limitCurrentValue
168
+ if @current < @low
169
+ @current = @low
170
+ CDK.Beep
171
+ elsif @current > @high
172
+ @current = @high
173
+ CDK.Beep
174
+ end
175
+ end
176
+
177
+ # Move the cursor to the given edit-position.
178
+ def moveToEditPosition(new_position)
179
+ #return @field_win.move(0,
180
+ # @field_width + self.formattedSize(@current) - new_position)
181
+ @field_win
182
+ end
183
+
184
+ # Check if the cursor is on a valid edit-position. This must be one of
185
+ # the non-blank cells in the field.
186
+ def validEditPosition(new_position)
187
+ if new_position <= 0 || new_position >= @field_width
188
+ return false
189
+ end
190
+ if self.moveToEditPosition(new_position) == Curses::Error
191
+ return false
192
+ end
193
+ ch = @field_win.inch
194
+ if CDK.CharOf(ch) != ' '
195
+ return true
196
+ end
197
+ if new_position > 1
198
+ # Don't use recursion - only one level is wanted
199
+ if self.moveToEditPosition(new_position - 1) == Curses::Error
200
+ return false
201
+ end
202
+ ch = @field_win.inch
203
+ return CDK.CharOf(ch) != ' '
204
+ end
205
+ return false
206
+ end
207
+
208
+ # Set the edit position. Normally the cursor is one cell to the right of
209
+ # the editable field. Moving it left, over the field, allows the user to
210
+ # modify cells by typing in replacement characters for the field's value.
211
+ def setEditPosition(new_position)
212
+ if new_position < 0
213
+ CDK.Beep
214
+ elsif new_position == 0
215
+ @field_edit = new_position
216
+ elsif self.validEditPosition(new_position)
217
+ @field_edit = new_position
218
+ else
219
+ CDK.Beep
220
+ end
221
+ end
222
+
223
+ # Remove the character from the string at the given column, if it is blank.
224
+ # Returns true if a change was made.
225
+ def self.removeChar(string, col)
226
+ result = false
227
+ if col >= 0 && string[col] != ' '
228
+ while col < string.size - 1
229
+ string[col] = string[col + 1]
230
+ col += 1
231
+ end
232
+ string.chop!
233
+ result = true
234
+ end
235
+ return result
236
+ end
237
+
238
+ # Perform an editing function for the field.
239
+ def performEdit(input)
240
+ result = false
241
+ modify = true
242
+ base = @field_width
243
+ need = self.formattedSize(@current)
244
+ temp = ''
245
+ col = need - @field_edit
246
+
247
+ adj = if col < 0 then -col else 0 end
248
+ if adj != 0
249
+ temp = ' ' * adj
250
+ end
251
+ @field_win.move(0, base)
252
+ @field_win.winnstr(temp, need)
253
+ temp << ' '
254
+ if CDK.isChar(input) # Replace the char at the cursor
255
+ temp[col] = input.chr
256
+ elsif input == Curses::KEY_BACKSPACE
257
+ # delete the char before the cursor
258
+ modify = CDK::SLIDER.removeChar(temp, col - 1)
259
+ elsif input == Curses::KEY_DC
260
+ # delete the char at the cursor
261
+ modify = CDK::SLIDER.removeChar(temp, col)
262
+ else
263
+ modify = false
264
+ end
265
+ if modify &&
266
+ ((value, test) = temp.scanf(self.SCAN_FMT)).size == 2 &&
267
+ test == ' ' && value >= @low && value <= @high
268
+ self.setValue(value)
269
+ result = true
270
+ end
271
+ return result
272
+ end
273
+
274
+ def self.Decrement(value, by)
275
+ if value - by < value
276
+ value - by
277
+ else
278
+ value
279
+ end
280
+ end
281
+
282
+ def self.Increment(value, by)
283
+ if value + by > value
284
+ value + by
285
+ else
286
+ value
287
+ end
288
+ end
289
+
290
+ # This function injects a single character into the widget.
291
+ def inject(input)
292
+ pp_return = 1
293
+ ret = -1
294
+ complete = false
295
+
296
+ # Set the exit type.
297
+ self.setExitType(0)
298
+
299
+ # Draw the field.
300
+ self.drawField
301
+
302
+ # Check if there is a pre-process function to be called.
303
+ unless @pre_process_func.nil?
304
+ # Call the pre-process function.
305
+ pp_return = @pre_process_func.call(:SLIDER, self,
306
+ @pre_process_data, input)
307
+ end
308
+
309
+ # Should we continue?
310
+ if pp_return != 0
311
+ # Check for a key binding.
312
+ if self.checkBind(:SLIDER, input)
313
+ complete = true
314
+ else
315
+ case input
316
+ when Curses::KEY_LEFT
317
+ self.setEditPosition(@field_edit + 1)
318
+ when Curses::KEY_RIGHT
319
+ self.setEditPosition(@field_edit - 1)
320
+ when Curses::KEY_DOWN
321
+ @current = CDK::SLIDER.Decrement(@current, @inc)
322
+ when Curses::KEY_UP
323
+ @current = CDK::SLIDER.Increment(@current, @inc)
324
+ when Curses::KEY_PPAGE
325
+ @current = CDK::SLIDER.Increment(@current, @fastinc)
326
+ when Curses::KEY_NPAGE
327
+ @current = CDK::SLIDER.Decrement(@current, @fastinc)
328
+ when Curses::KEY_HOME
329
+ @current = @low
330
+ when Curses::KEY_END
331
+ @current = @high
332
+ when CDK::KEY_TAB, CDK::KEY_RETURN, Curses::KEY_ENTER
333
+ self.setExitType(input)
334
+ ret = @current
335
+ complete = true
336
+ when CDK::KEY_ESC
337
+ self.setExitType(input)
338
+ complete = true
339
+ when Curses::Error
340
+ self.setExitType(input)
341
+ complete = true
342
+ when CDK::REFRESH
343
+ @screen.erase
344
+ @screen.refresh
345
+ else
346
+ if @field_edit != 0
347
+ if !self.performEdit(input)
348
+ CDK.Beep
349
+ end
350
+ else
351
+ # The cursor is not within the editable text. Interpret
352
+ # input as commands.
353
+ case input
354
+ when 'd', '-'
355
+ return self.inject(Curses::KEY_DOWN)
356
+ when '+'
357
+ return self.inject(Curses::KEY_UP)
358
+ when 'D'
359
+ return self.inject(Curses::KEY_NPAGE)
360
+ when '0'
361
+ return self.inject(Curses::KEY_HOME)
362
+ else
363
+ CDK.Beep
364
+ end
365
+ end
366
+ end
367
+ end
368
+ self.limitCurrentValue
369
+
370
+ # Should we call a post-process?
371
+ if !complete && !(@post_process_func.nil?)
372
+ @post_process_func.call(:SLIDER, self, @post_process_data, input)
373
+ end
374
+ end
375
+
376
+ if !complete
377
+ self.drawField
378
+ self.setExitType(0)
379
+ end
380
+
381
+ @return_data = 0
382
+ return ret
383
+ end
384
+
385
+ # This moves the widget's data field to the given location.
386
+ def move(xplace, yplace, relative, refresh_flag)
387
+ windows = [@win, @label_win, @field_win, @shadow_win]
388
+ self.move_specific(xplace, yplace, relative, refresh_flag,
389
+ windows, [])
390
+ end
391
+
392
+ # This function draws the widget.
393
+ def draw(box)
394
+ # Draw the shadow.
395
+ unless @shadow_win.nil?
396
+ Draw.drawShadow(@shadow_win)
397
+ end
398
+
399
+ # Box the widget if asked.
400
+ if box
401
+ Draw.drawObjBox(@win, self)
402
+ end
403
+
404
+ self.drawTitle(@win)
405
+
406
+ # Draw the label.
407
+ unless @label_win.nil?
408
+ Draw.writeChtype(@label_win, 0, 0, @label, CDK::HORIZONTAL,
409
+ 0, @label_len)
410
+ @label_win.refresh
411
+ end
412
+ @win.refresh
413
+
414
+ # Draw the field window.
415
+ self.drawField
416
+ end
417
+
418
+ # This draws the widget.
419
+ def drawField
420
+ step = 1.0 * @field_width / (@high - @low)
421
+
422
+ # Determine how many filler characters need to be drawn.
423
+ filler_characters = (@current - @low) * step
424
+
425
+ @field_win.erase
426
+
427
+ # Add the character to the window.
428
+ (0...filler_characters).each do |x|
429
+ @field_win.mvwaddch(0, x, @filler)
430
+ end
431
+
432
+ # Draw the value in the field.
433
+ Draw.writeCharAttrib(@field_win, @field_width, 0, @current.to_s,
434
+ Curses::A_NORMAL, CDK::HORIZONTAL, 0, @current.to_s.size)
435
+
436
+ self.moveToEditPosition(@field_edit)
437
+ @field_win.refresh
438
+ end
439
+
440
+ # This sets the background attribute of the widget.
441
+ def setBKattr(attrib)
442
+ # Set the widget's background attribute.
443
+ @win.wbkgd(attrib)
444
+ @field_win.wbkgd(attrib)
445
+ unless @label_win.nil?
446
+ @label_win.wbkgd(attrib)
447
+ end
448
+ end
449
+
450
+ # This function destroys the widget.
451
+ def destroy
452
+ self.cleanTitle
453
+ @label = []
454
+
455
+ # Clean up the windows.
456
+ CDK.deleteCursesWindow(@field_win)
457
+ CDK.deleteCursesWindow(@label_win)
458
+ CDK.deleteCursesWindow(@shadow_win)
459
+ CDK.deleteCursesWindow(@win)
460
+
461
+ # Clean the key bindings.
462
+ self.cleanBindings(:SLIDER)
463
+
464
+ # Unregister this object.
465
+ CDK::SCREEN.unregister(:SLIDER, self)
466
+ end
467
+
468
+ # This function erases the widget from the screen.
469
+ def erase
470
+ if self.validCDKObject
471
+ CDK.eraseCursesWindow(@label_win)
472
+ CDK.eraseCursesWindow(@field_win)
473
+ CDK.eraseCursesWindow(@lwin)
474
+ CDK.eraseCursesWindow(@shadow_win)
475
+ end
476
+ end
477
+
478
+ def formattedSize(value)
479
+ return value.to_s.size
480
+ end
481
+
482
+ # This function sets the low/high/current values of the widget.
483
+ def set(low, high, value, box)
484
+ self.setLowHigh(low, high)
485
+ self.setValue(value)
486
+ self.setBox(box)
487
+ end
488
+
489
+ # This sets the widget's value.
490
+ def setValue(value)
491
+ @current = value
492
+ self.limitCurrentValue
493
+ end
494
+
495
+ def getValue
496
+ return @current
497
+ end
498
+
499
+ # This function sets the low/high values of the widget.
500
+ def setLowHigh(low, high)
501
+ # Make sure the values aren't out of bounds.
502
+ if low <= high
503
+ @low = low
504
+ @high = high
505
+ elsif low > high
506
+ @low = high
507
+ @high = low
508
+ end
509
+
510
+ # Make sure the user hasn't done something silly.
511
+ self.limitCurrentValue
512
+ end
513
+
514
+ def getLowValue
515
+ return @low
516
+ end
517
+
518
+ def getHighValue
519
+ return @high
520
+ end
521
+
522
+ def focus
523
+ self.draw(@box)
524
+ end
525
+
526
+ def unfocus
527
+ self.draw(@box)
528
+ end
529
+
530
+ def SCAN_FMT
531
+ '%d%c'
532
+ end
533
+
534
+ def position
535
+ super(@win)
536
+ end
537
+
538
+ def object_type
539
+ :SLIDER
540
+ end
541
+ end
542
+ end