weft-qda 0.9.6 → 0.9.8
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.
- 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
|
-
};
|