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/utilities.rb
CHANGED
@@ -1,126 +1,105 @@
|
|
1
|
+
# temporary hack to turn c++ style set_foo(), get_foo() and is_foo()
|
2
|
+
# accessors into ruby style foo=() foo() and foo?()
|
3
|
+
wx_classes = Wx::constants.collect { | c | Wx::const_get(c) }.grep(Class)
|
4
|
+
wx_classes.each do | klass |
|
5
|
+
klass.instance_methods.grep(/^([gs]et|evt|is)_/).each do | meth |
|
6
|
+
case meth
|
7
|
+
when /^get_(\w+)$/
|
8
|
+
klass.class_eval("alias :#{$1} :#{meth}")
|
9
|
+
when /^set_(\w+)$/
|
10
|
+
klass.class_eval("alias :#{$1}= :#{meth}")
|
11
|
+
when /^is_(\w+)$/
|
12
|
+
klass.class_eval("alias :#{$1}? :#{meth}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Wx::NORMAL_CURSOR = Wx::Cursor.new(Wx::CURSOR_ARROW)
|
18
|
+
Wx::BUSY_CURSOR = Wx::Cursor.new(Wx::CURSOR_WAIT)
|
19
|
+
|
1
20
|
module QDA::GUI
|
2
21
|
# ItemData - allows widgets to store information about assocations
|
3
22
|
# with underlying project objects such as categories and documents.
|
4
23
|
module ItemData
|
5
24
|
# for faked-up item data
|
6
25
|
def get_item_data(id)
|
7
|
-
|
26
|
+
data[id]
|
8
27
|
end
|
9
28
|
alias :get_client_data :get_item_data
|
10
29
|
|
11
30
|
# for faked-up item data
|
12
|
-
def set_item_data(id,
|
13
|
-
|
14
|
-
@data_table[id] = data
|
15
|
-
return nil
|
31
|
+
def set_item_data(id, data_obj)
|
32
|
+
data[id] = data_obj
|
16
33
|
end
|
17
34
|
alias :set_client_data :set_item_data
|
18
|
-
|
35
|
+
|
19
36
|
# returns the key associated with the value +value+
|
20
37
|
def value_to_ident(value)
|
21
38
|
return nil if value.nil?
|
22
|
-
|
39
|
+
if value.nil?
|
40
|
+
return nil
|
41
|
+
elsif value.respond_to?(:dbid)
|
42
|
+
the_id = value.dbid
|
43
|
+
elsif value.kind_of?(Fixnum)
|
44
|
+
the_id = value
|
45
|
+
else
|
23
46
|
Kernel.raise "Cannot search for invalid value #{value.inspect}"
|
24
47
|
end
|
25
|
-
|
26
|
-
return k if val.dbid == value.dbid
|
27
|
-
end
|
28
|
-
return nil
|
48
|
+
return index(the_id)
|
29
49
|
end
|
30
50
|
end
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
module Broadcaster
|
37
|
-
SUBSCRIBABLE_EVENTS = [ :document_added, :document_changed,
|
38
|
-
:document_deleted, :category_added, :category_changed,
|
39
|
-
:category_deleted, :focus_category, :savestate_changed ]
|
40
|
-
|
41
|
-
# intended to be module private variables to managed which widgets
|
42
|
-
# are subscribing to which events
|
43
|
-
attr_accessor :bc__subscribers, :bc__subscriptions
|
44
|
-
|
45
|
-
# broadcast an event to all subscribers to event of type
|
46
|
-
# +event_type+, optionally including +content+
|
47
|
-
def broadcast(event_type, content = nil)
|
48
|
-
for subscriber in bc__subscriptions[event_type]
|
49
|
-
subscriber.notify(event_type, content)
|
50
|
-
end
|
51
|
+
|
52
|
+
module ListLikeItemData
|
53
|
+
include ItemData
|
54
|
+
def data()
|
55
|
+
@data_table ||= Array.new()
|
51
56
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
events.each do | e |
|
57
|
-
bc__subscriptions[e].push(subscriber)
|
58
|
-
end
|
59
|
-
subscriber.evt_close do | e |
|
60
|
-
delete_subscriber(subscriber)
|
61
|
-
e.skip()
|
57
|
+
|
58
|
+
def delete_item_data(the_data)
|
59
|
+
if i = value_to_ident(the_data)
|
60
|
+
data.delete_at(i)
|
62
61
|
end
|
63
62
|
end
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
subscriber.associated_subscribers.each do | child |
|
69
|
-
delete_subscriber(child)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
bc__subscribers.delete(subscriber)
|
73
|
-
bc__subscriptions.each_value do | subscriber_set |
|
74
|
-
subscriber_set.delete(subscriber)
|
75
|
-
end
|
63
|
+
|
64
|
+
def unshift_item_data(the_data)
|
65
|
+
delete_item_data(the_data)
|
66
|
+
data.unshift(the_data)
|
76
67
|
end
|
77
68
|
|
78
|
-
def
|
79
|
-
|
80
|
-
|
81
|
-
obj.reset_subscriptions()
|
69
|
+
def push_item_data(the_data)
|
70
|
+
delete_item_data(the_data)
|
71
|
+
data.push(the_data)
|
82
72
|
end
|
83
|
-
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
raise "Cannot subscribe to unknown event #{ev}"
|
73
|
+
|
74
|
+
def index(the_id)
|
75
|
+
data.each_with_index do | val, i |
|
76
|
+
return i if val.dbid == the_id
|
88
77
|
end
|
89
|
-
|
78
|
+
return nil
|
90
79
|
end
|
91
80
|
end
|
92
81
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
module Subscriber
|
98
|
-
# receive notification that an event of type +ev+ has been called,
|
99
|
-
# optionally passing +content+ for additional information about the
|
100
|
-
# object the event concerned.
|
101
|
-
def notify(ev, content = nil)
|
102
|
-
receiver = "receive_#{ev}".intern
|
103
|
-
if respond_to?(receiver)
|
104
|
-
send(receiver, content)
|
105
|
-
else
|
106
|
-
warn "#{self} received unhandled event #{ev}"
|
107
|
-
end
|
82
|
+
module HashLikeItemData
|
83
|
+
include ItemData
|
84
|
+
def data()
|
85
|
+
@data_table ||= Hash.new()
|
108
86
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
# For every event type to which a subscriber subscribes, it should
|
116
|
-
# implement a corresponding receive_xxx method, where xxx is the
|
117
|
-
# name of the event type. To act upon category changes, the
|
118
|
-
# subscriber should implement +receive_category_changed+
|
119
|
-
def subscribe(*events)
|
120
|
-
$wxapp.add_subscriber(self, *events)
|
87
|
+
|
88
|
+
def index(the_id)
|
89
|
+
data.each do | k, val |
|
90
|
+
return k if val.dbid == the_id
|
91
|
+
end
|
92
|
+
return nil
|
121
93
|
end
|
122
94
|
end
|
123
|
-
|
95
|
+
|
96
|
+
class LabelledVSizer < Wx::StaticBoxSizer
|
97
|
+
def initialize(sized_object, label)
|
98
|
+
static_box = Wx::StaticBox.new(sized_object, -1, label)
|
99
|
+
super(static_box, Wx::VERTICAL)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
124
103
|
# extension to Wx::Colour
|
125
104
|
class Wx::Colour
|
126
105
|
# Create a new colour by mixing +self_parts+ of +self+ with
|
data/lib/weft/wxgui/workarea.rb
CHANGED
@@ -1,27 +1,28 @@
|
|
1
1
|
module QDA::GUI
|
2
2
|
# The main application window, where docuemnts, categories, searches
|
3
3
|
# etc are displayed. Only one instance per application.
|
4
|
-
|
5
|
-
include Subscriber
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
restore_size()
|
13
|
-
subscribe(:savestate_changed)
|
14
|
-
end
|
15
|
-
|
4
|
+
module InspectorWindowManager
|
5
|
+
include QDA::Subscriber
|
6
|
+
|
7
|
+
# This sets the last-focussed child. However, when later requesting info
|
8
|
+
# on the currently active window, MDI reports the active_child() call,
|
9
|
+
# whereas the @active_child variable is read by MultiframeWindowManager
|
10
|
+
# - in MDI there is a foremost window which may not have received focus.
|
11
|
+
attr_writer :active_child
|
16
12
|
# Resizes the workarea to the state when the application was last used
|
17
13
|
def restore_size()
|
18
14
|
conf = Wx::ConfigBase::get()
|
19
15
|
conf.path = "/MainFrame"
|
20
|
-
x = conf.read_int("x",
|
21
|
-
y = conf.read_int("y",
|
22
|
-
w = conf.read_int("w",
|
23
|
-
h = conf.read_int("h",
|
16
|
+
x = conf.read_int( "x", -1 )
|
17
|
+
y = conf.read_int( "y", -1 )
|
18
|
+
w = conf.read_int( "w", -1 )
|
19
|
+
h = conf.read_int( "h", -1 )
|
24
20
|
max = conf.read_bool("max", false)
|
21
|
+
|
22
|
+
x = DEF_POS.x if x < 0
|
23
|
+
y = DEF_POS.y if y < 0
|
24
|
+
w = DEF_SIZE.width if w < 200
|
25
|
+
h = DEF_SIZE.height if h < 200
|
25
26
|
|
26
27
|
if max
|
27
28
|
maximize(true)
|
@@ -32,6 +33,9 @@ module QDA::GUI
|
|
32
33
|
end
|
33
34
|
|
34
35
|
# save layout to Config so window is same position next time
|
36
|
+
|
37
|
+
|
38
|
+
|
35
39
|
def remember_size()
|
36
40
|
conf = Wx::ConfigBase::get()
|
37
41
|
# global window size settings
|
@@ -70,7 +74,7 @@ module QDA::GUI
|
|
70
74
|
else
|
71
75
|
w_i = window_ident(bound_obj)
|
72
76
|
last_layout = restore_layout( w_i )
|
73
|
-
win = klass.new( bound_obj, @
|
77
|
+
win = klass.new( bound_obj, @client, self, last_layout )
|
74
78
|
@window_map[w_i] = win
|
75
79
|
|
76
80
|
# set up a hook to clean up when this window is closed later
|
@@ -127,7 +131,7 @@ module QDA::GUI
|
|
127
131
|
# remember how the windows were arranged
|
128
132
|
def remember_layouts()
|
129
133
|
# project specific settings
|
130
|
-
if
|
134
|
+
if self.app
|
131
135
|
@window_map.each do | key, window |
|
132
136
|
remember_layout(key, window, true) unless key.nil?
|
133
137
|
end
|
@@ -135,64 +139,167 @@ module QDA::GUI
|
|
135
139
|
end
|
136
140
|
|
137
141
|
def remember_layout(window_id, window, open = false)
|
138
|
-
if
|
139
|
-
layouts =
|
142
|
+
if self.app
|
143
|
+
layouts = app.get_preference('Layouts') || {}
|
140
144
|
layouts[window_id] = window.layout
|
141
145
|
layouts[window_id][:open] = open
|
142
|
-
|
146
|
+
app.save_preference('Layouts', layouts)
|
143
147
|
end
|
144
148
|
end
|
145
149
|
|
146
150
|
def restore_layout(window_id)
|
147
|
-
if
|
148
|
-
layouts =
|
151
|
+
if self.app
|
152
|
+
layouts = app.get_preference('Layouts') || {}
|
149
153
|
return layouts[window_id]
|
150
154
|
end
|
151
155
|
end
|
152
156
|
|
153
157
|
def restore_layouts()
|
154
|
-
if
|
155
|
-
layouts =
|
158
|
+
if self.app
|
159
|
+
layouts = app.get_preference('Layouts')
|
156
160
|
return unless layouts
|
157
161
|
# get all previously open windows
|
158
162
|
open = layouts.find_all { | x | x[1][:open] }
|
159
163
|
open.each do | ident, layout |
|
160
164
|
if ident =~ /^Document(\w+)$/
|
161
|
-
|
165
|
+
@client.on_document_open($1)
|
162
166
|
elsif ident =~ /^Category(\w+)/ and
|
163
|
-
cat =
|
164
|
-
|
167
|
+
cat = app.get_category($1, true)
|
168
|
+
@client.on_category_open(cat)
|
165
169
|
end
|
166
170
|
end
|
167
171
|
end
|
168
172
|
end
|
169
173
|
|
170
|
-
def set_display_font(font)
|
171
|
-
# super(font)
|
172
|
-
@window_map.values.each do | child |
|
173
|
-
child.set_display_font(font) if child.respond_to?(:set_display_font)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
174
|
def close_all()
|
178
175
|
@window_map.values.each { | child | child.close() }
|
176
|
+
close_d_and_c()
|
179
177
|
end
|
180
178
|
|
181
|
-
def
|
182
|
-
|
183
|
-
|
184
|
-
|
179
|
+
def notify(ev, content = nil)
|
180
|
+
case ev
|
181
|
+
when :focus_category
|
182
|
+
return # ignore
|
183
|
+
when :ended
|
184
|
+
title = 'Weft QDA'
|
185
|
+
when :saved, :started
|
186
|
+
title = 'Weft QDA'
|
187
|
+
if self.app.dbfile
|
185
188
|
title << " - #{app.dbfile}"
|
186
189
|
else
|
187
190
|
title << " - [New Project]"
|
188
191
|
end
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
+
else
|
193
|
+
title = get_title()
|
194
|
+
title << '*' if title[-1,1] != '*'
|
195
|
+
end
|
196
|
+
set_title(title)
|
197
|
+
end
|
198
|
+
|
199
|
+
def app()
|
200
|
+
@client.app
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
class MDIWorkArea < Wx::MDIParentFrame
|
206
|
+
include InspectorWindowManager
|
207
|
+
def initialize(weft_client)
|
208
|
+
@client = weft_client
|
209
|
+
@window_map = {}
|
210
|
+
super(nil, -1, "Weft QDA")
|
211
|
+
restore_size()
|
212
|
+
subscribe(@client, :all)
|
213
|
+
end
|
214
|
+
|
215
|
+
def d_and_c()
|
216
|
+
@sidebar
|
217
|
+
end
|
218
|
+
|
219
|
+
# display the listings of documents and categories
|
220
|
+
def show_d_and_c()
|
221
|
+
if not @sidebar
|
222
|
+
# see sidebar.rb
|
223
|
+
@sidebar = SideBar.new( self, @client )
|
192
224
|
end
|
193
|
-
|
225
|
+
@sidebar.show()
|
226
|
+
end
|
227
|
+
|
228
|
+
def hide_d_and_c()
|
229
|
+
@sidebar.hide() if @sidebar
|
230
|
+
end
|
231
|
+
|
232
|
+
def close_d_and_c()
|
233
|
+
return if not @sidebar
|
234
|
+
@sidebar.remember_size()
|
235
|
+
@sidebar.close()
|
236
|
+
@sidebar = nil
|
237
|
+
end
|
238
|
+
|
239
|
+
def d_and_c_visible?()
|
240
|
+
@sidebar.shown?
|
241
|
+
end
|
242
|
+
|
243
|
+
def hide_all()
|
244
|
+
hide_d_and_c()
|
245
|
+
self.hide()
|
194
246
|
end
|
195
247
|
end
|
248
|
+
|
249
|
+
# presentation of main window as a standalone window, launching inspector
|
250
|
+
# windows as separate frames
|
251
|
+
class MultiFrameWorkArea < Wx::Frame
|
252
|
+
include InspectorWindowManager
|
253
|
+
|
254
|
+
attr_reader :d_and_c, :active_child
|
255
|
+
|
256
|
+
def initialize(weft_client)
|
257
|
+
@client = weft_client
|
258
|
+
@window_map = {}
|
259
|
+
super(nil, -1, "Weft QDA")
|
260
|
+
restore_size()
|
261
|
+
set_sizer( @sizer = Wx::BoxSizer.new(Wx::VERTICAL) )
|
262
|
+
subscribe(@client, :all)
|
263
|
+
end
|
264
|
+
|
265
|
+
def show_d_and_c()
|
266
|
+
if not @d_and_c
|
267
|
+
@d_and_c ||= DocumentsAndCategories.new(self, @client)
|
268
|
+
conf = Wx::ConfigBase::get()
|
269
|
+
conf.path = "/SideFrame"
|
270
|
+
split = conf.read_int("split", 200)
|
271
|
+
# sometimes weirdly ends up with negative value
|
272
|
+
split = client_size.height / 2 if split <= 0
|
273
|
+
@d_and_c.set_sash_position(split)
|
274
|
+
@sizer.add(@d_and_c, 1, Wx::GROW|Wx::ALL, 0)
|
275
|
+
@sizer.layout()
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def hide_d_and_c()
|
280
|
+
# do nothing
|
281
|
+
end
|
282
|
+
|
283
|
+
def close_d_and_c()
|
284
|
+
@sizer.remove(@d_and_c)
|
285
|
+
@d_and_c.destroy()
|
286
|
+
@d_and_c = nil
|
287
|
+
end
|
288
|
+
|
289
|
+
def hide_all()
|
290
|
+
@window_map.each { | w | w.hide() }
|
291
|
+
self.hide()
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
|
297
|
+
if WINDOW_MODE == :MDI
|
298
|
+
WorkArea = MDIWorkArea
|
299
|
+
else
|
300
|
+
WorkArea = MultiFrameWorkArea
|
301
|
+
end
|
302
|
+
|
196
303
|
|
197
304
|
class EasyMenu < Wx::Menu
|
198
305
|
def EasyMenu.next_base()
|