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/menu.rb ADDED
@@ -0,0 +1,448 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class MENU < CDK::CDKOBJS
5
+ TITLELINES = 1
6
+ MAX_MENU_ITEMS = 30
7
+ MAX_SUB_ITEMS = 98
8
+
9
+ attr_reader :current_title, :current_subtitle
10
+ attr_reader :sublist
11
+
12
+ def initialize(cdkscreen, menu_list, menu_items, subsize,
13
+ menu_location, menu_pos, title_attr, subtitle_attr)
14
+ super()
15
+
16
+ right_count = menu_items - 1
17
+ rightloc = cdkscreen.window.maxx
18
+ leftloc = 0
19
+ xpos = cdkscreen.window.begx
20
+ ypos = cdkscreen.window.begy
21
+ ymax = cdkscreen.window.maxy
22
+
23
+ # Start making a copy of the information.
24
+ @screen = cdkscreen
25
+ @box = false
26
+ @accepts_focus = false
27
+ rightcount = menu_items - 1
28
+ @parent = cdkscreen.window
29
+ @menu_items = menu_items
30
+ @title_attr = title_attr
31
+ @subtitle_attr = subtitle_attr
32
+ @current_title = 0
33
+ @current_subtitle = 0
34
+ @last_selection = -1
35
+ @menu_pos = menu_pos
36
+
37
+ @pull_win = [nil] * menu_items
38
+ @title_win = [nil] * menu_items
39
+ @title = [''] * menu_items
40
+ @title_len = [0] * menu_items
41
+ @sublist = (1..menu_items).map {[nil] * subsize.max}.compact
42
+ @sublist_len = (1..menu_items).map {
43
+ [0] * subsize.max}.compact
44
+ @subsize = [0] * menu_items
45
+
46
+
47
+ # Create the pull down menus.
48
+ (0...menu_items).each do |x|
49
+ x1 = if menu_location[x] == CDK::LEFT
50
+ then x
51
+ else
52
+ rightcount -= 1
53
+ rightcount + 1
54
+ end
55
+ x2 = 0
56
+ y1 = if menu_pos == CDK::BOTTOM then ymax - 1 else 0 end
57
+ y2 = if menu_pos == CDK::BOTTOM
58
+ then ymax - subsize[x] - 2
59
+ else CDK::MENU::TITLELINES
60
+ end
61
+ high = subsize[x] + CDK::MENU::TITLELINES
62
+
63
+ # Limit the menu height to fit on the screen.
64
+ if high + y2 > ymax
65
+ high = ymax - CDK::MENU::TITLELINES
66
+ end
67
+
68
+ max = -1
69
+ (CDK::MENU::TITLELINES...subsize[x]).to_a.each do |y|
70
+ y0 = y - CDK::MENU::TITLELINES
71
+ sublist_len = []
72
+ @sublist[x1][y0] = CDK.char2Chtype(menu_list[x][y],
73
+ sublist_len, [])
74
+ @sublist_len[x1][y0] = sublist_len[0]
75
+ max = [max, sublist_len[0]].max
76
+ end
77
+
78
+ if menu_location[x] == CDK::LEFT
79
+ x2 = leftloc
80
+ else
81
+ x2 = (rightloc -= max + 2)
82
+ end
83
+
84
+ title_len = []
85
+ @title[x1] = CDK.char2Chtype(menu_list[x][0], title_len, [])
86
+ @title_len[x1] = title_len[0]
87
+ @subsize[x1] = subsize[x] - CDK::MENU::TITLELINES
88
+ @title_win[x1] = cdkscreen.window.subwin(CDK::MENU::TITLELINES,
89
+ @title_len[x1] + 2, ypos + y1, xpos + x2)
90
+ @pull_win[x1] = cdkscreen.window.subwin(high, max + 2,
91
+ ypos + y2, xpos + x2)
92
+ if @title_win[x1].nil? || @pull_win[x1].nil?
93
+ self.destroy
94
+ return nil
95
+ end
96
+
97
+ leftloc += @title_len[x] + 1
98
+ @title_win[x1].keypad(true)
99
+ @pull_win[x1].keypad(true)
100
+ end
101
+ @input_window = @title_win[@current_title]
102
+
103
+ # Register this baby.
104
+ cdkscreen.register(:MENU, self)
105
+ end
106
+
107
+ # This activates the CDK Menu
108
+ def activate(actions)
109
+ ret = 0
110
+
111
+ # Draw in the screen.
112
+ @screen.refresh
113
+
114
+ # Display the menu titles.
115
+ self.draw(@box)
116
+
117
+ # Highlight the current title and window.
118
+ self.drawSubwin
119
+
120
+ # If the input string is empty this is an interactive activate.
121
+ if actions.nil? || actions.size == 0
122
+ @input_window = @title_win[@current_title]
123
+
124
+ # Start taking input from the keyboard.
125
+ while true
126
+ input = self.getch([])
127
+
128
+ # Inject the character into the widget.
129
+ ret = self.inject(input)
130
+ if @exit_type != :EARLY_EXIT
131
+ return ret
132
+ end
133
+ end
134
+ else
135
+ actions.each do |action|
136
+ if @exit_type != :EARLY_EXIT
137
+ return ret
138
+ end
139
+ end
140
+ end
141
+
142
+ # Set the exit type and return.
143
+ self.setExitType(0)
144
+ return -1
145
+ end
146
+
147
+ def drawTitle(item)
148
+ Draw.writeChtype(@title_win[item], 0, 0, @title[item],
149
+ CDK::HORIZONTAL, 0, @title_len[item])
150
+ end
151
+
152
+ def drawItem(item, offset)
153
+ Draw.writeChtype(@pull_win[@current_title], 1,
154
+ item + CDK::MENU::TITLELINES - offset,
155
+ @sublist[@current_title][item],
156
+ CDK::HORIZONTAL, 0, @sublist_len[@current_title][item])
157
+ end
158
+
159
+ # Highlight the current sub-menu item
160
+ def selectItem(item, offset)
161
+ Draw.writeChtypeAttrib(@pull_win[@current_title], 1,
162
+ item + CDK::MENU::TITLELINES - offset,
163
+ @sublist[@current_title][item], @subtitle_attr,
164
+ CDK::HORIZONTAL, 0, @sublist_len[@current_title][item])
165
+ end
166
+
167
+ def withinSubmenu(step)
168
+ next_item = CDK::MENU.wrapped(@current_subtitle + step,
169
+ @subsize[@current_title])
170
+
171
+ if next_item != @current_subtitle
172
+ ymax = @screen.window.maxy
173
+
174
+ if 1 + @pull_win[@current_title].begy + @subsize[@current_title] >=
175
+ ymax
176
+ @current_subtitle = next_item
177
+ self.drawSubwin
178
+ else
179
+ # Erase the old subtitle.
180
+ self.drawItem(@current_subtitle, 0)
181
+
182
+ # Set the values
183
+ @current_subtitle = next_item
184
+
185
+ # Draw the new sub-title.
186
+ self.selectItem(@current_subtitle, 0)
187
+
188
+ @pull_win[@current_title].refresh
189
+ end
190
+
191
+ @input_window = @title_win[@current_title]
192
+ end
193
+ end
194
+
195
+ def acrossSubmenus(step)
196
+ next_item = CDK::MENU.wrapped(@current_title + step, @menu_items)
197
+
198
+ if next_item != @current_title
199
+ # Erase the menu sub-window.
200
+ self.eraseSubwin
201
+ @screen.refresh
202
+
203
+ # Set the values.
204
+ @current_title = next_item
205
+ @current_subtitle = 0
206
+
207
+ # Draw the new menu sub-window.
208
+ self.drawSubwin
209
+ @input_window = @title_win[@current_title]
210
+ end
211
+ end
212
+
213
+ # Inject a character into the menu widget.
214
+ def inject(input)
215
+ pp_return = 1
216
+ ret = -1
217
+ complete = false
218
+
219
+ # Set the exit type.
220
+ self.setExitType(0)
221
+
222
+ # Check if there is a pre-process function to be called.
223
+ unless @pre_process_func.nil?
224
+ # Call the pre-process function.
225
+ pp_return = @pre_process_func.call(:MENU, self,
226
+ @pre_process_data, input)
227
+ end
228
+
229
+ # Should we continue?
230
+
231
+ if pp_return != 0
232
+ # Check for key bindings.
233
+ if self.checkBind(:MENU, input)
234
+ complete = true
235
+ else
236
+ case input
237
+ when Curses::KEY_LEFT
238
+ self.acrossSubmenus(-1)
239
+ when Curses::KEY_RIGHT, CDK::KEY_TAB
240
+ self.acrossSubmenus(1)
241
+ when Curses::KEY_UP
242
+ self.withinSubmenu(-1)
243
+ when Curses::KEY_DOWN, ' '
244
+ self.withinSubmenu(1)
245
+ when Curses::KEY_ENTER, CDK::KEY_RETURN
246
+ self.cleanUpMenu
247
+ self.setExitType(input)
248
+ @last_selection = @current_title * 100 + @current_subtitle
249
+ ret = @last_selection
250
+ complete = true
251
+ when CDK::KEY_ESC
252
+ self.cleanUpMenu
253
+ self.setExitType(input)
254
+ @last_selection = -1
255
+ ret = @last_selection
256
+ complete = true
257
+ when Curses::Error
258
+ self.setExitType(input)
259
+ complete = true
260
+ when CDK::REFRESH
261
+ self.erase
262
+ self.refresh
263
+ end
264
+ end
265
+
266
+ # Should we call a post-process?
267
+ if !complete && !(@post_process_func.nil?)
268
+ @post_process_func.call(:MENU, self, @post_process_data, input)
269
+ end
270
+ end
271
+
272
+ if !complete
273
+ self.setExitType(0)
274
+ end
275
+
276
+ @result_data = ret
277
+ return ret
278
+ end
279
+
280
+ # Draw a menu item subwindow
281
+ def drawSubwin
282
+ high = @pull_win[@current_title].maxy - 2
283
+ x0 = 0
284
+ x1 = @subsize[@current_title]
285
+
286
+ if x1 > high
287
+ x1 = high
288
+ end
289
+
290
+ if @current_subtitle >= x1
291
+ x0 = @current_subtitle - x1 + 1
292
+ x1 += x0
293
+ end
294
+
295
+ # Box the window
296
+ @pull_win[@current_title]
297
+ @pull_win[@current_title].box(CDK::ACS_VLINE, CDK::ACS_HLINE)
298
+ if @menu_pos == CDK::BOTTOM
299
+ @pull_win[@current_title].mvwaddch(@subsize[@current_title] + 1,
300
+ 0, CDK::ACS_LTEE)
301
+ else
302
+ @pull_win[@current_title].mvwaddch(0, 0, CDK::ACS_LTEE)
303
+ end
304
+
305
+ # Draw the items.
306
+ (x0...x1).each do |x|
307
+ self.drawItem(x, x0)
308
+ end
309
+
310
+ self.selectItem(@current_subtitle, x0)
311
+ @pull_win[@current_title].refresh
312
+
313
+ # Highlight the title.
314
+ Draw.writeChtypeAttrib(@title_win[@current_title], 0, 0,
315
+ @title[@current_title], @title_attr, CDK::HORIZONTAL,
316
+ 0, @title_len[@current_title])
317
+ @title_win[@current_title].refresh
318
+ end
319
+
320
+ # Erase a menu item subwindow
321
+ def eraseSubwin
322
+ CDK.eraseCursesWindow(@pull_win[@current_title])
323
+
324
+ # Redraw the sub-menu title.
325
+ self.drawTitle(@current_title)
326
+ @title_win[@current_title].refresh
327
+ end
328
+
329
+ # Draw the menu.
330
+ def draw(box)
331
+ # Draw in the menu titles.
332
+ (0...@menu_items).each do |x|
333
+ self.drawTitle(x)
334
+ @title_win[x].refresh
335
+ end
336
+ end
337
+
338
+ # Move the menu to the given location.
339
+ def move(xplace, yplace, relative, refresh_flag)
340
+ windows = [@screen.window]
341
+ (0...@menu_items).each do |x|
342
+ windows << @title_win[x]
343
+ end
344
+ self.move_specific(xplace, yplace, relative, refresh_flag,
345
+ windows, [])
346
+ end
347
+
348
+ # Set the background attribute of the widget.
349
+ def setBKattr(attrib)
350
+ (0...@menu_items).each do |x|
351
+ @title_win[x].wbkgd(attrib)
352
+ @pull_win[x].wbkgd(attrib)
353
+ end
354
+ end
355
+
356
+ # Destroy a menu widget.
357
+ def destroy
358
+ # Clean up the windows
359
+ (0...@menu_items).each do |x|
360
+ CDK.deleteCursesWindow(@title_win[x])
361
+ CDK.deleteCursesWindow(@pull_win[x])
362
+ end
363
+
364
+ # Clean the key bindings.
365
+ self.cleanBindings(:MENU)
366
+
367
+ # Unregister the object
368
+ CDK::SCREEN.unregister(:MENU, self)
369
+ end
370
+
371
+ # Erase the menu widget from the screen.
372
+ def erase
373
+ if self.validCDKObject
374
+ (0...@menu_items).each do |x|
375
+ @title_win[x].erase
376
+ @title_win[x].refresh
377
+ @pull_win[x].erase
378
+ @pull_win[x].refresh
379
+ end
380
+ end
381
+ end
382
+
383
+ def set(menu_item, submenu_item, title_highlight, subtitle_highlight)
384
+ self.setCurrentItem(menu_item, submenu_item)
385
+ self.setTitleHighlight(title_highlight)
386
+ self.setSubTitleHighlight(subtitle_highlight)
387
+ end
388
+
389
+ # Set the current menu item to highlight.
390
+ def setCurrentItem(menuitem, submenuitem)
391
+ @current_title = CDK::MENU.wrapped(menuitem, @menu_items)
392
+ @current_subtitle = CDK::MENU.wrapped(
393
+ submenuitem, @subsize[@current_title])
394
+ end
395
+
396
+ def getCurrentItem(menu_item, submenu_item)
397
+ menu_item << @current_title
398
+ submenu_item << @current_subtitle
399
+ end
400
+
401
+ # Set the attribute of the menu titles.
402
+ def setTitleHighlight(highlight)
403
+ @title_attr = highlight
404
+ end
405
+
406
+ def getTitleHighlight
407
+ return @title_attr
408
+ end
409
+
410
+ # Set the attribute of the sub-title.
411
+ def setSubTitleHighlight(highlight)
412
+ @subtitle_attr = highlight
413
+ end
414
+
415
+ def getSubTitleHighlight
416
+ return @subtitle_attr
417
+ end
418
+
419
+ # Exit the menu.
420
+ def cleanUpMenu
421
+ # Erase the sub-menu.
422
+ self.eraseSubwin
423
+ @pull_win[@current_title].refresh
424
+
425
+ # Refresh the screen.
426
+ @screen.refresh
427
+ end
428
+
429
+ def focus
430
+ self.drawSubwin
431
+ @input_window = @title_win[@current_title]
432
+ end
433
+
434
+ # The "%" operator is simpler but does not handle negative values
435
+ def self.wrapped(within, limit)
436
+ if within < 0
437
+ within = limit - 1
438
+ elsif within >= limit
439
+ within = 0
440
+ end
441
+ return within
442
+ end
443
+
444
+ def object_type
445
+ :MENU
446
+ end
447
+ end
448
+ end