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/scale.rb ADDED
@@ -0,0 +1,529 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class SCALE < CDK::CDKOBJS
5
+ def initialize(cdkscreen, xplace, yplace, title, label, field_attr,
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
+
21
+ self.setBox(box)
22
+
23
+ box_height = @border_size * 2 + 1
24
+ box_width = field_width + 2 * @border_size
25
+
26
+ # Set some basic values of the widget's data field.
27
+ @label = []
28
+ @label_len = 0
29
+ @label_win = nil
30
+
31
+ # If the field_width is a negative value, the field_width will
32
+ # be COLS-field_width, otherwise the field_width will be the
33
+ # given width.
34
+ field_width = CDK.setWidgetDimension(parent_width, field_width, 0)
35
+ box_width = field_width + 2 * @border_size
36
+
37
+ # Translate the label string to a chtype array
38
+ unless label.nil?
39
+ label_len = []
40
+ @label = CDK.char2Chtype(label, label_len, [])
41
+ @label_len = label_len[0]
42
+ box_width = @label_len + field_width + 2
43
+ end
44
+
45
+ old_width = box_width
46
+ box_width = self.setTitle(title, box_width)
47
+ horizontal_adjust = (box_width - old_width) / 2
48
+
49
+ box_height += @title_lines
50
+
51
+ # Make sure we didn't extend beyond the dimensions of the window.
52
+ box_width = [box_width, parent_width].min
53
+ box_height = [box_height, parent_height].min
54
+ field_width = [field_width,
55
+ box_width - @label_len - 2 * @border_size].min
56
+
57
+ # Rejustify the x and y positions if we need to.
58
+ xtmp = [xplace]
59
+ ytmp = [yplace]
60
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
61
+ xpos = xtmp[0]
62
+ ypos = ytmp[0]
63
+
64
+ # Make the widget's window.
65
+ @win = Curses::Window.new(box_height, box_width, ypos, xpos)
66
+
67
+ # Is the main window nil?
68
+ if @win.nil?
69
+ self.destroy
70
+ return nil
71
+ end
72
+
73
+ # Create the widget's label window.
74
+ if @label.size > 0
75
+ @label_win = @win.subwin(1, @label_len,
76
+ ypos + @title_lines + @border_size,
77
+ xpos + horizontal_adjust + @border_size)
78
+ if @label_win.nil?
79
+ self.destroy
80
+ return nil
81
+ end
82
+ end
83
+
84
+ # Create the widget's data field window.
85
+ @field_win = @win.subwin(1, field_width,
86
+ ypos + @title_lines + @border_size,
87
+ xpos + @label_len + horizontal_adjust + @border_size)
88
+
89
+ if @field_win.nil?
90
+ self.destroy
91
+ return nil
92
+ end
93
+ @field_win.keypad(true)
94
+ @win.keypad(true)
95
+
96
+ # Create the widget's data field.
97
+ @screen = cdkscreen
98
+ @parent = cdkscreen.window
99
+ @shadow_win = nil
100
+ @box_width = box_width
101
+ @box_height = box_height
102
+ @field_width = field_width
103
+ @field_attr = field_attr
104
+ @current = low
105
+ @low = low
106
+ @high = high
107
+ @current = start
108
+ @inc = inc
109
+ @fastinc = fast_inc
110
+ @accepts_focus = true
111
+ @input_window = @win
112
+ @shadow = shadow
113
+ @field_edit = 0
114
+
115
+ # Do we want a shadow?
116
+ if shadow
117
+ @shadow_win = Curses::Window.new(box_height, box_width,
118
+ ypos + 1, xpos + 1)
119
+ if @shadow_win.nil?
120
+ self.destroy
121
+ return nil
122
+ end
123
+ end
124
+
125
+ # Setup the key bindings.
126
+ bindings.each do |from, to|
127
+ self.bind(self.object_type, from, :getc, to)
128
+ end
129
+
130
+ cdkscreen.register(self.object_type, self)
131
+ end
132
+
133
+ # This allows the person to use the widget's data field.
134
+ def activate(actions)
135
+ ret = -1
136
+ # Draw the widget.
137
+ self.draw(@box)
138
+
139
+ if actions.nil? || actions.size == 0
140
+ input = 0
141
+ while true
142
+ input = self.getch([])
143
+
144
+ # Inject the character into the widget.
145
+ ret = self.inject(input)
146
+ if @exit_type != :EARLY_EXIT
147
+ return ret
148
+ end
149
+ end
150
+ else
151
+ # Inject each character one at a time.
152
+ actions.each do |action|
153
+ ret = self.inject(action)
154
+ end
155
+ if @exit_type != :EARLY_EXIT
156
+ return ret
157
+ end
158
+ end
159
+
160
+ # Set the exit type and return.
161
+ self.setExitType(0)
162
+ return ret
163
+ end
164
+
165
+ # Check if the value lies outsid the low/high range. If so, force it in.
166
+ def limitCurrentValue
167
+ if @current < @low
168
+ @current = @low
169
+ CDK.Beep
170
+ elsif @current > @high
171
+ @current = @high
172
+ CDK.Beep
173
+ end
174
+ end
175
+
176
+ # Move the cursor to the given edit-position
177
+ # Once again, I cannot figure out why this move method is called
178
+ # and removing the call fixes the widget.
179
+ def moveToEditPosition(new_position)
180
+ #return @field_win.move(0, @field_width - new_position - 1)
181
+ #return @field_win.move(24, @field_width - new_position - 1)
182
+ @field_win
183
+ end
184
+
185
+ # Check if the cursor is on a valid edit-position. This must be one of
186
+ # the non-blank cells in the field.
187
+ def validEditPosition(new_position)
188
+ if new_position <= 0 || new_position >= @field_width
189
+ return false
190
+ end
191
+ if self.moveToEditPosition(new_position) == Curses::Error
192
+ return false
193
+ end
194
+ ch = @field_win.inch
195
+ if ch.chr != ' '
196
+ return true
197
+ end
198
+ if new_position > 1
199
+ # Don't use recursion - only one level is wanted
200
+ if self.moveToEditPosition(new_position - 1) == Curses::Error
201
+ return false
202
+ end
203
+ ch = @field_win.inch
204
+ return ch.chr != ' '
205
+ end
206
+ return false
207
+ end
208
+
209
+ # Set the edit position. Normally the cursor is one cell to the right of
210
+ # the editable field. Moving it left over the field allows the user to
211
+ # modify cells by typing in replacement characters for the field's value.
212
+ def setEditPosition(new_position)
213
+ if new_position < 0
214
+ CDK.Beep
215
+ elsif new_position == 0
216
+ @field_edit = new_position
217
+ elsif self.validEditPosition(new_position)
218
+ @field_edit = new_position
219
+ else
220
+ CDK.Beep
221
+ end
222
+ end
223
+
224
+ # Remove the character from the string at the given column, if it is blank.
225
+ # Returns true if a change was made.
226
+ def self.removeChar(string, col)
227
+ result = false
228
+ if col >= 0 && string[col] != ' '
229
+ while col < string.size - 1
230
+ string[col] = string[col + 1]
231
+ col += 1
232
+ end
233
+ string.chop!
234
+ result = true
235
+ end
236
+ return result
237
+ end
238
+
239
+ # Perform an editing function for the field.
240
+ def performEdit(input)
241
+ result = false
242
+ modify = true
243
+ base = 0
244
+ need = @field_width
245
+ temp = ''
246
+ col = need - @field_edit - 1
247
+
248
+ @field_win.move(0, base)
249
+ @field_win.winnstr(temp, need)
250
+ temp << ' '
251
+ if CDK.isChar(input) # Replace the char at the cursor
252
+ temp[col] = input.chr
253
+ elsif input == Curses::KEY_BACKSPACE
254
+ # delete the char before the cursor
255
+ modify = CDK::SCALE.removeChar(temp, col - 1)
256
+ elsif input == Curses::KEY_DC
257
+ # delete the char at the cursor
258
+ modify = CDK::SCALE.removeChar(temp, col)
259
+ else
260
+ modify = false
261
+ end
262
+ if modify &&
263
+ ((value, test) = temp.scanf(self.SCAN_FMT)).size == 2 &&
264
+ test == ' ' &&
265
+ value >= @low && value <= @high
266
+ self.setValue(value)
267
+ result = true
268
+ end
269
+
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(self.object_type, self,
305
+ @pre_process_data, input)
306
+ end
307
+
308
+ # Should we continue?
309
+ if pp_return != 0
310
+ # Check for a key bindings.
311
+ if self.checkBind(self.object_type, input)
312
+ complete = true
313
+ else
314
+ case input
315
+ when Curses::KEY_LEFT
316
+ self.setEditPosition(@field_edit + 1)
317
+ when Curses::KEY_RIGHT
318
+ self.setEditPosition(@field_edit - 1)
319
+ when Curses::KEY_DOWN
320
+ @current = CDK::SCALE.Decrement(@current, @inc)
321
+ when Curses::KEY_UP
322
+ @current = CDK::SCALE.Increment(@current, @inc)
323
+ when Curses::KEY_PPAGE
324
+ @current = CDK::SCALE.Increment(@current, @fastinc)
325
+ when Curses::KEY_NPAGE
326
+ @current = CDK::SCALE.Decrement(@current, @fastinc)
327
+ when Curses::KEY_HOME
328
+ @current = @low
329
+ when Curses::KEY_END
330
+ @current = @high
331
+ when CDK::KEY_TAB, CDK::KEY_RETURN, Curses::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 Curses::Error
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', '-'
354
+ return self.inject(Curses::KEY_DOWN)
355
+ when '+'
356
+ return self.inject(Curses::KEY_UP)
357
+ when 'D'
358
+ return self.inject(Curses::KEY_NPAGE)
359
+ when '0'
360
+ return self.inject(Curses::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(self.object_type, self,
372
+ @post_process_data, input)
373
+ end
374
+ end
375
+
376
+ if !complete
377
+ self.drawField
378
+ self.setExitType(0)
379
+ end
380
+
381
+ @result_data = ret
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
+ @field_win.erase
421
+
422
+ # Draw the value in the field.
423
+ temp = @current.to_s
424
+ Draw.writeCharAttrib(@field_win,
425
+ @field_width - temp.size - 1, 0, temp, @field_attr,
426
+ CDK::HORIZONTAL, 0, temp.size)
427
+
428
+ self.moveToEditPosition(@field_edit)
429
+ @field_win.refresh
430
+ end
431
+
432
+ # This sets the background attribute of teh widget.
433
+ def setBKattr(attrib)
434
+ @win.wbkgd(attrib)
435
+ @field_win.wbkgd(attrib)
436
+ unless @label_win.nil?
437
+ @label_win.wbkgd(attrib)
438
+ end
439
+ end
440
+
441
+ # This function destroys the widget.
442
+ def destroy
443
+ self.cleanTitle
444
+ @label = []
445
+
446
+ # Clean up the windows.
447
+ CDK.deleteCursesWindow(@field_win)
448
+ CDK.deleteCursesWindow(@label_win)
449
+ CDK.deleteCursesWindow(@shadow_win)
450
+ CDK.deleteCursesWindow(@win)
451
+
452
+ # Clean the key bindings.
453
+ self.cleanBindings(self.object_type)
454
+
455
+ # Unregister this object
456
+ CDK::SCREEN.unregister(self.object_type, self)
457
+ end
458
+
459
+ # This function erases the widget from the screen.
460
+ def erase
461
+ if self.validCDKObject
462
+ CDK.eraseCursesWindow(@label_win)
463
+ CDK.eraseCursesWindow(@field_win)
464
+ CDK.eraseCursesWindow(@win)
465
+ CDK.eraseCursesWindow(@shadow_win)
466
+ end
467
+ end
468
+
469
+ # This function sets the low/high/current values of the widget.
470
+ def set(low, high, value, box)
471
+ self.setLowHigh(low, high)
472
+ self.setValue(value)
473
+ self.setBox(box)
474
+ end
475
+
476
+ # This sets the widget's value
477
+ def setValue(value)
478
+ @current = value
479
+ self.limitCurrentValue
480
+ end
481
+
482
+ def getValue
483
+ return @current
484
+ end
485
+
486
+ # This function sets the low/high values of the widget.
487
+ def setLowHigh(low, high)
488
+ # Make sure the values aren't out of bounds.
489
+ if low <= high
490
+ @low = low
491
+ @high = high
492
+ elsif low > high
493
+ @low = high
494
+ @high = low
495
+ end
496
+
497
+ # Make sure the user hasn't done something silly.
498
+ self.limitCurrentValue
499
+ end
500
+
501
+ def getLowValue
502
+ return @low
503
+ end
504
+
505
+ def getHighValue
506
+ return @high
507
+ end
508
+
509
+ def focus
510
+ self.draw(@box)
511
+ end
512
+
513
+ def unfocus
514
+ self.draw(@box)
515
+ end
516
+
517
+ def position
518
+ super(@win)
519
+ end
520
+
521
+ def SCAN_FMT
522
+ '%d%c'
523
+ end
524
+
525
+ def object_type
526
+ :SCALE
527
+ end
528
+ end
529
+ end