cdk 0.9.0

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.
@@ -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