cdk 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,541 @@
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.getmaxx
9
+ parent_height = cdkscreen.window.getmaxy
10
+ bindings = {
11
+ 'u' => Ncurses::KEY_UP,
12
+ 'U' => Ncurses::KEY_PPAGE,
13
+ CDK::BACKCHAR => Ncurses::KEY_PPAGE,
14
+ CDK::FORCHAR => Ncurses::KEY_NPAGE,
15
+ 'g' => Ncurses::KEY_HOME,
16
+ '^' => Ncurses::KEY_HOME,
17
+ 'G' => Ncurses::KEY_END,
18
+ '$' => Ncurses::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 = Ncurses::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 = Ncurses::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.wmove(0,
180
+ @field_width + self.formattedSize(@current) - new_position)
181
+ end
182
+
183
+ # Check if the cursor is on a valid edit-position. This must be one of
184
+ # the non-blank cells in the field.
185
+ def validEditPosition(new_position)
186
+ if new_position <= 0 || new_position >= @field_width
187
+ return false
188
+ end
189
+ if self.moveToEditPosition(new_position) == Ncurses::ERR
190
+ return false
191
+ end
192
+ ch = @field_win.winch
193
+ if CDK.CharOf(ch) != ' '
194
+ return true
195
+ end
196
+ if new_position > 1
197
+ # Don't use recursion - only one level is wanted
198
+ if self.moveToEditPosition(new_position - 1) == Ncurses::ERR
199
+ return false
200
+ end
201
+ ch = @field_win.winch
202
+ return CDK.CharOf(ch) != ' '
203
+ end
204
+ return false
205
+ end
206
+
207
+ # Set the edit position. Normally the cursor is one cell to the right of
208
+ # the editable field. Moving it left, over the field, allows the user to
209
+ # modify cells by typing in replacement characters for the field's value.
210
+ def setEditPosition(new_position)
211
+ if new_position < 0
212
+ CDK.Beep
213
+ elsif new_position == 0
214
+ @field_edit = new_position
215
+ elsif self.validEditPosition(new_position)
216
+ @field_edit = new_position
217
+ else
218
+ CDK.Beep
219
+ end
220
+ end
221
+
222
+ # Remove the character from the string at the given column, if it is blank.
223
+ # Returns true if a change was made.
224
+ def self.removeChar(string, col)
225
+ result = false
226
+ if col >= 0 && string[col] != ' '
227
+ while col < string.size - 1
228
+ string[col] = string[col + 1]
229
+ col += 1
230
+ end
231
+ string.chop!
232
+ result = true
233
+ end
234
+ return result
235
+ end
236
+
237
+ # Perform an editing function for the field.
238
+ def performEdit(input)
239
+ result = false
240
+ modify = true
241
+ base = @field_width
242
+ need = self.formattedSize(@current)
243
+ temp = ''
244
+ col = need - @field_edit
245
+
246
+ adj = if col < 0 then -col else 0 end
247
+ if adj != 0
248
+ temp = ' ' * adj
249
+ end
250
+ @field_win.wmove(0, base)
251
+ @field_win.winnstr(temp, need)
252
+ temp << ' '
253
+ if CDK.isChar(input) # Replace the char at the cursor
254
+ temp[col] = input.chr
255
+ elsif input == Ncurses::KEY_BACKSPACE
256
+ # delete the char before the cursor
257
+ modify = CDK::SLIDER.removeChar(temp, col - 1)
258
+ elsif input == Ncurses::KEY_DC
259
+ # delete the char at the cursor
260
+ modify = CDK::SLIDER.removeChar(temp, col)
261
+ else
262
+ modify = false
263
+ end
264
+ if modify &&
265
+ ((value, test) = temp.scanf(self.SCAN_FMT)).size == 2 &&
266
+ test == ' ' && value >= @low && value <= @high
267
+ self.setValue(value)
268
+ result = true
269
+ end
270
+ return result
271
+ end
272
+
273
+ def self.Decrement(value, by)
274
+ if value - by < value
275
+ value - by
276
+ else
277
+ value
278
+ end
279
+ end
280
+
281
+ def self.Increment(value, by)
282
+ if value + by > value
283
+ value + by
284
+ else
285
+ value
286
+ end
287
+ end
288
+
289
+ # This function injects a single character into the widget.
290
+ def inject(input)
291
+ pp_return = 1
292
+ ret = -1
293
+ complete = false
294
+
295
+ # Set the exit type.
296
+ self.setExitType(0)
297
+
298
+ # Draw the field.
299
+ self.drawField
300
+
301
+ # Check if there is a pre-process function to be called.
302
+ unless @pre_process_func.nil?
303
+ # Call the pre-process function.
304
+ pp_return = @pre_process_func.call(:SLIDER, self,
305
+ @pre_process_data, input)
306
+ end
307
+
308
+ # Should we continue?
309
+ if pp_return != 0
310
+ # Check for a key binding.
311
+ if self.checkBind(:SLIDER, input)
312
+ complete = true
313
+ else
314
+ case input
315
+ when Ncurses::KEY_LEFT
316
+ self.setEditPosition(@field_edit + 1)
317
+ when Ncurses::KEY_RIGHT
318
+ self.setEditPosition(@field_edit - 1)
319
+ when Ncurses::KEY_DOWN
320
+ @current = CDK::SLIDER.Decrement(@current, @inc)
321
+ when Ncurses::KEY_UP
322
+ @current = CDK::SLIDER.Increment(@current, @inc)
323
+ when Ncurses::KEY_PPAGE
324
+ @current = CDK::SLIDER.Increment(@current, @fastinc)
325
+ when Ncurses::KEY_NPAGE
326
+ @current = CDK::SLIDER.Decrement(@current, @fastinc)
327
+ when Ncurses::KEY_HOME
328
+ @current = @low
329
+ when Ncurses::KEY_END
330
+ @current = @high
331
+ when CDK::KEY_TAB, CDK::KEY_RETURN, Ncurses::KEY_ENTER
332
+ self.setExitType(input)
333
+ ret = @current
334
+ complete = true
335
+ when CDK::KEY_ESC
336
+ self.setExitType(input)
337
+ complete = true
338
+ when Ncurses::ERR
339
+ self.setExitType(input)
340
+ complete = true
341
+ when CDK::REFRESH
342
+ @screen.erase
343
+ @screen.refresh
344
+ else
345
+ if @field_edit != 0
346
+ if !self.performEdit(input)
347
+ CDK.Beep
348
+ end
349
+ else
350
+ # The cursor is not within the editable text. Interpret
351
+ # input as commands.
352
+ case input
353
+ when 'd'.ord, '-'.ord
354
+ return self.inject(Ncurses::KEY_DOWN)
355
+ when '+'.ord
356
+ return self.inject(Ncurses::KEY_UP)
357
+ when 'D'.ord
358
+ return self.inject(Ncurses::KEY_NPAGE)
359
+ when '0'.ord
360
+ return self.inject(Ncurses::KEY_HOME)
361
+ else
362
+ CDK.Beep
363
+ end
364
+ end
365
+ end
366
+ end
367
+ self.limitCurrentValue
368
+
369
+ # Should we call a post-process?
370
+ if !complete && !(@post_process_func.nil?)
371
+ @post_process_func.call(:SLIDER, self, @post_process_data, input)
372
+ end
373
+ end
374
+
375
+ if !complete
376
+ self.drawField
377
+ self.setExitType(0)
378
+ end
379
+
380
+ @return_data = 0
381
+ return ret
382
+ end
383
+
384
+ # This moves the widget's data field to the given location.
385
+ def move(xplace, yplace, relative, refresh_flag)
386
+ windows = [@win, @label_win, @field_win, @shadow_win]
387
+ self.move_specific(xplace, yplace, relative, refresh_flag,
388
+ windows, [])
389
+ end
390
+
391
+ # This function draws the widget.
392
+ def draw(box)
393
+ # Draw the shadow.
394
+ unless @shadow_win.nil?
395
+ Draw.drawShadow(@shadow_win)
396
+ end
397
+
398
+ # Box the widget if asked.
399
+ if box
400
+ Draw.drawObjBox(@win, self)
401
+ end
402
+
403
+ self.drawTitle(@win)
404
+
405
+ # Draw the label.
406
+ unless @label_win.nil?
407
+ Draw.writeChtype(@label_win, 0, 0, @label, CDK::HORIZONTAL,
408
+ 0, @label_len)
409
+ @label_win.wrefresh
410
+ end
411
+ @win.wrefresh
412
+
413
+ # Draw the field window.
414
+ self.drawField
415
+ end
416
+
417
+ # This draws the widget.
418
+ def drawField
419
+ step = 1.0 * @field_width / (@high - @low)
420
+
421
+ # Determine how many filler characters need to be drawn.
422
+ filler_characters = (@current - @low) * step
423
+
424
+ @field_win.werase
425
+
426
+ # Add the character to the window.
427
+ (0...filler_characters).each do |x|
428
+ @field_win.mvwaddch(0, x, @filler)
429
+ end
430
+
431
+ # Draw the value in the field.
432
+ Draw.writeCharAttrib(@field_win, @field_width, 0, @current.to_s,
433
+ Ncurses::A_NORMAL, CDK::HORIZONTAL, 0, @current.to_s.size)
434
+
435
+ self.moveToEditPosition(@field_edit)
436
+ @field_win.wrefresh
437
+ end
438
+
439
+ # This sets the background attribute of the widget.
440
+ def setBKattr(attrib)
441
+ # Set the widget's background attribute.
442
+ @win.wbkgd(attrib)
443
+ @field_win.wbkgd(attrib)
444
+ unless @label_win.nil?
445
+ @label_win.wbkgd(attrib)
446
+ end
447
+ end
448
+
449
+ # This function destroys the widget.
450
+ def destroy
451
+ self.cleanTitle
452
+ @label = []
453
+
454
+ # Clean up the windows.
455
+ CDK.deleteCursesWindow(@field_win)
456
+ CDK.deleteCursesWindow(@label_win)
457
+ CDK.deleteCursesWindow(@shadow_win)
458
+ CDK.deleteCursesWindow(@win)
459
+
460
+ # Clean the key bindings.
461
+ self.cleanBindings(:SLIDER)
462
+
463
+ # Unregister this object.
464
+ CDK::SCREEN.unregister(:SLIDER, self)
465
+ end
466
+
467
+ # This function erases the widget from the screen.
468
+ def erase
469
+ if self.validCDKObject
470
+ CDK.eraseCursesWindow(@label_win)
471
+ CDK.eraseCursesWindow(@field_win)
472
+ CDK.eraseCursesWindow(@lwin)
473
+ CDK.eraseCursesWindow(@shadow_win)
474
+ end
475
+ end
476
+
477
+ def formattedSize(value)
478
+ return value.to_s.size
479
+ end
480
+
481
+ # This function sets the low/high/current values of the widget.
482
+ def set(low, high, value, box)
483
+ self.setLowHigh(low, high)
484
+ self.setValue(value)
485
+ self.setBox(box)
486
+ end
487
+
488
+ # This sets the widget's value.
489
+ def setValue(value)
490
+ @current = value
491
+ self.limitCurrentValue
492
+ end
493
+
494
+ def getValue
495
+ return @current
496
+ end
497
+
498
+ # This function sets the low/high values of the widget.
499
+ def setLowHigh(low, high)
500
+ # Make sure the values aren't out of bounds.
501
+ if low <= high
502
+ @low = low
503
+ @high = high
504
+ elsif low > high
505
+ @low = high
506
+ @high = low
507
+ end
508
+
509
+ # Make sure the user hasn't done something silly.
510
+ self.limitCurrentValue
511
+ end
512
+
513
+ def getLowValue
514
+ return @low
515
+ end
516
+
517
+ def getHighValue
518
+ return @high
519
+ end
520
+
521
+ def focus
522
+ self.draw(@box)
523
+ end
524
+
525
+ def unfocus
526
+ self.draw(@box)
527
+ end
528
+
529
+ def SCAN_FMT
530
+ '%d%c'
531
+ end
532
+
533
+ def position
534
+ super(@win)
535
+ end
536
+
537
+ def object_type
538
+ :SLIDER
539
+ end
540
+ end
541
+ end