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,727 @@
1
+ require_relative 'cdk_objs'
2
+
3
+ module CDK
4
+ class DIALOG < CDK::CDKOBJS
5
+ attr_reader :current_button
6
+ MIN_DIALOG_WIDTH = 10
7
+
8
+ def initialize(cdkscreen, xplace, yplace, mesg, rows, button_label,
9
+ button_count, highlight, separator, box, shadow)
10
+ super()
11
+ box_width = DIALOG::MIN_DIALOG_WIDTH
12
+ max_message_width = -1
13
+ button_width = 0
14
+ xpos = xplace
15
+ ypos = yplace
16
+ temp = 0
17
+ buttonadj = 0
18
+ @info = []
19
+ @info_len = []
20
+ @info_pos = []
21
+ @button_label = []
22
+ @button_len = []
23
+ @button_pos = []
24
+
25
+ if rows <= 0 || button_count <= 0
26
+ self.destroy
27
+ return nil
28
+ end
29
+
30
+ self.setBox(box)
31
+ box_height = if separator then 1 else 0 end
32
+ box_height += rows + 2 * @border_size + 1
33
+
34
+ # Translate the string message to a chtype array
35
+ (0...rows).each do |x|
36
+ info_len = []
37
+ info_pos = []
38
+ @info << CDK.char2Chtype(mesg[x], info_len, info_pos)
39
+ @info_len << info_len[0]
40
+ @info_pos << info_pos[0]
41
+ max_message_width = [max_message_width, info_len[0]].max
42
+ end
43
+
44
+ # Translate the button label string to a chtype array
45
+ (0...button_count).each do |x|
46
+ button_len = []
47
+ @button_label << CDK.char2Chtype(button_label[x], button_len, [])
48
+ @button_len << button_len[0]
49
+ button_width += button_len[0] + 1
50
+ end
51
+
52
+ button_width -= 1
53
+
54
+ # Determine the final dimensions of the box.
55
+ box_width = [box_width, max_message_width, button_width].max
56
+ box_width = box_width + 2 + 2 * @border_size
57
+
58
+ # Now we have to readjust the x and y positions.
59
+ xtmp = [xpos]
60
+ ytmp = [ypos]
61
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
62
+ xpos = xtmp[0]
63
+ ypos = ytmp[0]
64
+
65
+ # Set up the dialog box attributes.
66
+ @screen = cdkscreen
67
+ @parent = cdkscreen.window
68
+ @win = Ncurses::WINDOW.new(box_height, box_width, ypos, xpos)
69
+ @shadow_win = nil
70
+ @button_count = button_count
71
+ @current_button = 0
72
+ @message_rows = rows
73
+ @box_height = box_height
74
+ @box_width = box_width
75
+ @highlight = highlight
76
+ @separator = separator
77
+ @accepts_focus = true
78
+ @input_window = @win
79
+ @shadow = shadow
80
+
81
+ # If we couldn't create the window, we should return a nil value.
82
+ if @win.nil?
83
+ self.destroy
84
+ return nil
85
+ end
86
+ @win.keypad(true)
87
+
88
+ # Find the button positions.
89
+ buttonadj = (box_width - button_width) / 2
90
+ (0...button_count).each do |x|
91
+ @button_pos[x] = buttonadj
92
+ buttonadj = buttonadj + @button_len[x] + @border_size
93
+ end
94
+
95
+ # Create the string alignments.
96
+ (0...rows).each do |x|
97
+ @info_pos[x] = CDK.justifyString(box_width - 2 * @border_size,
98
+ @info_len[x], @info_pos[x])
99
+ end
100
+
101
+ # Was there a shadow?
102
+ if shadow
103
+ @shadow_win = Ncurses::WINDOW.new(box_height, box_width,
104
+ ypos + 1, xpos + 1)
105
+ end
106
+
107
+ # Register this baby.
108
+ cdkscreen.register(:DIALOG, self)
109
+ end
110
+
111
+ # This lets the user select the button.
112
+ def activate(actions)
113
+ input = 0
114
+
115
+ # Draw the dialog box.
116
+ self.draw(@box)
117
+
118
+ # Lets move to the first button.
119
+ Draw.writeChtypeAttrib(@win, @button_pos[@current_button],
120
+ @box_height - 1 - @border_size, @button_label[@current_button],
121
+ @highlight, CDK::HORIZONTAL, 0, @button_len[@current_button])
122
+ @win.wrefresh
123
+
124
+ if actions.nil? || actions.size == 0
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
+ # Inject each character one at a time.
136
+ actions.each do |action|
137
+ ret = self.inject(action)
138
+ if @exit_type != :EARLY_EXIT
139
+ return ret
140
+ end
141
+ end
142
+ end
143
+
144
+ # Set the exit type and exit
145
+ self.setExitType(0)
146
+ return -1
147
+ end
148
+
149
+ # This injects a single character into the dialog widget
150
+ def inject(input)
151
+ first_button = 0
152
+ last_button = @button_count - 1
153
+ pp_return = 1
154
+ ret = -1
155
+ complete = false
156
+
157
+ # Set the exit type.
158
+ self.setExitType(0)
159
+
160
+ # Check if there is a pre-process function to be called.
161
+ unless @pre_process_func.nil?
162
+ pp_return = @pre_process_func.call(:DIALOG, self,
163
+ @pre_process_data, input)
164
+ end
165
+
166
+ # Should we continue?
167
+ if pp_return != 0
168
+ # Check for a key binding.
169
+ if self.checkBind(:DIALOG, input)
170
+ complete = true
171
+ else
172
+ case input
173
+ when Ncurses::KEY_LEFT, Ncurses::KEY_BTAB, Ncurses::KEY_BACKSPACE
174
+ if @current_button == first_button
175
+ @current_button = last_button
176
+ else
177
+ @current_button -= 1
178
+ end
179
+ when Ncurses::KEY_RIGHT, CDK::KEY_TAB, ' '.ord
180
+ if @current_button == last_button
181
+ @current_button = first_button
182
+ else
183
+ @current_button += 1
184
+ end
185
+ when Ncurses::KEY_UP, Ncurses::KEY_DOWN
186
+ CDK.Beep
187
+ when CDK::REFRESH
188
+ @screen.erase
189
+ @screen.refresh
190
+ when CDK::KEY_ESC
191
+ self.setExitType(input)
192
+ complete = true
193
+ when Ncurses::ERR
194
+ self.setExitType(input)
195
+ when Ncurses::KEY_ENTER, CDK::KEY_RETURN
196
+ self.setExitType(input)
197
+ ret = @current_button
198
+ complete = true
199
+ end
200
+ end
201
+
202
+ # Should we call a post_process?
203
+ if !complete && !(@post_process_func.nil?)
204
+ @post_process_func.call(:DIALOG, self,
205
+ @post_process_data, input)
206
+ end
207
+ end
208
+
209
+ unless complete
210
+ self.drawButtons
211
+ @win.wrefresh
212
+ self.setExitType(0)
213
+ end
214
+
215
+ @result_data = ret
216
+ return ret
217
+ end
218
+
219
+ # This moves the dialog field to the given location.
220
+ # Inherited
221
+ # def move(xplace, yplace, relative, refresh_flag)
222
+ # end
223
+
224
+ # This function draws the dialog widget.
225
+ def draw(box)
226
+ # Is there a shadow?
227
+ unless @shadow_win.nil?
228
+ Draw.drawShadow(@shadow_win)
229
+ end
230
+
231
+ # Box the widget if they asked.
232
+ if box
233
+ Draw.drawObjBox(@win, self)
234
+ end
235
+
236
+ # Draw in the message.
237
+ (0...@message_rows).each do |x|
238
+ Draw.writeChtype(@win,
239
+ @info_pos[x] + @border_size, x + @border_size, @info[x],
240
+ CDK::HORIZONTAL, 0, @info_len[x])
241
+ end
242
+
243
+ # Draw in the buttons.
244
+ self.drawButtons
245
+
246
+ @win.wrefresh
247
+ end
248
+
249
+ # This function destroys the dialog widget.
250
+ def destroy
251
+ # Clean up the windows.
252
+ CDK.deleteCursesWindow(@win)
253
+ CDK.deleteCursesWindow(@shadow_win)
254
+
255
+ # Clean the key bindings
256
+ self.cleanBindings(:DIALOG)
257
+
258
+ # Unregister this object
259
+ CDK::SCREEN.unregister(:DIALOG, self)
260
+ end
261
+
262
+ # This function erases the dialog widget from the screen.
263
+ def erase
264
+ if self.validCDKObject
265
+ CDK.eraseCursesWindow(@win)
266
+ CDK.eraseCursesWindow(@shadow_win)
267
+ end
268
+ end
269
+
270
+ # This sets attributes of the dialog box.
271
+ def set(highlight, separator, box)
272
+ self.setHighlight(highlight)
273
+ self.setSeparator(separator)
274
+ self.setBox(box)
275
+ end
276
+
277
+ # This sets the highlight attribute for the buttons.
278
+ def setHighlight(highlight)
279
+ @highlight = highlight
280
+ end
281
+
282
+ def getHighlight
283
+ return @highlight
284
+ end
285
+
286
+ # This sets whether or not the dialog box will have a separator line.
287
+ def setSeparator(separator)
288
+ @separator = separator
289
+ end
290
+
291
+ def getSeparator
292
+ return @separator
293
+ end
294
+
295
+ # This sets the background attribute of the widget.
296
+ def setBKattr(attrib)
297
+ @win.wbkgd(attrib)
298
+ end
299
+
300
+ # This draws the dialog buttons and the separation line.
301
+ def drawButtons
302
+ (0...@button_count).each do |x|
303
+ Draw.writeChtype(@win, @button_pos[x],
304
+ @box_height -1 - @border_size,
305
+ @button_label[x], CDK::HORIZONTAL, 0,
306
+ @button_len[x])
307
+ end
308
+
309
+ # Draw the separation line.
310
+ if @separator
311
+ boxattr = @BXAttr
312
+
313
+ (1...@box_width).each do |x|
314
+ @win.mvwaddch(@box_height - 2 - @border_size, x,
315
+ Ncurses::ACS_HLINE | boxattr)
316
+ end
317
+ @win.mvwaddch(@box_height - 2 - @border_size, 0,
318
+ Ncurses::ACS_LTEE | boxattr)
319
+ @win.mvwaddch(@box_height - 2 - @border_size, @win.getmaxx - 1,
320
+ Ncurses::ACS_RTEE | boxattr)
321
+ end
322
+ Draw.writeChtypeAttrib(@win, @button_pos[@current_button],
323
+ @box_height - 1 - @border_size, @button_label[@current_button],
324
+ @highlight, CDK::HORIZONTAL, 0, @button_len[@current_button])
325
+ end
326
+
327
+ def focus
328
+ self.draw(@box)
329
+ end
330
+
331
+ def unfocus
332
+ self.draw(@box)
333
+ end
334
+
335
+ def object_type
336
+ :DIALOG
337
+ end
338
+
339
+ def position
340
+ super(@win)
341
+ end
342
+ end
343
+
344
+ class GRAPH < CDK::CDKOBJS
345
+ def initialize(cdkscreen, xplace, yplace, height, width,
346
+ title, xtitle, ytitle)
347
+ super()
348
+ parent_width = cdkscreen.window.getmaxx
349
+ parent_height = cdkscreen.window.getmaxy
350
+
351
+ self.setBox(false)
352
+
353
+ box_height = CDK.setWidgetDimension(parent_height, height, 3)
354
+ box_width = CDK.setWidgetDimension(parent_width, width, 0)
355
+ box_width = self.setTitle(title, box_width)
356
+ box_height += @title_lines
357
+ box_width = [parent_width, box_width].min
358
+ box_height = [parent_height, box_height].min
359
+
360
+ # Rejustify the x and y positions if we need to
361
+ xtmp = [xplace]
362
+ ytmp = [yplace]
363
+ CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
364
+ xpos = xtmp[0]
365
+ ypos = ytmp[0]
366
+
367
+ # Create the widget pointer
368
+ @screen = cdkscreen
369
+ @parent = cdkscreen.window
370
+ @win = Ncurses::WINDOW.new(box_height, box_width, ypos, xpos)
371
+ @box_height = box_height
372
+ @box_width = box_width
373
+ @minx = 0
374
+ @maxx = 0
375
+ @xscale = 0
376
+ @yscale = 0
377
+ @count = 0
378
+ @display_type = :LINE
379
+
380
+ if @win.nil?
381
+ self.destroy
382
+ return nil
383
+ end
384
+ @win.keypad(true)
385
+
386
+ # Translate the X axis title string to a chtype array
387
+ if !(xtitle.nil?) && xtitle.size > 0
388
+ xtitle_len = []
389
+ xtitle_pos = []
390
+ @xtitle = CDK.char2Chtype(xtitle, xtitle_len, xtitle_pos)
391
+ @xtitle_len = xtitle_len[0]
392
+ @xtitle_pos = CDK.justifyString(@box_height,
393
+ @xtitle_len, xtitle_pos[0])
394
+ else
395
+ xtitle_len = []
396
+ xtitle_pos = []
397
+ @xtitle = CDK.char2Chtype("<C></5>X Axis", xtitle_len, xtitle_pos)
398
+ @xtitle_len = title_len[0]
399
+ @xtitle_pos = CDK.justifyString(@box_height,
400
+ @xtitle_len, xtitle_pos[0])
401
+ end
402
+
403
+ # Translate the Y Axis title string to a chtype array
404
+ if !(ytitle.nil?) && ytitle.size > 0
405
+ ytitle_len = []
406
+ ytitle_pos = []
407
+ @ytitle = CDK.char2Chtype(ytitle, ytitle_len, ytitle_pos)
408
+ @ytitle_len = ytitle_len[0]
409
+ @ytitle_pos = CDK.justifyString(@box_width, @ytitle_len, ytitle_pos[0])
410
+ else
411
+ ytitle_len = []
412
+ ytitle_pos = []
413
+ @ytitle = CDK.char2Chtype("<C></5>Y Axis", ytitle_len, ytitle_pos)
414
+ @ytitle_len = ytitle_len[0]
415
+ @ytitle_pos = CDK.justifyString(@box_width, @ytitle_len, ytitle_pos[0])
416
+ end
417
+
418
+ @graph_char = 0
419
+ @values = []
420
+
421
+ cdkscreen.register(:GRAPH, self)
422
+ end
423
+
424
+ # This was added for the builder.
425
+ def activate(actions)
426
+ self.draw(@box)
427
+ end
428
+
429
+ # Set multiple attributes of the widget
430
+ def set(values, count, graph_char, start_at_zero, display_type)
431
+ ret = self.setValues(values, count, start_at_zero)
432
+ self.setCharacters(graph_char)
433
+ self.setDisplayType(display_type)
434
+ return ret
435
+ end
436
+
437
+ # Set the scale factors for the graph after wee have loaded new values.
438
+ def setScales
439
+ @xscale = (@maxx - @minx) / [1, @box_height - @title_lines - 5].max
440
+ if @xscale <= 0
441
+ @xscale = 1
442
+ end
443
+
444
+ @yscale = (@box_width - 4) / [1, @count].max
445
+ if @yscale <= 0
446
+ @yscale = 1
447
+ end
448
+ end
449
+
450
+ # Set the values of the graph.
451
+ def setValues(values, count, start_at_zero)
452
+ min = 2**30
453
+ max = -2**30
454
+
455
+ # Make sure everything is happy.
456
+ if count < 0
457
+ return false
458
+ end
459
+
460
+ if !(@values.nil?) && @values.size > 0
461
+ @values = []
462
+ @count = 0
463
+ end
464
+
465
+ # Copy the X values
466
+ values.each do |value|
467
+ min = [value, @minx].min
468
+ max = [value, @maxx].max
469
+
470
+ # Copy the value.
471
+ @values << value
472
+ end
473
+
474
+ # Keep the count and min/max values
475
+ @count = count
476
+ @minx = min
477
+ @maxx = max
478
+
479
+ # Check the start at zero status.
480
+ if start_at_zero
481
+ @minx = 0
482
+ end
483
+
484
+ self.setScales
485
+
486
+ return true
487
+ end
488
+
489
+ def getValues(size)
490
+ size << @count
491
+ return @values
492
+ end
493
+
494
+ # Set the value of the graph at the given index.
495
+ def setValue(index, value, start_at_zero)
496
+ # Make sure the index is within range.
497
+ if index < 0 || index >= @count
498
+ return false
499
+ end
500
+
501
+ # Set the min, max, and value for the graph
502
+ @minx = [value, @minx].min
503
+ @maxx = [value, @maxx].max
504
+ @values[index] = value
505
+
506
+ # Check the start at zero status
507
+ if start_at_zero
508
+ @minx = 0
509
+ end
510
+
511
+ self.setScales
512
+
513
+ return true
514
+ end
515
+
516
+ def getValue(index)
517
+ if index >= 0 && index < @count then @values[index] else 0 end
518
+ end
519
+
520
+ # Set the characters of the graph widget.
521
+ def setCharacters(characters)
522
+ char_count = []
523
+ new_tokens = CDK.char2Chtype(characters, char_count, [])
524
+
525
+ if char_count[0] != @count
526
+ return false
527
+ end
528
+
529
+ @graph_char = new_tokens
530
+ return true
531
+ end
532
+
533
+ def getCharacters
534
+ return @graph_char
535
+ end
536
+
537
+ # Set the character of the graph widget of the given index.
538
+ def setCharacter(index, character)
539
+ # Make sure the index is within range
540
+ if index < 0 || index > @count
541
+ return false
542
+ end
543
+
544
+ # Convert the string given to us
545
+ char_count = []
546
+ new_tokens = CDK.char2Chtype(character, char_count, [])
547
+
548
+ # Check if the number of characters back is the same as the number
549
+ # of elements in the list.
550
+ if char_count[0] != @count
551
+ return false
552
+ end
553
+
554
+ # Everything OK so far. Set the value of the array.
555
+ @graph_char[index] = new_tokens[0]
556
+ return true
557
+ end
558
+
559
+ def getCharacter(index)
560
+ return graph_char[index]
561
+ end
562
+
563
+ # Set the display type of the graph.
564
+ def setDisplayType(type)
565
+ @display_type = type
566
+ end
567
+
568
+ def getDisplayType
569
+ @display_type
570
+ end
571
+
572
+ # Set the background attribute of the widget.
573
+ def setBKattr(attrib)
574
+ @win.wbkgd(attrib)
575
+ end
576
+
577
+ # Move the graph field to the given location.
578
+ def move(xplace, yplace, relative, refresh_flag)
579
+ current_x = @win.getbegx
580
+ current_y = @win.getbegy
581
+ xpos = xplace
582
+ ypos = yplace
583
+
584
+ # If this is a relative move, then we will adjust where we want
585
+ # to move to
586
+ if relative
587
+ xpos = @win.getbegx + xplace
588
+ ypos = @win.getbegy + yplace
589
+ end
590
+
591
+ # Adjust the window if we need to.
592
+ xtmp = [xpos]
593
+ tymp = [ypos]
594
+ CDK.alignxy(@screen.window, xtmp, ytmp, @box_width, @box_height)
595
+ xpos = xtmp[0]
596
+ ypos = ytmp[0]
597
+
598
+ # Get the difference
599
+ xdiff = current_x - xpos
600
+ ydiff = current_y - ypos
601
+
602
+ # Move the window to the new location.
603
+ CDK.moveCursesWindow(@win, -xdiff, -ydiff)
604
+ CDK.moveCursesWindow(@shadow_win, -xdiff, -ydiff)
605
+
606
+ # Touch the windows so they 'move'.
607
+ CDK::SCREEN.refreshCDKWindow(@screen.window)
608
+
609
+ # Reraw the windowk if they asked for it
610
+ if refresh_flag
611
+ self.draw(@box)
612
+ end
613
+ end
614
+
615
+ # Draw the grpah widget
616
+ def draw(box)
617
+ adj = 2 + (if @xtitle.nil? || @xtitle.size == 0 then 0 else 1 end)
618
+ spacing = 0
619
+ attrib = ' '.ord | Ncurses::A_REVERSE
620
+
621
+ if box
622
+ Draw.drawObjBox(@win, self)
623
+ end
624
+
625
+ # Draw in the vertical axis
626
+ Draw.drawLine(@win, 2, @title_lines + 1, 2, @box_height - 3,
627
+ Ncurses::ACS_VLINE)
628
+
629
+ # Draw in the horizontal axis
630
+ Draw.drawLine(@win, 3, @box_height - 3, @box_width, @box_height - 3,
631
+ Ncurses::ACS_HLINE)
632
+
633
+ self.drawTitle(@win)
634
+
635
+ # Draw in the X axis title.
636
+ if !(@xtitle.nil?) && @xtitle.size > 0
637
+ Draw.writeChtype(@win, 0, @xtitle_pos, @xtitle, CDK::VERTICAL,
638
+ 0, @xtitle_len)
639
+ attrib = @xtitle[0] & Ncurses::A_ATTRIBUTES
640
+ end
641
+
642
+ # Draw in the X axis high value
643
+ temp = "%d" % [@maxx]
644
+ Draw.writeCharAttrib(@win, 1, @title_lines + 1, temp, attrib,
645
+ CDK::VERTICAL, 0, temp.size)
646
+
647
+ # Draw in the X axis low value.
648
+ temp = "%d" % [@minx]
649
+ Draw.writeCharAttrib(@win, 1, @box_height - 2 - temp.size, temp, attrib,
650
+ CDK::VERTICAL, 0, temp.size)
651
+
652
+ # Draw in the Y axis title
653
+ if !(@ytitle.nil?) && @ytitle.size > 0
654
+ Draw.writeChtype(@win, @ytitle_pos, @box_height - 1, @ytitle,
655
+ CDK::HORIZONTAL, 0, @ytitle_len)
656
+ end
657
+
658
+ # Draw in the Y axis high value.
659
+ temp = "%d" % [@count]
660
+ Draw.writeCharAttrib(@win, @box_width - temp.size - adj,
661
+ @box_height - 2, temp, attrib, CDK::HORIZONTAL, 0, temp.size)
662
+
663
+ # Draw in the Y axis low value.
664
+ Draw.writeCharAttrib(@win, 3, @box_height - 2, "0", attrib,
665
+ CDK::HORIZONTAL, 0, "0".size)
666
+
667
+ # If the count is zero then there aren't any points.
668
+ if @count == 0
669
+ @win.wrefresh
670
+ return
671
+ end
672
+
673
+ spacing = (@box_width - 3) / @count # FIXME magic number (TITLE_LM)
674
+
675
+ # Draw in the graph line/plot points.
676
+ (0...@count).each do |y|
677
+ colheight = (@values[y] / @xscale) - 1
678
+ # Add the marker on the Y axis.
679
+ @win.mvwaddch(@box_height - 3, (y + 1) * spacing + adj,
680
+ Ncurses::ACS_TTEE)
681
+
682
+ # If this is a plot graph, all we do is draw a dot.
683
+ if @display_type == :PLOT
684
+ xpos = @box_height - 4 - colheight
685
+ ypos = (y + 1) * spacing + adj
686
+ @win.mvwaddch(xpos, ypos, @graph_char[y])
687
+ else
688
+ (0..@yscale).each do |x|
689
+ xpos = @box_height - 3
690
+ ypos = (y + 1) * spacing - adj
691
+ Draw.drawLine(@win, ypos, xpos - colheight, ypos, xpos,
692
+ @graph_char[y])
693
+ end
694
+ end
695
+ end
696
+
697
+ # Draw in the axis corners.
698
+ @win.mvwaddch(@title_lines, 2, Ncurses::ACS_URCORNER)
699
+ @win.mvwaddch(@box_height - 3, 2, Ncurses::ACS_LLCORNER)
700
+ @win.mvwaddch(@box_height - 3, @box_width, Ncurses::ACS_URCORNER)
701
+
702
+ # Refresh and lets see it
703
+ @win.wrefresh
704
+ end
705
+
706
+ def destroy
707
+ self.cleanTitle
708
+ self.cleanBindings(:GRAPH)
709
+ CDK::SCREEN.unregister(:GRAPH, self)
710
+ CDK.deleteCursesWindow(@win)
711
+ end
712
+
713
+ def erase
714
+ if self.validCDKObject
715
+ CDK.eraseCursesWindow(@win)
716
+ end
717
+ end
718
+
719
+ def object_type
720
+ :GRAPH
721
+ end
722
+
723
+ def position
724
+ super(@win)
725
+ end
726
+ end
727
+ end