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,762 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class SWINDOW < CDK::CDKOBJS
5
+ def initialize(cdkscreen, xplace, yplace, height, width, title,
6
+ save_lines, box, shadow)
7
+ super()
8
+ parent_width = cdkscreen.window.getmaxx
9
+ parent_height = cdkscreen.window.getmaxy
10
+ box_width = width
11
+ box_height = height
12
+ bindings = {
13
+ CDK::BACKCHAR => Ncurses::KEY_PPAGE,
14
+ 'b' => Ncurses::KEY_PPAGE,
15
+ 'B' => Ncurses::KEY_PPAGE,
16
+ CDK::FORCHAR => Ncurses::KEY_NPAGE,
17
+ ' ' => Ncurses::KEY_NPAGE,
18
+ 'f' => Ncurses::KEY_NPAGE,
19
+ 'F' => Ncurses::KEY_NPAGE,
20
+ '|' => Ncurses::KEY_HOME,
21
+ '$' => Ncurses::KEY_END,
22
+ }
23
+
24
+ self.setBox(box)
25
+
26
+ # If the height is a negative value, the height will be
27
+ # ROWS-height, otherwise the height will be the given height.
28
+ box_height = CDK.setWidgetDimension(parent_height, height, 0)
29
+
30
+ # If the width is a negative value, the width will be
31
+ # COLS-width, otherwise the widget will be the given width.
32
+ box_width = CDK.setWidgetDimension(parent_width, width, 0)
33
+ box_width = self.setTitle(title, box_width)
34
+
35
+ # Set the box height.
36
+ box_height += @title_lines + 1
37
+
38
+ # Make sure we didn't extend beyond the dimensions of the window.
39
+ box_width = [box_width, parent_width].min
40
+ box_height = [box_height, parent_height].min
41
+
42
+ # Set the rest of the variables.
43
+ @title_adj = @title_lines + 1
44
+
45
+ # Rejustify the x and y positions if we need to.
46
+ xtmp = [xplace]
47
+ ytmp = [yplace]
48
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
49
+ xpos = xtmp[0]
50
+ ypos = ytmp[0]
51
+
52
+ # Make the scrolling window.
53
+ @win = Ncurses::WINDOW.new(box_height, box_width, ypos, xpos)
54
+ if @win.nil?
55
+ self.destroy
56
+ return nil
57
+ end
58
+ @win.keypad(true)
59
+
60
+ # Make the field window
61
+ @field_win = @win.subwin(box_height - @title_lines - 2, box_width - 2,
62
+ ypos + @title_lines + 1, xpos + 1)
63
+ @field_win.keypad(true)
64
+
65
+ # Set the rest of the variables
66
+ @screen = cdkscreen
67
+ @parent = cdkscreen.window
68
+ @shadow_win = nil
69
+ @box_height = box_height
70
+ @box_width = box_width
71
+ @view_size = box_height - @title_lines - 2
72
+ @current_top = 0
73
+ @max_top_line = 0
74
+ @left_char = 0
75
+ @max_left_char = 0
76
+ @list_size = 0
77
+ @widest_line = -1
78
+ @save_lines = save_lines
79
+ @accepts_focus = true
80
+ @input_window = @win
81
+ @shadow = shadow
82
+
83
+ if !self.createList(save_lines)
84
+ self.destroy
85
+ return nil
86
+ end
87
+
88
+ # Do we need to create a shadow?
89
+ if shadow
90
+ @shadow_win = Ncurses::WINDOW.new(box_height, box_width,
91
+ ypos + 1, xpos + 1)
92
+ end
93
+
94
+ # Create the key bindings
95
+ bindings.each do |from, to|
96
+ self.bind(:SWINDOW, from, :getc, to)
97
+ end
98
+
99
+ # Register this baby.
100
+ cdkscreen.register(:SWINDOW, self)
101
+ end
102
+
103
+ # This sets the lines and the box attribute of the scrolling window.
104
+ def set(list, lines, box)
105
+ self.setContents(list, lines)
106
+ self.setBox(box)
107
+ end
108
+
109
+ def setupLine(list, x)
110
+ list_len = []
111
+ list_pos = []
112
+ @list[x] = CDK.char2Chtype(list, list_len, list_pos)
113
+ @list_len[x] = list_len[0]
114
+ @list_pos[x] = CDK.justifyString(@box_width, list_len[0], list_pos[0])
115
+ @widest_line = [@widest_line, @list_len[x]].max
116
+ end
117
+
118
+ # This sets all the lines inside the scrolling window.
119
+ def setContents(list, list_size)
120
+ # First let's clean all the lines in the window.
121
+ self.clean
122
+ self.createList(list_size)
123
+
124
+ # Now let's set all the lines inside the window.
125
+ (0...list_size).each do |x|
126
+ self.setupLine(list[x], x)
127
+ end
128
+
129
+ # Set some more important members of the scrolling window.
130
+ @list_size = list_size
131
+ @max_top_line = @list_size - @view_size
132
+ @max_top_line = [@max_top_line, 0].max
133
+ @max_left_char = @widest_line - (@box_width - 2)
134
+ @current_top = 0
135
+ @left_char = 0
136
+ end
137
+
138
+ def getContents(size)
139
+ size << @list_size
140
+ return @list
141
+ end
142
+
143
+ def freeLine(x)
144
+ # if x < @list_size
145
+ # @list[x] = 0
146
+ # end
147
+ end
148
+
149
+ # This adds a line to the scrolling window.
150
+ def add(list, insert_pos)
151
+ # If we are at the maximum number of save lines erase the first
152
+ # position and bump everything up one spot
153
+ if @list_size == @save_lines and @list_size > 0
154
+ @list = @list[1..-1]
155
+ @list_pos = @list_pos[1..-1]
156
+ @list_len = @list_len[1..-1]
157
+ @list_size -= 1
158
+ end
159
+
160
+ # Determine where the line is being added.
161
+ if insert_pos == CDK::TOP
162
+ # We need to 'bump' everything down one line...
163
+ @list = [@list[0]] + @list
164
+ @list_pos = [@list_pos[0]] + @list_pos
165
+ @list_len = [@list_len[0]] + @list_len
166
+
167
+ # Add it into the scrolling window.
168
+ self.setupLine(list, 0)
169
+
170
+ # set some variables.
171
+ @current_top = 0
172
+ if @list_size < @save_lines
173
+ @list_size += 1
174
+ end
175
+
176
+ # Set the maximum top line.
177
+ @max_top_line = @list_size - @view_size
178
+ @max_top_line = [@max_top_line, 0].max
179
+
180
+ @max_left_char = @widest_line - (@box_width - 2)
181
+ else
182
+ # Add to the bottom.
183
+ @list += ['']
184
+ @list_pos += [0]
185
+ @list_len += [0]
186
+ self.setupLine(list, @list_size)
187
+
188
+ @max_left_char = @widest_line - (@box_width - 2)
189
+
190
+ # Increment the item count and zero out the next row.
191
+ if @list_size < @save_lines
192
+ @list_size += 1
193
+ self.freeLine(@list_size)
194
+ end
195
+
196
+ # Set the maximum top line.
197
+ if @list_size <= @view_size
198
+ @max_top_line = 0
199
+ @current_top = 0
200
+ else
201
+ @max_top_line = @list_size - @view_size
202
+ @current_top = @max_top_line
203
+ end
204
+ end
205
+
206
+ # Draw in the list.
207
+ self.drawList(@box)
208
+ end
209
+
210
+ # This jumps to a given line.
211
+ def jumpToLine(line)
212
+ # Make sure the line is in bounds.
213
+ if line == CDK::BOTTOM || line >= @list_size
214
+ # We are moving to the last page.
215
+ @current_top = @list_size - @view_size
216
+ elsif line == TOP || line <= 0
217
+ # We are moving to the top of the page.
218
+ @current_top = 0
219
+ else
220
+ # We are moving in the middle somewhere.
221
+ if @view_size + line < @list_size
222
+ @current_top = line
223
+ else
224
+ @current_top = @list_size - @view_size
225
+ end
226
+ end
227
+
228
+ # A little sanity check to make sure we don't do something silly
229
+ if @current_top < 0
230
+ @current_top = 0
231
+ end
232
+
233
+ # Redraw the window.
234
+ self.draw(@box)
235
+ end
236
+
237
+ # This removes all the lines inside the scrolling window.
238
+ def clean
239
+ # Clean up the memory used...
240
+ (0...@list_size).each do |x|
241
+ self.freeLine(x)
242
+ end
243
+
244
+ # Reset some variables.
245
+ @list_size = 0
246
+ @max_left_char = 0
247
+ @widest_line = 0
248
+ @current_top = 0
249
+ @max_top_line = 0
250
+
251
+ # Redraw the window.
252
+ self.draw(@box)
253
+ end
254
+
255
+ # This trims lines from the scrolling window.
256
+ def trim(begin_line, end_line)
257
+ # Check the value of begin_line
258
+ if begin_line < 0
259
+ start = 0
260
+ elsif begin_line >= @list_size
261
+ start = @list_size - 1
262
+ else
263
+ start = begin_line
264
+ end
265
+
266
+ # Check the value of end_line
267
+ if end_line < 0
268
+ finish = 0
269
+ elsif end_line >= @list_size
270
+ finish = @list_size - 1
271
+ else
272
+ finish = end_line
273
+ end
274
+
275
+ # Make sure the start is lower than the end.
276
+ if start > finish
277
+ return
278
+ end
279
+
280
+ # Start nuking elements from the window
281
+ (start..finish).each do |x|
282
+ self.freeLine(x)
283
+
284
+ if x < list_size - 1
285
+ @list[x] = @list[x + 1]
286
+ @list_pos[x] = @list_pos[x + 1]
287
+ @list_len[x] = @list_len[x + 1]
288
+ end
289
+ end
290
+
291
+ # Adjust the item count correctly.
292
+ @list_size = @list_size - (end_line - begin_line) - 1
293
+
294
+ # Redraw the window.
295
+ self.draw(@box)
296
+ end
297
+
298
+ # This allows the user to play inside the scrolling window.
299
+ def activate(actions)
300
+ # Draw the scrolling list.
301
+ self.draw(@box)
302
+
303
+ if actions.nil? || actions.size == 0
304
+ while true
305
+ input = self.getch([])
306
+
307
+ # inject the character into the widget.
308
+ self.inject(input)
309
+ if @exit_type != :EARLY_EXIT
310
+ return
311
+ end
312
+ end
313
+ else
314
+ #Inject each character one at a time
315
+ actions.each do |action|
316
+ self.inject(action)
317
+ if @exit_type != :EARLY_EXIT
318
+ return
319
+ end
320
+ end
321
+ end
322
+
323
+ # Set the exit type and return.
324
+ self.setExitType(0)
325
+ end
326
+
327
+ # This injects a single character into the widget.
328
+ def inject(input)
329
+ pp_return = 1
330
+ ret = -1
331
+ complete = false
332
+
333
+ # Set the exit type.
334
+ self.setExitType(0)
335
+
336
+ # Draw the window....
337
+ self.draw(@box)
338
+
339
+ # Check if there is a pre-process function to be called.
340
+ unless @pre_process_func.nil?
341
+ # Call the pre-process function.
342
+ pp_return = @pre_process_func.call(:SWINDOW, self,
343
+ @pre_process_data, input)
344
+ end
345
+
346
+ # Should we continue?
347
+ if pp_return != 0
348
+ # Check for a key binding.
349
+ if self.checkBind(:SWINDOW, input)
350
+ complete = true
351
+ else
352
+ case input
353
+ when Ncurses::KEY_UP
354
+ if @current_top > 0
355
+ @current_top -= 1
356
+ else
357
+ CDK.Beep
358
+ end
359
+ when Ncurses::KEY_DOWN
360
+ if @current_top >= 0 && @current_top < @max_top_line
361
+ @current_top += 1
362
+ else
363
+ CDK.Beep
364
+ end
365
+ when Ncurses::KEY_RIGHT
366
+ if @left_char < @max_left_char
367
+ @left_char += 1
368
+ else
369
+ CDK.Beep
370
+ end
371
+ when Ncurses::KEY_LEFT
372
+ if @left_char > 0
373
+ @left_char -= 1
374
+ else
375
+ CDK.Beep
376
+ end
377
+ when Ncurses::KEY_PPAGE
378
+ if @current_top != 0
379
+ if @current_top >= @view_size
380
+ @current_top = @current_top - (@view_size - 1)
381
+ else
382
+ @current_top = 0
383
+ end
384
+ else
385
+ CDK.Beep
386
+ end
387
+ when Ncurses::KEY_NPAGE
388
+ if @current_top != @max_top_line
389
+ if @current_top + @view_size < @max_top_line
390
+ @current_top = @current_top + (@view_size - 1)
391
+ else
392
+ @current_top = @max_top_line
393
+ end
394
+ else
395
+ CDK.Beep
396
+ end
397
+ when Ncurses::KEY_HOME
398
+ @left_char = 0
399
+ when Ncurses::KEY_END
400
+ @left_char = @max_left_char + 1
401
+ when 'g'.ord, '1'.ord, '<'.ord
402
+ @current_top = 0
403
+ when 'G'.ord, '>'.ord
404
+ @current_top = @max_top_line
405
+ when 'l'.ord, 'L'.ord
406
+ self.loadInformation
407
+ when 's'.ord, 'S'.ord
408
+ self.saveInformation
409
+ when CDK::KEY_TAB, CDK::KEY_RETURN, Ncurses::KEY_ENTER
410
+ self.setExitType(input)
411
+ ret = 1
412
+ complete = true
413
+ when CDK::KEY_ESC
414
+ self.setExitType(input)
415
+ complete = true
416
+ when Ncurses::ERR
417
+ self.setExitType(input)
418
+ complete = true
419
+ when CDK::REFRESH
420
+ @screen.erase
421
+ @screen.refresh
422
+ end
423
+ end
424
+
425
+ # Should we call a post-process?
426
+ if !complete && !(@post_process_func.nil?)
427
+ @post_process_func.call(:SWINDOW, self, @post_process_data, input)
428
+ end
429
+ end
430
+
431
+ if !complete
432
+ self.drawList(@box)
433
+ self.setExitType(0)
434
+ end
435
+
436
+ @return_data = ret
437
+ return ret
438
+ end
439
+
440
+ # This moves the window field to the given location.
441
+ # Inherited
442
+ # def move(xplace, yplace, relative, refresh_flag)
443
+ # end
444
+
445
+ # This function draws the swindow window widget.
446
+ def draw(box)
447
+ # Do we need to draw in the shadow.
448
+ unless @shadow_win.nil?
449
+ Draw.drawShadow(@shadow_win)
450
+ end
451
+
452
+ # Box the widget if needed
453
+ if box
454
+ Draw.drawObjBox(@win, self)
455
+ end
456
+
457
+ self.drawTitle(@win)
458
+
459
+ @win.wrefresh
460
+
461
+ # Draw in the list.
462
+ self.drawList(box)
463
+ end
464
+
465
+ # This draws in the contents of the scrolling window
466
+ def drawList(box)
467
+ # Determine the last line to draw.
468
+ if @list_size < @view_size
469
+ last_line = @list_size
470
+ else
471
+ last_line = @view_size
472
+ end
473
+
474
+ # Erase the scrolling window.
475
+ @field_win.werase
476
+
477
+ # Start drawing in each line.
478
+ (0...last_line).each do |x|
479
+ screen_pos = @list_pos[x + @current_top] - @left_char
480
+
481
+ # Write in the correct line.
482
+ if screen_pos >= 0
483
+ Draw.writeChtype(@field_win, screen_pos, x,
484
+ @list[x + @current_top], CDK::HORIZONTAL, 0,
485
+ @list_len[x + @current_top])
486
+ else
487
+ Draw.writeChtype(@field_win, 0, x, @list[x + @current_top],
488
+ CDK::HORIZONTAL, @left_char - @list_pos[x + @current_top],
489
+ @list_len[x + @current_top])
490
+ end
491
+ end
492
+
493
+ @field_win.wrefresh
494
+ end
495
+
496
+ # This sets the background attribute of the widget.
497
+ def setBKattr(attrib)
498
+ @win.wbkgd(attrib)
499
+ @field_win.wbkgd(attrib)
500
+ end
501
+
502
+ # Free any storage associated with the info-list.
503
+ def destroyInfo
504
+ @list = []
505
+ @list_pos = []
506
+ @list_len = []
507
+ end
508
+
509
+ # This function destroys the scrolling window widget.
510
+ def destroy
511
+ self.destroyInfo
512
+
513
+ self.cleanTitle
514
+
515
+ # Delete the windows.
516
+ CDK.deleteCursesWindow(@shadow_win)
517
+ CDK.deleteCursesWindow(@field_win)
518
+ CDK.deleteCursesWindow(@win)
519
+
520
+ # Clean the key bindings.
521
+ self.cleanBindings(:SWINDOW)
522
+
523
+ # Unregister this object.
524
+ CDK::SCREEN.unregister(:SWINDOW, self)
525
+ end
526
+
527
+ # This function erases the scrolling window widget.
528
+ def erase
529
+ if self.validCDKObject
530
+ CDK.eraseCursesWindow(@win)
531
+ CDK.eraseCursesWindow(@shadow_win)
532
+ end
533
+ end
534
+
535
+ # This execs a command and redirects the output to the scrolling window.
536
+ def exec(command, insert_pos)
537
+ count = -1
538
+ Ncurses.endwin
539
+
540
+ # Try to open the command.
541
+ # XXX This especially needs exception handling given how Ruby
542
+ # implements popen
543
+ unless (ps = IO.popen(command.split, 'r')).nil?
544
+ # Start reading.
545
+ until (temp = ps.gets).nil?
546
+ if temp.size != 0 && temp[-1] == '\n'
547
+ temp = temp[0...-1]
548
+ end
549
+ # Add the line to the scrolling window.
550
+ self.add(temp, insert_pos)
551
+ count += 1
552
+ end
553
+
554
+ # Close the pipe
555
+ ps.close
556
+ end
557
+ return count
558
+ end
559
+
560
+ def showMessage2(msg, msg2, filename)
561
+ mesg = [
562
+ msg,
563
+ msg2,
564
+ "<C>(%s)" % [filename],
565
+ ' ',
566
+ '<C> Press any key to continue.',
567
+ ]
568
+ @screen.popupLabel(mesg, mesg.size)
569
+ end
570
+
571
+ # This function allows the user to dump the information from the
572
+ # scrolling window to a file.
573
+ def saveInformation
574
+ # Create the entry field to get the filename.
575
+ entry = CDK::ENTRY.new(@screen, CDK::CENTER, CDK::CENTER,
576
+ '<C></B/5>Enter the filename of the save file.',
577
+ 'Filename: ', Ncurses::A_NORMAL, '_'.ord, :MIXED,
578
+ 20, 1, 256, true, false)
579
+
580
+ # Get the filename.
581
+ filename = entry.activate([])
582
+
583
+ # Did they hit escape?
584
+ if entry.exit_type == :ESCAPE_HIT
585
+ # Popup a message.
586
+ mesg = [
587
+ '<C></B/5>Save Canceled.',
588
+ '<C>Escape hit. Scrolling window information not saved.',
589
+ ' ',
590
+ '<C>Press any key to continue.'
591
+ ]
592
+ @screen.popupLabel(mesg, 4)
593
+
594
+ # Clean up and exit.
595
+ entry.destroy
596
+ end
597
+
598
+ # Write the contents of the scrolling window to the file.
599
+ lines_saved = self.dump(filename)
600
+
601
+ # Was the save successful?
602
+ if lines_saved == -1
603
+ # Nope, tell 'em
604
+ self.showMessage2('<C></B/16>Error', '<C>Could not save to the file.',
605
+ filename)
606
+ else
607
+ # Yep, let them know how many lines were saved.
608
+ self.showMessage2('<C></B/5>Save Successful',
609
+ '<C>There were %d lines saved to the file' % [lines_saved],
610
+ filename)
611
+ end
612
+
613
+ # Clean up and exit.
614
+ entry.destroy
615
+ @screen.erase
616
+ @screen.draw
617
+ end
618
+
619
+ # This function allows the user to load new information into the scrolling
620
+ # window.
621
+ def loadInformation
622
+ # Create the file selector to choose the file.
623
+ fselect = CDK::FSELECT.new(@screen, CDK::CENTER, CDK::CENTER, 20, 55,
624
+ '<C>Load Which File', 'FIlename', Ncurses::A_NORMAL, '.',
625
+ Ncurses::A_REVERSE, '</5>', '</48>', '</N>', '</N>', true, false)
626
+
627
+ # Get the filename to load.
628
+ filename = fselect.activate([])
629
+
630
+ # Make sure they selected a file.
631
+ if fselect.exit_type == :ESCAPE_HIT
632
+ # Popup a message.
633
+ mesg = [
634
+ '<C></B/5>Load Canceled.',
635
+ ' ',
636
+ '<C>Press any key to continue.',
637
+ ]
638
+ @screen.popupLabel(mesg, 3)
639
+
640
+ # Clean up and exit
641
+ fselect.destroy
642
+ return
643
+ end
644
+
645
+ # Copy the filename and destroy the file selector.
646
+ filename = fselect.pathname
647
+ fselect.destroy
648
+
649
+ # Maybe we should check before nuking all the information in the
650
+ # scrolling window...
651
+ if @list_size > 0
652
+ # Create the dialog message.
653
+ mesg = [
654
+ '<C></B/5>Save Information First',
655
+ '<C>There is information in the scrolling window.',
656
+ '<C>Do you want to save it to a file first?',
657
+ ]
658
+ button = ['(Yes)', '(No)']
659
+
660
+ # Create the dialog widget.
661
+ dialog = CDK::DIALOG.new(@screen, CDK::CENTER, CDK::CENTER,
662
+ mesg, 3, button, 2, Ncurses.COLOR_PAIR(2) | Ncurses::A_REVERSE,
663
+ true, true, false)
664
+
665
+ # Activate the widet.
666
+ answer = dialog.activate([])
667
+ dialog.destroy
668
+
669
+ # Check the answer.
670
+ if (answer == -1 || answer == 0)
671
+ # Save the information.
672
+ self.saveInformation
673
+ end
674
+ end
675
+
676
+ # Open the file and read it in.
677
+ f = File.open(filename)
678
+ file_info = f.readlines.map do |line|
679
+ if line.size > 0 && line[-1] == "\n"
680
+ line[0...-1]
681
+ else
682
+ line
683
+ end
684
+ end.compact
685
+
686
+ # TODO error handling
687
+ # if (lines == -1)
688
+ # {
689
+ # /* The file read didn't work. */
690
+ # showMessage2 (swindow,
691
+ # "<C></B/16>Error",
692
+ # "<C>Could not read the file",
693
+ # filename);
694
+ # freeChar (filename);
695
+ # return;
696
+ # }
697
+
698
+ # Clean out the scrolling window.
699
+ self.clean
700
+
701
+ # Set the new information in the scrolling window.
702
+ self.set(file_info, file_info.size, @box)
703
+ end
704
+
705
+ # This actually dumps the information from the scrolling window to a file.
706
+ def dump(filename)
707
+ # Try to open the file.
708
+ #if ((outputFile = fopen (filename, "w")) == 0)
709
+ #{
710
+ # return -1;
711
+ #}
712
+ output_file = File.new(filename, 'w')
713
+
714
+ # Start writing out the file.
715
+ @list.each do |item|
716
+ raw_line = CDK.chtype2Char(item)
717
+ output_file << "%s\n" % raw_line
718
+ end
719
+
720
+ # Close the file and return the number of lines written.
721
+ output_file.close
722
+ return @list_size
723
+ end
724
+
725
+ def focus
726
+ self.draw(@box)
727
+ end
728
+
729
+ def unfocus
730
+ self.draw(@box)
731
+ end
732
+
733
+ def createList(list_size)
734
+ status = false
735
+
736
+ if list_size >= 0
737
+ new_list = []
738
+ new_pos = []
739
+ new_len = []
740
+
741
+ status = true
742
+ self.destroyInfo
743
+
744
+ @list = new_list
745
+ @list_pos = new_pos
746
+ @list_len = new_len
747
+ else
748
+ self.destroyInfo
749
+ status = false
750
+ end
751
+ return status
752
+ end
753
+
754
+ def position
755
+ super(@win)
756
+ end
757
+
758
+ def object_type
759
+ :SWINDOW
760
+ end
761
+ end
762
+ end