weft-qda 0.9.6 → 0.9.8

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 (86) hide show
  1. data/lib/weft.rb +16 -1
  2. data/lib/weft/WEFT-VERSION-STRING.rb +1 -1
  3. data/lib/weft/application.rb +17 -74
  4. data/lib/weft/backend.rb +6 -32
  5. data/lib/weft/backend/sqlite.rb +222 -164
  6. data/lib/weft/backend/sqlite/category_tree.rb +52 -48
  7. data/lib/weft/backend/sqlite/database.rb +57 -0
  8. data/lib/weft/backend/sqlite/upgradeable.rb +7 -0
  9. data/lib/weft/broadcaster.rb +90 -0
  10. data/lib/weft/category.rb +139 -47
  11. data/lib/weft/codereview.rb +160 -0
  12. data/lib/weft/coding.rb +74 -23
  13. data/lib/weft/document.rb +23 -10
  14. data/lib/weft/exceptions.rb +10 -0
  15. data/lib/weft/filters.rb +47 -224
  16. data/lib/weft/filters/indexers.rb +137 -0
  17. data/lib/weft/filters/input.rb +118 -0
  18. data/lib/weft/filters/output.rb +101 -0
  19. data/lib/weft/filters/templates.rb +80 -0
  20. data/lib/weft/filters/win32backtick.rb +246 -0
  21. data/lib/weft/query.rb +169 -0
  22. data/lib/weft/wxgui.rb +349 -294
  23. data/lib/weft/wxgui/constants.rb +43 -0
  24. data/lib/weft/wxgui/controls.rb +6 -0
  25. data/lib/weft/wxgui/controls/category_dropdown.rb +192 -0
  26. data/lib/weft/wxgui/controls/category_tree.rb +314 -0
  27. data/lib/weft/wxgui/controls/document_list.rb +97 -0
  28. data/lib/weft/wxgui/controls/multitype_control.rb +37 -0
  29. data/lib/weft/wxgui/{inspectors → controls}/textcontrols.rb +235 -64
  30. data/lib/weft/wxgui/dialogs.rb +144 -41
  31. data/lib/weft/wxgui/error_handler.rb +116 -36
  32. data/lib/weft/wxgui/exceptions.rb +7 -0
  33. data/lib/weft/wxgui/inspectors.rb +61 -208
  34. data/lib/weft/wxgui/inspectors/category.rb +19 -16
  35. data/lib/weft/wxgui/inspectors/codereview.rb +90 -132
  36. data/lib/weft/wxgui/inspectors/document.rb +12 -8
  37. data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -56
  38. data/lib/weft/wxgui/inspectors/query.rb +284 -0
  39. data/lib/weft/wxgui/inspectors/script.rb +147 -23
  40. data/lib/weft/wxgui/lang/en.rb +69 -0
  41. data/lib/weft/wxgui/sidebar.rb +90 -432
  42. data/lib/weft/wxgui/utilities.rb +70 -91
  43. data/lib/weft/wxgui/workarea.rb +150 -43
  44. data/share/icons/category.ico +0 -0
  45. data/share/icons/category.xpm +109 -0
  46. data/share/icons/codereview.ico +0 -0
  47. data/share/icons/codereview.xpm +54 -0
  48. data/share/icons/d_and_c.xpm +126 -0
  49. data/share/icons/document.ico +0 -0
  50. data/share/icons/document.xpm +70 -0
  51. data/share/icons/project.ico +0 -0
  52. data/share/icons/query.ico +0 -0
  53. data/share/icons/query.xpm +56 -0
  54. data/{lib/weft/wxgui → share/icons}/search.xpm +0 -0
  55. data/share/icons/weft.ico +0 -0
  56. data/share/icons/weft.xpm +62 -0
  57. data/share/icons/weft16.ico +0 -0
  58. data/share/icons/weft32.ico +0 -0
  59. data/share/templates/category_plain.html +18 -0
  60. data/share/templates/codereview_plain.html +18 -0
  61. data/share/templates/document_plain.html +13 -0
  62. data/share/templates/document_plain.txt +7 -0
  63. data/test/001-document.rb +55 -36
  64. data/test/002-category.rb +81 -6
  65. data/test/003-code.rb +8 -4
  66. data/test/004-application.rb +13 -34
  67. data/test/005-query_review.rb +139 -0
  68. data/test/006-filters.rb +54 -42
  69. data/test/007-output_filters.rb +113 -0
  70. data/test/009a-backend_sqlite_basic.rb +95 -24
  71. data/test/009b-backend_sqlite_complex.rb +43 -62
  72. data/test/009c_backend_sqlite_bench.rb +5 -10
  73. data/test/053-doc_inspector.rb +46 -0
  74. data/test/055-query_window.rb +50 -0
  75. data/test/all-tests.rb +1 -0
  76. data/test/test-common.rb +19 -0
  77. data/test/testdata/empty.qdp +0 -0
  78. data/test/testdata/simple with space.pdf +0 -0
  79. data/test/testdata/simple.pdf +0 -0
  80. data/weft-qda.rb +40 -7
  81. metadata +74 -14
  82. data/lib/weft/wxgui/category.xpm +0 -26
  83. data/lib/weft/wxgui/document.xpm +0 -25
  84. data/lib/weft/wxgui/inspectors/search.rb +0 -265
  85. data/lib/weft/wxgui/mondrian.xpm +0 -44
  86. data/lib/weft/wxgui/weft16.xpm +0 -31
@@ -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.
@@ -1,449 +1,40 @@
1
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)
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
- # 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)
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(@app, panel_docs)
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
- 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 |
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
- @app.on_document_open( @doc_list.selected_document )
27
+ @client.on_document_open( @doc_list.selected_document )
438
28
  end
439
29
  end
440
- button.disable()
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(button, 0, Wx::ALL|Wx::ALIGN_LEFT, 4)
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 | @app.on_import_document(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(@splitter)
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(@app, panel_cats, false)
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
- @app.on_category_open( @tree_list.selected_category )
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.app.each_doc { | d | @doc_list.append(d) }
493
- @tree_list.populate( @app.app.get_all_categories() )
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
- @splitter.split_horizontally(panel_docs, panel_cats, 200)
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