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
@@ -0,0 +1,284 @@
|
|
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 QDA::Subscriber
|
9
|
+
attr_reader :query, :rows, :funcs, :args, :exprs
|
10
|
+
private :rows, :funcs, :args, :exprs
|
11
|
+
|
12
|
+
# obj is a simple object with a +dbid+ representing a project
|
13
|
+
# unique identifier for this query
|
14
|
+
def initialize(obj, client, workarea, layout)
|
15
|
+
@client = client
|
16
|
+
|
17
|
+
|
18
|
+
@rows, @funcs, @args, @exprs = [], [], [], []
|
19
|
+
|
20
|
+
super(workarea, "Query #{obj.dbid}", layout) do | parent |
|
21
|
+
@text_box = CompositeText.new(parent, '', 0, @client)
|
22
|
+
end
|
23
|
+
set_icon( @client.fetch_icon("query") )
|
24
|
+
construct_query_panel
|
25
|
+
# go to the query constructor panel, if query not defined, else show
|
26
|
+
# results pane
|
27
|
+
self.query = obj
|
28
|
+
if query.empty?
|
29
|
+
@notebook.selection = 1
|
30
|
+
else
|
31
|
+
if results = get_results(query)
|
32
|
+
populate(results)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def obj
|
38
|
+
@query
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
def query=(the_query)
|
44
|
+
@query = the_query
|
45
|
+
i = 0
|
46
|
+
query.unroll do | func, expr |
|
47
|
+
show_row(i)
|
48
|
+
case func
|
49
|
+
when QDA::Query::CodedByFunction
|
50
|
+
funcs[i].string_selection = 'CODED BY'
|
51
|
+
args[i].show( 0 )
|
52
|
+
cat = @client.app.get_category( func.identifier )
|
53
|
+
args[i].visible_item.set_active_category( cat )
|
54
|
+
when QDA::Query::WordSearchFunction
|
55
|
+
funcs[i].string_selection = 'CONTAINS WORD'
|
56
|
+
args[i].show( 1 )
|
57
|
+
args[i].visible_item.value = func.word
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
case expr
|
62
|
+
when QDA::Query::AND
|
63
|
+
exprs[i].string_selection = 'AND'
|
64
|
+
when QDA::Query::OR
|
65
|
+
exprs[i].string_selection = 'OR'
|
66
|
+
when QDA::Query::AND_NOT
|
67
|
+
exprs[i].string_selection = 'AND NOT'
|
68
|
+
end
|
69
|
+
i += 1
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def associated_subscribers()
|
74
|
+
super << @args.grep(CategoryDropDown)
|
75
|
+
end
|
76
|
+
|
77
|
+
# runs a query using the currently selected query options
|
78
|
+
def get_results(query)
|
79
|
+
# remember the last query we did
|
80
|
+
@last_query = query
|
81
|
+
set_cursor( Wx::BUSY_CURSOR )
|
82
|
+
@last_results = @last_query.calculate()
|
83
|
+
set_cursor( Wx::NORMAL_CURSOR )
|
84
|
+
return @last_results
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_target_argument(offset, as_string = false)
|
88
|
+
return nil unless @objects[offset].is_shown
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
def get_function(i)
|
93
|
+
arg_ctrl = args[i].visible_item
|
94
|
+
case funcs[i].string_selection
|
95
|
+
when 'IS CODED BY'
|
96
|
+
return QDA::Query::CodedByFunction.new(@client.app,
|
97
|
+
arg_ctrl.current_category)
|
98
|
+
when 'CONTAINS WORD'
|
99
|
+
return QDA::Query::WordSearchFunction.new(@client.app,
|
100
|
+
arg_ctrl.get_value(),
|
101
|
+
:wrap_both => 200 )
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def get_query()
|
106
|
+
q = QDA::Query.new( get_function(0) )
|
107
|
+
1.upto(rows.length - 1) do | i |
|
108
|
+
expr = exprs[i - 1].string_selection
|
109
|
+
break if expr.empty?
|
110
|
+
q.add_expression(expr, get_function(i))
|
111
|
+
end
|
112
|
+
return q
|
113
|
+
rescue ArgumentError => err
|
114
|
+
ErrorDialog.display("Error in query", err.to_s)
|
115
|
+
return
|
116
|
+
end
|
117
|
+
|
118
|
+
# returns a stringified representation of the query. The only
|
119
|
+
# reason this is here at the moment is because the underlying
|
120
|
+
# query code parser doesn't accept string literal paths or names
|
121
|
+
# for categories- only database ids. This should be remedied, so
|
122
|
+
# the string passed to query and this are the same.
|
123
|
+
def get_query_string(join_char = " ")
|
124
|
+
get_query.to_s
|
125
|
+
end
|
126
|
+
|
127
|
+
# run the query, display the results, view them
|
128
|
+
def on_view(e)
|
129
|
+
return unless query = get_query()
|
130
|
+
return unless results = get_results( query )
|
131
|
+
|
132
|
+
populate(results)
|
133
|
+
@notebook.selection = 0
|
134
|
+
end
|
135
|
+
|
136
|
+
def populate(results)
|
137
|
+
@text_box.populate( results )
|
138
|
+
if curr_category = @drop_down.current_category()
|
139
|
+
@text_box.highlight_codingtable(curr_category.codes)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def on_save_results(e)
|
144
|
+
# check for saved results
|
145
|
+
Wx::BusyCursor.busy do
|
146
|
+
results = get_results( get_query )
|
147
|
+
return unless results
|
148
|
+
@text_box.populate(results)
|
149
|
+
end
|
150
|
+
|
151
|
+
q_string = get_query_string
|
152
|
+
title = "'Query #{obj.dbid} results'"
|
153
|
+
search_parent = @client.app.get_root_category('SEARCHES')
|
154
|
+
title = search_parent.unique_name(title)
|
155
|
+
query_cat = QDA::Category.new( title, search_parent )
|
156
|
+
query_cat.codetable = @last_results
|
157
|
+
query_cat.memo = get_query_string("\n")
|
158
|
+
|
159
|
+
@client.app.save_category(query_cat, true)
|
160
|
+
end
|
161
|
+
|
162
|
+
def on_focus_dropdown(e)
|
163
|
+
# p "focussed #{e.event_object}"
|
164
|
+
# e.skip()
|
165
|
+
end
|
166
|
+
|
167
|
+
def on_blur_dropdown(e)
|
168
|
+
# p "blurred #{e.event_object}"
|
169
|
+
# p active?
|
170
|
+
# e.skip()
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
def show_row(idx)
|
175
|
+
if rows[idx].nil?
|
176
|
+
add_row()
|
177
|
+
else
|
178
|
+
@crit_sizer.show( rows[idx], true )
|
179
|
+
end
|
180
|
+
|
181
|
+
# show the next row below if this one ends with a valid expression
|
182
|
+
if not exprs[idx].get_string_selection.empty?
|
183
|
+
show_row(idx + 1)
|
184
|
+
end
|
185
|
+
@crit_sizer.layout()
|
186
|
+
end
|
187
|
+
|
188
|
+
def hide_row(idx)
|
189
|
+
return if rows[idx].nil?
|
190
|
+
@crit_sizer.show( rows[idx], false )
|
191
|
+
@crit_sizer.layout()
|
192
|
+
hide_row(idx + 1)
|
193
|
+
end
|
194
|
+
|
195
|
+
# handle changing the /AND/OR/AND NOT/ selector at the end of each row,
|
196
|
+
# hiding or showing rows below as appropriate
|
197
|
+
def on_alter_expression(expr_sel, row_index)
|
198
|
+
# Previously used to receive parameters as (event, row_index)
|
199
|
+
# .. but ..
|
200
|
+
# This seems to cause regular segfaults - wxruby 0.6.0
|
201
|
+
# expr_sel = event.event_object()
|
202
|
+
expr = expr_sel.string_selection()
|
203
|
+
if expr.empty?
|
204
|
+
hide_row(row_index + 1)
|
205
|
+
else
|
206
|
+
show_row(row_index + 1)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
def construct_query_panel()
|
211
|
+
panel2 = Wx::Panel.new(@notebook, -1)
|
212
|
+
sizer2 = Wx::BoxSizer.new(Wx::VERTICAL)
|
213
|
+
sizer2.add( Wx::StaticText.new(panel2, -1, 'Find text that:'),
|
214
|
+
0, Wx::ALL|Wx::ADJUST_MINSIZE, 4 )
|
215
|
+
|
216
|
+
@crit_panel = Wx::Panel.new(panel2, -1)
|
217
|
+
@crit_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
218
|
+
|
219
|
+
add_row()
|
220
|
+
@crit_panel.set_sizer(@crit_sizer)
|
221
|
+
sizer2.add(@crit_panel, 1, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4 )
|
222
|
+
|
223
|
+
butt_panel = Wx::Panel.new(panel2, -1)
|
224
|
+
bott_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
225
|
+
|
226
|
+
button = Wx::Button.new(butt_panel, -1, 'View')
|
227
|
+
button.evt_button(button.get_id) { | e | on_view(e) }
|
228
|
+
bott_sizer.add(button, 1, Wx::ALL, 4)
|
229
|
+
|
230
|
+
button = Wx::Button.new(butt_panel, -1, 'Save Results')
|
231
|
+
button.evt_button(button.get_id) { | e | on_save_results(e) }
|
232
|
+
bott_sizer.add(button, 1, Wx::ALL, 4)
|
233
|
+
|
234
|
+
butt_panel.set_sizer(bott_sizer)
|
235
|
+
sizer2.add(butt_panel, 0,
|
236
|
+
Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALIGN_BOTTOM)
|
237
|
+
@notebook.add_page(panel2, 'query')
|
238
|
+
panel2.set_sizer_and_fit(sizer2)
|
239
|
+
end
|
240
|
+
|
241
|
+
def add_row()
|
242
|
+
row_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
243
|
+
row_index = @rows.length
|
244
|
+
|
245
|
+
func_sel = Wx::Choice.new( @crit_panel, -1)
|
246
|
+
func_sel.append('IS CODED BY')
|
247
|
+
func_sel.append('CONTAINS WORD')
|
248
|
+
func_sel.selection = 0
|
249
|
+
row_sizer.add( func_sel, 2, Wx::GROW|Wx::ALL, 2)
|
250
|
+
@funcs.push( func_sel )
|
251
|
+
|
252
|
+
cat_dd = CategoryDropDown.new(@client, @crit_panel, nil, true)
|
253
|
+
arg_sel = MultiTypedControl.new(cat_dd)
|
254
|
+
arg_sel.add_control( Wx::TextCtrl.new(@crit_panel, -1, '') )
|
255
|
+
# TODO - allow currently highlighted category dropdown to
|
256
|
+
# receive global focus_category events
|
257
|
+
# type_op2.evt_set_focus() { | e | on_focus_dropdown(e) }
|
258
|
+
# type_op2.evt_kill_focus() { | e | on_blur_dropdown(e) }
|
259
|
+
row_sizer.add( arg_sel, 2, Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL, 2)
|
260
|
+
@args.push( arg_sel )
|
261
|
+
|
262
|
+
|
263
|
+
# switch inputs
|
264
|
+
evt_choice(func_sel.get_id) { arg_sel.show( func_sel.selection() ) }
|
265
|
+
|
266
|
+
expr_sel = Wx::Choice.new(@crit_panel, -1)
|
267
|
+
expr_sel.append('')
|
268
|
+
expr_sel.append('OR')
|
269
|
+
expr_sel.append('AND')
|
270
|
+
expr_sel.append('AND NOT')
|
271
|
+
expr_sel.selection = 0
|
272
|
+
# Previously passed the event, but see on_alter_expression
|
273
|
+
# evt_choice(expr_sel.get_id) { | e | on_alter_expression(e, row_index) }
|
274
|
+
evt_choice(expr_sel.get_id) { on_alter_expression(expr_sel, row_index) }
|
275
|
+
|
276
|
+
row_sizer.add( expr_sel, 1, Wx::GROW|Wx::ALL, 2)
|
277
|
+
@exprs.push( expr_sel )
|
278
|
+
@crit_sizer.add( row_sizer, 0,
|
279
|
+
Wx::GROW|Wx::ALL|Wx::ALIGN_TOP, 1 )
|
280
|
+
|
281
|
+
@rows.push(row_sizer)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
@@ -1,35 +1,159 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
1
3
|
module QDA::GUI
|
4
|
+
# stub class - to be moved to core weft lib?
|
5
|
+
class Script
|
6
|
+
attr_accessor :dbid
|
7
|
+
end
|
8
|
+
|
9
|
+
class CodeTextCtrl < Wx::TextCtrl
|
10
|
+
def initialize(parent, code = '')
|
11
|
+
super( parent, -1, code, DEF_POS, DEF_SIZE,
|
12
|
+
Wx::TE_MULTILINE|Wx::TE_RICH|Wx::TE_PROCESS_TAB )
|
13
|
+
set_default_style( normal_style )
|
14
|
+
evt_set_focus() { reset() }
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset
|
18
|
+
set_style(0, get_last_position, normal_style)
|
19
|
+
end
|
20
|
+
|
21
|
+
def normal_font
|
22
|
+
@normal_font ||= Wx::Font.new( 10, Wx::MODERN, Wx::NORMAL, Wx::NORMAL )
|
23
|
+
end
|
24
|
+
def normal_style
|
25
|
+
@normal_style ||= Wx::TextAttr.new(Wx::BLACK, Wx::WHITE, normal_font)
|
26
|
+
end
|
27
|
+
def bold_font
|
28
|
+
@bold_font ||= Wx::Font.new( 10, Wx::MODERN, Wx::NORMAL, Wx::BOLD )
|
29
|
+
end
|
30
|
+
|
31
|
+
def bold_style
|
32
|
+
@bold_style ||= Wx::TextAttr.new(Wx::RED, Wx::WHITE, bold_font)
|
33
|
+
end
|
34
|
+
|
35
|
+
def highlight_line(lineno)
|
36
|
+
line_start = xy_to_position(0, lineno)
|
37
|
+
line_end = line_start + get_line_length(lineno)
|
38
|
+
set_style(line_start, line_end, bold_style)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
2
42
|
class ScriptWindow < WorkAreaWindow
|
3
|
-
|
43
|
+
attr_reader :code, :output, :obj
|
44
|
+
private :code, :output
|
45
|
+
|
46
|
+
def initialize(obj, client, workarea, layout)
|
4
47
|
super(workarea, "Script #{obj.dbid}", nil)
|
5
|
-
@
|
6
|
-
|
7
|
-
|
8
|
-
@code = Wx::TextCtrl.new(self, -1, code, Wx::DEFAULT_POSITION,
|
9
|
-
Wx::DEFAULT_SIZE, Wx::TE_MULTILINE)
|
10
|
-
sizer.add(@code, 2, Wx::GROW|Wx::ALL, 5)
|
11
|
-
|
12
|
-
button = Wx::Button.new(self, -1, 'Run')
|
13
|
-
button.evt_button(button.get_id) { | e | on_run(e) }
|
14
|
-
sizer.add(button, 0, Wx::ALL, 5)
|
15
|
-
|
16
|
-
@output = Wx::TextCtrl.new(self, -1, '', Wx::DEFAULT_POSITION,
|
17
|
-
Wx::DEFAULT_SIZE,
|
18
|
-
Wx::TE_MULTILINE|Wx::TE_READONLY)
|
19
|
-
sizer.add(@output, 2, Wx::GROW|Wx::ALL, 5)
|
20
|
-
self.sizer = sizer
|
48
|
+
@client = client
|
49
|
+
@obj = obj
|
50
|
+
self.construct()
|
21
51
|
end
|
52
|
+
|
53
|
+
|
54
|
+
def construct()
|
55
|
+
@splitter = Wx::SplitterWindow.new(self, -1,
|
56
|
+
Wx::DEFAULT_POSITION,
|
57
|
+
Wx::DEFAULT_SIZE,Wx::SP_3DSASH)
|
58
|
+
@splitter.minimum_pane_size = 75
|
59
|
+
|
60
|
+
# the input pane and buttons
|
61
|
+
panel_code = Wx::Panel.new(@splitter)
|
62
|
+
code_panel_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
63
|
+
|
64
|
+
code_sizer = LabelledVSizer.new(panel_code, 'Script')
|
65
|
+
|
66
|
+
@code = CodeTextCtrl.new(panel_code)
|
67
|
+
code_sizer.add(@code, 1, Wx::GROW|Wx::ALL, 4)
|
68
|
+
|
69
|
+
b_panel = Wx::Panel.new(panel_code)
|
70
|
+
b_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
71
|
+
@button_run = Wx::Button.new(b_panel, -1, 'Run')
|
72
|
+
@button_run.evt_button(@button_run.get_id) { | e | on_run(e) }
|
73
|
+
b_sizer.add(@button_run, 0, Wx::ALL, 4)
|
74
|
+
|
75
|
+
@button_load = Wx::Button.new(b_panel, -1, 'Load from file...')
|
76
|
+
@button_load.evt_button(@button_load.get_id) { | e | on_load_script(e) }
|
77
|
+
b_sizer.add(@button_load, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
22
78
|
|
79
|
+
@button_save = Wx::Button.new(b_panel, -1, 'Save to file...')
|
80
|
+
@button_save.evt_button(@button_save.get_id) { | e | on_save_script(e) }
|
81
|
+
b_sizer.add(@button_save, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
82
|
+
b_panel.set_sizer(b_sizer)
|
83
|
+
|
84
|
+
code_sizer.add(b_panel, 0, Wx::ADJUST_MINSIZE|Wx::ALL)
|
85
|
+
code_panel_sizer.add(code_sizer, 1, Wx::ALL|Wx::GROW, 4)
|
86
|
+
panel_code.set_sizer(code_panel_sizer)
|
87
|
+
|
88
|
+
|
89
|
+
# the output pane
|
90
|
+
panel_outp = Wx::Panel.new(@splitter)
|
91
|
+
outp_panel_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
92
|
+
outp_sizer = LabelledVSizer.new(panel_outp, 'Output')
|
93
|
+
|
94
|
+
@output = Wx::TextCtrl.new(panel_outp, -1, '', DEF_POS, DEF_SIZE,
|
95
|
+
Wx::TE_MULTILINE|Wx::TE_READONLY)
|
96
|
+
outp_sizer.add(@output, 1, Wx::GROW|Wx::ALL, 4)
|
97
|
+
outp_panel_sizer.add(outp_sizer, 1, Wx::GROW|Wx::ALL, 4)
|
98
|
+
panel_outp.set_sizer(outp_panel_sizer)
|
99
|
+
|
100
|
+
@splitter.split_horizontally( panel_code, panel_outp,
|
101
|
+
get_client_size.height / 2)
|
102
|
+
end
|
103
|
+
|
23
104
|
def on_run(e)
|
24
105
|
$stdout = result = StringIO.new()
|
25
|
-
|
26
|
-
|
106
|
+
$stderr = errors = StringIO.new()
|
107
|
+
output.value = ''
|
108
|
+
code.reset()
|
109
|
+
|
110
|
+
file_id = "Script #{obj.dbid}"
|
111
|
+
@button_run.disable()
|
112
|
+
@button_load.disable()
|
113
|
+
@button_save.disable()
|
114
|
+
|
115
|
+
set_cursor( Wx::BUSY_CURSOR )
|
116
|
+
client.app.instance_eval( @code.get_value(), file_id)
|
117
|
+
|
27
118
|
result.rewind()
|
28
|
-
|
29
|
-
$stdout = STDOUT
|
119
|
+
output.value = result.read()
|
30
120
|
rescue Exception => err
|
31
|
-
|
32
|
-
|
121
|
+
script_line = /\A#{file_id}:(\d)/
|
122
|
+
trace = err.backtrace
|
123
|
+
# remove backtrace info unrelated to the specific script
|
124
|
+
trace.pop while trace[-1] !~ script_line
|
125
|
+
# find the first backtrace item from within the script
|
126
|
+
first_script_err = trace.detect { | x | x =~ script_line }
|
127
|
+
# find the script line that caused the problem
|
128
|
+
line = first_script_err.match(script_line)[1].to_i - 1
|
129
|
+
code.highlight_line(line)
|
130
|
+
# spit the error out in the output box
|
131
|
+
output.value += "#{err}\n"
|
132
|
+
output.value += trace.join("\n")
|
133
|
+
ensure
|
134
|
+
$stdout, $stderr = STDOUT, STDERR
|
135
|
+
set_cursor( Wx::NORMAL_CURSOR )
|
136
|
+
@button_run.enable()
|
137
|
+
@button_load.enable()
|
138
|
+
@button_save.enable()
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_load_script(e)
|
142
|
+
file_dialog = ScriptFileDialog.new(self, false)
|
143
|
+
return unless file_dialog.show_modal == Wx::ID_OK
|
144
|
+
@code.load_file( file_dialog.get_path() )
|
145
|
+
end
|
146
|
+
|
147
|
+
def on_save_script(e)
|
148
|
+
file_dialog = ScriptFileDialog.new(self, true)
|
149
|
+
return unless file_dialog.show_modal == Wx::ID_OK
|
150
|
+
@code.save_file( file_dialog.get_path() )
|
151
|
+
end
|
152
|
+
|
153
|
+
def on_save_output(e)
|
154
|
+
file_dialog = ScriptOutputFileDialog.new(self)
|
155
|
+
return unless file_dialog.show_modal == Wx::ID_OK
|
156
|
+
@output.save_file( file_dialog.get_path() )
|
33
157
|
end
|
34
158
|
end
|
35
159
|
end
|