slithernix-cdk 0.0.1

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