cdk 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,280 @@
1
+ module CDK
2
+ class SCREEN
3
+ attr_accessor :object_focus, :object_count, :object_limit, :object, :window
4
+ attr_accessor :exit_status
5
+
6
+ NOEXIT = 0
7
+ EXITOK = 1
8
+ EXITCANCEL = 2
9
+
10
+ def initialize (window)
11
+ # initialization for the first time
12
+ if CDK::ALL_SCREENS.size == 0
13
+ # Set up basic curses settings.
14
+ # #ifdef HAVE_SETLOCALE
15
+ # setlocale (LC_ALL, "");
16
+ # #endif
17
+
18
+ Ncurses.noecho
19
+ Ncurses.cbreak
20
+ end
21
+
22
+ CDK::ALL_SCREENS << self
23
+ @object_count = 0
24
+ @object_limit = 2
25
+ @object = Array.new(@object_limit, nil)
26
+ @window = window
27
+ @object_focus = 0
28
+ end
29
+
30
+ # This registers a CDK object with a screen.
31
+ def register(cdktype, object)
32
+ if @object_count + 1 >= @object_limit
33
+ @object_limit += 2
34
+ @object_limit *= 2
35
+ @object.concat(Array.new(@object_limit - @object.size, nil))
36
+ end
37
+
38
+ if object.validObjType(cdktype)
39
+ self.setScreenIndex(@object_count, object)
40
+ @object_count += 1
41
+ end
42
+ end
43
+
44
+ # This removes an object from the CDK screen.
45
+ def self.unregister(cdktype, object)
46
+ if object.validObjType(cdktype) && object.screen_index >= 0
47
+ screen = object.screen
48
+
49
+ unless screen.nil?
50
+ index = object.screen_index
51
+ object.screen_index = -1
52
+
53
+ # Resequence the objects
54
+ (index...screen.object_count - 1).each do |x|
55
+ screen.setScreenIndex(x, screen.object[x+1])
56
+ end
57
+
58
+ if screen.object_count <= 1
59
+ # if no more objects, remove the array
60
+ screen.object = []
61
+ screen.object_count = 0
62
+ screen.object_limit = 0
63
+ else
64
+ screen.object[screen.object_count] = nil
65
+ screen.object_count -= 1
66
+
67
+ # Update the object-focus
68
+ if screen.object_focus == index
69
+ screen.object_focus -= 1
70
+ Traverse.setCDKFocusNext(screen)
71
+ elsif screen.object_focus > index
72
+ screen.object_focus -= 1
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ def setScreenIndex(number, obj)
80
+ obj.screen_index = number
81
+ obj.screen = self
82
+ @object[number] = obj
83
+ end
84
+
85
+ def validIndex(n)
86
+ n >= 0 && n < @object_count
87
+ end
88
+
89
+ def swapCDKIndices(n1, n2)
90
+ if n1 != n2 && self.validIndex(n1) && self.validIndex(n2)
91
+ o1 = @object[n1]
92
+ o2 = @object[n2]
93
+ self.setScreenIndex(n1, o2)
94
+ self.setScreenIndex(n2, o1)
95
+
96
+ if @object_focus == n1
97
+ @object_focus = n2
98
+ elsif @object_focus == n2
99
+ @object_focus = n1
100
+ end
101
+ end
102
+ end
103
+
104
+ # This 'brings' a CDK object to the top of the stack.
105
+ def self.raiseCDKObject(cdktype, object)
106
+ if object.validObjType(cdktype)
107
+ screen = object.screen
108
+ screen.swapCDKIndices(object.screen_index, screen.object_count - 1)
109
+ end
110
+ end
111
+
112
+ # This 'lowers' an object.
113
+ def self.lowerCDKObject(cdktype, object)
114
+ if object.validObjType(cdktype)
115
+ object.screen.swapCDKIndices(object.screen_index, 0)
116
+ end
117
+ end
118
+
119
+ # This pops up a message.
120
+ def popupLabel(mesg, count)
121
+ #Create the label.
122
+ popup = CDK::LABEL.new(self, CENTER, CENTER, mesg, count, true, false)
123
+
124
+ old_state = Ncurses.curs_set(0)
125
+ #Draw it on the screen
126
+ popup.draw(true)
127
+
128
+ # Wait for some input.
129
+ popup.win.keypad(true)
130
+ popup.getch([])
131
+
132
+ # Kill it.
133
+ popup.destroy
134
+
135
+ # Clean the screen.
136
+ Ncurses.curs_set(old_state)
137
+ self.erase
138
+ self.refresh
139
+ end
140
+
141
+ # This pops up a message
142
+ def popupLabelAttrib(mesg, count, attrib)
143
+ # Create the label.
144
+ popup = CDK::LABEL.new(self, CENTER, CENTER, mesg, count, true, false)
145
+ popup.setBackgroundAttrib
146
+
147
+ old_state = Ncurses.curs_set(0)
148
+ # Draw it on the screen)
149
+ popup.draw(true)
150
+
151
+ # Wait for some input
152
+ popup.win.keypad(true)
153
+ popup.getch([])
154
+
155
+ # Kill it.
156
+ popup.destroy
157
+
158
+ # Clean the screen.
159
+ Ncurses.curs_set(old_state)
160
+ screen.erase
161
+ screen.refresh
162
+ end
163
+
164
+ # This pops up a dialog box.
165
+ def popupDialog(mesg, mesg_count, buttons, button_count)
166
+ # Create the dialog box.
167
+ popup = CDK::DIALOG.new(self, CDK::CENTER, CDK::CENTER,
168
+ mesg, mesg_count, buttons, button_count, Ncurses::A_REVERSE,
169
+ true, true, false)
170
+
171
+ # Activate the dialog box
172
+ popup.draw(true)
173
+
174
+ # Get the choice
175
+ choice = popup.activate('')
176
+
177
+ # Destroy the dialog box
178
+ popup.destroy
179
+
180
+ # Clean the screen.
181
+ self.erase
182
+ self.refresh
183
+
184
+ return choice
185
+ end
186
+
187
+ # This calls SCREEN.refresh, (made consistent with widgets)
188
+ def draw
189
+ self.refresh
190
+ end
191
+
192
+ # Refresh one CDK window.
193
+ # FIXME(original): this should be rewritten to use the panel library, so
194
+ # it would not be necessary to touch the window to ensure that it covers
195
+ # other windows.
196
+ def SCREEN.refreshCDKWindow(win)
197
+ win.touchwin
198
+ win.wrefresh
199
+ end
200
+
201
+ # This refreshes all the objects in the screen.
202
+ def refresh
203
+ focused = -1
204
+ visible = -1
205
+
206
+ CDK::SCREEN.refreshCDKWindow(@window)
207
+
208
+ # We erase all the invisible objects, then only draw it all back, so
209
+ # that the objects can overlap, and the visible ones will always be
210
+ # drawn after all the invisible ones are erased
211
+ (0...@object_count).each do |x|
212
+ obj = @object[x]
213
+ if obj.validObjType(obj.object_type)
214
+ if obj.is_visible
215
+ if visible < 0
216
+ visible = x
217
+ end
218
+ if obj.has_focus && focused < 0
219
+ focused = x
220
+ end
221
+ else
222
+ obj.erase
223
+ end
224
+ end
225
+ end
226
+
227
+ (0...@object_count).each do |x|
228
+ obj = @object[x]
229
+
230
+ if obj.validObjType(obj.object_type)
231
+ obj.has_focus = (x == focused)
232
+
233
+ if obj.is_visible
234
+ obj.draw(obj.box)
235
+ end
236
+ end
237
+ end
238
+ end
239
+
240
+ # This clears all the objects in the screen
241
+ def erase
242
+ # We just call the object erase function
243
+ (0...@object_count).each do |x|
244
+ obj = @object[x]
245
+ if obj.validObjType(obj.object_type)
246
+ obj.erase
247
+ end
248
+ end
249
+
250
+ # Refresh the screen.
251
+ @window.wrefresh
252
+ end
253
+
254
+ # Destroy all the objects on a screen
255
+ def destroyCDKScreenObjects
256
+ (0...@object_count).each do |x|
257
+ obj = @object[x]
258
+ before = @object_count
259
+
260
+ if obj.validObjType(obj.object_type)
261
+ obj.erase
262
+ obj.destroy
263
+ x -= (@object_count - before)
264
+ end
265
+ end
266
+ end
267
+
268
+ # This destroys a CDK screen.
269
+ def destroy
270
+ CDK::ALL_SCREENS.delete(self)
271
+ end
272
+
273
+ # This is added to remain consistent
274
+ def self.endCDK
275
+ Ncurses.echo
276
+ Ncurses.nocbreak
277
+ Ncurses.endwin
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,994 @@
1
+ require_relative 'scroller'
2
+
3
+ module CDK
4
+ class SCROLL < CDK::SCROLLER
5
+ attr_reader :item, :list_size, :current_item, :highlight
6
+
7
+ def initialize (cdkscreen, xplace, yplace, splace, height, width, title,
8
+ list, list_size, numbers, highlight, box, shadow)
9
+ super()
10
+ parent_width = cdkscreen.window.getmaxx
11
+ parent_height = cdkscreen.window.getmaxy
12
+ box_width = width
13
+ box_height = height
14
+ xpos = xplace
15
+ ypos = yplace
16
+ scroll_adjust = 0
17
+ bindings = {
18
+ CDK::BACKCHAR => Ncurses::KEY_PPAGE,
19
+ CDK::FORCHAR => Ncurses::KEY_NPAGE,
20
+ 'g' => Ncurses::KEY_HOME,
21
+ '1' => Ncurses::KEY_HOME,
22
+ 'G' => Ncurses::KEY_END,
23
+ '<' => Ncurses::KEY_HOME,
24
+ '>' => Ncurses::KEY_END
25
+ }
26
+
27
+ self.setBox(box)
28
+
29
+ # If the height is a negative value, the height will be ROWS-height,
30
+ # otherwise the height will be the given height
31
+ box_height = CDK.setWidgetDimension(parent_height, height, 0)
32
+
33
+ # If the width is a negative value, the width will be COLS-width,
34
+ # otherwise the width will be the given width
35
+ box_width = CDK.setWidgetDimension(parent_width, width, 0)
36
+
37
+ box_width = self.setTitle(title, box_width)
38
+
39
+ # Set the box height.
40
+ if @title_lines > box_height
41
+ box_height = @title_lines + [list_size, 8].min + 2 * @border_size
42
+ end
43
+
44
+ # Adjust the box width if there is a scroll bar
45
+ if splace == CDK::LEFT || splace == CDK::RIGHT
46
+ @scrollbar = true
47
+ box_width += 1
48
+ else
49
+ @scrollbar = false
50
+ end
51
+
52
+ # Make sure we didn't extend beyond the dimensions of the window.
53
+ @box_width = if box_width > parent_width
54
+ then parent_width - scroll_adjust
55
+ else box_width
56
+ end
57
+ @box_height = if box_height > parent_height
58
+ then parent_height
59
+ else box_height
60
+ end
61
+
62
+ self.setViewSize(list_size)
63
+
64
+ # Rejustify the x and y positions if we need to.
65
+ xtmp = [xpos]
66
+ ytmp = [ypos]
67
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, @box_width, @box_height)
68
+ xpos = xtmp[0]
69
+ ypos = ytmp[0]
70
+
71
+ # Make the scrolling window
72
+ @win = Ncurses::WINDOW.new(@box_height, @box_width, ypos, xpos)
73
+
74
+ # Is the scrolling window null?
75
+ if @win.nil?
76
+ return nil
77
+ end
78
+
79
+ # Turn the keypad on for the window
80
+ @win.keypad(true)
81
+
82
+ # Create the scrollbar window.
83
+ if splace == CDK::RIGHT
84
+ @scrollbar_win = @win.subwin(self.maxViewSize, 1,
85
+ self.SCREEN_YPOS(ypos), xpos + box_width - @border_size - 1)
86
+ elsif splace == CDK::LEFT
87
+ @scrollbar_win = @win.subwin(self.maxViewSize, 1,
88
+ self.SCREEN_YPOS(ypos), self.SCREEN_XPOS(xpos))
89
+ else
90
+ @scrollbar_win = nil
91
+ end
92
+
93
+ # create the list window
94
+ @list_win = @win.subwin(self.maxViewSize,
95
+ box_width - (2 * @border_size) - scroll_adjust,
96
+ self.SCREEN_YPOS(ypos),
97
+ self.SCREEN_XPOS(xpos) + (if splace == CDK::LEFT then 1 else 0 end))
98
+
99
+ # Set the rest of the variables
100
+ @screen = cdkscreen
101
+ @parent = cdkscreen.window
102
+ @shadow_win = nil
103
+ @scrollbar_placement = splace
104
+ @max_left_char = 0
105
+ @left_char = 0
106
+ @highlight = highlight
107
+ # initExitType (scrollp);
108
+ @accepts_focus = true
109
+ @input_window = @win
110
+ @shadow = shadow
111
+
112
+ self.setPosition(0);
113
+
114
+ # Create the scrolling list item list and needed variables.
115
+ if self.createItemList(numbers, list, list_size) <= 0
116
+ return nil
117
+ end
118
+
119
+ # Do we need to create a shadow?
120
+ if shadow
121
+ @shadow_win = Ncurses::WINDOW.new(@box_height, box_width,
122
+ ypos + 1, xpos + 1)
123
+ end
124
+
125
+ # Set up the key bindings.
126
+ bindings.each do |from, to|
127
+ #self.bind(:SCROLL, from, getc_lambda, to)
128
+ self.bind(:SCROLL, from, :getc, to)
129
+ end
130
+
131
+ cdkscreen.register(:SCROLL, self);
132
+
133
+ return self
134
+ end
135
+
136
+ def object_type
137
+ :SCROLL
138
+ end
139
+
140
+ def position
141
+ super(@win)
142
+ end
143
+
144
+ # Put the cursor on the currently-selected item's row.
145
+ def fixCursorPosition
146
+ scrollbar_adj = if @scrollbar_placement == LEFT then 1 else 0 end
147
+ ypos = self.SCREEN_YPOS(@current_item - @current_top)
148
+ xpos = self.SCREEN_XPOS(0) + scrollbar_adj
149
+
150
+ @input_window.wmove(ypos, xpos)
151
+ @input_window.wrefresh
152
+ end
153
+
154
+ # This actually does all the 'real' work of managing the scrolling list.
155
+ def activate(actions)
156
+ # Draw the scrolling list
157
+ self.draw(@box)
158
+
159
+ if actions.nil? || actions.size == 0
160
+ while true
161
+ self.fixCursorPosition
162
+ input = self.getch([])
163
+
164
+ # Inject the character into the widget.
165
+ ret = self.inject(input)
166
+ if @exit_type != :EARLY_EXIT
167
+ return ret
168
+ end
169
+ end
170
+ else
171
+ # Inject each character one at a time.
172
+ actions.each do |action|
173
+ ret = self.inject(action)
174
+ if @exit_type != :EARLY_EXIT
175
+ return ret
176
+ end
177
+ end
178
+ end
179
+
180
+ # Set the exit type for the widget and return
181
+ self.setExitType(0)
182
+ return -1
183
+ end
184
+
185
+ # This injects a single character into the widget.
186
+ def inject(input)
187
+ pp_return = 1
188
+ ret = -1
189
+ complete = false
190
+
191
+ # Set the exit type for the widget.
192
+ self.setExitType(0)
193
+
194
+ # Draw the scrolling list
195
+ self.drawList(@box)
196
+
197
+ #Check if there is a pre-process function to be called.
198
+ unless @pre_process_func.nil?
199
+ pp_return = @pre_process_func.call(:SCROLL, self,
200
+ @pre_process_data, input)
201
+ end
202
+
203
+ # Should we continue?
204
+ if pp_return != 0
205
+ # Check for a predefined key binding.
206
+ if self.checkBind(:SCROLL, input) != false
207
+ #self.checkEarlyExit
208
+ complete = true
209
+ else
210
+ case input
211
+ when Ncurses::KEY_UP
212
+ self.KEY_UP
213
+ when Ncurses::KEY_DOWN
214
+ self.KEY_DOWN
215
+ when Ncurses::KEY_RIGHT
216
+ self.KEY_RIGHT
217
+ when Ncurses::KEY_LEFT
218
+ self.KEY_LEFT
219
+ when Ncurses::KEY_PPAGE
220
+ self.KEY_PPAGE
221
+ when Ncurses::KEY_NPAGE
222
+ self.KEY_NPAGE
223
+ when Ncurses::KEY_HOME
224
+ self.KEY_HOME
225
+ when Ncurses::KEY_END
226
+ self.KEY_END
227
+ when '$'
228
+ @left_char = @max_left_char
229
+ when '|'
230
+ @left_char = 0
231
+ when CDK::KEY_ESC
232
+ self.setExitType(input)
233
+ complete = true
234
+ when Ncurses::ERR
235
+ self.setExitType(input)
236
+ complete = true
237
+ when CDK::REFRESH
238
+ @screen.erase
239
+ @screen.refresh
240
+ when CDK::KEY_TAB, Ncurses::KEY_ENTER, CDK::KEY_RETURN
241
+ self.setExitType(input)
242
+ ret = @current_item
243
+ complete = true
244
+ end
245
+ end
246
+
247
+ if !complete && !(@post_process_func.nil?)
248
+ @post_process_func.call(:SCROLL, self, @post_process_data, input)
249
+ end
250
+ end
251
+
252
+ if !complete
253
+ self.drawList(@box)
254
+ self.setExitType(0)
255
+ end
256
+
257
+ self.fixCursorPosition
258
+ @result_data = ret
259
+
260
+ #return ret != -1
261
+ return ret
262
+ end
263
+
264
+ def getCurrentTop
265
+ return @current_top
266
+ end
267
+
268
+ def setCurrentTop(item)
269
+ if item < 0
270
+ item = 0
271
+ elsif item > @max_top_item
272
+ item = @max_top_item
273
+ end
274
+ @current_top = item
275
+
276
+ self.setPosition(item);
277
+ end
278
+
279
+ # This moves the scroll field to the given location.
280
+ def move(xplace, yplace, relative, refresh_flag)
281
+ windows = [@win, @list_win, @shadow_win, @scrollbar_win]
282
+ self.move_specific(xplace, yplace, relative, refresh_flag,
283
+ windows, [])
284
+ end
285
+
286
+ # This function draws the scrolling list widget.
287
+ def draw(box)
288
+ # Draw in the shadow if we need to.
289
+ unless @shadow_win.nil?
290
+ Draw.drawShadow(@shadow_win)
291
+ end
292
+
293
+ self.drawTitle(@win)
294
+
295
+ # Draw in the scrolling list items.
296
+ self.drawList(box)
297
+ end
298
+
299
+ def drawCurrent
300
+ # Rehighlight the current menu item.
301
+ screen_pos = @item_pos[@current_item] - @left_char
302
+ highlight = if self.has_focus
303
+ then @highlight
304
+ else Ncurses::A_NORMAL
305
+ end
306
+
307
+ Draw.writeChtypeAttrib(@list_win,
308
+ if screen_pos >= 0 then screen_pos else 0 end,
309
+ @current_high, @item[@current_item], highlight, CDK::HORIZONTAL,
310
+ if screen_pos >= 0 then 0 else 1 - screen_pos end,
311
+ @item_len[@current_item])
312
+ end
313
+
314
+ def drawList(box)
315
+ # If the list is empty, don't draw anything.
316
+ if @list_size > 0
317
+ # Redraw the list
318
+ (0...@view_size).each do |j|
319
+ k = j + @current_top
320
+
321
+ Draw.writeBlanks(@list_win, 0, j, CDK::HORIZONTAL, 0,
322
+ @box_width - (2 * @border_size))
323
+
324
+ # Draw the elements in the scrolling list.
325
+ if k < @list_size
326
+ screen_pos = @item_pos[k] - @left_char
327
+ ypos = j
328
+
329
+ # Write in the correct line.
330
+ Draw.writeChtype(@list_win,
331
+ if screen_pos >= 0 then screen_pos else 1 end,
332
+ ypos, @item[k], CDK::HORIZONTAL,
333
+ if screen_pos >= 0 then 0 else 1 - screen_pos end,
334
+ @item_len[k])
335
+ end
336
+ end
337
+
338
+ self.drawCurrent
339
+
340
+ # Determine where the toggle is supposed to be.
341
+ unless @scrollbar_win.nil?
342
+ @toggle_pos = (@current_item * @step).floor
343
+
344
+ # Make sure the toggle button doesn't go out of bounds.
345
+
346
+ if @toggle_pos >= @scrollbar_win.getmaxy
347
+ @toggle_pos = @scrollbar_win.getmaxy - 1
348
+ end
349
+
350
+ # Draw the scrollbar
351
+ @scrollbar_win.mvwvline(0, 0, Ncurses::ACS_CKBOARD,
352
+ @scrollbar_win.getmaxy)
353
+ @scrollbar_win.mvwvline(@toggle_pos, 0, ' '.ord | Ncurses::A_REVERSE,
354
+ @toggle_size)
355
+ end
356
+ end
357
+
358
+ # Box it if needed.
359
+ if box
360
+ Draw.drawObjBox(@win, self)
361
+ end
362
+
363
+ # Refresh the window
364
+ @win.wrefresh
365
+ end
366
+
367
+ # This sets the background attribute of the widget.
368
+ def setBKattr(attrib)
369
+ @win.wbkgd(attrib)
370
+ @list_win.wbkgd(attrib)
371
+ unless @scrollbar_win.nil?
372
+ @scrollbar_win.wbkgd(attrib)
373
+ end
374
+ end
375
+
376
+ # This function destroys
377
+ def destroy
378
+ self.cleanTitle
379
+
380
+ # Clean up the windows.
381
+ CDK.deleteCursesWindow(@scrollbar_win)
382
+ CDK.deleteCursesWindow(@shadow_win)
383
+ CDK.deleteCursesWindow(@list_win)
384
+ CDK.deleteCursesWindow(@win)
385
+
386
+ # Clean the key bindings.
387
+ self.cleanBindings(:SCROLL)
388
+
389
+ # Unregister this object
390
+ CDK::SCREEN.unregister(:SCROLL, self)
391
+ end
392
+
393
+ # This function erases the scrolling list from the screen.
394
+ def erase
395
+ CDK.eraseCursesWindow(@win)
396
+ CDK.eraseCursesWindow(@shadow_win)
397
+ end
398
+
399
+ def allocListArrays(old_size, new_size)
400
+ result = true
401
+ new_list = Array.new(new_size)
402
+ new_len = Array.new(new_size)
403
+ new_pos = Array.new(new_size)
404
+
405
+ (0...old_size).each do |n|
406
+ new_list[n] = @item[n]
407
+ new_len[n] = @item_len[n]
408
+ new_pos[n] = @item_pos[n]
409
+ end
410
+
411
+ @item = new_list
412
+ @item_len = new_len
413
+ @item_pos = new_pos
414
+
415
+ return result
416
+ end
417
+
418
+ def allocListItem(which, work, used, number, value)
419
+ if number > 0
420
+ value = "%4d. %s" % [number, value]
421
+ end
422
+
423
+ item_len = []
424
+ item_pos = []
425
+ @item[which] = CDK.char2Chtype(value, item_len, item_pos)
426
+ @item_len[which] = item_len[0]
427
+ @item_pos[which] = item_pos[0]
428
+
429
+ @item_pos[which] = CDK.justifyString(@box_width,
430
+ @item_len[which], @item_pos[which])
431
+ return true
432
+ end
433
+
434
+ # This function creates the scrolling list information and sets up the
435
+ # needed variables for the scrolling list to work correctly.
436
+ def createItemList(numbers, list, list_size)
437
+ status = 0
438
+ if list_size > 0
439
+ widest_item = 0
440
+ x = 0
441
+ have = 0
442
+ temp = ''
443
+ if allocListArrays(0, list_size)
444
+ # Create the items in the scrolling list.
445
+ status = 1
446
+ (0...list_size).each do |x|
447
+ number = if numbers then x + 1 else 0 end
448
+ if !self.allocListItem(x, temp, have, number, list[x])
449
+ status = 0
450
+ break
451
+ end
452
+
453
+ widest_item = [@item_len[x], widest_item].max
454
+ end
455
+
456
+ if status
457
+ self.updateViewWidth(widest_item);
458
+
459
+ # Keep the boolean flag 'numbers'
460
+ @numbers = numbers
461
+ end
462
+ end
463
+ else
464
+ status = 1 # null list is ok - for a while
465
+ end
466
+
467
+ return status
468
+ end
469
+
470
+ # This sets certain attributes of the scrolling list.
471
+ def set(list, list_size, numbers, highlight, box)
472
+ self.setItems(list, list_size, numbers)
473
+ self.setHighlight(highlight)
474
+ self.setBox(box)
475
+ end
476
+
477
+ # This sets the scrolling list items
478
+ def setItems(list, list_size, numbers)
479
+ if self.createItemList(numbers, list, list_size) <= 0
480
+ return
481
+ end
482
+
483
+ # Clean up the display.
484
+ (0...@view_size).each do |x|
485
+ Draw.writeBlanks(@win, 1, x, CDK::HORIZONTAL, 0, @box_width - 2);
486
+ end
487
+
488
+ self.setViewSize(list_size)
489
+ self.setPosition(0)
490
+ @left_char = 0
491
+ end
492
+
493
+ def getItems(list)
494
+ (0...@list_size).each do |x|
495
+ list << CDK.chtype2Char(@item[x])
496
+ end
497
+
498
+ return @list_size
499
+ end
500
+
501
+ # This sets the highlight of the scrolling list.
502
+ def setHighlight(highlight)
503
+ @highlight = highlight
504
+ end
505
+
506
+ def getHighlight(highlight)
507
+ return @highlight
508
+ end
509
+
510
+ # Resequence the numbers after an insertion/deletion.
511
+ def resequence
512
+ if @numbers
513
+ (0...@list_size).each do |j|
514
+ target = @item[j]
515
+
516
+ source = "%4d. %s" % [j + 1, ""]
517
+
518
+ k = 0
519
+ while k < source.size
520
+ # handle deletions that change the length of number
521
+ if source[k] == "." && target[k] != "."
522
+ source = source[0...k] + source[k+1..-1]
523
+ end
524
+
525
+ target[k] &= Ncurses::A_ATTRIBUTES
526
+ target[k] |= source[k].ord
527
+ k += 1
528
+ end
529
+ end
530
+ end
531
+ end
532
+
533
+ def insertListItem(item)
534
+ @item = @item[0..item] + @item[item..-1]
535
+ @item_len = @item_len[0..item] + @item_len[item..-1]
536
+ @item_pos = @item_pos[0..item] + @item_pos[item..-1]
537
+ return true
538
+ end
539
+
540
+ # This adds a single item to a scrolling list, at the end of the list.
541
+ def addItem(item)
542
+ item_number = @list_size
543
+ widest_item = self.WidestItem
544
+ temp = ''
545
+ have = 0
546
+
547
+ if self.allocListArrays(@list_size, @list_size + 1) &&
548
+ self.allocListItem(item_number, temp, have,
549
+ if @numbers then item_number + 1 else 0 end,
550
+ item)
551
+ # Determine the size of the widest item.
552
+ widest_item = [@item_len[item_number], widest_item].max
553
+
554
+ self.updateViewWidth(widest_item)
555
+ self.setViewSize(@list_size + 1)
556
+ end
557
+ end
558
+
559
+ # This adds a single item to a scrolling list before the current item
560
+ def insertItem(item)
561
+ widest_item = self.WidestItem
562
+ temp = ''
563
+ have = 0
564
+
565
+ if self.allocListArrays(@list_size, @list_size + 1) &&
566
+ self.insertListItem(@current_item) &&
567
+ self.allocListItem(@current_item, temp, have,
568
+ if @numbers then @current_item + 1 else 0 end,
569
+ item)
570
+ # Determine the size of the widest item.
571
+ widest_item = [@item_len[@current_item], widest_item].max
572
+
573
+ self.updateViewWidth(widest_item)
574
+ self.setViewSize(@list_size + 1)
575
+ self.resequence
576
+ end
577
+ end
578
+
579
+ # This removes a single item from a scrolling list.
580
+ def deleteItem(position)
581
+ if position >= 0 && position < @list_size
582
+ # Adjust the list
583
+ @item = @item[0...position] + @item[position+1..-1]
584
+ @item_len = @item_len[0...position] + @item_len[position+1..-1]
585
+ @item_pos = @item_pos[0...position] + @item_pos[position+1..-1]
586
+
587
+ self.setViewSize(@list_size - 1)
588
+
589
+ if @list_size > 0
590
+ self.resequence
591
+ end
592
+
593
+ if @list_size < self.maxViewSize
594
+ @win.werase # force the next redraw to be complete
595
+ end
596
+
597
+ # do this to update the view size, etc
598
+ self.setPosition(@current_item)
599
+ end
600
+ end
601
+
602
+ def focus
603
+ self.drawCurrent
604
+ @list_win.wrefresh
605
+ end
606
+
607
+ def unfocus
608
+ self.drawCurrent
609
+ @list_win.wrefresh
610
+ end
611
+
612
+ def AvailableWidth
613
+ @box_width - (2 * @border_size)
614
+ end
615
+
616
+ def updateViewWidth(widest)
617
+ @max_left_char = if @box_width > widest
618
+ then 0
619
+ else widest - self.AvailableWidth
620
+ end
621
+ end
622
+
623
+ def WidestItem
624
+ @max_left_char + self.AvailableWidth
625
+ end
626
+ end
627
+
628
+ class BUTTON < CDK::CDKOBJS
629
+ def initialize(cdkscreen, xplace, yplace, text, callback, box, shadow)
630
+ super()
631
+ parent_width = cdkscreen.window.getmaxx
632
+ parent_height = cdkscreen.window.getmaxy
633
+ box_width = 0
634
+ xpos = xplace
635
+ ypos = yplace
636
+
637
+ self.setBox(box)
638
+ box_height = 1 + 2 * @border_size
639
+
640
+ # Translate the string to a chtype array.
641
+ info_len = []
642
+ info_pos = []
643
+ @info = CDK.char2Chtype(text, info_len, info_pos)
644
+ @info_len = info_len[0]
645
+ @info_pos = info_pos[0]
646
+ box_width = [box_width, @info_len].max + 2 * @border_size
647
+
648
+ # Create the string alignments.
649
+ @info_pos = CDK.justifyString(box_width - 2 * @border_size,
650
+ @info_len, @info_pos)
651
+
652
+ # Make sure we didn't extend beyond the dimensions of the window.
653
+ box_width = if box_width > parent_width
654
+ then parent_width
655
+ else box_width
656
+ end
657
+ box_height = if box_height > parent_height
658
+ then parent_height
659
+ else box_height
660
+ end
661
+
662
+ # Rejustify the x and y positions if we need to.
663
+ xtmp = [xpos]
664
+ ytmp = [ypos]
665
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
666
+ xpos = xtmp[0]
667
+ ypos = ytmp[0]
668
+
669
+ # Create the button.
670
+ @screen = cdkscreen
671
+ # ObjOf (button)->fn = &my_funcs;
672
+ @parent = cdkscreen.window
673
+ @win = Ncurses::WINDOW.new(box_height, box_width, ypos, xpos)
674
+ @shadow_win = nil
675
+ @xpos = xpos
676
+ @ypos = ypos
677
+ @box_width = box_width
678
+ @box_height = box_height
679
+ @callback = callback
680
+ @input_window = @win
681
+ @accepts_focus = true
682
+ @shadow = shadow
683
+
684
+ if @win.nil?
685
+ self.destroy
686
+ return nil
687
+ end
688
+
689
+ @win.keypad(true)
690
+
691
+ # If a shadow was requested, then create the shadow window.
692
+ if shadow
693
+ @shadow_win = Ncurses::WINDOW.new(box_height, box_width,
694
+ ypos + 1, xpos + 1)
695
+ end
696
+
697
+ # Register this baby.
698
+ cdkscreen.register(:BUTTON, self)
699
+ end
700
+
701
+ # This was added for the builder.
702
+ def activate(actions)
703
+ self.draw(@box)
704
+ ret = -1
705
+
706
+ if actions.nil? || actions.size == 0
707
+ while true
708
+ input = self.getch([])
709
+
710
+ # Inject the character into the widget.
711
+ ret = self.inject(input)
712
+ if @exit_type != :EARLY_EXIT
713
+ return ret
714
+ end
715
+ end
716
+ else
717
+ # Inject each character one at a time.
718
+ actions.each do |x|
719
+ ret = self.inject(action)
720
+ if @exit_type == :EARLY_EXIT
721
+ return ret
722
+ end
723
+ end
724
+ end
725
+
726
+ # Set the exit type and exit
727
+ self.setExitType(0)
728
+ return -1
729
+ end
730
+
731
+ # This sets multiple attributes of the widget.
732
+ def set(mesg, box)
733
+ self.setMessage(mesg)
734
+ self.setBox(box)
735
+ end
736
+
737
+ # This sets the information within the button.
738
+ def setMessage(info)
739
+ info_len = []
740
+ info_pos = []
741
+ @info = CDK.char2Chtype(info, info_len, info_pos)
742
+ @info_len = info_len[0]
743
+ @info_pos = CDK.justifyString(@box_width - 2 * @border_size,
744
+ info_pos[0])
745
+
746
+ # Redraw the button widget.
747
+ self.erase
748
+ self.draw(box)
749
+ end
750
+
751
+ def getMessage
752
+ return @info
753
+ end
754
+
755
+ # This sets the background attribute of the widget.
756
+ def setBKattr(attrib)
757
+ @win.wbkgd(attrib)
758
+ end
759
+
760
+ def drawText
761
+ box_width = @box_width
762
+
763
+ # Draw in the message.
764
+ (0...(box_width - 2 * @border_size)).each do |i|
765
+ pos = @info_pos
766
+ len = @info_len
767
+ if i >= pos && (i - pos) < len
768
+ c = @info[i - pos]
769
+ else
770
+ c = ' '
771
+ end
772
+
773
+ if @has_focus
774
+ c = Ncurses::A_REVERSE | c
775
+ end
776
+
777
+ @win.mvwaddch(@border_size, i + @border_size, c)
778
+ end
779
+ end
780
+
781
+ # This draws the button widget
782
+ def draw(box)
783
+ # Is there a shadow?
784
+ unless @shadow_win.nil?
785
+ Draw.drawShadow(@shadow_win)
786
+ end
787
+
788
+ # Box the widget if asked.
789
+ if @box
790
+ Draw.drawObjBox(@win, self)
791
+ end
792
+ self.drawText
793
+ @win.wrefresh
794
+ end
795
+
796
+ # This erases the button widget.
797
+ def erase
798
+ if self.validCDKObject
799
+ CDK.eraseCursesWindow(@win)
800
+ CDK.eraseCursesWindow(@shadow_win)
801
+ end
802
+ end
803
+
804
+ # This moves the button field to the given location.
805
+ def move(xplace, yplace, relative, refresh_flag)
806
+ current_x = @win.getbegx
807
+ current_y = @win.getbegy
808
+ xpos = xplace
809
+ ypos = yplace
810
+
811
+ # If this is a relative move, then we will adjust where we want
812
+ # to move to.
813
+ if relative
814
+ xpos = @win.getbegx + xplace
815
+ ypos = @win.getbegy + yplace
816
+ end
817
+
818
+ # Adjust the window if we need to.
819
+ xtmp = [xpos]
820
+ ytmp = [ypos]
821
+ CDK.alignxy(@screen.window, xtmp, ytmp, @box_width, @box_height)
822
+ xpos = xtmp[0]
823
+ ypos = ytmp[0]
824
+
825
+ # Get the difference
826
+ xdiff = current_x - xpos
827
+ ydiff = current_y - ypos
828
+
829
+ # Move the window to the new location.
830
+ CDK.moveCursesWindow(@win, -xdiff, -ydiff)
831
+ CDK.moveCursesWindow(@shadow_win, -xdiff, -ydiff)
832
+
833
+ # Thouch the windows so they 'move'.
834
+ CDK::SCREEN.refreshCDKWindow(@screen.window)
835
+
836
+ # Redraw the window, if they asked for it.
837
+ if refresh_flag
838
+ self.draw(@box)
839
+ end
840
+ end
841
+
842
+ # This allows the user to use the cursor keys to adjust the
843
+ # position of the widget.
844
+ def position
845
+ # Declare some variables
846
+ orig_x = @win.getbegx
847
+ orig_y = @win.getbegy
848
+ key = 0
849
+
850
+ # Let them move the widget around until they hit return
851
+ while key != Ncurses::KEY_ENTER && key != CDK::KEY_RETURN
852
+ key = self.getch([])
853
+ if key == Ncurses::KEY_UP || key == '8'.ord
854
+ if @win.getbegy > 0
855
+ self.move(0, -1, true, true)
856
+ else
857
+ CDK.Beep
858
+ end
859
+ elsif key == Ncurses::KEY_DOWN || key == '2'.ord
860
+ if @win.getbegy + @win.getmaxy < @screen.window.getmaxy - 1
861
+ self.move(0, 1, true, true)
862
+ else
863
+ CDK.Beep
864
+ end
865
+ elsif key == Ncurses::KEY_LEFT || key == '4'.ord
866
+ if @win.getbegx > 0
867
+ self.move(-1, 0, true, true)
868
+ else
869
+ CDK.Beep
870
+ end
871
+ elsif key == Ncurses::KEY_RIGHT || key == '6'.ord
872
+ if @win.getbegx + @win.getmaxx < @screen.window.getmaxx - 1
873
+ self.move(1, 0, true, true)
874
+ else
875
+ CDK.Beep
876
+ end
877
+ elsif key == '7'.ord
878
+ if @win.getbegy > 0 && @win.getbegx > 0
879
+ self.move(-1, -1, true, true)
880
+ else
881
+ CDK.Beep
882
+ end
883
+ elsif key == '9'.ord
884
+ if @win.getbegx + @win.getmaxx < @screen.window.getmaxx - 1 &&
885
+ @win.getbegy > 0
886
+ self.move(1, -1, true, true)
887
+ else
888
+ CDK.Beep
889
+ end
890
+ elsif key == '1'.ord
891
+ if @win.getbegx > 0 &&
892
+ @win.getbegx + @win.getmaxx < @screen.window.getmaxx - 1
893
+ self.move(-1, 1, true, true)
894
+ else
895
+ CDK.Beep
896
+ end
897
+ elsif key == '3'.ord
898
+ if @win.getbegx + @win.getmaxx < @screen.window.getmaxx - 1 &&
899
+ @win.getbegy + @win.getmaxy < @screen.window.getmaxy - 1
900
+ self.move(1, 1, true, true)
901
+ else
902
+ CDK.Beep
903
+ end
904
+ elsif key == '5'.ord
905
+ self.move(CDK::CENTER, CDK::CENTER, false, true)
906
+ elsif key == 't'.ord
907
+ self.move(@win.getbegx, CDK::TOP, false, true)
908
+ elsif key == 'b'.ord
909
+ self.move(@win.getbegx, CDK::BOTTOM, false, true)
910
+ elsif key == 'l'.ord
911
+ self.move(CDK::LEFT, @win.getbegy, false, true)
912
+ elsif key == 'r'
913
+ self.move(CDK::RIGHT, @win.getbegy, false, true)
914
+ elsif key == 'c'
915
+ self.move(CDK::CENTER, @win.getbegy, false, true)
916
+ elsif key == 'C'
917
+ self.move(@win.getbegx, CDK::CENTER, false, true)
918
+ elsif key == CDK::REFRESH
919
+ @screen.erase
920
+ @screen.refresh
921
+ elsif key == CDK::KEY_ESC
922
+ self.move(orig_x, orig_y, false, true)
923
+ elsif key != CDK::KEY_RETURN && key != Ncurses::KEY_ENTER
924
+ CDK.Beep
925
+ end
926
+ end
927
+ end
928
+
929
+ # This destroys the button object pointer.
930
+ def destroy
931
+ CDK.deleteCursesWindow(@shadow_win)
932
+ CDK.deleteCursesWindow(@win)
933
+
934
+ self.cleanBindings(:BUTTON)
935
+
936
+ CDK::SCREEN.unregister(:BUTTON, self)
937
+ end
938
+
939
+ # This injects a single character into the widget.
940
+ def inject(input)
941
+ ret = -1
942
+ complete = false
943
+
944
+ self.setExitType(0)
945
+
946
+ # Check a predefined binding.
947
+ if self.checkBind(:BUTTON, input)
948
+ complete = true
949
+ else
950
+ case input
951
+ when CDK::KEY_ESC
952
+ self.setExitType(input)
953
+ complete = true
954
+ when Ncurses::ERR
955
+ self.setExitType(input)
956
+ complete = true
957
+ when ' '.ord, CDK::KEY_RETURN, Ncurses::KEY_ENTER
958
+ unless @callback.nil?
959
+ @callback.call(self)
960
+ end
961
+ self.setExitType(Ncurses::KEY_ENTER)
962
+ ret = 0
963
+ complete = true
964
+ when CDK::REFRESH
965
+ @screen.erase
966
+ @screen.refresh
967
+ else
968
+ CDK.Beep
969
+ end
970
+ end
971
+
972
+ unless complete
973
+ self.setExitType(0)
974
+ end
975
+
976
+ @result_data = ret
977
+ return ret
978
+ end
979
+
980
+ def focus
981
+ self.drawText
982
+ @win.wrefresh
983
+ end
984
+
985
+ def unfocus
986
+ self.drawText
987
+ @win.wrefresh
988
+ end
989
+
990
+ def object_type
991
+ :BUTTON
992
+ end
993
+ end
994
+ end