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.
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