weft-qda 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
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,165 @@
1
+ module QDA
2
+ module GUI
3
+ class CategoryWindow < InspectorWindow
4
+ include Subscriber
5
+
6
+ def initialize(cat, app, workarea, layout)
7
+ @cat = cat
8
+ @app = app
9
+
10
+ super(workarea, @cat.name, layout) do | parent |
11
+ text_box = CompositeText.new(parent, '', 0, @app)
12
+ text_box.populate( @app.app.get_text_at_category(@cat) )
13
+ text_box
14
+ end
15
+
16
+ construct_details_panel()
17
+ if ! @cat.children.empty?
18
+ construct_explore_panel()
19
+ end
20
+
21
+ @notebook.evt_notebook_page_changing(@notebook.get_id) do | e |
22
+ on_change_page(e)
23
+ end
24
+
25
+ set_icon( app.fetch_icon("category.xpm") )
26
+
27
+ subscribe(:document_deleted, :category_changed, :category_deleted)
28
+ end
29
+
30
+ # just a mucky method to reduce the length of the initializer -
31
+ # ideally i guess this would be done using XRC
32
+ def construct_details_panel()
33
+ panel2 = Wx::Panel.new(@notebook, -1)
34
+ sizer2 = Wx::BoxSizer.new(Wx::VERTICAL)
35
+
36
+ # the text entry box for the category's name
37
+ name_box_label = Wx::StaticBox.new(panel2, -1, 'Name')
38
+ name_box_sizer = Wx::StaticBoxSizer.new(name_box_label,
39
+ Wx::VERTICAL)
40
+ @name_field = Wx::TextCtrl.new(panel2, -1, @cat.name)
41
+ name_box_sizer.add(@name_field, 0,
42
+ Wx::ADJUST_MINSIZE|Wx::ALL|Wx::GROW, 4)
43
+ sizer2.add(name_box_sizer,0, Wx::ADJUST_MINSIZE|Wx::ALL|Wx::GROW, 4)
44
+
45
+ # Create the statistics panel
46
+ info_panel = Wx::Panel.new(panel2, -1)
47
+ info_box_label = Wx::StaticBox.new(info_panel, -1, 'Statistics')
48
+ info_box_sizer = Wx::StaticBoxSizer.new(info_box_label,
49
+ Wx::VERTICAL)
50
+
51
+ info_sizer = Wx::FlexGridSizer.new(3, 2, 5, 6)
52
+ info_sizer.add( Wx::StaticText.new(info_panel, -1, 'Documents:') )
53
+ @num_docs_label = Wx::StaticText.new(info_panel, -1,
54
+ @cat.num_of_docs.to_s)
55
+ info_sizer.add( @num_docs_label )
56
+
57
+ info_sizer.add( Wx::StaticText.new(info_panel, -1, 'Passages:') )
58
+ @num_codes_label = Wx::StaticText.new(info_panel, -1,
59
+ @cat.num_of_codes.to_s)
60
+ info_sizer.add( @num_codes_label )
61
+
62
+ info_sizer.add( Wx::StaticText.new(info_panel, -1, 'Characters:') )
63
+
64
+ @num_chars_label = Wx::StaticText.new(info_panel, -1,
65
+ @cat.num_of_chars.to_s)
66
+ info_sizer.add(@num_chars_label)
67
+ info_box_sizer.add(info_sizer, 0,
68
+ Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL, 4)
69
+ info_panel.set_sizer(info_box_sizer)
70
+ sizer2.add(info_panel, 0, Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL, 2)
71
+
72
+
73
+ memo_label = Wx::StaticBox.new(panel2, -1, 'Memo')
74
+ memo_sizer = Wx::StaticBoxSizer.new(memo_label, Wx::VERTICAL)
75
+
76
+ @memo_box = Wx::TextCtrl.new(panel2, -1, @cat.memo,
77
+ Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
78
+ Wx::TE_MULTILINE|Wx::TE_NOHIDESEL)
79
+
80
+ @memo_box.evt_enter_window do | e |
81
+ set_cursor Wx::Cursor.new(Wx::CURSOR_IBEAM)
82
+ end
83
+
84
+ @memo_box.evt_leave_window do | e |
85
+ set_cursor Wx::Cursor.new(Wx::CURSOR_ARROW)
86
+ end
87
+ memo_sizer.add(@memo_box, 10, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4)
88
+ sizer2.add(memo_sizer, 1, Wx::GROW|Wx::ADJUST_MINSIZE, 4)
89
+
90
+
91
+ button = Wx::Button.new(panel2, -1, 'Apply')
92
+ button.evt_button(button.get_id) { | e | on_save_memo(e) }
93
+ sizer2.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT|Wx::ADJUST_MINSIZE, 4)
94
+ panel2.set_sizer( sizer2 )
95
+
96
+
97
+ @notebook.add_page(panel2, 'details')
98
+ end
99
+
100
+ # cf construct_details_panel
101
+ def construct_explore_panel()
102
+ panel = Wx::Panel.new(@notebook, -1)
103
+ # panel.set_font(GLOBAL_FONT)
104
+ sizer = Wx::BoxSizer.new(Wx::VERTICAL)
105
+ # create a local locked copy
106
+ @tree_list = CategoryTree.new(@app, panel, true)
107
+ @tree_list.populate([@cat])
108
+ sizer.add(@tree_list, 10, Wx::GROW|Wx::ALL, 4)
109
+ panel.set_sizer( sizer )
110
+ @notebook.add_page(panel, 'explore')
111
+ end
112
+
113
+ def on_save_memo(e)
114
+ @cat.memo = @memo_box.value
115
+ @cat.name = @name_field.value
116
+ Wx::BusyCursor.busy { @app.app.save_category( @cat ) }
117
+ $wxapp.broadcast(:category_changed, @cat)
118
+ end
119
+
120
+ def on_change_page(e)
121
+ # relying on the fact we've only got two tabs - so if the
122
+ # previously selected is 0 (The first page) we should update
123
+ # the stats as we're going to that page
124
+ if e.get_selection == 0
125
+ update_stats()
126
+ end
127
+ e.allow()
128
+ end
129
+
130
+ def update_stats()
131
+ @num_docs_label.set_label( @cat.num_of_docs.to_s )
132
+ @num_codes_label.set_label( @cat.num_of_codes.to_s )
133
+ @num_chars_label.set_label( @cat.num_of_chars.to_s )
134
+ end
135
+
136
+ # updates the display from teh currently stored value for @cat,
137
+ # and refetches the displayed text from the database
138
+ def refresh()
139
+ set_title( @cat.name )
140
+ @text_box.value = ''
141
+ @text_box.populate( @app.app.get_text_at_category(@cat) )
142
+ @name_field.value = @cat.name
143
+ if curr_category = @drop_down.current_category()
144
+ @text_box.highlight_codingtable(curr_category.codes)
145
+ end
146
+ update_stats()
147
+ end
148
+
149
+ def receive_category_changed(category)
150
+ if category.dbid == @cat.dbid
151
+ @cat = category
152
+ refresh()
153
+ end
154
+ end
155
+
156
+ def receive_category_deleted(category)
157
+ close() if category.dbid == @cat.dbid
158
+ end
159
+
160
+ def receive_document_deleted(doc)
161
+ refresh if @cat.codes.codes?(doc)
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,275 @@
1
+ module QDA::GUI
2
+ class CodeReviewWindow < WorkAreaWindow
3
+ include Subscriber
4
+
5
+ W_WINDOW_DEF_SIZE = [ 0.8, 0.6 ]
6
+ BASE_COLOUR = Wx::Colour.new(255, 255, 255)
7
+ HIGHLIGHT_COLOUR = Wx::Colour.new(255, 0, 0)
8
+ GRID_COLOUR = Wx::Colour.new(223, 223, 233)
9
+
10
+ def initialize(obj, app, workarea, layout)
11
+ super(workarea, "Code Review #{obj.dbid}", nil)
12
+ @cols, @rows, @contents = [], [], []
13
+ @count_method = :num_of_docs
14
+
15
+ main_sizer = Wx::BoxSizer.new(Wx::VERTICAL)
16
+ panel = Wx::Panel.new(self)
17
+ @grid = Wx::Grid.new(panel, -1, Wx::DEFAULT_POSITION,
18
+ Wx::DEFAULT_SIZE, Wx::SIMPLE_BORDER)
19
+ @grid.create_grid( 12, 6 )
20
+ @grid.set_row_label_size(160)
21
+ @grid.set_grid_line_colour(GRID_COLOUR)
22
+ @grid.enable_editing(false)
23
+ 0.upto(5) { | i | @grid.set_col_label_value(i, '') }
24
+ 0.upto(11) { | i | @grid.set_row_label_value(i, '') }
25
+
26
+ evt_grid_label_left_click() { | evt | on_label_clicked(evt) }
27
+
28
+ # the controls panel
29
+ main_sizer.add(@grid, 1, Wx::GROW|Wx::ALL, 4)
30
+ # butt_panel = Wx::Panel.new(panel, -1)
31
+ butt_box_label = Wx::StaticBox.new(panel, -1, 'Select categories')
32
+ bott_sizer = Wx::StaticBoxSizer.new(butt_box_label,
33
+ Wx::HORIZONTAL)
34
+
35
+ @drop_down = CategoryDropDown.new(app, panel)
36
+ bott_sizer.add(@drop_down, 3, Wx::ALL, 4)
37
+
38
+ button = Wx::Button.new(panel, -1, 'Add as row')
39
+ button.evt_button(button.get_id) { | e | on_add_row(e) }
40
+ bott_sizer.add(button, 1, Wx::ALL, 4)
41
+
42
+ button = Wx::Button.new(panel, -1, 'Add as column')
43
+ button.evt_button(button.get_id) { | e | on_add_col(e) }
44
+ bott_sizer.add(button, 1, Wx::ALL, 4)
45
+
46
+ button = Wx::Button.new(panel, -1, 'Remove')
47
+ button.evt_button(button.get_id) { | e | on_remove(e) }
48
+ bott_sizer.add(button, 1, Wx::ALL, 4)
49
+
50
+ # button = Wx::Button.new(butt_panel, -1, 'Remove column')
51
+ # button.evt_button(button.get_id) { | e | on_remove_col(e) }
52
+ # bott_sizer.add(button, 1, Wx::ALL, 4)
53
+
54
+ # butt_panel.set_sizer(bott_sizer)
55
+ main_sizer.add(bott_sizer, 0,
56
+ Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL, 4)
57
+
58
+ # the numbers row
59
+ num_box_label = Wx::StaticBox.new(panel, -1,
60
+ Lang::DISPLAY_OPTIONS_LABEL)
61
+ num_sizer = Wx::StaticBoxSizer.new(num_box_label,
62
+ Wx::HORIZONTAL)
63
+
64
+ # num_cbox = Wx::CheckBox.new(num_panel, -1, 'Show numbers')
65
+ num_sizer.add( Wx::StaticText.new(panel, -1, 'Count what?'),
66
+ 0, Wx::ALL|Wx::ALIGN_RIGHT, 4)
67
+ @num_list = Wx::Choice.new(panel, -1)
68
+ @num_list.append('Number of documents')
69
+ @num_list.append('Number of passages')
70
+ @num_list.append('Number of characters')
71
+ @num_list.selection = 0
72
+ evt_choice( @num_list.get_id() ) { | e | on_change_method(e) }
73
+ num_sizer.add(@num_list, 0, Wx::ALL, 4)
74
+
75
+ main_sizer.add( num_sizer, 0,
76
+ Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL,
77
+ 4)
78
+
79
+ panel.set_sizer(main_sizer)
80
+ main_sizer.set_size_hints(panel)
81
+
82
+ subscribe(:category_deleted, :category_changed)
83
+ end
84
+
85
+ def associated_subscribers()
86
+ [ @drop_down ]
87
+ end
88
+
89
+ # set the currently highlighted row or column as the current item
90
+ # in the dropdown
91
+ def on_label_clicked(e)
92
+ if row = e.row and row >= 0 and @rows[row]
93
+ @drop_down.receive_focus_category( @rows[row] )
94
+ end
95
+ if col = e.col and col >= 0 and @cols[col]
96
+ @drop_down.receive_focus_category( @cols[col] )
97
+ end
98
+ end
99
+
100
+ METHOD_MAPPING = {
101
+ 'Number of documents' => :num_of_docs,
102
+ 'Number of passages' => :num_of_codes,
103
+ 'Number of characters' => :num_of_chars }
104
+
105
+ def on_change_method(e)
106
+ sel = @num_list.get_string_selection()
107
+ method = METHOD_MAPPING[ sel ]
108
+ unless method
109
+ raise RuntimeError.new("Unexpected selection #{sel}")
110
+ end
111
+ self.count_method = method
112
+ end
113
+
114
+ def count_method=(method)
115
+ if method != @count_method
116
+ @count_method = method
117
+ 0.upto(@rows.length - 1) { | i | calculate_row(i) }
118
+ recolour_table()
119
+ end
120
+ end
121
+
122
+ def on_add_col(e)
123
+ category = @drop_down.current_category
124
+ return false unless category
125
+ return false if @cols.include?(category)
126
+
127
+ @cols.push(category)
128
+ @grid.append_cols(1) if @cols.length > @grid.number_cols
129
+ col = @cols.length - 1
130
+
131
+ @grid.set_col_label_value( col, category.name )
132
+ # @grid.set_col_format_number( col )
133
+ calculate_col(col)
134
+ recolour_table()
135
+ end
136
+
137
+ def calculate_col(i)
138
+ col = @cols[i]
139
+ @rows.each_with_index do | row_cat, j |
140
+ @contents[j][i] = row_cat.codes.dup.join(col.codes)
141
+ update_cell(j, i)
142
+ end
143
+ end
144
+
145
+ def on_add_row(e)
146
+ category = @drop_down.current_category
147
+ return false unless category
148
+ # don't add twice
149
+ return false if @rows.include?(category)
150
+
151
+ @rows.push(category)
152
+ @grid.append_rows(1) if @rows.length > @grid.number_rows
153
+ row = @rows.length - 1
154
+
155
+ @grid.set_row_label_value( row, category.name )
156
+ @contents[row] = []
157
+ # @grid.set_row_format_number( row )
158
+ calculate_row(row)
159
+ recolour_table()
160
+ end
161
+
162
+ def calculate_row(i)
163
+ row = @rows[i]
164
+ @cols.each_with_index do | col_cat, j |
165
+ @contents[i][j] = col_cat.codes.dup.join(row.codes)
166
+ update_cell(i, j)
167
+ end
168
+ end
169
+
170
+ # removes the selected category from both rows and colums
171
+ def on_remove(e)
172
+ on_remove_col(e)
173
+ on_remove_row(e)
174
+ end
175
+
176
+ def on_remove_col(e)
177
+ category = @drop_down.current_category
178
+ return false unless category
179
+ return false unless idx = @cols.index(category)
180
+ remove_col(idx)
181
+ recolour_table()
182
+ end
183
+
184
+ def remove_col(idx)
185
+ @cols.delete_at(idx)
186
+ @contents.each { | row | row.delete_at(idx) }
187
+ @grid.delete_cols(idx, 1)
188
+ @grid.append_cols(1)
189
+ update_col_labels()
190
+ end
191
+
192
+ def on_remove_row(e)
193
+ category = @drop_down.current_category
194
+ return false unless category
195
+ return false unless idx = @rows.index(category)
196
+ remove_row(idx)
197
+ recolour_table()
198
+ end
199
+
200
+ def remove_row(idx)
201
+ @rows.delete_at(idx)
202
+ @contents.delete_at(idx)
203
+ @grid.delete_rows(idx, 1)
204
+ @grid.append_rows(1)
205
+ update_row_labels()
206
+ end
207
+
208
+ def update_cell(x, y)
209
+ content = @contents[x][y].send(@count_method)
210
+ @grid.set_cell_value( x, y, content.to_s )
211
+ end
212
+
213
+ def update_col_labels()
214
+ @cols.each_with_index do | c, i |
215
+ @grid.set_col_label_value(i, c.name)
216
+ end
217
+ # check if we need to clean up deleted and now empty row labels
218
+ # at the end - Wx 2.4.2 doesn't seem to do this automatically
219
+ @grid.set_col_label_value(@cols.length, '')
220
+ return if @cols.length == @grid.number_cols()
221
+ for empty in ( @cols.length ... @grid.number_cols() )
222
+ @grid.set_col_label_value(empty, '')
223
+ end
224
+ end
225
+
226
+ def update_row_labels()
227
+ @rows.each_with_index do | c, i |
228
+ @grid.set_row_label_value(i, c.name)
229
+ end
230
+ # check if we need to clean up deleted and now empty row labels
231
+ # at the end - Wx 2.4.2 doesn't seem to do this automatically
232
+ return if @rows.length == @grid.number_rows()
233
+ for empty in ( @rows.length ... @grid.number_rows() )
234
+ @grid.set_row_label_value(empty, '')
235
+ end
236
+ end
237
+
238
+ def recolour_table()
239
+ max = @contents.flatten.collect { | x | x.send(@count_method) }.max
240
+ @contents.each_with_index do | row_items, row |
241
+ row_items.each_with_index do | code_table, col |
242
+ clr = BASE_COLOUR.mix( HIGHLIGHT_COLOUR,
243
+ max, code_table.send(@count_method))
244
+ @grid.set_cell_background_colour(row, col, clr)
245
+ end
246
+ end
247
+ end
248
+
249
+ def receive_category_changed(cat)
250
+ if idx = @cols.index(cat)
251
+ @cols[idx] = cat
252
+ calculate_col(idx)
253
+ end
254
+
255
+ if idx = @rows.index(cat)
256
+ @rows[idx] = cat
257
+ calculate_row(idx)
258
+ end
259
+
260
+ recolour_table()
261
+ end
262
+
263
+ def receive_category_deleted(cat)
264
+ if idx = @cols.index(cat)
265
+ remove_col(idx)
266
+ end
267
+
268
+ if idx = @rows.index(cat)
269
+ remove_row(idx)
270
+ end
271
+
272
+ recolour_table()
273
+ end
274
+ end
275
+ end
@@ -0,0 +1,139 @@
1
+
2
+ module QDA
3
+ module GUI
4
+ class DocumentWindow < InspectorWindow
5
+ include Subscriber
6
+
7
+ def initialize(doc, app, workarea, layout)
8
+ @doc = doc
9
+ @app = app
10
+
11
+ super(workarea, doc.title, layout) do | txt_parent |
12
+ text_box = DocTextViewer.new(txt_parent, @doc.dbid)
13
+ text_box.set_font(@app.display_font)
14
+ text_box.append_text( @doc.text )
15
+ text_box.evt_enter_window do | e |
16
+ set_cursor Wx::Cursor.new(Wx::CURSOR_IBEAM)
17
+ end
18
+ text_box.evt_leave_window do | e |
19
+ set_cursor Wx::Cursor.new(Wx::CURSOR_ARROW)
20
+ end
21
+ text_box.show_position(0)
22
+ text_box
23
+ end
24
+
25
+ construct_details_panel()
26
+ set_icon( app.fetch_icon("document.xpm") )
27
+ subscribe(:document_changed, :document_deleted)
28
+ end
29
+
30
+ def receive_document_deleted(doc)
31
+ close() if doc.dbid == @doc.dbid
32
+ end
33
+
34
+ def receive_document_changed(new_doc)
35
+ if new_doc.dbid == @doc.dbid
36
+ @doc = new_doc
37
+ refresh()
38
+ end
39
+ end
40
+
41
+ def on_set_text_font(font)
42
+ @text_box.font = font
43
+ end
44
+
45
+ # update the display from the currently stored +@doc+
46
+ # TODO - currently doesn't update text as this shouldn't ever change
47
+ def refresh()
48
+ set_title(@doc.title)
49
+ label = Time.now.strftime("%a %d/%m/%Y %H:%M:%S")
50
+ @mod_date_label.set_label( label )
51
+ end
52
+
53
+ def on_save_details(e)
54
+ @doc.memo = @memo_box.value
55
+ @doc.title = @title_box.value
56
+ Wx::BusyCursor.busy { @app.app.save_document( @doc ) }
57
+ $wxapp.broadcast(:document_changed, @doc)
58
+ end
59
+
60
+ # scroll the window's text box to the line containing the
61
+ # character at +pos+
62
+ def jump_to(pos)
63
+ # p "----"
64
+ # p pos
65
+ true_pos = @text_box.true_index_to_pos(pos)
66
+ # p true_pos
67
+ # there's something weird going on here. For some reason, if
68
+ # jump_to is called just after the window has been constructed,
69
+ # the lines haven't yet been wrapped, and so it jumps to the
70
+ # wrong position .. weird
71
+ point = @text_box.position_to_xy(true_pos)
72
+ # p point.x
73
+ # p point.y
74
+ @text_box.show_position( true_pos )
75
+ end
76
+
77
+ def construct_details_panel()
78
+ main_panel = Wx::Panel.new(@notebook, -1)
79
+ sizer = Wx::BoxSizer.new(Wx::VERTICAL)
80
+ title_box_label = Wx::StaticBox.new(main_panel, -1, 'Title')
81
+ title_box_sizer = Wx::StaticBoxSizer.new(title_box_label,
82
+ Wx::VERTICAL)
83
+ @title_box = Wx::TextCtrl.new(main_panel, -1, @doc.title )
84
+ title_box_sizer.add(@title_box, 0,
85
+ Wx::ADJUST_MINSIZE|Wx::ALL|Wx::GROW, 4)
86
+ sizer.add(title_box_sizer,0, Wx::ADJUST_MINSIZE|Wx::ALL|Wx::GROW, 4)
87
+
88
+ info_panel = Wx::Panel.new(main_panel, -1)
89
+ info_box_label = Wx::StaticBox.new(info_panel, -1, 'Information')
90
+ info_box_sizer = Wx::StaticBoxSizer.new(info_box_label,
91
+ Wx::VERTICAL)
92
+
93
+ info_sizer = Wx::FlexGridSizer.new(2, 2, 5, 6)
94
+
95
+ # Create date label
96
+ info_sizer.add( Wx::StaticText.new(info_panel, -1, 'Created:') )
97
+ date_text = @doc.create_date.strftime("%a %d/%m/%Y %H:%M:%S")
98
+ @create_date_label = Wx::StaticText.new(info_panel, -1, date_text )
99
+ info_sizer.add( @create_date_label )
100
+
101
+ # Modified date label
102
+ info_sizer.add( Wx::StaticText.new(info_panel, -1, 'Modified:') )
103
+ date_text = @doc.mod_date.strftime("%a %d/%m/%Y %H:%M:%S")
104
+ @mod_date_label = Wx::StaticText.new(info_panel, -1, date_text)
105
+ info_sizer.add(@mod_date_label)
106
+ info_box_sizer.add(info_sizer, 0,
107
+ Wx::GROW|Wx::ADJUST_MINSIZE|Wx::ALL, 4)
108
+ info_panel.set_sizer( info_box_sizer )
109
+ sizer.add(info_panel, 0, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4)
110
+
111
+ # The memo box
112
+ memo_label = Wx::StaticBox.new(main_panel, -1, 'Memo')
113
+ memo_sizer = Wx::StaticBoxSizer.new(memo_label, Wx::VERTICAL)
114
+
115
+ @memo_box = Wx::TextCtrl.new(main_panel, -1, @doc.memo,
116
+ Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
117
+ Wx::TE_MULTILINE|Wx::TE_NOHIDESEL)
118
+ @memo_box.evt_enter_window do | e |
119
+ set_cursor Wx::Cursor.new(Wx::CURSOR_IBEAM)
120
+ end
121
+
122
+ @memo_box.evt_leave_window do | e |
123
+ set_cursor Wx::Cursor.new(Wx::CURSOR_ARROW)
124
+ end
125
+ memo_sizer.add(@memo_box, 10, Wx::GROW|Wx::ALL|Wx::ADJUST_MINSIZE, 4)
126
+ sizer.add(memo_sizer, 1, Wx::GROW|Wx::ADJUST_MINSIZE, 4)
127
+
128
+
129
+ button = Wx::Button.new(main_panel, -1, 'Apply')
130
+ button.evt_button(button.get_id) { | e | on_save_details(e) }
131
+ sizer.add(button, 0, Wx::ALL|Wx::ALIGN_RIGHT|Wx::ADJUST_MINSIZE, 4)
132
+ main_panel.set_sizer( sizer )
133
+
134
+ @notebook.add_page(main_panel, 'details')
135
+ end
136
+
137
+ end
138
+ end
139
+ end