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.
Files changed (55) hide show
  1. data/lib/weft.rb +21 -0
  2. data/lib/weft/WEFT-VERSION-STRING.rb +1 -0
  3. data/lib/weft/application.rb +130 -0
  4. data/lib/weft/backend.rb +39 -0
  5. data/lib/weft/backend/marshal.rb +26 -0
  6. data/lib/weft/backend/mysql.rb +267 -0
  7. data/lib/weft/backend/n6.rb +366 -0
  8. data/lib/weft/backend/sqlite.rb +633 -0
  9. data/lib/weft/backend/sqlite/category_tree.rb +104 -0
  10. data/lib/weft/backend/sqlite/schema.rb +152 -0
  11. data/lib/weft/backend/sqlite/upgradeable.rb +55 -0
  12. data/lib/weft/category.rb +157 -0
  13. data/lib/weft/coding.rb +355 -0
  14. data/lib/weft/document.rb +118 -0
  15. data/lib/weft/filters.rb +243 -0
  16. data/lib/weft/wxgui.rb +687 -0
  17. data/lib/weft/wxgui/category.xpm +26 -0
  18. data/lib/weft/wxgui/dialogs.rb +128 -0
  19. data/lib/weft/wxgui/document.xpm +25 -0
  20. data/lib/weft/wxgui/error_handler.rb +52 -0
  21. data/lib/weft/wxgui/inspectors.rb +361 -0
  22. data/lib/weft/wxgui/inspectors/category.rb +165 -0
  23. data/lib/weft/wxgui/inspectors/codereview.rb +275 -0
  24. data/lib/weft/wxgui/inspectors/document.rb +139 -0
  25. data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -0
  26. data/lib/weft/wxgui/inspectors/script.rb +35 -0
  27. data/lib/weft/wxgui/inspectors/search.rb +265 -0
  28. data/lib/weft/wxgui/inspectors/textcontrols.rb +304 -0
  29. data/lib/weft/wxgui/lang.rb +17 -0
  30. data/lib/weft/wxgui/lang/en.rb +45 -0
  31. data/lib/weft/wxgui/mondrian.xpm +44 -0
  32. data/lib/weft/wxgui/search.xpm +25 -0
  33. data/lib/weft/wxgui/sidebar.rb +498 -0
  34. data/lib/weft/wxgui/utilities.rb +148 -0
  35. data/lib/weft/wxgui/weft16.xpm +31 -0
  36. data/lib/weft/wxgui/workarea.rb +249 -0
  37. data/test/001-document.rb +196 -0
  38. data/test/002-category.rb +138 -0
  39. data/test/003-code.rb +370 -0
  40. data/test/004-application.rb +52 -0
  41. data/test/006-filters.rb +139 -0
  42. data/test/009a-backend_sqlite_basic.rb +280 -0
  43. data/test/009b-backend_sqlite_complex.rb +175 -0
  44. data/test/009c_backend_sqlite_bench.rb +81 -0
  45. data/test/010-backend_nudist.rb +5 -0
  46. data/test/all-tests.rb +1 -0
  47. data/test/manual-gui-script.txt +24 -0
  48. data/test/testdata/autocoding-test.txt +15 -0
  49. data/test/testdata/iso-8859-1.txt +5 -0
  50. data/test/testdata/sample_doc.txt +19 -0
  51. data/test/testdata/search_results.txt +1254 -0
  52. data/test/testdata/text1-dos-ascii.txt +2 -0
  53. data/test/testdata/text1-unix-utf8.txt +2 -0
  54. data/weft-qda.rb +28 -0
  55. 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