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.
- data/lib/weft.rb +16 -1
- data/lib/weft/WEFT-VERSION-STRING.rb +1 -1
- data/lib/weft/application.rb +17 -74
- data/lib/weft/backend.rb +6 -32
- data/lib/weft/backend/sqlite.rb +222 -164
- data/lib/weft/backend/sqlite/category_tree.rb +52 -48
- data/lib/weft/backend/sqlite/database.rb +57 -0
- data/lib/weft/backend/sqlite/upgradeable.rb +7 -0
- data/lib/weft/broadcaster.rb +90 -0
- data/lib/weft/category.rb +139 -47
- data/lib/weft/codereview.rb +160 -0
- data/lib/weft/coding.rb +74 -23
- data/lib/weft/document.rb +23 -10
- data/lib/weft/exceptions.rb +10 -0
- data/lib/weft/filters.rb +47 -224
- data/lib/weft/filters/indexers.rb +137 -0
- data/lib/weft/filters/input.rb +118 -0
- data/lib/weft/filters/output.rb +101 -0
- data/lib/weft/filters/templates.rb +80 -0
- data/lib/weft/filters/win32backtick.rb +246 -0
- data/lib/weft/query.rb +169 -0
- data/lib/weft/wxgui.rb +349 -294
- data/lib/weft/wxgui/constants.rb +43 -0
- data/lib/weft/wxgui/controls.rb +6 -0
- data/lib/weft/wxgui/controls/category_dropdown.rb +192 -0
- data/lib/weft/wxgui/controls/category_tree.rb +314 -0
- data/lib/weft/wxgui/controls/document_list.rb +97 -0
- data/lib/weft/wxgui/controls/multitype_control.rb +37 -0
- data/lib/weft/wxgui/{inspectors → controls}/textcontrols.rb +235 -64
- data/lib/weft/wxgui/dialogs.rb +144 -41
- data/lib/weft/wxgui/error_handler.rb +116 -36
- data/lib/weft/wxgui/exceptions.rb +7 -0
- data/lib/weft/wxgui/inspectors.rb +61 -208
- data/lib/weft/wxgui/inspectors/category.rb +19 -16
- data/lib/weft/wxgui/inspectors/codereview.rb +90 -132
- data/lib/weft/wxgui/inspectors/document.rb +12 -8
- data/lib/weft/wxgui/inspectors/imagedocument.rb +56 -56
- data/lib/weft/wxgui/inspectors/query.rb +284 -0
- data/lib/weft/wxgui/inspectors/script.rb +147 -23
- data/lib/weft/wxgui/lang/en.rb +69 -0
- data/lib/weft/wxgui/sidebar.rb +90 -432
- data/lib/weft/wxgui/utilities.rb +70 -91
- data/lib/weft/wxgui/workarea.rb +150 -43
- data/share/icons/category.ico +0 -0
- data/share/icons/category.xpm +109 -0
- data/share/icons/codereview.ico +0 -0
- data/share/icons/codereview.xpm +54 -0
- data/share/icons/d_and_c.xpm +126 -0
- data/share/icons/document.ico +0 -0
- data/share/icons/document.xpm +70 -0
- data/share/icons/project.ico +0 -0
- data/share/icons/query.ico +0 -0
- data/share/icons/query.xpm +56 -0
- data/{lib/weft/wxgui → share/icons}/search.xpm +0 -0
- data/share/icons/weft.ico +0 -0
- data/share/icons/weft.xpm +62 -0
- data/share/icons/weft16.ico +0 -0
- data/share/icons/weft32.ico +0 -0
- data/share/templates/category_plain.html +18 -0
- data/share/templates/codereview_plain.html +18 -0
- data/share/templates/document_plain.html +13 -0
- data/share/templates/document_plain.txt +7 -0
- data/test/001-document.rb +55 -36
- data/test/002-category.rb +81 -6
- data/test/003-code.rb +8 -4
- data/test/004-application.rb +13 -34
- data/test/005-query_review.rb +139 -0
- data/test/006-filters.rb +54 -42
- data/test/007-output_filters.rb +113 -0
- data/test/009a-backend_sqlite_basic.rb +95 -24
- data/test/009b-backend_sqlite_complex.rb +43 -62
- data/test/009c_backend_sqlite_bench.rb +5 -10
- data/test/053-doc_inspector.rb +46 -0
- data/test/055-query_window.rb +50 -0
- data/test/all-tests.rb +1 -0
- data/test/test-common.rb +19 -0
- data/test/testdata/empty.qdp +0 -0
- data/test/testdata/simple with space.pdf +0 -0
- data/test/testdata/simple.pdf +0 -0
- data/weft-qda.rb +40 -7
- metadata +74 -14
- data/lib/weft/wxgui/category.xpm +0 -26
- data/lib/weft/wxgui/document.xpm +0 -25
- data/lib/weft/wxgui/inspectors/search.rb +0 -265
- data/lib/weft/wxgui/mondrian.xpm +0 -44
- data/lib/weft/wxgui/weft16.xpm +0 -31
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module QDA
|
4
|
+
module GUI
|
5
|
+
# save some typing
|
6
|
+
DEF_POS = Wx::DEFAULT_POSITION
|
7
|
+
DEF_SIZE = Wx::DEFAULT_SIZE
|
8
|
+
|
9
|
+
# Some versions of WxRuby on OS X seem not to have Wx::RUBY_PLATFORM
|
10
|
+
# correctly defined
|
11
|
+
if not defined?(Wx::RUBY_PLATFORM) and ::RUBY_PLATFORM =~ /powerpc/
|
12
|
+
Wx::RUBY_PLATFORM = 'WXMAC'
|
13
|
+
end
|
14
|
+
|
15
|
+
# What window layout strategy should we use?
|
16
|
+
if defined? ::WEFT_MDI_MODE
|
17
|
+
WINDOW_MODE = :MDI
|
18
|
+
elsif defined? ::WEFT_MULTIFRAME_MODE
|
19
|
+
WINDOW_MODE = :MULTIFRAME
|
20
|
+
end
|
21
|
+
|
22
|
+
if defined? ::WEFT_CRASH_REPORTING
|
23
|
+
REPORT_CRASHES = true
|
24
|
+
CRASH_REPORT_URL = URI.parse('http://www.pressure.to/cgi-bin/weft-crash-report.rb')
|
25
|
+
else
|
26
|
+
REPORT_CRASHES = false
|
27
|
+
end
|
28
|
+
|
29
|
+
# use windows type icons by preference on windows, otherwise settle for
|
30
|
+
# XPMs (which don't do multi-resolution).
|
31
|
+
# Also - use MDI style by default on windows, otherwise use
|
32
|
+
# multiframe style
|
33
|
+
IconType = Struct.new(:extension, :constant)
|
34
|
+
if Wx::RUBY_PLATFORM == 'WXMSW'
|
35
|
+
ICON_EXTS = [ IconType[ :ico, Wx::BITMAP_TYPE_ICO ] ,
|
36
|
+
IconType[ :xpm, Wx::BITMAP_TYPE_XPM ] ]
|
37
|
+
WINDOW_MODE = :MDI if not defined?(WINDOW_MODE)
|
38
|
+
else
|
39
|
+
ICON_EXTS = [ IconType[ :xpm, Wx::BITMAP_TYPE_XPM ] ]
|
40
|
+
WINDOW_MODE = :MULTIFRAME if not defined?(WINDOW_MODE)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
# controls are little customised WxGUI widgets that aren't frames or dialogs
|
2
|
+
lib_patts = $LOAD_PATH.map { | path | /^#{Regexp.escape(path)}\// }
|
3
|
+
Dir.glob( __FILE__.sub(/\.rb$/, File::SEPARATOR + '*.rb' ) ).each do | f |
|
4
|
+
lib_patts.find { | x | f.sub!(x, '') }
|
5
|
+
require f
|
6
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
module QDA::GUI
|
2
|
+
class CategoryDropDown < Wx::ComboBox
|
3
|
+
include QDA::Subscriber
|
4
|
+
include ListLikeItemData
|
5
|
+
|
6
|
+
MAXIMUM_DROPDOWN_LENGTH = 7
|
7
|
+
attr_accessor :sticky
|
8
|
+
|
9
|
+
# +locked+ if true will prevent the dropdown from responding to
|
10
|
+
# global category focus events.
|
11
|
+
def initialize(app, parent, text_box = nil, locked = false)
|
12
|
+
super(parent, -1, '', Wx::DEFAULT_POSITION,
|
13
|
+
Wx::DEFAULT_SIZE, [])
|
14
|
+
@locked = locked
|
15
|
+
@client_data = {}
|
16
|
+
@text_box = text_box
|
17
|
+
@sticky = false
|
18
|
+
|
19
|
+
@app = app
|
20
|
+
|
21
|
+
evt_kill_focus() do | e |
|
22
|
+
find_and_add_categories()
|
23
|
+
e.skip()
|
24
|
+
end
|
25
|
+
|
26
|
+
evt_text_enter(self.get_id) do | e |
|
27
|
+
find_and_add_categories()
|
28
|
+
end
|
29
|
+
|
30
|
+
evt_combobox(self.get_id) do | e |
|
31
|
+
e.skip()
|
32
|
+
on_item_selected(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
subscribe(@app, :focus_category, :category_deleted, :category_changed)
|
36
|
+
if @app.current_category
|
37
|
+
set_active_category(@app.current_category)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def redraw()
|
42
|
+
delete(0) while count.nonzero?
|
43
|
+
data.each_with_index do | item, i |
|
44
|
+
append(item.name, nil)
|
45
|
+
end
|
46
|
+
set_selection(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def append_item(cat)
|
50
|
+
push_item_data(cat)
|
51
|
+
append(cat.name, nil)
|
52
|
+
end
|
53
|
+
|
54
|
+
def prepend_item(the_cat)
|
55
|
+
unshift_item_data(the_cat)
|
56
|
+
redraw()
|
57
|
+
end
|
58
|
+
|
59
|
+
def update_item(the_cat)
|
60
|
+
if i = value_to_ident(the_cat)
|
61
|
+
set_item_data(i, the_cat)
|
62
|
+
redraw()
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def remove_item(the_cat)
|
67
|
+
if i = value_to_ident(the_cat)
|
68
|
+
delete(i)
|
69
|
+
data.delete_at(i)
|
70
|
+
if count.zero?
|
71
|
+
set_selection(0)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# is this dropdown responding to global :focus_category events?
|
77
|
+
def locked?
|
78
|
+
@locked ? true : false
|
79
|
+
end
|
80
|
+
|
81
|
+
# make this dropdown receive global :focus_category events
|
82
|
+
def lock
|
83
|
+
@locked = true
|
84
|
+
end
|
85
|
+
|
86
|
+
# prevent this dropdown responding to global :focus_category events
|
87
|
+
def unlock
|
88
|
+
@locked = false
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
# highlight text coded by the category on the way.
|
93
|
+
def set_selection(idx)
|
94
|
+
super(idx)
|
95
|
+
on_item_selected(nil)
|
96
|
+
end
|
97
|
+
|
98
|
+
# highlight text coded by the newly-selected category
|
99
|
+
def on_item_selected(e)
|
100
|
+
if @text_box
|
101
|
+
if category = current_category
|
102
|
+
@text_box.highlight_codingtable(category.codes)
|
103
|
+
else
|
104
|
+
@text_box.unhighlight()
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def delete_first()
|
110
|
+
delete(0)
|
111
|
+
data.shift
|
112
|
+
end
|
113
|
+
|
114
|
+
def set_active_category(category)
|
115
|
+
# clear the first node unless it is sticky because it has been used,
|
116
|
+
# or if the category is already in the list
|
117
|
+
delete_first unless @sticky or value_to_ident(category)
|
118
|
+
|
119
|
+
trim()
|
120
|
+
prepend_item(category)
|
121
|
+
@sticky = false
|
122
|
+
set_selection(0)
|
123
|
+
end
|
124
|
+
|
125
|
+
def trim(to_length = MAXIMUM_DROPDOWN_LENGTH)
|
126
|
+
# clear any remaining excess items
|
127
|
+
while count > to_length
|
128
|
+
data.delete_at(count - 1)
|
129
|
+
delete(count - 1)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def find_and_add_categories()
|
134
|
+
typed_text = get_value()
|
135
|
+
return if find_string( typed_text )
|
136
|
+
return if typed_text.empty?
|
137
|
+
matches = @app.app.get_categories_by_path(typed_text)
|
138
|
+
matches.each do | cat |
|
139
|
+
prepend_item(cat) unless cat.parent.nil?
|
140
|
+
end
|
141
|
+
trim(matches.length) if count > MAXIMUM_DROPDOWN_LENGTH
|
142
|
+
set_selection( 0 )
|
143
|
+
end
|
144
|
+
|
145
|
+
# add the newly-focused category to this dropdown and highlight
|
146
|
+
# its text.
|
147
|
+
def receive_focus_category(cat)
|
148
|
+
set_active_category(cat) unless locked?
|
149
|
+
end
|
150
|
+
|
151
|
+
# if a category is deleted it should be removed from the list
|
152
|
+
def receive_category_deleted(cat)
|
153
|
+
remove_item(cat)
|
154
|
+
end
|
155
|
+
|
156
|
+
def receive_category_changed(cat)
|
157
|
+
update_item(cat)
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def on_blur(e)
|
162
|
+
find_and_add_categories()
|
163
|
+
e.skip()
|
164
|
+
end
|
165
|
+
|
166
|
+
# missing in wxruby, but part of WxWidgets
|
167
|
+
# this version differs by returning nil rather than -1 on failure
|
168
|
+
def find_string(str)
|
169
|
+
( 0 ... count ).each do | i |
|
170
|
+
return i if str == get_string(i)
|
171
|
+
end
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
|
175
|
+
|
176
|
+
# TODO - some visual cue to indicate that no category matched find-first
|
177
|
+
def set_broken(bool)
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_warning(bool)
|
182
|
+
|
183
|
+
end
|
184
|
+
# returns the Category object associated with the currently
|
185
|
+
# selected item in the drop down.
|
186
|
+
def current_category
|
187
|
+
if curr_sel = get_selection() and curr_sel >= 0
|
188
|
+
return get_client_data( curr_sel )
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,314 @@
|
|
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 HashLikeItemData
|
8
|
+
include QDA::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(weft_client, parent, locked = false)
|
15
|
+
@client = weft_client
|
16
|
+
@locked = locked
|
17
|
+
# a hash whose keys are the dbids which are expanded
|
18
|
+
@expanded = {}
|
19
|
+
super(parent, -1, Wx::DEFAULT_POSITION, Wx::DEFAULT_SIZE,
|
20
|
+
CATEGORY_TREE_STYLE)
|
21
|
+
my_id = self.get_id()
|
22
|
+
evt_tree_item_activated(my_id) { | e | on_item_activated(e) }
|
23
|
+
|
24
|
+
if ! @locked
|
25
|
+
evt_tree_sel_changed(my_id) { | e | on_item_selected(e) }
|
26
|
+
evt_tree_end_drag(my_id) { | e | on_drag_end(e) }
|
27
|
+
evt_tree_begin_drag(my_id) { | e | on_drag_begin(e) }
|
28
|
+
evt_tree_begin_label_edit(my_id) { | e | on_edit_label_begin(e) }
|
29
|
+
evt_tree_end_label_edit(my_id) { | e | on_edit_label_end(e) }
|
30
|
+
evt_tree_key_down(my_id) { | e | on_key_down(e) }
|
31
|
+
evt_tree_item_expanded(my_id) { | e | on_item_expanded(e) }
|
32
|
+
evt_tree_item_collapsed(my_id) { | e | on_item_collapsed(e) }
|
33
|
+
end
|
34
|
+
|
35
|
+
subscribe(@client, :category_deleted, :category_changed, :category_added)
|
36
|
+
end
|
37
|
+
|
38
|
+
# for faked-up item data
|
39
|
+
def append_item(parent, text, data = nil)
|
40
|
+
id = super(parent, text, -1, -1)
|
41
|
+
set_item_data(id, data)
|
42
|
+
set_item_bold(id) if parent == @root_id
|
43
|
+
id
|
44
|
+
end
|
45
|
+
|
46
|
+
# for faked-up item data
|
47
|
+
def prepend_item(parent, text, img = -1, sel_img = -1, data = nil)
|
48
|
+
id = super(parent, text, img, sel_img)
|
49
|
+
set_item_data(id, data)
|
50
|
+
return id
|
51
|
+
end
|
52
|
+
|
53
|
+
# for faked-up item data
|
54
|
+
def delete(id)
|
55
|
+
super(id)
|
56
|
+
del_cat = data.delete(id)
|
57
|
+
if del_cat.parent and old_parent_id = value_to_ident(del_cat.parent)
|
58
|
+
set_item_data( old_parent_id,
|
59
|
+
@client.app.get_category(del_cat.parent.dbid) )
|
60
|
+
end
|
61
|
+
return nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def populate(children)
|
65
|
+
# the root isn't shown, so the top level of +children+ is what
|
66
|
+
# appears at the base of the tree.
|
67
|
+
@root_id = add_root('ROOT')
|
68
|
+
append_recursively(@root_id, children)
|
69
|
+
refresh()
|
70
|
+
end
|
71
|
+
|
72
|
+
# returns an array of category ids
|
73
|
+
# NOT USED?
|
74
|
+
def expanded_items()
|
75
|
+
@expanded.keys
|
76
|
+
end
|
77
|
+
|
78
|
+
# Opens the category nodes corresponding to the category ids in +catids+
|
79
|
+
def expand_items(catids)
|
80
|
+
catids.keys.each do | catid |
|
81
|
+
if itemid = value_to_ident(catid)
|
82
|
+
# this is done here to prevent it being seen as a change by evt handler
|
83
|
+
@expanded[catid] = true
|
84
|
+
expand( itemid )
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def append_recursively(parent, children)
|
90
|
+
children.each do | child_cat |
|
91
|
+
name = child_cat.name.empty? ? 'DEFAULT' : child_cat.name
|
92
|
+
|
93
|
+
id = append_item(parent, name, child_cat )
|
94
|
+
# remember this for later use
|
95
|
+
@search_id = id if name == 'SEARCHES'
|
96
|
+
@codes_id = id if name == 'CATEGORIES' or name == 'CODES'
|
97
|
+
|
98
|
+
append_recursively(id, child_cat.children)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# get the currently active category
|
103
|
+
def get_current_category()
|
104
|
+
if curr_sel = get_selection()
|
105
|
+
return nil if curr_sel == 0
|
106
|
+
category = get_item_data( curr_sel )
|
107
|
+
return nil if category.nil? # important for GTK
|
108
|
+
return nil if category.parent.nil? # don't return root nodes
|
109
|
+
category
|
110
|
+
end
|
111
|
+
end
|
112
|
+
alias :selected_category :get_current_category
|
113
|
+
|
114
|
+
def on_item_expanded(evt)
|
115
|
+
cat = get_item_data(evt.item)
|
116
|
+
return unless cat
|
117
|
+
return if @expanded[cat.dbid] # prevents futile re-saving if already open
|
118
|
+
@expanded[cat.dbid] = true
|
119
|
+
@client.app.save_preference('TreeLayout', @expanded)
|
120
|
+
end
|
121
|
+
|
122
|
+
def on_item_collapsed(evt)
|
123
|
+
cat = get_item_data(evt.item)
|
124
|
+
return unless cat
|
125
|
+
@expanded.delete(cat.dbid)
|
126
|
+
@client.app.save_preference('TreeLayout', @expanded)
|
127
|
+
end
|
128
|
+
|
129
|
+
def on_edit_label_begin(evt)
|
130
|
+
# TODO - this should really talk to the underlying category,
|
131
|
+
# rather than just assuming bold = locked
|
132
|
+
evt.veto() if bold?(evt.item)
|
133
|
+
end
|
134
|
+
|
135
|
+
def on_edit_label_end(evt)
|
136
|
+
new_text = evt.label
|
137
|
+
if new_text == ""
|
138
|
+
evt.veto()
|
139
|
+
return
|
140
|
+
end
|
141
|
+
|
142
|
+
old_text = get_item_text( evt.item )
|
143
|
+
# don't bother saving if the text isn't changed
|
144
|
+
return if old_text == new_text
|
145
|
+
c_cdata = get_item_data( evt.item )
|
146
|
+
category = @client.app.get_category( c_cdata.dbid )
|
147
|
+
Wx::BusyCursor.busy do
|
148
|
+
begin
|
149
|
+
category.name = new_text
|
150
|
+
@client.app.save_category( category)
|
151
|
+
rescue QDA::BadNameError
|
152
|
+
category.name = old_text
|
153
|
+
ErrorDialog.display( Lang::BAD_CATEGORY_NAME_TITLE,
|
154
|
+
Lang::BAD_CATEGORY_NAME_WARNING )
|
155
|
+
evt.veto()
|
156
|
+
rescue QDA::NotUniqueNameError
|
157
|
+
category.name = old_text
|
158
|
+
ErrorDialog.display( Lang::DUPLICATE_CATEGORY_NAME_TITLE,
|
159
|
+
Lang::DUPLICATE_CATEGORY_NAME_WARNING )
|
160
|
+
evt.veto()
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
set_item_data(evt.item, category )
|
165
|
+
end
|
166
|
+
|
167
|
+
def on_drag_begin(evt)
|
168
|
+
@drag_subject = evt.item
|
169
|
+
evt.allow()
|
170
|
+
end
|
171
|
+
|
172
|
+
# relocates or merges the coding of the draggee to the drag target
|
173
|
+
def on_drag_end(evt)
|
174
|
+
@drag_target = evt.item
|
175
|
+
# control_down is a property of a mouse event, so this doesn't work
|
176
|
+
# p "CONTROL IS DOWN" if evt.control_down
|
177
|
+
move(@drag_subject, @drag_target)
|
178
|
+
|
179
|
+
@drag_subject = nil
|
180
|
+
@drag_target = nil
|
181
|
+
end
|
182
|
+
|
183
|
+
def dont_move(from)
|
184
|
+
select_item(from) if from != 0
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
# moves the item identifed by the tree id +from+ to be the last child of
|
189
|
+
# the category with the tree id +to+
|
190
|
+
def move(from, to)
|
191
|
+
return dont_move(from) unless from and to
|
192
|
+
return dont_move(from) if from == 0 or to == 0
|
193
|
+
return dont_move(from) if from == to
|
194
|
+
|
195
|
+
movee = get_item_data( from )
|
196
|
+
destination = get_item_data( to )
|
197
|
+
# don't move root nodes
|
198
|
+
return dont_move(from) if not movee.parent
|
199
|
+
# ignore if no move
|
200
|
+
return dont_move(from) if movee.parent == destination
|
201
|
+
# don't attach to descendants
|
202
|
+
return dont_move(from) if destination.is_descendant_of?(movee)
|
203
|
+
# complain if a child with this name already attached to parent
|
204
|
+
if destination[movee.name]
|
205
|
+
ErrorDialog.display( Lang::DUPLICATE_CATEGORY_NAME_TITLE,
|
206
|
+
Lang::DUPLICATE_CATEGORY_NAME_WARNING )
|
207
|
+
return dont_move(from)
|
208
|
+
end
|
209
|
+
|
210
|
+
Wx::BusyCursor.busy do
|
211
|
+
movee.parent = destination
|
212
|
+
@client.app.save_category( movee )
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# Moves the tree item identified by +from+ so that it is attached the parent
|
217
|
+
# identified by +new_parent+. Does nothing if that is already the location
|
218
|
+
# of the node.
|
219
|
+
def move_item(from, to)
|
220
|
+
# don't alter if unchanged
|
221
|
+
return from if get_item_parent(from) == to
|
222
|
+
new_child = append_item(to, get_item_text(from), get_item_data(from))
|
223
|
+
child = get_first_child(from)[0]
|
224
|
+
while child != 0
|
225
|
+
move_item(child, new_child)
|
226
|
+
child = get_first_child(from)[0]
|
227
|
+
end
|
228
|
+
delete( from )
|
229
|
+
new_child
|
230
|
+
end
|
231
|
+
|
232
|
+
# when we're asked to add an item. This will be attached to the
|
233
|
+
# currently selected category in the tree, or the default 'CATEGORIES'
|
234
|
+
# category if no item is selected
|
235
|
+
def on_create_item()
|
236
|
+
@client.on_add_category()
|
237
|
+
end
|
238
|
+
|
239
|
+
def on_item_selected(event)
|
240
|
+
if valid_selection?(event)
|
241
|
+
@client.current_category = get_item_data( event.get_item )
|
242
|
+
else
|
243
|
+
@client.current_category = nil
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
def on_item_activated(event)
|
248
|
+
item_id = event.get_item()
|
249
|
+
category = get_item_data(item_id)
|
250
|
+
return nil if category.parent.nil?
|
251
|
+
Wx::BusyCursor.busy() do
|
252
|
+
category = @client.app.get_category(category.dbid, true)
|
253
|
+
if category != nil
|
254
|
+
@client.on_category_open(category)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def valid_selection?( event )
|
260
|
+
# wxruby varies across platforms in how it indicates "no item
|
261
|
+
# selected" - on windows, get_selection returns 0, on Linux, a
|
262
|
+
# weird large integer id. Returning nil is the "correct" future behaviour
|
263
|
+
item_id = event.get_item()
|
264
|
+
if item_id.nil? or item_id == 0 or is_bold(item_id)
|
265
|
+
return false
|
266
|
+
elsif not get_item_data( item_id )
|
267
|
+
return false
|
268
|
+
else
|
269
|
+
return true
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
def delete_selection()
|
274
|
+
@client.on_delete_category()
|
275
|
+
end
|
276
|
+
|
277
|
+
def on_key_down(evt)
|
278
|
+
case evt.key_code()
|
279
|
+
when 127 # DEL
|
280
|
+
delete_selection()
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def receive_category_deleted(cat)
|
285
|
+
# may not include this item if it's a partial subtree
|
286
|
+
if tree_id = value_to_ident(cat)
|
287
|
+
delete( tree_id )
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def receive_category_changed(cat)
|
292
|
+
# may not include this item if it's a partial subtree
|
293
|
+
if tree_id = value_to_ident(cat)
|
294
|
+
# it's maybe moved
|
295
|
+
tree_id = move_item(tree_id, value_to_ident(cat.parent) )
|
296
|
+
set_item_text( tree_id, cat.name)
|
297
|
+
set_item_data( tree_id, cat)
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
def receive_category_added(cat)
|
302
|
+
if cat.parent.nil?
|
303
|
+
p_id = @root_id
|
304
|
+
else
|
305
|
+
p_id = value_to_ident(cat.parent)
|
306
|
+
end
|
307
|
+
return if not p_id
|
308
|
+
append_item( p_id, cat.name, cat)
|
309
|
+
expand(p_id)
|
310
|
+
id = value_to_ident(cat)
|
311
|
+
select_item(id)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|