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.
- checksums.yaml +7 -0
- data/lib/cdk.rb +916 -0
- data/lib/cdk/alphalist.rb +562 -0
- data/lib/cdk/buttonbox.rb +354 -0
- data/lib/cdk/calendar.rb +770 -0
- data/lib/cdk/cdk_objs.rb +463 -0
- data/lib/cdk/dialog.rb +727 -0
- data/lib/cdk/display.rb +63 -0
- data/lib/cdk/draw.rb +233 -0
- data/lib/cdk/dscale.rb +13 -0
- data/lib/cdk/entry.rb +556 -0
- data/lib/cdk/fscale.rb +44 -0
- data/lib/cdk/fselect.rb +940 -0
- data/lib/cdk/fslider.rb +61 -0
- data/lib/cdk/histogram.rb +410 -0
- data/lib/cdk/itemlist.rb +475 -0
- data/lib/cdk/label.rb +207 -0
- data/lib/cdk/marquee.rb +241 -0
- data/lib/cdk/matrix.rb +1176 -0
- data/lib/cdk/mentry.rb +614 -0
- data/lib/cdk/menu.rb +448 -0
- data/lib/cdk/radio.rb +533 -0
- data/lib/cdk/scale.rb +525 -0
- data/lib/cdk/screen.rb +280 -0
- data/lib/cdk/scroll.rb +994 -0
- data/lib/cdk/scroller.rb +183 -0
- data/lib/cdk/selection.rb +619 -0
- data/lib/cdk/slider.rb +541 -0
- data/lib/cdk/swindow.rb +762 -0
- data/lib/cdk/template.rb +562 -0
- data/lib/cdk/traverse.rb +289 -0
- data/lib/cdk/uscale.rb +14 -0
- data/lib/cdk/uslider.rb +14 -0
- data/lib/cdk/viewer.rb +812 -0
- metadata +91 -0
data/lib/cdk/traverse.rb
ADDED
@@ -0,0 +1,289 @@
|
|
1
|
+
module CDK
|
2
|
+
module Traverse
|
3
|
+
def Traverse.resetCDKScreen(screen)
|
4
|
+
refreshDataCDKScreen(screen)
|
5
|
+
end
|
6
|
+
|
7
|
+
def Traverse.exitOKCDKScreen(screen)
|
8
|
+
screen.exit_status = CDK::SCREEN::EXITOK
|
9
|
+
end
|
10
|
+
|
11
|
+
def Traverse.exitCancelCDKScreen(screen)
|
12
|
+
screen.exit_status = CDK::SCREEN::EXITCANCEL
|
13
|
+
end
|
14
|
+
|
15
|
+
def Traverse.exitOKCDKScreenOf(obj)
|
16
|
+
exitOKCDKScreen(obj.screen)
|
17
|
+
end
|
18
|
+
|
19
|
+
def Traverse.exitCancelCDKScreenOf(obj)
|
20
|
+
exitCancelCDKScreen(obj.screen)
|
21
|
+
end
|
22
|
+
|
23
|
+
def Traverse.resetCDKScreenOf(obj)
|
24
|
+
resetCDKScreen(obj.screen)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the object on which the focus lies.
|
28
|
+
def Traverse.getCDKFocusCurrent(screen)
|
29
|
+
result = nil
|
30
|
+
n = screen.object_focus
|
31
|
+
|
32
|
+
if n >= 0 && n < screen.object_count
|
33
|
+
result = screen.object[n]
|
34
|
+
end
|
35
|
+
|
36
|
+
return result
|
37
|
+
end
|
38
|
+
|
39
|
+
# Set focus to the next object, returning it.
|
40
|
+
def Traverse.setCDKFocusNext(screen)
|
41
|
+
result = nil
|
42
|
+
curobj = nil
|
43
|
+
n = getFocusIndex(screen)
|
44
|
+
first = n
|
45
|
+
|
46
|
+
while true
|
47
|
+
n+= 1
|
48
|
+
if n >= screen.object_count
|
49
|
+
n = 0
|
50
|
+
end
|
51
|
+
curobj = screen.object[n]
|
52
|
+
if !(curobj.nil?) && curobj.accepts_focus
|
53
|
+
result = curobj
|
54
|
+
break
|
55
|
+
else
|
56
|
+
if n == first
|
57
|
+
break
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
setFocusIndex(screen, if !(result.nil?) then n else -1 end)
|
63
|
+
return result
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set focus to the previous object, returning it.
|
67
|
+
def Traverse.setCDKFocusPrevious(screen)
|
68
|
+
result = nil
|
69
|
+
curobj = nil
|
70
|
+
n = getFocusIndex(screen)
|
71
|
+
first = n
|
72
|
+
|
73
|
+
while true
|
74
|
+
n -= 1
|
75
|
+
if n < 0
|
76
|
+
n = screen.object_count - 1
|
77
|
+
end
|
78
|
+
curobj = screen.object[n]
|
79
|
+
if !(curobj.nil?) && curobj.accepts_focus
|
80
|
+
result = curobj
|
81
|
+
break
|
82
|
+
elsif n == first
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
setFocusIndex(screen, if !(result.nil?) then n else -1 end)
|
88
|
+
return result
|
89
|
+
end
|
90
|
+
|
91
|
+
# Set focus to a specific object, returning it.
|
92
|
+
# If the object cannot be found, return nil.
|
93
|
+
def Traverse.setCDKFocusCurrent(screen, newobj)
|
94
|
+
result = nil
|
95
|
+
curobj = nil
|
96
|
+
n = getFocusIndex(screen)
|
97
|
+
first = n
|
98
|
+
|
99
|
+
while true
|
100
|
+
n += 1
|
101
|
+
if n >= screen.object_count
|
102
|
+
n = 0
|
103
|
+
end
|
104
|
+
|
105
|
+
curobj = screen.object[n]
|
106
|
+
if curobj == newobj
|
107
|
+
result = curobj
|
108
|
+
break
|
109
|
+
elsif n == first
|
110
|
+
break
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
setFocusIndex(screen, if !(result.nil?) then n else -1 end)
|
115
|
+
return result
|
116
|
+
end
|
117
|
+
|
118
|
+
# Set focus to the first object in the screen.
|
119
|
+
def Traverse.setCDKFocusFirst(screen)
|
120
|
+
setFocusIndex(screen, screen.object_count - 1)
|
121
|
+
return switchFocus(setCDKFocusNext(screen), nil)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Set focus to the last object in the screen.
|
125
|
+
def Traverse.setCDKFocusLast(screen)
|
126
|
+
setFocusIndex(screen, 0)
|
127
|
+
return switchFocus(setCDKFocusPrevious(screen), nil)
|
128
|
+
end
|
129
|
+
|
130
|
+
def Traverse.traverseCDKOnce(screen, curobj, key_code,
|
131
|
+
function_key, func_menu_key)
|
132
|
+
case key_code
|
133
|
+
when Ncurses::KEY_BTAB
|
134
|
+
switchFocus(setCDKFocusPrevious(screen), curobj)
|
135
|
+
when CDK::KEY_TAB
|
136
|
+
switchFocus(setCDKFocusNext(screen), curobj)
|
137
|
+
when CDK.KEY_F(10)
|
138
|
+
# save data and exit
|
139
|
+
exitOKCDKScreen(screen)
|
140
|
+
when CDK.CTRL('X')
|
141
|
+
exitCancelCDKScreen(screen)
|
142
|
+
when CDK.CTRL('R')
|
143
|
+
# reset data to defaults
|
144
|
+
resetCDKScreen(screen)
|
145
|
+
setFocus(curobj)
|
146
|
+
when CDK::REFRESH
|
147
|
+
# redraw screen
|
148
|
+
screen.refresh
|
149
|
+
setFocus(curobj)
|
150
|
+
else
|
151
|
+
# not everyone wants menus, so we make them optional here
|
152
|
+
if !(func_menu_key.nil?) &&
|
153
|
+
(func_menu_key.call(key_code, function_key))
|
154
|
+
# find and enable drop down menu
|
155
|
+
screen.object.each do |object|
|
156
|
+
if !(object.nil?) && object.object_type == :MENU
|
157
|
+
Traverse.handleMenu(screen, object, curobj)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
else
|
161
|
+
curobj.inject(key_code)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Traverse the widgets on a screen.
|
167
|
+
def Traverse.traverseCDKScreen(screen)
|
168
|
+
result = 0
|
169
|
+
curobj = setCDKFocusFirst(screen)
|
170
|
+
|
171
|
+
unless curobj.nil?
|
172
|
+
refreshDataCDKScreen(screen)
|
173
|
+
|
174
|
+
screen.exit_status = CDK::SCREEN::NOEXIT
|
175
|
+
|
176
|
+
while !((curobj = getCDKFocusCurrent(screen)).nil?) &&
|
177
|
+
screen.exit_status == CDK::SCREEN::NOEXIT
|
178
|
+
function = []
|
179
|
+
key = curobj.getch(function)
|
180
|
+
|
181
|
+
# TODO look at more direct way to do this
|
182
|
+
check_menu_key = lambda do |key_code, function_key|
|
183
|
+
Traverse.checkMenuKey(key_code, function_key)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
Traverse.traverseCDKOnce(screen, curobj, key,
|
188
|
+
function[0], check_menu_key)
|
189
|
+
end
|
190
|
+
|
191
|
+
if screen.exit_status == CDK::SCREEN::EXITOK
|
192
|
+
saveDataCDKScreen(screen)
|
193
|
+
result = 1
|
194
|
+
end
|
195
|
+
end
|
196
|
+
return result
|
197
|
+
end
|
198
|
+
|
199
|
+
private
|
200
|
+
|
201
|
+
def Traverse.limitFocusIndex(screen, value)
|
202
|
+
if value >= screen.object_count || value < 0
|
203
|
+
0
|
204
|
+
else
|
205
|
+
value
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def Traverse.getFocusIndex(screen)
|
210
|
+
return limitFocusIndex(screen, screen.object_focus)
|
211
|
+
end
|
212
|
+
|
213
|
+
def Traverse.setFocusIndex(screen, value)
|
214
|
+
screen.object_focus = limitFocusIndex(screen, value)
|
215
|
+
end
|
216
|
+
|
217
|
+
def Traverse.unsetFocus(obj)
|
218
|
+
Ncurses.curs_set(0)
|
219
|
+
unless obj.nil?
|
220
|
+
obj.has_focus = false
|
221
|
+
obj.unfocus
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def Traverse.setFocus(obj)
|
226
|
+
unless obj.nil?
|
227
|
+
obj.has_focus = true
|
228
|
+
obj.focus
|
229
|
+
end
|
230
|
+
Ncurses.curs_set(1)
|
231
|
+
end
|
232
|
+
|
233
|
+
def Traverse.switchFocus(newobj, oldobj)
|
234
|
+
if oldobj != newobj
|
235
|
+
Traverse.unsetFocus(oldobj)
|
236
|
+
Traverse.setFocus(newobj)
|
237
|
+
end
|
238
|
+
return newobj
|
239
|
+
end
|
240
|
+
|
241
|
+
def Traverse.checkMenuKey(key_code, function_key)
|
242
|
+
key_code == CDK::KEY_ESC && !function_key
|
243
|
+
end
|
244
|
+
|
245
|
+
def Traverse.handleMenu(screen, menu, oldobj)
|
246
|
+
done = false
|
247
|
+
|
248
|
+
switchFocus(menu, oldobj)
|
249
|
+
while !done
|
250
|
+
key = menu.getch([])
|
251
|
+
|
252
|
+
case key
|
253
|
+
when CDK::KEY_TAB
|
254
|
+
done = true
|
255
|
+
when CDK::KEY_ESC
|
256
|
+
# cleanup the menu
|
257
|
+
menu.inject(key)
|
258
|
+
done = true
|
259
|
+
else
|
260
|
+
done = (menu.inject(key) >= 0)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
if (newobj = Traverse.getCDKFocusCurrent(screen)).nil?
|
265
|
+
newobj = Traverse.setCDKFocusNext(screen)
|
266
|
+
end
|
267
|
+
|
268
|
+
return switchFocus(newobj, menu)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Save data in widgets on a screen
|
272
|
+
def Traverse.saveDataCDKScreen(screen)
|
273
|
+
screen.object.each do |object|
|
274
|
+
unless object.nil?
|
275
|
+
object.saveData
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# Refresh data in widgets on a screen
|
281
|
+
def Traverse.refreshDataCDKScreen(screen)
|
282
|
+
screen.object.each do |object|
|
283
|
+
unless object.nil?
|
284
|
+
object.refreshData
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
data/lib/cdk/uscale.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'scale'
|
2
|
+
|
3
|
+
module CDK
|
4
|
+
class USCALE < CDK::SCALE
|
5
|
+
# The original UScale handled unsigned values.
|
6
|
+
# Since Ruby's typing is different this is really just SCALE
|
7
|
+
# but is nice it's nice to have this for compatibility/completeness
|
8
|
+
# sake.
|
9
|
+
|
10
|
+
def object_type
|
11
|
+
:USCALE
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/cdk/uslider.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require_relative 'slider'
|
2
|
+
|
3
|
+
module CDK
|
4
|
+
class USLIDER < CDK::SLIDER
|
5
|
+
# The original USlider handled unsigned values.
|
6
|
+
# Since Ruby's typing is different this is really just SLIDER
|
7
|
+
# but is nice it's nice to have this for compatibility/completeness
|
8
|
+
# sake.
|
9
|
+
|
10
|
+
def object_type
|
11
|
+
:USLIDER
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/cdk/viewer.rb
ADDED
@@ -0,0 +1,812 @@
|
|
1
|
+
require_relative 'cdk_objs'
|
2
|
+
|
3
|
+
module CDK
|
4
|
+
class VIEWER < CDK::CDKOBJS
|
5
|
+
DOWN = 0
|
6
|
+
UP = 1
|
7
|
+
|
8
|
+
def initialize(cdkscreen, xplace, yplace, height, width,
|
9
|
+
buttons, button_count, button_highlight, box, shadow)
|
10
|
+
super()
|
11
|
+
parent_width = cdkscreen.window.getmaxx
|
12
|
+
parent_height = cdkscreen.window.getmaxy
|
13
|
+
box_width = width
|
14
|
+
box_height = height
|
15
|
+
button_width = 0
|
16
|
+
button_adj = 0
|
17
|
+
button_pos = 1
|
18
|
+
bindings = {
|
19
|
+
CDK::BACKCHAR => Ncurses::KEY_PPAGE,
|
20
|
+
'b' => Ncurses::KEY_PPAGE,
|
21
|
+
'B' => Ncurses::KEY_PPAGE,
|
22
|
+
CDK::FORCHAR => Ncurses::KEY_NPAGE,
|
23
|
+
' ' => Ncurses::KEY_NPAGE,
|
24
|
+
'f' => Ncurses::KEY_NPAGE,
|
25
|
+
'F' => Ncurses::KEY_NPAGE,
|
26
|
+
'|' => Ncurses::KEY_HOME,
|
27
|
+
'$' => Ncurses::KEY_END,
|
28
|
+
}
|
29
|
+
|
30
|
+
self.setBox(box)
|
31
|
+
|
32
|
+
box_height = CDK.setWidgetDimension(parent_height, height, 0)
|
33
|
+
box_width = CDK.setWidgetDimension(parent_width, width, 0)
|
34
|
+
|
35
|
+
# Rejustify the x and y positions if we need to.
|
36
|
+
xtmp = [xplace]
|
37
|
+
ytmp = [yplace]
|
38
|
+
CDK.alignxy(cdkscreen.window, xtmp, ytmp, box_width, box_height)
|
39
|
+
xpos = xtmp[0]
|
40
|
+
ypos = ytmp[0]
|
41
|
+
|
42
|
+
# Make the viewer window.
|
43
|
+
@win= Ncurses::WINDOW.new(box_height, box_width, ypos, xpos)
|
44
|
+
if @win.nil?
|
45
|
+
self.destroy
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# Turn the keypad on for the viewer.
|
50
|
+
@win.keypad(true)
|
51
|
+
|
52
|
+
# Create the buttons.
|
53
|
+
@button_count = button_count
|
54
|
+
@button = []
|
55
|
+
@button_len = []
|
56
|
+
@button_pos = []
|
57
|
+
if button_count > 0
|
58
|
+
(0...button_count).each do |x|
|
59
|
+
button_len = []
|
60
|
+
@button << CDK.char2Chtype(buttons[x], button_len, [])
|
61
|
+
@button_len << button_len[0]
|
62
|
+
button_width += @button_len[x] + 1
|
63
|
+
end
|
64
|
+
button_adj = (box_width - button_width) / (button_count + 1)
|
65
|
+
button_pos = 1 + button_adj
|
66
|
+
(0...button_count).each do |x|
|
67
|
+
@button_pos << button_pos
|
68
|
+
button_pos += button_adj + @button_len[x]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Set the rest of the variables.
|
73
|
+
@screen = cdkscreen
|
74
|
+
@parent = cdkscreen.window
|
75
|
+
@shadow_win = nil
|
76
|
+
@button_highlight = button_highlight
|
77
|
+
@box_height = box_height
|
78
|
+
@box_width = box_width - 2
|
79
|
+
@view_size = height - 2
|
80
|
+
@input_window = @win
|
81
|
+
@shadow = shadow
|
82
|
+
@current_button = 0
|
83
|
+
@current_top = 0
|
84
|
+
@length = 0
|
85
|
+
@left_char = 0
|
86
|
+
@max_left_char = 0
|
87
|
+
@max_top_line = 0
|
88
|
+
@characters = 0
|
89
|
+
@list_size = -1
|
90
|
+
@show_line_info = 1
|
91
|
+
@exit_type = :EARLY_EXIT
|
92
|
+
|
93
|
+
# Do we need to create a shadow?
|
94
|
+
if shadow
|
95
|
+
@shadow_win = Ncurses::WINDOW.new(box_height, box_width + 1,
|
96
|
+
ypos + 1, xpos + 1)
|
97
|
+
if @shadow_win.nil?
|
98
|
+
self.destroy
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Setup the key bindings.
|
104
|
+
bindings.each do |from, to|
|
105
|
+
self.bind(:VIEWER, from, :getc, to)
|
106
|
+
end
|
107
|
+
|
108
|
+
cdkscreen.register(:VIEWER, self)
|
109
|
+
end
|
110
|
+
|
111
|
+
# This function sets various attributes of the widget.
|
112
|
+
def set(title, list, list_size, button_highlight,
|
113
|
+
attr_interp, show_line_info, box)
|
114
|
+
self.setTitle(title)
|
115
|
+
self.setHighlight(button_highlight)
|
116
|
+
self.setInfoLine(show_line_info)
|
117
|
+
self.setBox(box)
|
118
|
+
return self.setInfo(list, list_size, attr_interp)
|
119
|
+
end
|
120
|
+
|
121
|
+
# This sets the title of the viewer. (A nil title is allowed.
|
122
|
+
# It just means that the viewer will not have a title when drawn.)
|
123
|
+
def setTitle(title)
|
124
|
+
super(title, -(@box_width + 1))
|
125
|
+
@title_adj = @title_lines
|
126
|
+
|
127
|
+
# Need to set @view_size
|
128
|
+
@view_size = @box_height - (@title_lines + 1) - 2
|
129
|
+
end
|
130
|
+
|
131
|
+
def getTitle
|
132
|
+
return @title
|
133
|
+
end
|
134
|
+
|
135
|
+
def setupLine(interpret, list, x)
|
136
|
+
# Did they ask for attribute interpretation?
|
137
|
+
if interpret
|
138
|
+
list_len = []
|
139
|
+
list_pos = []
|
140
|
+
@list[x] = CDK.char2Chtype(list, list_len, list_pos)
|
141
|
+
@list_len[x] = list_len[0]
|
142
|
+
@list_pos[x] = CDK.justifyString(@box_width, @list_len[x], list_pos[0])
|
143
|
+
else
|
144
|
+
# We must convert tabs and other nonprinting characters. The curses
|
145
|
+
# library normally does this, but we are bypassing it by writing
|
146
|
+
# chtypes directly.
|
147
|
+
t = ''
|
148
|
+
len = 0
|
149
|
+
(0...list.size).each do |y|
|
150
|
+
if list[y] == "\t".ord
|
151
|
+
begin
|
152
|
+
t << ' '
|
153
|
+
len += 1
|
154
|
+
end while (len & 7) != 0
|
155
|
+
elsif CDK.CharOf(list[y].ord).match(/^[[:print:]]$/)
|
156
|
+
t << CDK.CharOf(list[y].ord)
|
157
|
+
len += 1
|
158
|
+
else
|
159
|
+
t << Ncurses.unctrl(list[y].ord)
|
160
|
+
len += 1
|
161
|
+
end
|
162
|
+
end
|
163
|
+
@list[x] = t
|
164
|
+
@list_len[x] = t.size
|
165
|
+
@list_pos[x] = 0
|
166
|
+
end
|
167
|
+
@widest_line = [@widest_line, @list_len[x]].max
|
168
|
+
end
|
169
|
+
|
170
|
+
def freeLine(x)
|
171
|
+
if x < @list_size
|
172
|
+
@list[x] = ''
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# This function sets the contents of the viewer.
|
177
|
+
def setInfo(list, list_size, interpret)
|
178
|
+
current_line = 0
|
179
|
+
viewer_size = list_size
|
180
|
+
|
181
|
+
if list_size < 0
|
182
|
+
list_size = list.size
|
183
|
+
end
|
184
|
+
|
185
|
+
# Compute the size of the resulting display
|
186
|
+
viewer_size = list_size
|
187
|
+
if list.size > 0 && interpret
|
188
|
+
(0...list_size).each do |x|
|
189
|
+
filename = ''
|
190
|
+
if CDK.checkForLink(list[x], filename) == 1
|
191
|
+
file_contents = []
|
192
|
+
file_len = CDK.readFile(filename, file_contents)
|
193
|
+
|
194
|
+
if file_len >= 0
|
195
|
+
viewer_size += (file_len - 1)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Clean out the old viewer info. (if there is any)
|
202
|
+
@in_progress = true
|
203
|
+
self.clean
|
204
|
+
self.createList(viewer_size)
|
205
|
+
|
206
|
+
# Keep some semi-permanent info
|
207
|
+
@interpret = interpret
|
208
|
+
|
209
|
+
# Copy the information given.
|
210
|
+
current_line = 0
|
211
|
+
x = 0
|
212
|
+
while x < list_size && current_line < viewer_size
|
213
|
+
if list[x].size == 0
|
214
|
+
@list[current_line] = ''
|
215
|
+
@list_len[current_line] = 0
|
216
|
+
@list_pos[current_line] = 0
|
217
|
+
current_line += 1
|
218
|
+
else
|
219
|
+
# Check if we have a file link in this line.
|
220
|
+
filename = []
|
221
|
+
if CDK.checkForLink(list[x], filename) == 1
|
222
|
+
# We have a link, open the file.
|
223
|
+
file_contents = []
|
224
|
+
file_len = 0
|
225
|
+
|
226
|
+
# Open the file and put it into the viewer
|
227
|
+
file_len = CDK.readFile(filename, file_contents)
|
228
|
+
if file_len == -1
|
229
|
+
fopen_fmt = if Ncurses.has_colors?
|
230
|
+
then '<C></16>Link Failed: Could not open the file %s'
|
231
|
+
else '<C></K>Link Failed: Could not open the file %s'
|
232
|
+
end
|
233
|
+
temp = fopen_fmt % filename
|
234
|
+
self.setupLine(true, temp, current_line)
|
235
|
+
current_line += 1
|
236
|
+
else
|
237
|
+
# For each line read, copy it into the viewer.
|
238
|
+
file_len = [file_len, viewer_size - current_line].min
|
239
|
+
(0...file_len).each do |file_line|
|
240
|
+
if current_line >= viewer_size
|
241
|
+
break
|
242
|
+
end
|
243
|
+
self.setupLine(false, file_contents[file_line], current_line)
|
244
|
+
@characters += @list_len[current_line]
|
245
|
+
current_line += 1
|
246
|
+
end
|
247
|
+
end
|
248
|
+
elsif current_line < viewer_size
|
249
|
+
self.setupLine(@interpret, list[x], current_line)
|
250
|
+
@characters += @list_len[current_line]
|
251
|
+
current_line += 1
|
252
|
+
end
|
253
|
+
end
|
254
|
+
x+= 1
|
255
|
+
end
|
256
|
+
|
257
|
+
# Determine how many characters we can shift to the right before
|
258
|
+
# all the items have been viewer off the screen.
|
259
|
+
if @widest_line > @box_width
|
260
|
+
@max_left_char = (@widest_line - @box_width) + 1
|
261
|
+
else
|
262
|
+
@max_left_char = 0
|
263
|
+
end
|
264
|
+
|
265
|
+
# Set up the needed vars for the viewer list.
|
266
|
+
@in_progress = false
|
267
|
+
@list_size = viewer_size
|
268
|
+
if @list_size <= @view_size
|
269
|
+
@max_top_line = 0
|
270
|
+
else
|
271
|
+
@max_top_line = @list_size - 1
|
272
|
+
end
|
273
|
+
return @list_size
|
274
|
+
end
|
275
|
+
|
276
|
+
def getInfo(size)
|
277
|
+
size << @list_size
|
278
|
+
return @list
|
279
|
+
end
|
280
|
+
|
281
|
+
# This function sets the highlight type of the buttons.
|
282
|
+
def setHighlight(button_highlight)
|
283
|
+
@button_highlight = button_highlight
|
284
|
+
end
|
285
|
+
|
286
|
+
def getHighlight
|
287
|
+
return @button_highlight
|
288
|
+
end
|
289
|
+
|
290
|
+
# This sets whether or not you wnat to set the viewer info line.
|
291
|
+
def setInfoLine(show_line_info)
|
292
|
+
@show_line_info = show_line_info
|
293
|
+
end
|
294
|
+
|
295
|
+
def getInfoLine
|
296
|
+
return @show_line_info
|
297
|
+
end
|
298
|
+
|
299
|
+
# This removes all the lines inside the scrolling window.
|
300
|
+
def clean
|
301
|
+
# Clean up the memory used...
|
302
|
+
(0...@list_size).each do |x|
|
303
|
+
self.freeLine(x)
|
304
|
+
end
|
305
|
+
|
306
|
+
# Reset some variables.
|
307
|
+
@list_size = 0
|
308
|
+
@max_left_char = 0
|
309
|
+
@widest_line = 0
|
310
|
+
@current_top = 0
|
311
|
+
@max_top_line = 0
|
312
|
+
|
313
|
+
# Redraw the window.
|
314
|
+
self.draw(@box)
|
315
|
+
end
|
316
|
+
|
317
|
+
def PatternNotFound(pattern)
|
318
|
+
temp_info = [
|
319
|
+
"</U/5>Pattern '%s' not found.<!U!5>" % pattern,
|
320
|
+
]
|
321
|
+
self.popUpLabel(temp_info)
|
322
|
+
end
|
323
|
+
|
324
|
+
# This function actually controls the viewer...
|
325
|
+
def activate(actions)
|
326
|
+
refresh = false
|
327
|
+
# Create the information about the file stats.
|
328
|
+
file_info = [
|
329
|
+
'</5> </U>File Statistics<!U> <!5>',
|
330
|
+
'</5> <!5>',
|
331
|
+
'</5/R>Character Count:<!R> %-4d <!5>' % @characters,
|
332
|
+
'</5/R>Line Count :<!R> %-4d <!5>' % @list_size,
|
333
|
+
'</5> <!5>',
|
334
|
+
'<C></5>Press Any Key To Continue.<!5>'
|
335
|
+
]
|
336
|
+
|
337
|
+
temp_info = ['<C></5>Press Any Key To Continue.<!5>']
|
338
|
+
|
339
|
+
# Set the current button.
|
340
|
+
@current_button = 0
|
341
|
+
|
342
|
+
# Draw the widget list.
|
343
|
+
self.draw(@box)
|
344
|
+
|
345
|
+
# Do this until KEY_ENTER is hit.
|
346
|
+
while true
|
347
|
+
# Reset the refresh flag.
|
348
|
+
refresh = false
|
349
|
+
|
350
|
+
input = self.getch([])
|
351
|
+
if !self.checkBind(:VIEWER, input)
|
352
|
+
case input
|
353
|
+
when CDK::KEY_TAB
|
354
|
+
if @button_count > 1
|
355
|
+
if @current_button == @button_count - 1
|
356
|
+
@current_button = 0
|
357
|
+
else
|
358
|
+
@current_button += 1
|
359
|
+
end
|
360
|
+
|
361
|
+
# Redraw the buttons.
|
362
|
+
self.drawButtons
|
363
|
+
end
|
364
|
+
when CDK::PREV
|
365
|
+
if @button_count > 1
|
366
|
+
if @current_button == 0
|
367
|
+
@current_button = @button_count - 1
|
368
|
+
else
|
369
|
+
@current_button -= 1
|
370
|
+
end
|
371
|
+
|
372
|
+
# Redraw the buttons.
|
373
|
+
self.drawButtons
|
374
|
+
end
|
375
|
+
when Ncurses::KEY_UP
|
376
|
+
if @current_top > 0
|
377
|
+
@current_top -= 1
|
378
|
+
refresh = true
|
379
|
+
else
|
380
|
+
CDK.Beep
|
381
|
+
end
|
382
|
+
when Ncurses::KEY_DOWN
|
383
|
+
if @current_top < @max_top_line
|
384
|
+
@current_top += 1
|
385
|
+
refresh = true
|
386
|
+
else
|
387
|
+
CDK.Beep
|
388
|
+
end
|
389
|
+
when Ncurses::KEY_RIGHT
|
390
|
+
if @left_char < @max_left_char
|
391
|
+
@left_char += 1
|
392
|
+
refresh = true
|
393
|
+
else
|
394
|
+
CDK.Beep
|
395
|
+
end
|
396
|
+
when Ncurses::KEY_LEFT
|
397
|
+
if @left_char > 0
|
398
|
+
@left_char -= 1
|
399
|
+
refresh = true
|
400
|
+
else
|
401
|
+
CDK.Beep
|
402
|
+
end
|
403
|
+
when Ncurses::KEY_PPAGE
|
404
|
+
if @current_top > 0
|
405
|
+
if @current_top - (@view_size - 1) > 0
|
406
|
+
@current_top = @current_top - (@view_size - 1)
|
407
|
+
else
|
408
|
+
@current_top = 0
|
409
|
+
end
|
410
|
+
refresh = true
|
411
|
+
else
|
412
|
+
CDK.Beep
|
413
|
+
end
|
414
|
+
when Ncurses::KEY_NPAGE
|
415
|
+
if @current_top < @max_top_line
|
416
|
+
if @current_top + @view_size < @max_top_line
|
417
|
+
@current_top = @current_top + (@view_size - 1)
|
418
|
+
else
|
419
|
+
@current_top = @max_top_line
|
420
|
+
end
|
421
|
+
refresh = true
|
422
|
+
else
|
423
|
+
CDK.Beep
|
424
|
+
end
|
425
|
+
when Ncurses::KEY_HOME
|
426
|
+
@left_char = 0
|
427
|
+
refresh = true
|
428
|
+
when Ncurses::KEY_END
|
429
|
+
@left_char = @max_left_char
|
430
|
+
refresh = true
|
431
|
+
when 'g'.ord, '1'.ord, '<'.ord
|
432
|
+
@current_top = 0
|
433
|
+
refresh = true
|
434
|
+
when 'G'.ord, '>'.ord
|
435
|
+
@current_top = @max_top_line
|
436
|
+
refresh = true
|
437
|
+
when 'L'.ord
|
438
|
+
x = (@list_size + @current_top) / 2
|
439
|
+
if x < @max_top_line
|
440
|
+
@current_top = x
|
441
|
+
refresh = true
|
442
|
+
else
|
443
|
+
CDK.Beep
|
444
|
+
end
|
445
|
+
when 'l'.ord
|
446
|
+
x = @current_top / 2
|
447
|
+
if x >= 0
|
448
|
+
@current_top = x
|
449
|
+
refresh = true
|
450
|
+
else
|
451
|
+
CDK.Beep
|
452
|
+
end
|
453
|
+
when '?'.ord
|
454
|
+
@search_direction = CDK::VIEWER::UP
|
455
|
+
self.getAndStorePattern(@screen)
|
456
|
+
if !self.searchForWord(@search_pattern, @search_direction)
|
457
|
+
self.PatternNotFound(@search_pattern)
|
458
|
+
end
|
459
|
+
refresh = true
|
460
|
+
when '/'.ord
|
461
|
+
@search_direction = CDK::VIEWER:DOWN
|
462
|
+
self.getAndStorePattern(@screen)
|
463
|
+
if !self.searchForWord(@search_pattern, @search_direction)
|
464
|
+
self.PatternNotFound(@search_pattern)
|
465
|
+
end
|
466
|
+
refresh = true
|
467
|
+
when 'N'.ord, 'n'.ord
|
468
|
+
if @search_pattern == ''
|
469
|
+
temp_info[0] = '</5>There is no pattern in the buffer.<!5>'
|
470
|
+
self.popUpLabel(temp_info)
|
471
|
+
elsif !self.searchForWord(@search_pattern,
|
472
|
+
if input == 'n'.ord
|
473
|
+
then @search_direction
|
474
|
+
else 1 - @search_direction
|
475
|
+
end)
|
476
|
+
self.PatternNotFound(@search_pattern)
|
477
|
+
end
|
478
|
+
refresh = true
|
479
|
+
when ':'.ord
|
480
|
+
@current_top = self.jumpToLine
|
481
|
+
refresh = true
|
482
|
+
when 'i'.ord, 's'.ord, 'S'.ord
|
483
|
+
self.popUpLabel(file_info)
|
484
|
+
refresh = true
|
485
|
+
when CDK::KEY_ESC
|
486
|
+
self.setExitType(input)
|
487
|
+
return -1
|
488
|
+
when Ncurses::ERR
|
489
|
+
self.setExitType(input)
|
490
|
+
return -1
|
491
|
+
when Ncurses::KEY_ENTER, CDK::KEY_RETURN
|
492
|
+
self.setExitType(input)
|
493
|
+
return @current_button
|
494
|
+
when CDK::REFRESH
|
495
|
+
@screen.erase
|
496
|
+
@screen.refresh
|
497
|
+
else
|
498
|
+
CDK.Beep
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
# Do we need to redraw the screen?
|
503
|
+
if refresh
|
504
|
+
self.drawInfo
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
# This searches the document looking for the given word.
|
510
|
+
def getAndStorePattern(screen)
|
511
|
+
temp = ''
|
512
|
+
|
513
|
+
# Check the direction.
|
514
|
+
if @search_direction == CDK::VIEWER::UP
|
515
|
+
temp = '</5>Search Up : <!5>'
|
516
|
+
else
|
517
|
+
temp = '</5>Search Down: <!5>'
|
518
|
+
end
|
519
|
+
|
520
|
+
# Pop up the entry field.
|
521
|
+
get_pattern = CDK::ENTRY.new(screen, CDK::CENTER, CDK::CENTER,
|
522
|
+
'', label, Ncurses.COLOR_PAIR(5) | Ncurses::A_BOLD,
|
523
|
+
'.' | Ncurses.COLOR_PAIR(5) | Ncurses::A_BOLD,
|
524
|
+
:MIXED, 10, 0, 256, true, false)
|
525
|
+
|
526
|
+
# Is there an old search pattern?
|
527
|
+
if @search_pattern.size != 0
|
528
|
+
get_pattern.set(@search_pattern, get_pattern.min, get_pattern.max,
|
529
|
+
get_pattern.box)
|
530
|
+
end
|
531
|
+
|
532
|
+
# Activate this baby.
|
533
|
+
list = get_pattern.activate([])
|
534
|
+
|
535
|
+
# Save teh list.
|
536
|
+
if list.size != 0
|
537
|
+
@search_pattern = list
|
538
|
+
end
|
539
|
+
|
540
|
+
# Clean up.
|
541
|
+
get_pattern.destroy
|
542
|
+
end
|
543
|
+
|
544
|
+
# This searches for a line containing the word and realigns the value on
|
545
|
+
# the screen.
|
546
|
+
def searchForWord(pattern, direction)
|
547
|
+
found = false
|
548
|
+
|
549
|
+
# If the pattern is empty then return.
|
550
|
+
if pattern.size != 0
|
551
|
+
if direction == CDK::VIEWER::DOWN
|
552
|
+
# Start looking from 'here' down.
|
553
|
+
x = @current_top + 1
|
554
|
+
while !found && x < @list_size
|
555
|
+
pos = 0
|
556
|
+
y = 0
|
557
|
+
while y < @list[x].size
|
558
|
+
plain_char = CDK.CharOf(@list[x][y])
|
559
|
+
|
560
|
+
pos += 1
|
561
|
+
if @CDK.CharOf(pattern[pos-1]) != plain_char
|
562
|
+
y -= (pos - 1)
|
563
|
+
pos = 0
|
564
|
+
elsif pos == pattern.size
|
565
|
+
@current_top = [x, @max_top_line].min
|
566
|
+
@left_char = if y < @box_width then 0 else @max_left_char end
|
567
|
+
found = true
|
568
|
+
break
|
569
|
+
end
|
570
|
+
y += 1
|
571
|
+
end
|
572
|
+
x += 1
|
573
|
+
end
|
574
|
+
else
|
575
|
+
# Start looking from 'here' up.
|
576
|
+
x = @current_top - 1
|
577
|
+
while ! found && x >= 0
|
578
|
+
y = 0
|
579
|
+
pos = 0
|
580
|
+
while y < @list[x].size
|
581
|
+
plain_char = CDK.CharOf(@list[x][y])
|
582
|
+
|
583
|
+
pos += 1
|
584
|
+
if CDK.CharOf(pattern[pos-1]) != plain_char
|
585
|
+
y -= (pos - 1)
|
586
|
+
pos = 0
|
587
|
+
elsif pos == pattern.size
|
588
|
+
@current_top = x
|
589
|
+
@left_char = if y < @box_width then 0 else @max_left_char end
|
590
|
+
found = true
|
591
|
+
break
|
592
|
+
end
|
593
|
+
end
|
594
|
+
end
|
595
|
+
end
|
596
|
+
end
|
597
|
+
return found
|
598
|
+
end
|
599
|
+
|
600
|
+
# This allows us to 'jump' to a given line in the file.
|
601
|
+
def jumpToLine
|
602
|
+
newline = CDK::SCALE.new(@screen, CDK::CENTER, CDK::CENTER,
|
603
|
+
'<C>Jump To Line', '</5>Line :', Ncurses::A_BOLD,
|
604
|
+
@list_size.size + 1, @current_top + 1, 0, @max_top_line + 1,
|
605
|
+
1, 10, true, true)
|
606
|
+
line = newline.activate([])
|
607
|
+
newline.destroy
|
608
|
+
return line - 1
|
609
|
+
end
|
610
|
+
|
611
|
+
# This pops a little message up on the screen.
|
612
|
+
def popUpLabel(mesg)
|
613
|
+
# Set up variables.
|
614
|
+
label = CDK::LABEL.new(@screen, CDK::CENTER, CDK::CENTER,
|
615
|
+
mesg, mesg.size, true, false)
|
616
|
+
|
617
|
+
# Draw the label and wait.
|
618
|
+
label.draw(true)
|
619
|
+
label.getch([])
|
620
|
+
|
621
|
+
# Clean up.
|
622
|
+
label.destroy
|
623
|
+
end
|
624
|
+
|
625
|
+
# This moves the viewer field to the given location.
|
626
|
+
# Inherited
|
627
|
+
# def move(xplace, yplace, relative, refresh_flag)
|
628
|
+
# end
|
629
|
+
|
630
|
+
# This function draws the viewer widget.
|
631
|
+
def draw(box)
|
632
|
+
# Do we need to draw in the shadow?
|
633
|
+
unless @shadow_win.nil?
|
634
|
+
Draw.drawShadow(@shadow_win)
|
635
|
+
end
|
636
|
+
|
637
|
+
# Box it if it was asked for.
|
638
|
+
if box
|
639
|
+
Draw.drawObjBox(@win, self)
|
640
|
+
@win.wrefresh
|
641
|
+
end
|
642
|
+
|
643
|
+
# Draw the info in the viewer.
|
644
|
+
self.drawInfo
|
645
|
+
end
|
646
|
+
|
647
|
+
# This redraws the viewer buttons.
|
648
|
+
def drawButtons
|
649
|
+
# No buttons, no drawing
|
650
|
+
if @button_count == 0
|
651
|
+
return
|
652
|
+
end
|
653
|
+
|
654
|
+
# Redraw the buttons.
|
655
|
+
(0...@button_count).each do |x|
|
656
|
+
Draw.writeChtype(@win, @button_pos[x], @box_height - 2,
|
657
|
+
@button[x], CDK::HORIZONTAL, 0, @button_len[x])
|
658
|
+
end
|
659
|
+
|
660
|
+
# Highlight the current button.
|
661
|
+
(0...@button_len[@current_button]).each do |x|
|
662
|
+
# Strip the character of any extra attributes.
|
663
|
+
character = CDK.CharOf(@button[@current_button][x])
|
664
|
+
|
665
|
+
# Add the character into the window.
|
666
|
+
@win.mvwaddch(@box_height - 2, @button_pos[@current_button] + x,
|
667
|
+
character.ord | @button_highlight)
|
668
|
+
end
|
669
|
+
|
670
|
+
# Refresh the window.
|
671
|
+
@win.wrefresh
|
672
|
+
end
|
673
|
+
|
674
|
+
# This sets the background attribute of the widget.
|
675
|
+
def setBKattr(attrib)
|
676
|
+
@win.wbkgd(attrib)
|
677
|
+
end
|
678
|
+
|
679
|
+
def destroyInfo
|
680
|
+
@list = []
|
681
|
+
@list_pos = []
|
682
|
+
@list_len = []
|
683
|
+
end
|
684
|
+
|
685
|
+
# This function destroys the viewer widget.
|
686
|
+
def destroy
|
687
|
+
self.destroyInfo
|
688
|
+
|
689
|
+
self.cleanTitle
|
690
|
+
|
691
|
+
# Clean up the windows.
|
692
|
+
CDK.deleteCursesWindow(@shadow_win)
|
693
|
+
CDK.deleteCursesWindow(@win)
|
694
|
+
|
695
|
+
# Clean the key bindings.
|
696
|
+
self.cleanBindings(:VIEWER)
|
697
|
+
|
698
|
+
# Unregister this object.
|
699
|
+
CDK::SCREEN.unregister(:VIEWER, self)
|
700
|
+
end
|
701
|
+
|
702
|
+
# This function erases the viewer widget from the screen.
|
703
|
+
def erase
|
704
|
+
if self.validCDKObject
|
705
|
+
CDK.eraseCursesWindow(@win)
|
706
|
+
CDK.eraseCursesWindow(@shadow_win)
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
# This draws the viewer info lines.
|
711
|
+
def drawInfo
|
712
|
+
temp = ''
|
713
|
+
line_adjust = false
|
714
|
+
|
715
|
+
# Clear the window.
|
716
|
+
@win.werase
|
717
|
+
|
718
|
+
self.drawTitle(@win)
|
719
|
+
|
720
|
+
# Draw in the current line at the top.
|
721
|
+
if @show_line_info == true
|
722
|
+
# Set up the info line and draw it.
|
723
|
+
if @in_progress
|
724
|
+
temp = 'processing...'
|
725
|
+
elsif @list_size != 0
|
726
|
+
temp = '%d/%d %2.0f%%' % [@current_top + 1, @list_size,
|
727
|
+
((1.0 * @current_top + 1) / (@list_size)) * 100]
|
728
|
+
else
|
729
|
+
temp = '%d/%d %2.0f%%' % [0, 0, 0.0]
|
730
|
+
end
|
731
|
+
|
732
|
+
# The list_adjust variable tells us if we have to shift down one line
|
733
|
+
# because the person asked for the line X of Y line at the top of the
|
734
|
+
# screen. We only want to set this to true if they asked for the info
|
735
|
+
# line and there is no title or if the two items overlap.
|
736
|
+
if @title_lines == '' || @title_pos[0] < temp.size + 2
|
737
|
+
list_adjust = true
|
738
|
+
end
|
739
|
+
Draw.writeChar(@win, 1,
|
740
|
+
if list_adjust then @title_lines else 0 end + 1,
|
741
|
+
temp, CDK::HORIZONTAL, 0, temp.size)
|
742
|
+
end
|
743
|
+
|
744
|
+
# Determine the last line to draw.
|
745
|
+
last_line = [@list_size, @view_size].min
|
746
|
+
last_line -= if list_adjust then 1 else 0 end
|
747
|
+
|
748
|
+
# Redraw the list.
|
749
|
+
(0...last_line).each do |x|
|
750
|
+
if @current_top + x < @list_size
|
751
|
+
screen_pos = @list_pos[@current_top + x] + 1 - @left_char
|
752
|
+
|
753
|
+
Draw.writeChtype(@win,
|
754
|
+
if screen_pos >= 0 then screen_pos else 1 end,
|
755
|
+
x + @title_lines + if list_adjust then 1 else 0 end + 1,
|
756
|
+
@list[x + @current_top], CDK::HORIZONTAL,
|
757
|
+
if screen_pos >= 0
|
758
|
+
then 0
|
759
|
+
else @left_char - @list_pos[@current_top + x]
|
760
|
+
end,
|
761
|
+
@list_len[x + @current_top])
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
# Box it if we have to.
|
766
|
+
if @box
|
767
|
+
Draw.drawObjBox(@win, self)
|
768
|
+
@win.wrefresh
|
769
|
+
end
|
770
|
+
|
771
|
+
# Draw the separation line.
|
772
|
+
if @button_count > 0
|
773
|
+
boxattr = @BXAttr
|
774
|
+
|
775
|
+
(1..@box_width).each do |x|
|
776
|
+
@win.mvwaddch(@box_height - 3, x, @HZChar | boxattr)
|
777
|
+
end
|
778
|
+
|
779
|
+
@win.mvwaddch(@box_height - 3, 0, Ncurses::ACS_LTEE | boxattr)
|
780
|
+
@win.mvwaddch(@box_height - 3, @win.getmaxx - 1,
|
781
|
+
Ncurses::ACS_RTEE | boxattr)
|
782
|
+
end
|
783
|
+
|
784
|
+
# Draw the buttons. This will call refresh on the viewer win.
|
785
|
+
self.drawButtons
|
786
|
+
end
|
787
|
+
|
788
|
+
# The list_size may be negative, to assign no definite limit.
|
789
|
+
def createList(list_size)
|
790
|
+
status = false
|
791
|
+
|
792
|
+
self.destroyInfo
|
793
|
+
|
794
|
+
if list_size >= 0
|
795
|
+
status = true
|
796
|
+
|
797
|
+
@list = []
|
798
|
+
@list_pos = []
|
799
|
+
@list_len = []
|
800
|
+
end
|
801
|
+
return status
|
802
|
+
end
|
803
|
+
|
804
|
+
def position
|
805
|
+
super(@win)
|
806
|
+
end
|
807
|
+
|
808
|
+
def object_type
|
809
|
+
:VIEWER
|
810
|
+
end
|
811
|
+
end
|
812
|
+
end
|