weft-qda 0.9.6 → 0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/weft.rb +16 -1
- data/lib/weft/WEFT-VERSION-STRING.rb +1 -1
- data/lib/weft/application.rb +17 -74
- data/lib/weft/backend.rb +6 -32
- data/lib/weft/backend/sqlite.rb +222 -164
- data/lib/weft/backend/sqlite/category_tree.rb +52 -48
- data/lib/weft/backend/sqlite/database.rb +57 -0
- data/lib/weft/backend/sqlite/upgradeable.rb +7 -0
- data/lib/weft/broadcaster.rb +90 -0
- data/lib/weft/category.rb +139 -47
- data/lib/weft/codereview.rb +160 -0
- data/lib/weft/coding.rb +74 -23
- data/lib/weft/document.rb +23 -10
- data/lib/weft/exceptions.rb +10 -0
- data/lib/weft/filters.rb +47 -224
- data/lib/weft/filters/indexers.rb +137 -0
- data/lib/weft/filters/input.rb +118 -0
- data/lib/weft/filters/output.rb +101 -0
- data/lib/weft/filters/templates.rb +80 -0
- data/lib/weft/filters/win32backtick.rb +246 -0
- data/lib/weft/query.rb +169 -0
- data/lib/weft/wxgui.rb +349 -294
- data/lib/weft/wxgui/constants.rb +43 -0
- data/lib/weft/wxgui/controls.rb +6 -0
- data/lib/weft/wxgui/controls/category_dropdown.rb +192 -0
- data/lib/weft/wxgui/controls/category_tree.rb +314 -0
- data/lib/weft/wxgui/controls/document_list.rb +97 -0
- data/lib/weft/wxgui/controls/multitype_control.rb +37 -0
- data/lib/weft/wxgui/{inspectors → controls}/textcontrols.rb +235 -64
- data/lib/weft/wxgui/dialogs.rb +144 -41
- data/lib/weft/wxgui/error_handler.rb +116 -36
- data/lib/weft/wxgui/exceptions.rb +7 -0
- data/lib/weft/wxgui/inspectors.rb +61 -208
- data/lib/weft/wxgui/inspectors/category.rb +19 -16
- data/lib/weft/wxgui/inspectors/codereview.rb +90 -132
- data/lib/weft/wxgui/inspectors/document.rb +12 -8
- data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -56
- data/lib/weft/wxgui/inspectors/query.rb +284 -0
- data/lib/weft/wxgui/inspectors/script.rb +147 -23
- data/lib/weft/wxgui/lang/en.rb +69 -0
- data/lib/weft/wxgui/sidebar.rb +90 -432
- data/lib/weft/wxgui/utilities.rb +70 -91
- data/lib/weft/wxgui/workarea.rb +150 -43
- data/share/icons/category.ico +0 -0
- data/share/icons/category.xpm +109 -0
- data/share/icons/codereview.ico +0 -0
- data/share/icons/codereview.xpm +54 -0
- data/share/icons/d_and_c.xpm +126 -0
- data/share/icons/document.ico +0 -0
- data/share/icons/document.xpm +70 -0
- data/share/icons/project.ico +0 -0
- data/share/icons/query.ico +0 -0
- data/share/icons/query.xpm +56 -0
- data/{lib/weft/wxgui → share/icons}/search.xpm +0 -0
- data/share/icons/weft.ico +0 -0
- data/share/icons/weft.xpm +62 -0
- data/share/icons/weft16.ico +0 -0
- data/share/icons/weft32.ico +0 -0
- data/share/templates/category_plain.html +18 -0
- data/share/templates/codereview_plain.html +18 -0
- data/share/templates/document_plain.html +13 -0
- data/share/templates/document_plain.txt +7 -0
- data/test/001-document.rb +55 -36
- data/test/002-category.rb +81 -6
- data/test/003-code.rb +8 -4
- data/test/004-application.rb +13 -34
- data/test/005-query_review.rb +139 -0
- data/test/006-filters.rb +54 -42
- data/test/007-output_filters.rb +113 -0
- data/test/009a-backend_sqlite_basic.rb +95 -24
- data/test/009b-backend_sqlite_complex.rb +43 -62
- data/test/009c_backend_sqlite_bench.rb +5 -10
- data/test/053-doc_inspector.rb +46 -0
- data/test/055-query_window.rb +50 -0
- data/test/all-tests.rb +1 -0
- data/test/test-common.rb +19 -0
- data/test/testdata/empty.qdp +0 -0
- data/test/testdata/simple with space.pdf +0 -0
- data/test/testdata/simple.pdf +0 -0
- data/weft-qda.rb +40 -7
- metadata +74 -14
- data/lib/weft/wxgui/category.xpm +0 -26
- data/lib/weft/wxgui/document.xpm +0 -25
- data/lib/weft/wxgui/inspectors/search.rb +0 -265
- data/lib/weft/wxgui/mondrian.xpm +0 -44
- data/lib/weft/wxgui/weft16.xpm +0 -31
data/lib/weft/wxgui/category.xpm
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
/* XPM */
|
2
|
-
static char *category[] = {
|
3
|
-
/* columns rows colors chars-per-pixel */
|
4
|
-
"16 16 4 1",
|
5
|
-
" c black",
|
6
|
-
". c gray60",
|
7
|
-
"X c gray100",
|
8
|
-
"o c None",
|
9
|
-
/* pixels */
|
10
|
-
"oooooooooooooooo",
|
11
|
-
"oo ooooooooo",
|
12
|
-
"o XXXXX oooooooo",
|
13
|
-
"o XXXXX oooooooo",
|
14
|
-
"o XXXXX oooooooo",
|
15
|
-
"oo ooooooooo",
|
16
|
-
"oooo.ooooooooooo",
|
17
|
-
"oooo.ooooooooooo",
|
18
|
-
"oooo.ooo oo",
|
19
|
-
"oooo.ooo XXXX oo",
|
20
|
-
"oooo.... oo",
|
21
|
-
"oooo.ooooooooooo",
|
22
|
-
"oooo.ooo oo",
|
23
|
-
"oooo.ooo XXXX oo",
|
24
|
-
"oooo.... oo",
|
25
|
-
"oooooooooooooooo"
|
26
|
-
};
|
data/lib/weft/wxgui/document.xpm
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
/* XPM */
|
2
|
-
static char *document[] = {
|
3
|
-
/* columns rows colors chars-per-pixel */
|
4
|
-
"16 16 3 1",
|
5
|
-
" c black",
|
6
|
-
". c gray100",
|
7
|
-
"X c None",
|
8
|
-
/* pixels */
|
9
|
-
"XXXXXXXXXXXXXXXX",
|
10
|
-
"XX XXXXX",
|
11
|
-
"XX ........ XXXX",
|
12
|
-
"XX ......... XXX",
|
13
|
-
"XX . ... XX",
|
14
|
-
"XX .......... XX",
|
15
|
-
"XX . . XX",
|
16
|
-
"XX .......... XX",
|
17
|
-
"XX . . XX",
|
18
|
-
"XX .......... XX",
|
19
|
-
"XX . . XX",
|
20
|
-
"XX .......... XX",
|
21
|
-
"XX . . XX",
|
22
|
-
"XX .......... XX",
|
23
|
-
"XX XX",
|
24
|
-
"XXXXXXXXXXXXXXXX"
|
25
|
-
};
|
@@ -1,265 +0,0 @@
|
|
1
|
-
module QDA::GUI
|
2
|
-
# a window that allows the entry of criteria (category
|
3
|
-
# intersections and unions, text search, document attributes), and
|
4
|
-
# interactively presents the text meeting the criteria, and to save
|
5
|
-
# the results as a permanent category (memo describing the rules
|
6
|
-
# used to produce the codes.
|
7
|
-
class QueryWindow < InspectorWindow
|
8
|
-
include Subscriber
|
9
|
-
|
10
|
-
# obj is a simple object with a +dbid+ representing a project
|
11
|
-
# unique identifier for this query
|
12
|
-
def initialize(obj, app, workarea, layout)
|
13
|
-
@app = app
|
14
|
-
@query = obj # currently a pretty much empty object
|
15
|
-
|
16
|
-
super(workarea, "Query #{@query.dbid}", layout) do | parent |
|
17
|
-
@text_box = CompositeText.new(parent, '', 0, @app)
|
18
|
-
end
|
19
|
-
set_icon( app.fetch_icon("search.xpm") )
|
20
|
-
construct_query_panel
|
21
|
-
@notebook.selection = 1
|
22
|
-
subscribe()
|
23
|
-
# evt_set_focus() { | e | p "focussed #{e.event_object}"; e.skip() }
|
24
|
-
end
|
25
|
-
|
26
|
-
def associated_subscribers()
|
27
|
-
super << @objects.grep(CategoryDropDown)
|
28
|
-
end
|
29
|
-
|
30
|
-
# runs a query using the currently selected query options
|
31
|
-
def get_results()
|
32
|
-
begin
|
33
|
-
# remember the last query we did
|
34
|
-
@last_query = get_query_string
|
35
|
-
@last_results = @app.app.do_query( *get_query_arguments )
|
36
|
-
rescue Exception => err
|
37
|
-
Wx::MessageDialog.new(nil, err.to_s,
|
38
|
-
"Error running query",
|
39
|
-
Wx::OK|Wx::ICON_EXCLAMATION).show_modal()
|
40
|
-
return
|
41
|
-
end
|
42
|
-
return @last_results
|
43
|
-
end
|
44
|
-
|
45
|
-
def get_target_argument(offset, as_string = false)
|
46
|
-
return nil unless @objects[offset].is_shown
|
47
|
-
if @rules[offset].value == 'IS CODED BY'
|
48
|
-
if @objects[offset].current_category
|
49
|
-
category = @objects[offset].current_category
|
50
|
-
return as_string ? category.name : category.dbid
|
51
|
-
else
|
52
|
-
# use fail rather than raise as it gets interp as a method...
|
53
|
-
fail RuntimeError.new("No category value set for CODED BY")
|
54
|
-
end
|
55
|
-
elsif @rules[offset].value == 'CONTAINS WORD'
|
56
|
-
str = @objects[offset].get_value
|
57
|
-
if str.empty?
|
58
|
-
fail RuntimeError.new("No text search set for CONTAINS WORD")
|
59
|
-
else
|
60
|
-
return str
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def get_query_arguments()
|
66
|
-
return @rules[0].value, get_target_argument(0),
|
67
|
-
@ops[0].value,
|
68
|
-
@rules[1].value, get_target_argument(1)
|
69
|
-
end
|
70
|
-
|
71
|
-
# TODO
|
72
|
-
# returns a stringified representation of the query. The only
|
73
|
-
# reason this is here at the moment is because the underlying
|
74
|
-
# query code parser doesn't accept string literal paths or names
|
75
|
-
# for categories- only database ids. This should be remedied, so
|
76
|
-
# the string passed to query and this are the same.
|
77
|
-
def get_query_string(join_char = " ")
|
78
|
-
begin
|
79
|
-
query_parts = [ @rules[0].value, get_target_argument(0, true) ]
|
80
|
-
# add on the second bit if used
|
81
|
-
# TODO - this is barbaric - this whole thing needs tidying
|
82
|
-
# and being made more extensible n = many
|
83
|
-
if @ops[0].value != ''
|
84
|
-
query_parts += [ @ops[0].value,
|
85
|
-
@rules[1].value, get_target_argument(1, true) ]
|
86
|
-
end
|
87
|
-
return query_parts.join(join_char)
|
88
|
-
rescue Exception => err
|
89
|
-
return ''
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
# run the query, display the results, view them
|
94
|
-
def on_view(e)
|
95
|
-
Wx::BusyCursor.busy do
|
96
|
-
results = get_results()
|
97
|
-
return unless results
|
98
|
-
@text_box.populate( results )
|
99
|
-
if curr_category = @drop_down.current_category()
|
100
|
-
@text_box.highlight_codingtable(curr_category.codes)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
@notebook.selection = 0
|
104
|
-
end
|
105
|
-
|
106
|
-
def on_save_results(e)
|
107
|
-
# check for saved results
|
108
|
-
unless @last_results and ! @last_query.empty? and
|
109
|
-
@last_query == get_query_string()
|
110
|
-
Wx::BusyCursor.busy do
|
111
|
-
results = get_results()
|
112
|
-
return unless results
|
113
|
-
@text_box.populate(results)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
q_string = get_query_string
|
118
|
-
title = "'#{q_string[0, 30]}...' (query results)"
|
119
|
-
search_parent = @app.app.get_root_category('SEARCHES')
|
120
|
-
query_cat = Category.new( title, search_parent )
|
121
|
-
query_cat.codetable = @last_results
|
122
|
-
query_cat.memo = get_query_string("\n")
|
123
|
-
$wxapp.app.save_category(query_cat)
|
124
|
-
$wxapp.broadcast(:category_added, query_cat)
|
125
|
-
end
|
126
|
-
|
127
|
-
def on_focus_dropdown(e)
|
128
|
-
# p "focussed #{e.event_object}"
|
129
|
-
# e.skip()
|
130
|
-
end
|
131
|
-
|
132
|
-
def on_blur_dropdown(e)
|
133
|
-
# p "blurred #{e.event_object}"
|
134
|
-
# p active?
|
135
|
-
# e.skip()
|
136
|
-
end
|
137
|
-
|
138
|
-
def construct_query_panel()
|
139
|
-
panel2 = Wx::Panel.new(@notebook, -1)
|
140
|
-
sizer2 = Wx::BoxSizer.new(Wx::VERTICAL)
|
141
|
-
sizer2.add( Wx::StaticText.new(panel2, -1, 'Find text that:'),
|
142
|
-
0, Wx::ALL|Wx::ADJUST_MINSIZE, 4 )
|
143
|
-
|
144
|
-
# criteria consist of rules ('is coded by', 'text matches'),
|
145
|
-
# targets ('category "Foo"', 'text "Bar"') and may be joined by
|
146
|
-
# ops ('AND', 'OR')
|
147
|
-
|
148
|
-
@rules = []
|
149
|
-
@objects = []
|
150
|
-
@ops = []
|
151
|
-
|
152
|
-
@crit_panel = Wx::Panel.new(panel2, -1)
|
153
|
-
@crit_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
154
|
-
|
155
|
-
add_criterion(true)
|
156
|
-
add_criterion(false)
|
157
|
-
|
158
|
-
|
159
|
-
@ops[0].evt_combobox(@ops[0].get_id) do | e |
|
160
|
-
operator = @ops[0].get_string_selection
|
161
|
-
if operator != ""
|
162
|
-
@rules[1].show(true)
|
163
|
-
@objects[1].show(true)
|
164
|
-
else
|
165
|
-
@rules[1].hide()
|
166
|
-
@objects[1].hide()
|
167
|
-
end
|
168
|
-
end
|
169
|
-
@rules[1].hide()
|
170
|
-
@objects[1].hide()
|
171
|
-
|
172
|
-
@crit_panel.set_sizer(@crit_sizer)
|
173
|
-
sizer2.add(@crit_panel, 1, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4 )
|
174
|
-
|
175
|
-
butt_panel = Wx::Panel.new(panel2, -1)
|
176
|
-
bott_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
177
|
-
|
178
|
-
button = Wx::Button.new(butt_panel, -1, 'View')
|
179
|
-
button.evt_button(button.get_id) { | e | on_view(e) }
|
180
|
-
bott_sizer.add(button, 1, Wx::ALL, 4)
|
181
|
-
|
182
|
-
button = Wx::Button.new(butt_panel, -1, 'Save Results')
|
183
|
-
button.evt_button(button.get_id) { | e | on_save_results(e) }
|
184
|
-
bott_sizer.add(button, 1, Wx::ALL, 4)
|
185
|
-
|
186
|
-
butt_panel.set_sizer(bott_sizer)
|
187
|
-
sizer2.add(butt_panel, 0,
|
188
|
-
Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALIGN_BOTTOM)
|
189
|
-
@notebook.add_page(panel2, 'query')
|
190
|
-
panel2.set_sizer_and_fit(sizer2)
|
191
|
-
# sizer2.set_size_hints(panel2)
|
192
|
-
end
|
193
|
-
|
194
|
-
def add_criterion(with_bool = false)
|
195
|
-
row_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
196
|
-
row_n = @rules.length
|
197
|
-
|
198
|
-
type_op1 = Wx::ComboBox.new(@crit_panel, -1, '', Wx::DEFAULT_POSITION,
|
199
|
-
Wx::DEFAULT_SIZE, [],
|
200
|
-
Wx::CB_READONLY|Wx::CB_DROPDOWN)
|
201
|
-
type_op1.append('IS CODED BY')
|
202
|
-
type_op1.append('CONTAINS WORD')
|
203
|
-
|
204
|
-
type_op1.selection = 0
|
205
|
-
row_sizer.add( type_op1, 1, Wx::GROW|Wx::ALL)
|
206
|
-
@rules.push( type_op1 )
|
207
|
-
|
208
|
-
op2_placeholder = Wx::BoxSizer.new(Wx::VERTICAL)
|
209
|
-
type_op2 = CategoryDropDown.new(@app, @crit_panel, nil, true)
|
210
|
-
# TODO - allow currently highlighted category dropdown to
|
211
|
-
# receive global focus_category events
|
212
|
-
# type_op2.evt_set_focus() { | e | on_focus_dropdown(e) }
|
213
|
-
# type_op2.evt_kill_focus() { | e | on_blur_dropdown(e) }
|
214
|
-
|
215
|
-
op2_placeholder.add(type_op2, 1,
|
216
|
-
Wx:: GROW|Wx::ALL)
|
217
|
-
|
218
|
-
type_op2_a = Wx::TextCtrl.new(@crit_panel, -1, '')
|
219
|
-
type_op2_a.hide()
|
220
|
-
row_sizer.add( op2_placeholder, 1, Wx::ADJUST_MINSIZE)
|
221
|
-
|
222
|
-
@objects.push( type_op2 )
|
223
|
-
|
224
|
-
|
225
|
-
# switch inputs
|
226
|
-
evt_combobox(type_op1.get_id) do | e |
|
227
|
-
function = type_op1.get_string_selection()
|
228
|
-
if function == "IS CODED BY" and not type_op2.shown?
|
229
|
-
type_op2_a.hide()
|
230
|
-
op2_placeholder.remove(type_op2_a)
|
231
|
-
|
232
|
-
op2_placeholder.add(type_op2, 1,
|
233
|
-
Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL)
|
234
|
-
type_op2.show()
|
235
|
-
op2_placeholder.layout()
|
236
|
-
@objects[row_n] = type_op2
|
237
|
-
elsif function == "CONTAINS WORD" and not type_op2_a.shown?
|
238
|
-
type_op2.hide()
|
239
|
-
op2_placeholder.remove(type_op2)
|
240
|
-
op2_placeholder.add(type_op2_a, 1,
|
241
|
-
Wx:: GROW|Wx::ADJUST_MINSIZE|Wx::ALL)
|
242
|
-
type_op2_a.show()
|
243
|
-
op2_placeholder.layout()
|
244
|
-
@objects[row_n] = type_op2_a
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
type_op3 = Wx::ComboBox.new(@crit_panel, -1, '', Wx::DEFAULT_POSITION,
|
249
|
-
Wx::DEFAULT_SIZE, [],
|
250
|
-
Wx::CB_READONLY|Wx::CB_DROPDOWN)
|
251
|
-
type_op3.append('')
|
252
|
-
type_op3.append('AND')
|
253
|
-
type_op3.append('AND NOT')
|
254
|
-
type_op3.append('OR')
|
255
|
-
type_op3.selection = 0
|
256
|
-
|
257
|
-
row_sizer.add( type_op3, 1, Wx::GROW|Wx::ALL, 2)
|
258
|
-
@ops.push( type_op3 )
|
259
|
-
unless with_bool
|
260
|
-
type_op3.hide()
|
261
|
-
end
|
262
|
-
@crit_sizer.add( row_sizer, 0, Wx::GROW|Wx::ALL|Wx::ALIGN_TOP )
|
263
|
-
end
|
264
|
-
end
|
265
|
-
end
|
data/lib/weft/wxgui/mondrian.xpm
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
/* XPM */
|
2
|
-
static char *mondrian_xpm[] = {
|
3
|
-
/* columns rows colors chars-per-pixel */
|
4
|
-
"32 32 6 1",
|
5
|
-
" c Black",
|
6
|
-
". c Blue",
|
7
|
-
"X c #00bf00",
|
8
|
-
"o c Red",
|
9
|
-
"O c Yellow",
|
10
|
-
"+ c Gray100",
|
11
|
-
/* pixels */
|
12
|
-
" ",
|
13
|
-
" oooooo +++++++++++++++++++++++ ",
|
14
|
-
" oooooo +++++++++++++++++++++++ ",
|
15
|
-
" oooooo +++++++++++++++++++++++ ",
|
16
|
-
" oooooo +++++++++++++++++++++++ ",
|
17
|
-
" oooooo +++++++++++++++++++++++ ",
|
18
|
-
" oooooo +++++++++++++++++++++++ ",
|
19
|
-
" oooooo +++++++++++++++++++++++ ",
|
20
|
-
" ",
|
21
|
-
" ++++++ ++++++++++++++++++ .... ",
|
22
|
-
" ++++++ ++++++++++++++++++ .... ",
|
23
|
-
" ++++++ ++++++++++++++++++ .... ",
|
24
|
-
" ++++++ ++++++++++++++++++ .... ",
|
25
|
-
" ++++++ ++++++++++++++++++ .... ",
|
26
|
-
" ++++++ ++++++++++++++++++ ",
|
27
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
28
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
29
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
30
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
31
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
32
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
33
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
34
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
35
|
-
" ++++++ ++++++++++++++++++ ++++ ",
|
36
|
-
" ++++++ ++++ ",
|
37
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
38
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
39
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
40
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
41
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
42
|
-
" ++++++ OOOOOOOOOOOO XXXXX ++++ ",
|
43
|
-
" "
|
44
|
-
};
|
data/lib/weft/wxgui/weft16.xpm
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
/* XPM */
|
2
|
-
static char *weft16[] = {
|
3
|
-
/* columns rows colors chars-per-pixel */
|
4
|
-
"16 16 9 1",
|
5
|
-
" c #400000",
|
6
|
-
". c #CCCC00",
|
7
|
-
"X c #E9E991",
|
8
|
-
"o c #999900",
|
9
|
-
"O c gray100",
|
10
|
-
"+ c black",
|
11
|
-
"@ c black",
|
12
|
-
"# c black",
|
13
|
-
"$ c None",
|
14
|
-
/* pixels */
|
15
|
-
"$$$$$ $$$ $$$$",
|
16
|
-
"$$$$ XX $ XX $$$",
|
17
|
-
"$$$$ .XX XX. $$$",
|
18
|
-
"$$ $ .XX o $ $$",
|
19
|
-
"$ XX $ .XX $ X $",
|
20
|
-
"$ XX. o .X. XX. ",
|
21
|
-
"$$ . XXo . XX. $",
|
22
|
-
"$$$ XX. $ XX. $$",
|
23
|
-
"$$ XX. o XX. o $",
|
24
|
-
"$ XX. o.X . o.X ",
|
25
|
-
" XX. $ XXX $ X $",
|
26
|
-
" X. $ o XXX $ $$",
|
27
|
-
"$ $ XXo XXX $$$",
|
28
|
-
"$$$ XX. $ XX $$$",
|
29
|
-
"$$$ X. $$$ $$$$",
|
30
|
-
"$$$$ $$$$$$$$$$"
|
31
|
-
};
|