weft-qda 0.9.6
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 +21 -0
- data/lib/weft/WEFT-VERSION-STRING.rb +1 -0
- data/lib/weft/application.rb +130 -0
- data/lib/weft/backend.rb +39 -0
- data/lib/weft/backend/marshal.rb +26 -0
- data/lib/weft/backend/mysql.rb +267 -0
- data/lib/weft/backend/n6.rb +366 -0
- data/lib/weft/backend/sqlite.rb +633 -0
- data/lib/weft/backend/sqlite/category_tree.rb +104 -0
- data/lib/weft/backend/sqlite/schema.rb +152 -0
- data/lib/weft/backend/sqlite/upgradeable.rb +55 -0
- data/lib/weft/category.rb +157 -0
- data/lib/weft/coding.rb +355 -0
- data/lib/weft/document.rb +118 -0
- data/lib/weft/filters.rb +243 -0
- data/lib/weft/wxgui.rb +687 -0
- data/lib/weft/wxgui/category.xpm +26 -0
- data/lib/weft/wxgui/dialogs.rb +128 -0
- data/lib/weft/wxgui/document.xpm +25 -0
- data/lib/weft/wxgui/error_handler.rb +52 -0
- data/lib/weft/wxgui/inspectors.rb +361 -0
- data/lib/weft/wxgui/inspectors/category.rb +165 -0
- data/lib/weft/wxgui/inspectors/codereview.rb +275 -0
- data/lib/weft/wxgui/inspectors/document.rb +139 -0
- data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -0
- data/lib/weft/wxgui/inspectors/script.rb +35 -0
- data/lib/weft/wxgui/inspectors/search.rb +265 -0
- data/lib/weft/wxgui/inspectors/textcontrols.rb +304 -0
- data/lib/weft/wxgui/lang.rb +17 -0
- data/lib/weft/wxgui/lang/en.rb +45 -0
- data/lib/weft/wxgui/mondrian.xpm +44 -0
- data/lib/weft/wxgui/search.xpm +25 -0
- data/lib/weft/wxgui/sidebar.rb +498 -0
- data/lib/weft/wxgui/utilities.rb +148 -0
- data/lib/weft/wxgui/weft16.xpm +31 -0
- data/lib/weft/wxgui/workarea.rb +249 -0
- data/test/001-document.rb +196 -0
- data/test/002-category.rb +138 -0
- data/test/003-code.rb +370 -0
- data/test/004-application.rb +52 -0
- data/test/006-filters.rb +139 -0
- data/test/009a-backend_sqlite_basic.rb +280 -0
- data/test/009b-backend_sqlite_complex.rb +175 -0
- data/test/009c_backend_sqlite_bench.rb +81 -0
- data/test/010-backend_nudist.rb +5 -0
- data/test/all-tests.rb +1 -0
- data/test/manual-gui-script.txt +24 -0
- data/test/testdata/autocoding-test.txt +15 -0
- data/test/testdata/iso-8859-1.txt +5 -0
- data/test/testdata/sample_doc.txt +19 -0
- data/test/testdata/search_results.txt +1254 -0
- data/test/testdata/text1-dos-ascii.txt +2 -0
- data/test/testdata/text1-unix-utf8.txt +2 -0
- data/weft-qda.rb +28 -0
- metadata +96 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
module QDA::GUI
|
2
|
+
module Lang
|
3
|
+
# +lang_code+ = 'En'
|
4
|
+
def Lang.set_language(lang_code)
|
5
|
+
begin
|
6
|
+
require "weft/wxgui/lang/#{lang_code.downcase}"
|
7
|
+
rescue LoadError
|
8
|
+
raise LoadError, "No language file for language '#{lang_code}'"
|
9
|
+
end
|
10
|
+
language = QDA::GUI::Lang.const_get(lang_code)
|
11
|
+
language.constants.each do | interface_element |
|
12
|
+
translation = language::const_get(interface_element)
|
13
|
+
const_set( interface_element, translation )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module QDA::GUI::Lang
|
2
|
+
module En
|
3
|
+
UNSAVED_CHANGES_CONFIRM = "You have unsaved changes in your project; do you wish to save these?"
|
4
|
+
UNSAVED_CHANGES_CONFIRM_TITLE = "Save changes?"
|
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
|
+
SIDEBAR_TITLE = "Documents & Categories"
|
7
|
+
SEARCH_RESULTS = "search results"
|
8
|
+
REINDEX_DOCS_WARNING =
|
9
|
+
"This will reindex all previously imported documents, which will take
|
10
|
+
some time. You only need to do this once if:
|
11
|
+
1) you first created this project in version 0.9.3 or earlier OR
|
12
|
+
2) you have recently imported documents from N6.
|
13
|
+
Otherwise there is no need to run this command.
|
14
|
+
|
15
|
+
Are you sure you wish to proceed and reindex all documents? "
|
16
|
+
|
17
|
+
REINDEX_DOCS_WARNING_TITLE = "Reindex all documents?"
|
18
|
+
|
19
|
+
NO_SEARCH_TERM_SPECIFIED = "No search term specified"
|
20
|
+
DOC_PANEL_TITLE = 'Documents'
|
21
|
+
IMPORT_DOC_BUTTON = 'Import...'
|
22
|
+
VIEW_DOC_BUTTON = 'View'
|
23
|
+
CAT_PANEL_TITLE = 'Categories'
|
24
|
+
NEW_CAT_BUTTON = 'New...'
|
25
|
+
VIEW_CAT_BUTTON = 'View'
|
26
|
+
DISPLAY_OPTIONS_LABEL = 'Display options'
|
27
|
+
|
28
|
+
HELP_ABOUT_MESSAGE = "Thank you for trying Weft QDA
|
29
|
+
Version '#{::WEFT_VERSION}'\n\n-- qda@pressure.to"
|
30
|
+
|
31
|
+
if RUBY_PLATFORM =~ /mswin/
|
32
|
+
HELP_HELP_MESSAGE = <<HELP
|
33
|
+
Help files are installed with Weft QDA.
|
34
|
+
Please check in your 'Start' menu for a shortcut the Weft Manual help file.
|
35
|
+
|
36
|
+
Alternatively, read the manual online
|
37
|
+
http://www.pressure.to/qda/doc/
|
38
|
+
HELP
|
39
|
+
|
40
|
+
else
|
41
|
+
HELP_HELP_MESSAGE = "Please see the online documentation at
|
42
|
+
http://www.pressure.to/qda/doc/"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,44 @@
|
|
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
|
+
};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
/* XPM */
|
2
|
+
static char *search[] = {
|
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
|
+
"XXXXXXXXX XXX",
|
11
|
+
"XXXXXXXX .... XX",
|
12
|
+
"XXXXXXX ...... X",
|
13
|
+
"XXXXXXX ...... X",
|
14
|
+
"XXXXXXX ...... X",
|
15
|
+
"XXXXXXX ...... X",
|
16
|
+
"XXXXXXXX .... XX",
|
17
|
+
"XXXXXXX X XXX",
|
18
|
+
"XXXXXX XXXXXXXXX",
|
19
|
+
"XXXXX XXXXXXXXXX",
|
20
|
+
"XXXX XXXXXXXXXXX",
|
21
|
+
"XXX XXXXXXXXXXXX",
|
22
|
+
"XX XXXXXXXXXXXXX",
|
23
|
+
"XXXXXXXXXXXXXXXX",
|
24
|
+
"XXXXXXXXXXXXXXXX"
|
25
|
+
};
|
@@ -0,0 +1,498 @@
|
|
1
|
+
module QDA::GUI
|
2
|
+
# the category tree list for the side panel
|
3
|
+
class CategoryTree < Wx::TreeCtrl
|
4
|
+
CATEGORY_TREE_STYLE = Wx::TR_HIDE_ROOT|Wx::TR_HAS_BUTTONS|
|
5
|
+
Wx::TR_LINES_AT_ROOT|Wx::TR_EDIT_LABELS
|
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)
|
286
|
+
|
287
|
+
# evt_context_menu() { | event | on_context_menu(event) }
|
288
|
+
# @menu = Wx::Menu.new()
|
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)
|
420
|
+
panel_docs_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
421
|
+
|
422
|
+
doc_label = Wx::StaticBox.new(panel_docs, -1, Lang::DOC_PANEL_TITLE)
|
423
|
+
doc_box_sizer = Wx::StaticBoxSizer.new(doc_label, Wx::VERTICAL)
|
424
|
+
|
425
|
+
# Document List
|
426
|
+
@doc_list = DocumentList.new(@app, panel_docs)
|
427
|
+
doc_box_sizer.add(@doc_list, 1, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4)
|
428
|
+
|
429
|
+
# Buttons Below Document List
|
430
|
+
butt_panel = Wx::Panel.new(panel_docs)
|
431
|
+
butt_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
432
|
+
|
433
|
+
button = Wx::Button.new(butt_panel, -1, Lang::VIEW_DOC_BUTTON)
|
434
|
+
@view_doc_button = button
|
435
|
+
button.evt_button(button.get_id) do | e |
|
436
|
+
if @doc_list.selected_document
|
437
|
+
@app.on_document_open( @doc_list.selected_document )
|
438
|
+
end
|
439
|
+
end
|
440
|
+
button.disable()
|
441
|
+
evt_listbox( @doc_list.get_id ) { | e | @view_doc_button.enable(true) }
|
442
|
+
|
443
|
+
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_LEFT, 4)
|
444
|
+
|
445
|
+
button = Wx::Button.new(butt_panel, -1, Lang::IMPORT_DOC_BUTTON)
|
446
|
+
button.evt_button(button.get_id) { | e | @app.on_import_document(e) }
|
447
|
+
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
448
|
+
|
449
|
+
butt_panel.set_sizer(butt_sizer)
|
450
|
+
doc_box_sizer.add(butt_panel, 0, Wx::ALL|Wx::GROW|Wx::ADJUST_MINSIZE)
|
451
|
+
panel_docs_sizer.add(doc_box_sizer, 1, Wx::ALL|Wx::GROW, 4)
|
452
|
+
panel_docs.set_sizer(panel_docs_sizer)
|
453
|
+
|
454
|
+
# Category Tree
|
455
|
+
panel_cats = Wx::Panel.new(@splitter)
|
456
|
+
panel_cats_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
|
457
|
+
|
458
|
+
cats_label = Wx::StaticBox.new(panel_cats, -1, Lang::CAT_PANEL_TITLE)
|
459
|
+
cats_box_sizer = Wx::StaticBoxSizer.new(cats_label, Wx::VERTICAL)
|
460
|
+
|
461
|
+
@tree_list = CategoryTree.new(@app, panel_cats, false)
|
462
|
+
cats_box_sizer.add(@tree_list, 3, Wx::GROW|Wx::ALL, 4)
|
463
|
+
|
464
|
+
# Buttons below Category Tree
|
465
|
+
butt_panel = Wx::Panel.new(panel_cats)
|
466
|
+
butt_sizer = Wx::BoxSizer.new(Wx::HORIZONTAL)
|
467
|
+
|
468
|
+
button = Wx::Button.new(butt_panel, -1, Lang::VIEW_CAT_BUTTON)
|
469
|
+
button.evt_button(button.get_id) do | e |
|
470
|
+
if @tree_list.selected_category
|
471
|
+
@app.on_category_open( @tree_list.selected_category )
|
472
|
+
end
|
473
|
+
end
|
474
|
+
button.disable()
|
475
|
+
@view_cat_button = button
|
476
|
+
evt_tree_sel_changing( @tree_list.get_id ) do | e |
|
477
|
+
@tree_list.valid_selection(e) ?
|
478
|
+
@view_cat_button.enable(true) : @view_cat_button.disable()
|
479
|
+
end
|
480
|
+
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
|
481
|
+
|
482
|
+
button = Wx::Button.new(butt_panel, -1, Lang::NEW_CAT_BUTTON)
|
483
|
+
button.evt_button(button.get_id) { | e | @tree_list.on_create_item() }
|
484
|
+
butt_sizer.add(button, 0, Wx::ALL|Wx::ALIGN_LEFT, 4)
|
485
|
+
|
486
|
+
butt_panel.set_sizer(butt_sizer)
|
487
|
+
cats_box_sizer.add(butt_panel, 0, Wx::ALL|Wx::GROW|Wx::ADJUST_MINSIZE)
|
488
|
+
panel_cats_sizer.add(cats_box_sizer, 1, Wx::ALL|Wx::GROW, 4)
|
489
|
+
panel_cats.set_sizer(panel_cats_sizer)
|
490
|
+
|
491
|
+
|
492
|
+
@app.app.each_doc { | d | @doc_list.append(d) }
|
493
|
+
@tree_list.populate( @app.app.get_all_categories() )
|
494
|
+
|
495
|
+
@splitter.split_horizontally(panel_docs, panel_cats, 200)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|