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
@@ -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
|