slithernix-cdk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.maxx
9
+ parent_height = cdkscreen.window.maxy
10
+ box_width = width
11
+ box_height = height
12
+ bindings = {
13
+ CDK::BACKCHAR => Curses::KEY_PPAGE,
14
+ 'b' => Curses::KEY_PPAGE,
15
+ 'B' => Curses::KEY_PPAGE,
16
+ CDK::FORCHAR => Curses::KEY_NPAGE,
17
+ ' ' => Curses::KEY_NPAGE,
18
+ 'f' => Curses::KEY_NPAGE,
19
+ 'F' => Curses::KEY_NPAGE,
20
+ '|' => Curses::KEY_HOME,
21
+ '$' => Curses::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 = Curses::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 = Curses::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 Curses::KEY_UP
354
+ if @current_top > 0
355
+ @current_top -= 1
356
+ else
357
+ CDK.Beep
358
+ end
359
+ when Curses::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 Curses::KEY_RIGHT
366
+ if @left_char < @max_left_char
367
+ @left_char += 1
368
+ else
369
+ CDK.Beep
370
+ end
371
+ when Curses::KEY_LEFT
372
+ if @left_char > 0
373
+ @left_char -= 1
374
+ else
375
+ CDK.Beep
376
+ end
377
+ when Curses::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 Curses::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 Curses::KEY_HOME
398
+ @left_char = 0
399
+ when Curses::KEY_END
400
+ @left_char = @max_left_char + 1
401
+ when 'g', '1', '<'
402
+ @current_top = 0
403
+ when 'G', '>'
404
+ @current_top = @max_top_line
405
+ when 'l', 'L'
406
+ self.loadInformation
407
+ when 's', 'S'
408
+ self.saveInformation
409
+ when CDK::KEY_TAB, CDK::KEY_RETURN, Curses::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 Curses::Error
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.refresh
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.erase
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.refresh
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
+ Curses.close_screen
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: ', Curses::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', Curses::A_NORMAL, '.',
625
+ Curses::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, Curses.color_pair(2) | Curses::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