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/lang/en.rb
CHANGED
@@ -5,6 +5,17 @@ module QDA::GUI::Lang
|
|
5
5
|
FILE_ALREADY_EXISTS = "A project file with this name already exists. If you continue, all data in it will be lost. Are you sure?"
|
6
6
|
SIDEBAR_TITLE = "Documents & Categories"
|
7
7
|
SEARCH_RESULTS = "search results"
|
8
|
+
|
9
|
+
OPEN_PROJECT_ERROR_TITLE = "Cannot open project"
|
10
|
+
IMPORT_DOC_DIALOGUE_TITLE = "Import a Document From File"
|
11
|
+
EXPORT_DOC_DIALOGUE_TITLE = "Export to File"
|
12
|
+
|
13
|
+
REVERT_WARNING_TITLE = "Really revert project?"
|
14
|
+
REVERT_WARNING_MESSAGE =
|
15
|
+
"Are you sure you want to revert the project to its last saved state?
|
16
|
+
|
17
|
+
This will undo all changes made since the project was last saved"
|
18
|
+
|
8
19
|
REINDEX_DOCS_WARNING =
|
9
20
|
"This will reindex all previously imported documents, which will take
|
10
21
|
some time. You only need to do this once if:
|
@@ -15,7 +26,40 @@ Otherwise there is no need to run this command.
|
|
15
26
|
Are you sure you wish to proceed and reindex all documents? "
|
16
27
|
|
17
28
|
REINDEX_DOCS_WARNING_TITLE = "Reindex all documents?"
|
29
|
+
NO_EXPORT_FILTER_TITLE = "No export filter available"
|
30
|
+
NO_EXPORT_FILTER_WARNING =
|
31
|
+
"Sorry, there are no export filters available to output a %s"
|
32
|
+
DUPLICATE_CATEGORY_NAME_TITLE = "Duplicate category name"
|
33
|
+
DUPLICATE_CATEGORY_NAME_WARNING =
|
34
|
+
"A category with the same name already exists at this position.
|
35
|
+
|
36
|
+
Either choose a different name, or attach the category elsewhere in the tree."
|
37
|
+
|
38
|
+
BAD_CATEGORY_NAME_TITLE = "Bad Category Name"
|
39
|
+
BAD_CATEGORY_NAME_WARNING =
|
40
|
+
"A category may not have the character '/' at the beginning or end
|
41
|
+
of its name.
|
42
|
+
|
43
|
+
Please choose a different name and try again."
|
44
|
+
|
45
|
+
DELETE_DOCUMENT_TITLE= "Confirm document deletion"
|
46
|
+
DELETE_DOCUMENT_WARNING =
|
47
|
+
"The document '%s' will permanently deleted, along with its memo and
|
48
|
+
any coding applied to it.
|
49
|
+
|
50
|
+
Are you sure you want to proceed?"
|
51
|
+
|
52
|
+
DELETE_CATEGORY_TITLE= "Confirm category deletion"
|
53
|
+
DELETE_CATEGORY_WARNING =
|
54
|
+
"The category '%s' will permanently deleted, along with its memo and
|
55
|
+
any coding associated with it.
|
18
56
|
|
57
|
+
Are you sure you want to proceed?"
|
58
|
+
|
59
|
+
NO_COPYING_ERROR_TEXT =
|
60
|
+
"The author or publisher of this PDF document has locked it to
|
61
|
+
prevent copying and extraction of its text. It is not possible to
|
62
|
+
import this document."
|
19
63
|
NO_SEARCH_TERM_SPECIFIED = "No search term specified"
|
20
64
|
DOC_PANEL_TITLE = 'Documents'
|
21
65
|
IMPORT_DOC_BUTTON = 'Import...'
|
@@ -28,6 +72,31 @@ Are you sure you wish to proceed and reindex all documents? "
|
|
28
72
|
HELP_ABOUT_MESSAGE = "Thank you for trying Weft QDA
|
29
73
|
Version '#{::WEFT_VERSION}'\n\n-- qda@pressure.to"
|
30
74
|
|
75
|
+
|
76
|
+
CRASH_REPORT_TITLE = 'Weft QDA Crash reporting'
|
77
|
+
CRASH_REPORT_MESSAGE =
|
78
|
+
"The program has encountered an error and has to close. Any unsaved
|
79
|
+
work has been lost. Sorry for the inconvenience.
|
80
|
+
|
81
|
+
Weft QDA has gathered information about this error that can be used
|
82
|
+
to improve the software. This does not include any personal information
|
83
|
+
about you. It may occasionally include small sections of project data;
|
84
|
+
you can preview the full contents of the error report below.
|
85
|
+
|
86
|
+
To submit the error report and exit, click 'Send'. To exit without sending
|
87
|
+
any information, click 'Don't Send'."
|
88
|
+
|
89
|
+
CRASH_REPORT = 'Crash report'
|
90
|
+
CRASH_REPORT_EMAIL = 'Email (optional)'
|
91
|
+
CRASH_REPORT_INFO = 'Further information about the crash (optional)'
|
92
|
+
CRASH_REPORT_DATA = 'Crash information'
|
93
|
+
|
94
|
+
FAILED_CRASH_SEND_TITLE = 'Failed to send crash report'
|
95
|
+
FAILED_CRASH_SEND_MESSAGE =
|
96
|
+
'It was not possible to send the error report at this time.
|
97
|
+
|
98
|
+
%s'
|
99
|
+
|
31
100
|
if RUBY_PLATFORM =~ /mswin/
|
32
101
|
HELP_HELP_MESSAGE = <<HELP
|
33
102
|
Help files are installed with Weft QDA.
|
data/lib/weft/wxgui/sidebar.rb
CHANGED
@@ -1,449 +1,40 @@
|
|
1
1
|
module QDA::GUI
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
include ItemData
|
8
|
-
include Subscriber
|
9
|
-
|
10
|
-
attr_reader :root_id
|
11
|
-
|
12
|
-
# a +locked+ control is one that is not editable, and does not
|
13
|
-
# affect the selection of categories in other widgetes.
|
14
|
-
def initialize(app, parent, locked = false)
|
15
|
-
@app = app
|
16
|
-
@locked = locked
|
17
|
-
super(parent, -1, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
|
18
|
-
CATEGORY_TREE_STYLE)
|
19
|
-
my_id = self.get_id()
|
20
|
-
evt_tree_item_activated(my_id) { | e | on_item_activated(e) }
|
21
|
-
|
22
|
-
if ! @locked
|
23
|
-
evt_tree_sel_changed(my_id) { | e | on_item_selected(e) }
|
24
|
-
evt_tree_end_drag(my_id) { | e | on_drag_end(e) }
|
25
|
-
evt_tree_begin_drag(my_id) { | e | on_drag_begin(e) }
|
26
|
-
evt_tree_begin_label_edit(my_id) { | e | on_edit_label_begin(e) }
|
27
|
-
evt_tree_end_label_edit(my_id) { | e | on_edit_label_end(e) }
|
28
|
-
evt_tree_key_down(my_id) { | e | on_key_down(e) }
|
29
|
-
end
|
30
|
-
|
31
|
-
subscribe(:category_deleted, :category_changed, :category_added)
|
32
|
-
end
|
33
|
-
|
34
|
-
# for faked-up item data
|
35
|
-
def append_item(parent, text, data = nil)
|
36
|
-
id = super(parent, text, -1, -1)
|
37
|
-
set_item_data(id, data)
|
38
|
-
set_item_bold(id) if parent == @root_id
|
39
|
-
id
|
40
|
-
end
|
41
|
-
|
42
|
-
# for faked-up item data
|
43
|
-
def prepend_item(parent, text, img = -1, sel_img = -1, data = nil)
|
44
|
-
id = super(parent, text, img, sel_img)
|
45
|
-
set_item_data(id, data)
|
46
|
-
return id
|
47
|
-
end
|
48
|
-
|
49
|
-
# for faked-up item data
|
50
|
-
def delete(id)
|
51
|
-
super(id)
|
52
|
-
@data_table.delete(id)
|
53
|
-
return nil
|
54
|
-
end
|
55
|
-
|
56
|
-
def populate(children)
|
57
|
-
# the root isn't shown, so the top level of +children+ is what
|
58
|
-
# appears at the base of the tree.
|
59
|
-
@root_id = add_root('ROOT')
|
60
|
-
append_recursively(@root_id, children)
|
61
|
-
refresh()
|
62
|
-
end
|
63
|
-
|
64
|
-
def append_recursively(parent, children)
|
65
|
-
children.each do | child_cat |
|
66
|
-
name = child_cat.name.empty? ? 'DEFAULT' : child_cat.name
|
67
|
-
|
68
|
-
id = append_item(parent, name, child_cat )
|
69
|
-
# remember this for later use
|
70
|
-
@search_id = id if name == 'SEARCHES'
|
71
|
-
@codes_id = id if name == 'CATEGORIES' or name == 'CODES'
|
72
|
-
|
73
|
-
append_recursively(id, child_cat.children)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
# get the currently active category
|
78
|
-
def get_current_category()
|
79
|
-
if curr_sel = get_selection()
|
80
|
-
return nil if curr_sel == 0
|
81
|
-
category = get_item_data( curr_sel )
|
82
|
-
return nil if category.nil? # important for GTK
|
83
|
-
return nil if category.parent.nil? # don't return root nodes
|
84
|
-
category
|
85
|
-
end
|
86
|
-
end
|
87
|
-
alias :selected_category :get_current_category
|
88
|
-
|
89
|
-
|
90
|
-
def on_edit_label_begin(evt)
|
91
|
-
# TODO - this should really talk to the underlying category,
|
92
|
-
# rather than just assuming bold = locked
|
93
|
-
evt.veto() if bold?(evt.item)
|
94
|
-
end
|
95
|
-
|
96
|
-
def on_edit_label_end(evt)
|
97
|
-
new_text = evt.label
|
98
|
-
if new_text == ""
|
99
|
-
evt.veto()
|
100
|
-
return
|
101
|
-
end
|
102
|
-
|
103
|
-
old_text = get_item_text( evt.item )
|
104
|
-
# don't bother saving if the text isn't changed
|
105
|
-
return if old_text == new_text
|
106
|
-
|
107
|
-
Wx::BusyCursor.busy do
|
108
|
-
c_cdata = get_item_data( evt.item )
|
109
|
-
category = @app.app.get_category( c_cdata.dbid )
|
110
|
-
category.name = new_text
|
111
|
-
@app.app.save_category( category)
|
112
|
-
set_item_data(evt.item, category )
|
113
|
-
$wxapp.broadcast(:category_changed, category)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
def on_drag_begin(evt)
|
118
|
-
@drag_subject = evt.item
|
119
|
-
evt.allow()
|
120
|
-
end
|
121
|
-
|
122
|
-
# relocates or merges the coding of the draggee to the drag target
|
123
|
-
def on_drag_end(evt)
|
124
|
-
@drag_target = evt.item
|
125
|
-
# control_down is a property of a mouse event, so this doesn't work
|
126
|
-
# p "CONTROL IS DOWN" if evt.control_down
|
127
|
-
move_item(@drag_subject, @drag_target)
|
128
|
-
|
129
|
-
@drag_subject = nil
|
130
|
-
@drag_target = nil
|
131
|
-
end
|
132
|
-
|
133
|
-
def dont_move(from)
|
134
|
-
select_item(from) if from != 0
|
135
|
-
end
|
136
|
-
# moves the item identifed by +from+ to be the last child of
|
137
|
-
# +to+
|
138
|
-
def move_item(from, to)
|
139
|
-
return dont_move(from) unless from and to
|
140
|
-
return dont_move(from) if from == 0 or to == 0
|
141
|
-
return dont_move(from) if from == to
|
142
|
-
|
143
|
-
movee = get_item_data( from )
|
144
|
-
destination = get_item_data( to )
|
145
|
-
# don't move root nodes
|
146
|
-
return dont_move(from) if not movee.parent
|
147
|
-
# ignore if no move
|
148
|
-
return dont_move(from) if movee.parent == destination
|
149
|
-
# don't attach to descendants
|
150
|
-
return dont_move(from) if @app.app.is_descendant?(movee, destination)
|
151
|
-
|
152
|
-
shift_nodes = Proc.new do | node, parent |
|
153
|
-
c_text = get_item_text( node )
|
154
|
-
category = get_item_data( node )
|
155
|
-
new_parent = get_item_data( parent )
|
156
|
-
|
157
|
-
# TODO - make expanded persist
|
158
|
-
# c_expand = is_expanded( node )
|
159
|
-
category.parent = new_parent
|
160
|
-
new_child = append_item(parent, c_text, category)
|
161
|
-
child = get_first_child(node)[0]
|
162
|
-
while child != 0
|
163
|
-
shift_nodes.call(child, new_child)
|
164
|
-
child = get_first_child(node)[0]
|
165
|
-
end
|
166
|
-
delete( node )
|
167
|
-
new_child
|
168
|
-
end
|
169
|
-
Wx::BusyCursor.busy do
|
170
|
-
new_cat = shift_nodes.call(from, to)
|
171
|
-
@app.app.save_category( get_item_data(new_cat) )
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
# when we're asked to add an item. This will be attached to the
|
176
|
-
# currently selected category in the tree, or the default 'CATEGORIES'
|
177
|
-
# category if no item is selected
|
178
|
-
def on_create_item()
|
179
|
-
# attach to currently selected
|
180
|
-
parent_item = get_selection()
|
181
|
-
# or default to 'CATEGORIES'
|
182
|
-
if parent_item.nil? || parent_item == 0
|
183
|
-
parent_item = @codes_id
|
184
|
-
end
|
185
|
-
|
186
|
-
dialog = Wx::TextEntryDialog.new(@app.sidebar, "Add Category\n",
|
187
|
-
"Enter the new category name",
|
188
|
-
"", Wx::OK | Wx::CANCEL)
|
189
|
-
|
190
|
-
if dialog.show_modal() == Wx::ID_OK
|
191
|
-
parent = get_item_data(parent_item)
|
192
|
-
cat = QDA::Category.new( dialog.get_value(), parent )
|
193
|
-
@app.app.save_category(cat)
|
194
|
-
$wxapp.broadcast(:category_added, cat)
|
195
|
-
|
196
|
-
expand(parent_item)
|
197
|
-
id = value_to_ident(cat)
|
198
|
-
select_item(id)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def valid_selection(event)
|
203
|
-
item_id = event.get_item()
|
204
|
-
return false if item_id.nil? or item_id == 0
|
205
|
-
return false if is_bold(item_id)
|
206
|
-
get_item_data(item_id)
|
207
|
-
end
|
208
|
-
|
209
|
-
def on_item_selected(event)
|
210
|
-
category = valid_selection(event) or return()
|
211
|
-
@app.current_category = category
|
212
|
-
end
|
213
|
-
|
214
|
-
def on_item_activated(event)
|
215
|
-
item_id = event.get_item()
|
216
|
-
category = get_item_data(item_id)
|
217
|
-
return nil if category.parent.nil?
|
218
|
-
Wx::BusyCursor.busy() do
|
219
|
-
category = @app.app.get_category(category.dbid, 1)
|
220
|
-
if category != nil
|
221
|
-
@app.on_category_open(category)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
def delete_item(itemid)
|
227
|
-
Wx::BusyCursor.busy() do
|
228
|
-
return false if is_bold(itemid)
|
229
|
-
cat = get_item_data(itemid)
|
230
|
-
if cat
|
231
|
-
# delete_category is recursive and so several categories may be
|
232
|
-
# removed, and each is broadcast separately
|
233
|
-
dels = @app.app.delete_category(cat)
|
234
|
-
dels.each do | deletion |
|
235
|
-
$wxapp.broadcast(:category_deleted, deletion)
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
def on_key_down(evt)
|
242
|
-
sel = get_selection()
|
243
|
-
case evt.key_code()
|
244
|
-
when 127 # DEL
|
245
|
-
delete_item(sel)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
|
249
|
-
def receive_category_deleted(cat)
|
250
|
-
# may not include this item if it's a partial subtree
|
251
|
-
if tree_id = value_to_ident(cat)
|
252
|
-
delete( value_to_ident(cat) )
|
253
|
-
@data_table.delete(cat)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
|
257
|
-
def receive_category_changed(cat)
|
258
|
-
# may not include this item if it's a partial subtree
|
259
|
-
if tree_id = value_to_ident(cat)
|
260
|
-
set_item_data( value_to_ident(cat), cat)
|
261
|
-
set_item_text( value_to_ident(cat), cat.name)
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
def receive_category_added(cat)
|
266
|
-
if cat.parent.nil?
|
267
|
-
p_id = @root_id
|
268
|
-
else
|
269
|
-
p_id = value_to_ident(cat.parent)
|
270
|
-
end
|
271
|
-
append_item( p_id, cat.name, cat) if p_id
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
class DocumentList < Wx::ListBox
|
276
|
-
include ItemData
|
277
|
-
include Subscriber
|
278
|
-
|
279
|
-
def initialize(app, *args)
|
280
|
-
@app = app
|
281
|
-
super(*args)
|
282
|
-
evt_left_dclick() { | event | on_double_click(event) }
|
283
|
-
evt_key_down() { | e | on_key_down(e) }
|
284
|
-
|
285
|
-
subscribe(:document_added, :document_deleted, :document_changed)
|
2
|
+
class DocumentsAndCategories < Wx::SplitterWindow
|
3
|
+
def initialize(parent, weft_client)
|
4
|
+
@client = weft_client
|
5
|
+
@app = @client.app
|
6
|
+
super(parent, -1, DEF_POS, DEF_SIZE, Wx::SP_3DSASH)
|
286
7
|
|
287
|
-
|
288
|
-
|
289
|
-
# @menu.append(1234, 'Foo')
|
290
|
-
# @menu.append(1235, 'Bar')
|
291
|
-
end
|
292
|
-
|
293
|
-
# add the document to the end of the list.
|
294
|
-
def append(doc)
|
295
|
-
set_item_data(count, doc)
|
296
|
-
super(doc.title)
|
297
|
-
end
|
298
|
-
|
299
|
-
# needs to rejig the datatable to reflect the fact that all items
|
300
|
-
# below the deleted item have been deleted, and the datatable maps
|
301
|
-
# positions in the list to document values. Urgh. TODO - would be
|
302
|
-
# easier if data_table was an Array under the hood - but it is
|
303
|
-
# currently shared with TreeList data which is more Hash-ish.
|
304
|
-
def delete(item)
|
305
|
-
super(item)
|
306
|
-
@data_table.delete(item)
|
307
|
-
@data_table.keys.find_all { | k | k > item }.sort.each do | k |
|
308
|
-
set_item_data(k - 1, get_item_data(k) )
|
309
|
-
@data_table.delete(k)
|
310
|
-
end
|
311
|
-
# p @data_table
|
312
|
-
end
|
313
|
-
|
314
|
-
|
315
|
-
def on_context_menu(e)
|
316
|
-
popup_menu_xy(@menu, e.get_x, e.get_y)
|
317
|
-
end
|
318
|
-
|
319
|
-
def on_double_click(e)
|
320
|
-
if string_selection != ''
|
321
|
-
@app.on_document_open( selected_document() )
|
322
|
-
else
|
323
|
-
@app.import_document()
|
324
|
-
end
|
325
|
-
end
|
326
|
-
|
327
|
-
def selected_document()
|
328
|
-
sel = get_selection()
|
329
|
-
return nil unless sel and sel != -1
|
330
|
-
get_item_data( sel )
|
331
|
-
end
|
332
|
-
|
333
|
-
def receive_document_deleted(doc)
|
334
|
-
0.upto(count - 1) do | i |
|
335
|
-
if get_item_data(i).dbid == doc.dbid
|
336
|
-
delete(i)
|
337
|
-
return true
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
|
342
|
-
def receive_document_changed(doc)
|
343
|
-
set_string( value_to_ident(doc), doc.title )
|
344
|
-
end
|
345
|
-
|
346
|
-
def receive_document_added(doc)
|
347
|
-
append(doc)
|
348
|
-
end
|
349
|
-
|
350
|
-
def on_key_down(evt)
|
351
|
-
sel = get_selection()
|
352
|
-
return if sel == -1
|
353
|
-
case evt.key_code()
|
354
|
-
when 127 # DEL
|
355
|
-
doc = get_item_data(sel)
|
356
|
-
@app.app.delete_document( doc.dbid )
|
357
|
-
$wxapp.broadcast(:document_deleted, doc)
|
358
|
-
else
|
359
|
-
evt.skip()
|
360
|
-
end
|
361
|
-
end
|
362
|
-
end
|
363
|
-
|
364
|
-
class SideBar < Wx::Frame
|
365
|
-
attr_reader :tree_list
|
366
|
-
|
367
|
-
def initialize(parent, app)
|
368
|
-
super(parent, -1, Lang::SIDEBAR_TITLE,
|
369
|
-
Wx::Point.new(0, 0),
|
370
|
-
parent.proportional_size(0.3, 0.995) )
|
371
|
-
@app = app
|
372
|
-
self.construct()
|
373
|
-
|
374
|
-
conf = Wx::ConfigBase::get()
|
375
|
-
conf.path = "/SideFrame"
|
376
|
-
x = conf.read_int("x", 0)
|
377
|
-
y = conf.read_int("y", 0)
|
378
|
-
w = conf.read_int("w", 200)
|
379
|
-
h = conf.read_int("h", 400)
|
380
|
-
split = conf.read_int("split", 200)
|
381
|
-
split = 200 if split.zero? # otherwise "Documents" are invisible
|
382
|
-
move( Wx::Point.new(x, y) )
|
383
|
-
set_size( Wx::Size.new(w, h) )
|
384
|
-
@splitter.set_sash_position(split)
|
385
|
-
|
386
|
-
|
387
|
-
evt_close() { | e | on_close(e) }
|
388
|
-
end
|
389
|
-
|
390
|
-
def on_close(e)
|
391
|
-
if shown?
|
392
|
-
@app.on_toggle_dandc(e)
|
393
|
-
end
|
394
|
-
e.veto() # don't actually destroy the window
|
395
|
-
end
|
396
|
-
|
397
|
-
# save layout to Config so window is same position next time
|
398
|
-
def remember_size()
|
399
|
-
conf = Wx::ConfigBase::get()
|
400
|
-
# global window size settings
|
401
|
-
if conf
|
402
|
-
size = get_size()
|
403
|
-
pos = get_position
|
404
|
-
conf.path = '/SideFrame'
|
405
|
-
conf.write("x", pos.x)
|
406
|
-
conf.write("y", pos.y)
|
407
|
-
conf.write("w", size.width)
|
408
|
-
conf.write("h", size.height)
|
409
|
-
conf.write("split", @splitter.sash_position)
|
410
|
-
end
|
411
|
-
end
|
412
|
-
|
413
|
-
def construct()
|
414
|
-
# build the sidebar - should go in sidebar.rb
|
415
|
-
@splitter = Wx::SplitterWindow.new(self, -1,
|
416
|
-
Wx::DEFAULT_POSITION,
|
417
|
-
Wx::DEFAULT_SIZE,Wx::SP_3DSASH)
|
418
|
-
|
419
|
-
panel_docs = Wx::Panel.new(@splitter)
|
8
|
+
minimum_pane_size = 75
|
9
|
+
panel_docs = Wx::Panel.new(self)
|
420
10
|
panel_docs_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
421
11
|
|
422
12
|
doc_label = Wx::StaticBox.new(panel_docs, -1, Lang::DOC_PANEL_TITLE)
|
423
13
|
doc_box_sizer = Wx::StaticBoxSizer.new(doc_label, Wx::VERTICAL)
|
424
14
|
|
425
15
|
# Document List
|
426
|
-
@doc_list = DocumentList.new(@
|
16
|
+
@doc_list = DocumentList.new(@client, panel_docs)
|
427
17
|
doc_box_sizer.add(@doc_list, 1, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4)
|
428
18
|
|
429
19
|
# Buttons Below Document List
|
430
20
|
butt_panel = Wx::Panel.new(panel_docs)
|
431
21
|
butt_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
432
22
|
|
433
|
-
|
434
|
-
|
435
|
-
|
23
|
+
@view_doc_button = Wx::Button.new(butt_panel, -1,
|
24
|
+
Lang::VIEW_DOC_BUTTON )
|
25
|
+
@view_doc_button.evt_button(@view_doc_button.get_id) do | e |
|
436
26
|
if @doc_list.selected_document
|
437
|
-
@
|
27
|
+
@client.on_document_open( @doc_list.selected_document )
|
438
28
|
end
|
439
29
|
end
|
440
|
-
|
30
|
+
|
31
|
+
@view_doc_button.disable()
|
441
32
|
evt_listbox( @doc_list.get_id ) { | e | @view_doc_button.enable(true) }
|
442
33
|
|
443
|
-
butt_sizer.add(
|
34
|
+
butt_sizer.add(@view_doc_button, 0, Wx::ALL|Wx::ALIGN_LEFT, 4)
|
444
35
|
|
445
36
|
button = Wx::Button.new(butt_panel, -1, Lang::IMPORT_DOC_BUTTON)
|
446
|
-
button.evt_button(button.get_id) { | e | @
|
37
|
+
button.evt_button(button.get_id) { | e | @client.on_import_document(e) }
|
447
38
|
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
448
39
|
|
449
40
|
butt_panel.set_sizer(butt_sizer)
|
@@ -452,13 +43,13 @@ module QDA::GUI
|
|
452
43
|
panel_docs.set_sizer(panel_docs_sizer)
|
453
44
|
|
454
45
|
# Category Tree
|
455
|
-
panel_cats = Wx::Panel.new(
|
46
|
+
panel_cats = Wx::Panel.new(self)
|
456
47
|
panel_cats_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
457
48
|
|
458
49
|
cats_label = Wx::StaticBox.new(panel_cats, -1, Lang::CAT_PANEL_TITLE)
|
459
50
|
cats_box_sizer = Wx::StaticBoxSizer.new(cats_label, Wx::VERTICAL)
|
460
51
|
|
461
|
-
@tree_list = CategoryTree.new(@
|
52
|
+
@tree_list = CategoryTree.new(@client, panel_cats, false)
|
462
53
|
cats_box_sizer.add(@tree_list, 3, Wx::GROW|Wx::ALL, 4)
|
463
54
|
|
464
55
|
# Buttons below Category Tree
|
@@ -468,13 +59,13 @@ module QDA::GUI
|
|
468
59
|
button = Wx::Button.new(butt_panel, -1, Lang::VIEW_CAT_BUTTON)
|
469
60
|
button.evt_button(button.get_id) do | e |
|
470
61
|
if @tree_list.selected_category
|
471
|
-
@
|
62
|
+
@client.on_category_open( @tree_list.selected_category )
|
472
63
|
end
|
473
64
|
end
|
474
65
|
button.disable()
|
475
66
|
@view_cat_button = button
|
476
67
|
evt_tree_sel_changing( @tree_list.get_id ) do | e |
|
477
|
-
@tree_list.valid_selection(e) ?
|
68
|
+
@tree_list.valid_selection?(e) ?
|
478
69
|
@view_cat_button.enable(true) : @view_cat_button.disable()
|
479
70
|
end
|
480
71
|
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
@@ -489,10 +80,77 @@ module QDA::GUI
|
|
489
80
|
panel_cats.set_sizer(panel_cats_sizer)
|
490
81
|
|
491
82
|
|
492
|
-
@app.
|
493
|
-
@tree_list.populate( @app.
|
83
|
+
@app.each_doc { | d | @doc_list.append_item(d) }
|
84
|
+
@tree_list.populate( @app.get_all_categories() )
|
85
|
+
if opened = @app.get_preference('TreeLayout')
|
86
|
+
@tree_list.expand_items(opened)
|
87
|
+
end
|
88
|
+
|
89
|
+
split_horizontally(panel_docs, panel_cats, 200)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class SideBar < Wx::Frame
|
94
|
+
attr_reader :tree_list, :doc_list
|
95
|
+
|
96
|
+
def initialize(parent, weft_client)
|
97
|
+
super(parent, -1, Lang::SIDEBAR_TITLE,
|
98
|
+
Wx::Point.new(0, 0),
|
99
|
+
parent.proportional_size(0.3, 0.995) )
|
100
|
+
@client = weft_client
|
101
|
+
self.icon = @client.fetch_icon('project')
|
102
|
+
@splitter = DocumentsAndCategories.new(self, @client)
|
103
|
+
|
104
|
+
conf = Wx::ConfigBase::get()
|
105
|
+
conf.path = "/SideFrame"
|
106
|
+
x = conf.read_int("x", -1)
|
107
|
+
x = DEF_POS.x if x < 0
|
108
|
+
y = conf.read_int("y", -1)
|
109
|
+
y = DEF_POS.y if y < 0
|
110
|
+
w = conf.read_int("w", -1)
|
111
|
+
w = ( DEF_SIZE.width / 2 ) if w < 160
|
112
|
+
h = conf.read_int("h", -1)
|
113
|
+
h = DEF_SIZE.height if h < 160
|
114
|
+
max = conf.read_bool("max", false)
|
115
|
+
|
116
|
+
if max
|
117
|
+
maximize(true)
|
118
|
+
else
|
119
|
+
move( Wx::Point.new(x, y) )
|
120
|
+
set_size( Wx::Size.new(w, h) )
|
121
|
+
end
|
122
|
+
|
123
|
+
split = conf.read_int("split", 200)
|
124
|
+
# sometimes weirdly ends up with negative value
|
125
|
+
split = client_size.height / 2 if split <= 0
|
126
|
+
@splitter.set_sash_position(split)
|
494
127
|
|
495
|
-
|
128
|
+
evt_close() { | e | on_close(e) }
|
129
|
+
end
|
130
|
+
|
131
|
+
def on_close(e)
|
132
|
+
if shown?
|
133
|
+
@client.on_toggle_dandc(e)
|
134
|
+
end
|
135
|
+
e.veto() # don't actually destroy the window
|
496
136
|
end
|
137
|
+
|
138
|
+
# save layout to Config so window is same position next time
|
139
|
+
def remember_size()
|
140
|
+
conf = Wx::ConfigBase::get()
|
141
|
+
# global window size settings
|
142
|
+
if conf
|
143
|
+
size = get_size()
|
144
|
+
pos = get_position()
|
145
|
+
conf.path = '/SideFrame'
|
146
|
+
conf.write("x", pos.x)
|
147
|
+
conf.write("y", pos.y)
|
148
|
+
conf.write("w", size.width)
|
149
|
+
conf.write("h", size.height)
|
150
|
+
conf.write("split", @splitter.sash_position)
|
151
|
+
conf.write("max", maximized?)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
497
155
|
end
|
498
156
|
end
|