1 | // ============================================================= // |
---|
2 | // // |
---|
3 | // File : NT_group_search.cxx // |
---|
4 | // Purpose : GUI for group search // |
---|
5 | // // |
---|
6 | // Coded by Ralf Westram (coder@reallysoft.de) in April 2017 // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // ============================================================= // |
---|
10 | |
---|
11 | #include "NT_group_search.h" |
---|
12 | #include "NT_local.h" |
---|
13 | #include "ad_trees.h" |
---|
14 | |
---|
15 | #include <group_search.h> |
---|
16 | #include <TreeDisplay.hxx> |
---|
17 | |
---|
18 | #include <awt_config_manager.hxx> |
---|
19 | #include <awt_sel_boxes.hxx> |
---|
20 | |
---|
21 | #include <aw_select.hxx> |
---|
22 | #include <aw_root.hxx> |
---|
23 | #include <aw_awar.hxx> |
---|
24 | #include <aw_msg.hxx> |
---|
25 | #include <aw_awar_defs.hxx> |
---|
26 | |
---|
27 | #include <ad_cb_prot.h> |
---|
28 | |
---|
29 | using namespace std; |
---|
30 | |
---|
31 | #if defined(DEVEL_RALF) && 0 |
---|
32 | #define TRACE(msg) fprintf(stderr, "TRACE: %s\n", (msg)) |
---|
33 | #else // !DEVEL_RALF |
---|
34 | #define TRACE(msg) |
---|
35 | #endif |
---|
36 | |
---|
37 | // -------------------------------- |
---|
38 | // AWARs for group search |
---|
39 | |
---|
40 | #define GS_AWARS "group_search/" |
---|
41 | #define GS_AWARS_DUPS GS_AWARS "dup/" |
---|
42 | #define GS_AWARS_TMP "tmp/" GS_AWARS |
---|
43 | |
---|
44 | #define AWAR_MAYBE_INVALID_GROUP GS_AWARS_TMP "sellist" // may point to deleted or unlisted group |
---|
45 | #define AWAR_SELECTED_RESULT_GROUP GS_AWARS_TMP "selected" // bound to AWAR_MAYBE_INVALID_GROUP, but never points to deleted or unlisted group |
---|
46 | #define AWAR_GROUP_HIT_COUNT GS_AWARS_TMP "hits" |
---|
47 | #define AWAR_SELECTED_GROUP_NAME GS_AWARS_TMP "selname" |
---|
48 | #define AWAR_RESULTING_GROUP_NAME GS_AWARS_TMP "resname" |
---|
49 | #define AWAR_TREE_SELECTED GS_AWARS_TMP "treesel" |
---|
50 | #define AWAR_RESULT_ORDER GS_AWARS_TMP "order" |
---|
51 | |
---|
52 | #define AWAR_SEARCH_WHICH_TREES GS_AWARS "trees" |
---|
53 | #define AWAR_SEARCH_MODE GS_AWARS "mode" |
---|
54 | #define AWAR_MATCH_MODE GS_AWARS "match" |
---|
55 | #define AWAR_MARK_TARGET GS_AWARS "markwhat" |
---|
56 | #define AWAR_RENAME_EXPRESSION GS_AWARS "aci" |
---|
57 | |
---|
58 | #define AWAR_DUPLICATE_MODE GS_AWARS_DUPS "mode" |
---|
59 | #define AWAR_DUP_TREE_MODE GS_AWARS_DUPS "locmode" |
---|
60 | #define AWAR_DUP_NAME_MATCH GS_AWARS_DUPS "namematch" |
---|
61 | #define AWAR_DUP_MIN_CLUSTER_SIZE GS_AWARS_DUPS "clustsize" |
---|
62 | #define AWAR_DUP_MIN_WORDS GS_AWARS_DUPS "minwords" |
---|
63 | #define AWAR_DUP_IGNORE_CASE GS_AWARS_DUPS "ignore_case" |
---|
64 | #define AWAR_DUP_EXCLUDED_WORDS GS_AWARS_DUPS "excluded" |
---|
65 | #define AWAR_DUP_WORD_SEPARATORS GS_AWARS_DUPS "separators" |
---|
66 | |
---|
67 | #define AWARFORMAT_CRIT_OPERATOR GS_AWARS "op%i" |
---|
68 | #define AWARFORMAT_CRIT_KEY GS_AWARS "key%i" |
---|
69 | #define AWARFORMAT_CRIT_EQUALS GS_AWARS "equals%i" |
---|
70 | #define AWARFORMAT_CRIT_MATCHES GS_AWARS "match%i" |
---|
71 | |
---|
72 | #define MAX_CRITERIA 3 |
---|
73 | |
---|
74 | inline const char *criterion_awar_name(const char *format, int crit) { |
---|
75 | gs_assert(crit>=1 && crit<=MAX_CRITERIA); |
---|
76 | return GBS_global_string(format, crit); |
---|
77 | } |
---|
78 | |
---|
79 | enum TreeSearchRange { |
---|
80 | SEARCH_CURRENT_TREE, |
---|
81 | SEARCH_SELECTED_TREES, |
---|
82 | SEARCH_ALL_TREES, |
---|
83 | }; |
---|
84 | |
---|
85 | enum DuplicateMode { |
---|
86 | DONT_MIND_DUPLICATES, |
---|
87 | ONLY_DUPLICATES, |
---|
88 | ONLY_UNIQUE, |
---|
89 | }; |
---|
90 | |
---|
91 | // --------------------- |
---|
92 | // data for UI |
---|
93 | |
---|
94 | class GroupUIdata : virtual Noncopyable { |
---|
95 | GBDATA *gb_main; |
---|
96 | SmartPtr<GroupSearch> group_search; |
---|
97 | bool show_tree_name; // show treename in result list? |
---|
98 | AW_selection_list *result_list; |
---|
99 | AW_selection *tree_list; |
---|
100 | |
---|
101 | void update_search_filters(); |
---|
102 | void update_search_range(); |
---|
103 | void update_duplicate_settings(); |
---|
104 | |
---|
105 | void refresh_hit_count(size_t count) { |
---|
106 | AW_root::SINGLETON->awar(AWAR_GROUP_HIT_COUNT)->write_int(count); // update hit count |
---|
107 | } |
---|
108 | void result_order_changed_cb(AW_root *awr) { |
---|
109 | if (group_search.isSet()) { |
---|
110 | GroupSortCriterion crit = GroupSortCriterion(awr->awar(AWAR_RESULT_ORDER)->read_int()); |
---|
111 | group_search->addSortCriterion(crit); |
---|
112 | refill_result_list(); |
---|
113 | } |
---|
114 | } |
---|
115 | void result_list_awar_changed_cb(AW_root *awr); |
---|
116 | void selected_group_name_changed_cb(AW_root *awr); |
---|
117 | void selected_group_changed_cb(AW_root *awr); |
---|
118 | void update_resulting_groupname_cb(AW_root *awr); |
---|
119 | |
---|
120 | // callback wrappers: |
---|
121 | static void refresh_result_list_cb(GroupSearch*, GroupUIdata *data) { data->refill_result_list(); } |
---|
122 | static void cleanup_on_exit(GBDATA*, GroupUIdata *data) { data->cleanup(); } |
---|
123 | static void result_order_changed_cb(AW_root *awr, GroupUIdata *data) { data->result_order_changed_cb(awr); } |
---|
124 | static void result_list_awar_changed_cb(AW_root *awr, GroupUIdata *data) { data->result_list_awar_changed_cb(awr); } |
---|
125 | static void selected_group_name_changed_cb(AW_root *awr, GroupUIdata *data) { data->selected_group_name_changed_cb(awr); } |
---|
126 | static void selected_group_changed_cb(AW_root *awr, GroupUIdata *data) { data->selected_group_changed_cb(awr); } |
---|
127 | static void update_resulting_groupname_cb(AW_root *awr, GroupUIdata *data) { data->update_resulting_groupname_cb(awr); } |
---|
128 | |
---|
129 | void install_callbacks(AW_root *awr) { |
---|
130 | awr->awar(AWAR_RESULT_ORDER) ->add_callback(makeRootCallback(GroupUIdata::result_order_changed_cb, this)); |
---|
131 | awr->awar(AWAR_MAYBE_INVALID_GROUP) ->add_callback(makeRootCallback(GroupUIdata::result_list_awar_changed_cb, this)); |
---|
132 | awr->awar(AWAR_SELECTED_GROUP_NAME) ->add_callback(makeRootCallback(GroupUIdata::selected_group_name_changed_cb, this)); |
---|
133 | awr->awar(AWAR_SELECTED_RESULT_GROUP)->add_callback(makeRootCallback(GroupUIdata::selected_group_changed_cb, this)); |
---|
134 | awr->awar(AWAR_RENAME_EXPRESSION) ->add_callback(makeRootCallback(GroupUIdata::update_resulting_groupname_cb, this)); |
---|
135 | } |
---|
136 | void remove_callbacks(AW_root *awr) { |
---|
137 | awr->awar(AWAR_RESULT_ORDER) ->remove_callback(makeRootCallback(GroupUIdata::result_order_changed_cb, this)); |
---|
138 | awr->awar(AWAR_MAYBE_INVALID_GROUP) ->remove_callback(makeRootCallback(GroupUIdata::result_list_awar_changed_cb, this)); |
---|
139 | awr->awar(AWAR_SELECTED_GROUP_NAME) ->remove_callback(makeRootCallback(GroupUIdata::selected_group_name_changed_cb, this)); |
---|
140 | awr->awar(AWAR_SELECTED_RESULT_GROUP)->remove_callback(makeRootCallback(GroupUIdata::selected_group_changed_cb, this)); |
---|
141 | awr->awar(AWAR_RENAME_EXPRESSION) ->remove_callback(makeRootCallback(GroupUIdata::update_resulting_groupname_cb, this)); |
---|
142 | } |
---|
143 | |
---|
144 | public: |
---|
145 | GroupUIdata(GBDATA *gb_main_) : |
---|
146 | gb_main(gb_main_), |
---|
147 | show_tree_name(false), |
---|
148 | result_list(NULp) |
---|
149 | { |
---|
150 | GB_atclose_callback(gb_main, makeDatabaseCallback(GroupUIdata::cleanup_on_exit, this)); |
---|
151 | } |
---|
152 | |
---|
153 | void initialize() { // called after popup |
---|
154 | if (group_search.isNull()) { // avoid reinit if group-search-menu-entry is pressed while group-search-window is still open |
---|
155 | TRACE("initialize GroupUIdata"); |
---|
156 | group_search = new GroupSearch(gb_main, makeGroupSearchCallback(GroupUIdata::refresh_result_list_cb, this)); |
---|
157 | install_callbacks(AW_root::SINGLETON); |
---|
158 | } |
---|
159 | } |
---|
160 | |
---|
161 | GBDATA *get_gb_main() const { return gb_main; } |
---|
162 | |
---|
163 | void announce_result_list(AW_selection_list *result_list_) { result_list = result_list_; } |
---|
164 | void announce_tree_select_list(AW_selection *tree_list_) { tree_list = tree_list_; } |
---|
165 | |
---|
166 | void cleanup() { // called on popdown |
---|
167 | TRACE("cleanup GroupUIdata"); |
---|
168 | remove_callbacks(AW_root::SINGLETON); |
---|
169 | group_search.setNull(); |
---|
170 | clear_result_list(); |
---|
171 | } |
---|
172 | |
---|
173 | void run_search() { |
---|
174 | update_search_filters(); |
---|
175 | update_search_range(); |
---|
176 | update_duplicate_settings(); |
---|
177 | |
---|
178 | { |
---|
179 | AW_root *awr = AW_root::SINGLETON; |
---|
180 | GroupSearchMode mode = GroupSearchMode(awr->awar(AWAR_SEARCH_MODE)->read_int() ^ awr->awar(AWAR_MATCH_MODE)->read_int()); |
---|
181 | group_search->perform_search(mode); |
---|
182 | } |
---|
183 | refill_result_list(); |
---|
184 | } |
---|
185 | |
---|
186 | void refill_result_list(); |
---|
187 | void remove_selected_result(); |
---|
188 | void remove_all_results(); |
---|
189 | void clear_result_list(); |
---|
190 | |
---|
191 | GBDATA *get_selected_group() const { |
---|
192 | AW_scalar awar_value = result_list->get_awar_value(); |
---|
193 | int idx = result_list->get_index_of(awar_value); |
---|
194 | |
---|
195 | return idx == -1 ? NULp : awar_value.get_pointer(); |
---|
196 | } |
---|
197 | |
---|
198 | // modify groups |
---|
199 | void delete_selected_group(); |
---|
200 | void delete_listed_groups(); |
---|
201 | |
---|
202 | void rename_selected_group(); |
---|
203 | void rename_listed_groups(); |
---|
204 | |
---|
205 | void toggle_selected_group_folding(); |
---|
206 | void change_listed_groups_folding(GroupFoldingMode mode); |
---|
207 | |
---|
208 | void mark_species(GroupMarkMode mode); |
---|
209 | }; |
---|
210 | |
---|
211 | void GroupUIdata::clear_result_list() { |
---|
212 | refresh_hit_count(0); |
---|
213 | |
---|
214 | result_list->clear(); |
---|
215 | result_list->insert_default("<none>", (GBDATA*)NULp); |
---|
216 | result_list->update(); |
---|
217 | } |
---|
218 | void GroupUIdata::refill_result_list() { |
---|
219 | const QueriedGroups& foundGroups = group_search->get_results(); |
---|
220 | |
---|
221 | AW_root *awr = AW_root::SINGLETON; |
---|
222 | AW_awar *awar_sel = awr->awar(AWAR_SELECTED_RESULT_GROUP); |
---|
223 | |
---|
224 | if (foundGroups.empty()) { |
---|
225 | clear_result_list(); |
---|
226 | awar_sel->write_pointer(NULp); |
---|
227 | } |
---|
228 | else { |
---|
229 | GB_transaction ta(gb_main); |
---|
230 | |
---|
231 | GBDATA *gb_sellist_group = awr->awar(AWAR_MAYBE_INVALID_GROUP)->read_pointer(); |
---|
232 | bool seen_selected = false; |
---|
233 | |
---|
234 | refresh_hit_count(foundGroups.size()); |
---|
235 | |
---|
236 | result_list->clear(); |
---|
237 | for (FoundGroupCIter g = foundGroups.begin(); g != foundGroups.end(); ++g) { |
---|
238 | const char *display = foundGroups.get_group_display(*g, show_tree_name); |
---|
239 | GBDATA *gb_group = g->get_pointer(); |
---|
240 | |
---|
241 | result_list->insert(display, gb_group); |
---|
242 | |
---|
243 | if (gb_group == gb_sellist_group) seen_selected = true; |
---|
244 | } |
---|
245 | result_list->insert_default("<none>", (GBDATA*)NULp); |
---|
246 | result_list->update(); |
---|
247 | |
---|
248 | awar_sel->rewrite_pointer(seen_selected ? gb_sellist_group : NULp); |
---|
249 | } |
---|
250 | } |
---|
251 | |
---|
252 | void GroupUIdata::update_search_filters() { |
---|
253 | AW_root *awr = AW_root::SINGLETON; |
---|
254 | |
---|
255 | group_search->forgetQExpressions(); |
---|
256 | for (int crit = 1; crit<=MAX_CRITERIA; ++crit) { |
---|
257 | CriterionOperator op = crit == 1 ? CO_OR : CriterionOperator(awr->awar(criterion_awar_name(AWARFORMAT_CRIT_OPERATOR, crit))->read_int()); |
---|
258 | CriterionType type = CriterionType(awr->awar(criterion_awar_name(AWARFORMAT_CRIT_KEY, crit))->read_int()); |
---|
259 | CriterionMatch mtype = CriterionMatch(awr->awar(criterion_awar_name(AWARFORMAT_CRIT_EQUALS, crit))->read_int()); |
---|
260 | const char *expression = awr->awar(criterion_awar_name(AWARFORMAT_CRIT_MATCHES, crit))->read_char_pntr(); |
---|
261 | |
---|
262 | group_search->addQueryExpression(op, type, mtype, expression); |
---|
263 | } |
---|
264 | } |
---|
265 | |
---|
266 | void GroupUIdata::update_search_range() { |
---|
267 | AW_root *awr = AW_root::SINGLETON; |
---|
268 | TreeSearchRange range = TreeSearchRange(awr->awar(AWAR_SEARCH_WHICH_TREES)->read_int()); |
---|
269 | TreeNameSet trees; |
---|
270 | |
---|
271 | switch (range) { |
---|
272 | case SEARCH_ALL_TREES: break; // empty set means "all trees" |
---|
273 | case SEARCH_CURRENT_TREE: { |
---|
274 | const char *currentTree = awr->awar(AWAR_TREE_NAME)->read_char_pntr(); |
---|
275 | if (currentTree[0]) trees.insert(currentTree); |
---|
276 | break; |
---|
277 | } |
---|
278 | case SEARCH_SELECTED_TREES: |
---|
279 | if (tree_list) { |
---|
280 | StrArray tree_names; |
---|
281 | tree_list->get_values(tree_names); |
---|
282 | for (int t = 0; tree_names[t]; ++t) { |
---|
283 | trees.insert(tree_names[t]); |
---|
284 | } |
---|
285 | } |
---|
286 | break; |
---|
287 | } |
---|
288 | if (trees.empty() && range != SEARCH_ALL_TREES) { |
---|
289 | aw_message("No tree selected -> searching all trees instead"); |
---|
290 | } |
---|
291 | group_search->setSearchRange(trees); |
---|
292 | show_tree_name = trees.size() != 1; // (0->all) |
---|
293 | } |
---|
294 | |
---|
295 | void GroupUIdata::update_duplicate_settings() { |
---|
296 | AW_root *awr = AW_root::SINGLETON; |
---|
297 | DuplicateMode dmode = DuplicateMode(awr->awar(AWAR_DUPLICATE_MODE)->read_int()); |
---|
298 | |
---|
299 | if (dmode == DONT_MIND_DUPLICATES) { |
---|
300 | group_search->forgetDupCriteria(); |
---|
301 | } |
---|
302 | else { |
---|
303 | gs_assert(dmode == ONLY_UNIQUE || dmode == ONLY_DUPLICATES); |
---|
304 | |
---|
305 | DupTreeCriterionType treetype = DupTreeCriterionType(awr->awar(AWAR_DUP_TREE_MODE)->read_int()); |
---|
306 | DupNameCriterionType nametype = DupNameCriterionType(awr->awar(AWAR_DUP_NAME_MATCH)->read_int()); |
---|
307 | |
---|
308 | int minClusterSize = awr->awar(AWAR_DUP_MIN_CLUSTER_SIZE)->read_int(); |
---|
309 | bool listDups = dmode == ONLY_DUPLICATES; |
---|
310 | GB_CASE sens = awr->awar(AWAR_DUP_IGNORE_CASE)->read_int() ? GB_IGNORE_CASE : GB_MIND_CASE; |
---|
311 | |
---|
312 | if (nametype == DNC_WHOLENAME) { |
---|
313 | group_search->setDupCriteria(listDups, nametype, sens, treetype, minClusterSize); |
---|
314 | } |
---|
315 | else { |
---|
316 | int minWords = awr->awar(AWAR_DUP_MIN_WORDS)->read_int(); |
---|
317 | const char *excludedWords = awr->awar(AWAR_DUP_EXCLUDED_WORDS)->read_char_pntr(); |
---|
318 | const char *wordSeparators = awr->awar(AWAR_DUP_WORD_SEPARATORS)->read_char_pntr(); |
---|
319 | |
---|
320 | group_search->setDupCriteria(listDups, nametype, sens, minWords, excludedWords, wordSeparators, treetype, minClusterSize); |
---|
321 | } |
---|
322 | } |
---|
323 | } |
---|
324 | |
---|
325 | |
---|
326 | void GroupUIdata::remove_selected_result() { |
---|
327 | int selidx = result_list->get_index_of_selected(); |
---|
328 | if (selidx != -1) { // group is selected |
---|
329 | result_list->move_selection(1); |
---|
330 | result_list->delete_element_at(selidx); |
---|
331 | result_list->update(); |
---|
332 | group_search->remove_hit(selidx); |
---|
333 | } |
---|
334 | } |
---|
335 | void GroupUIdata::remove_all_results() { |
---|
336 | group_search->forget_results(); |
---|
337 | refill_result_list(); |
---|
338 | } |
---|
339 | |
---|
340 | void GroupUIdata::delete_selected_group() { |
---|
341 | int sel = result_list->get_index_of_selected(); |
---|
342 | if (sel != -1) { // group is selected |
---|
343 | result_list->select_default(); // avoid invalid access to group deleted below |
---|
344 | aw_message_if(group_search->delete_group(sel)); |
---|
345 | } |
---|
346 | } |
---|
347 | |
---|
348 | void GroupUIdata::delete_listed_groups() { |
---|
349 | if (group_search->has_results()) { |
---|
350 | result_list->select_default(); // avoid invalid access to group deleted below |
---|
351 | aw_message_if(group_search->delete_found_groups()); |
---|
352 | } |
---|
353 | } |
---|
354 | |
---|
355 | // callback wrappers: |
---|
356 | static void popdown_search_window_cb(AW_window*, GroupUIdata *data) { data->cleanup(); } |
---|
357 | static void runGroupSearch_cb(AW_window*, GroupUIdata *data) { data->run_search(); } |
---|
358 | static void remove_hit_cb(AW_window*, GroupUIdata *data) { data->remove_selected_result(); } |
---|
359 | static void clear_results_cb(AW_window*, GroupUIdata *data) { data->remove_all_results(); } |
---|
360 | static void delete_selected_group_cb(AW_window*, GroupUIdata *data) { data->delete_selected_group(); } |
---|
361 | static void delete_listed_groups_cb(AW_window*, GroupUIdata *data) { data->delete_listed_groups(); } |
---|
362 | static void rename_selected_group_cb(AW_window*, GroupUIdata *data) { data->rename_selected_group(); } |
---|
363 | static void rename_listed_groups_cb(AW_window*, GroupUIdata *data) { data->rename_listed_groups(); } |
---|
364 | static void double_click_group_cb(AW_window*, GroupUIdata *data) { data->toggle_selected_group_folding(); } |
---|
365 | static void listed_groups_folding_cb(AW_window*, GroupUIdata *data, GroupFoldingMode mode) { data->change_listed_groups_folding(mode); } |
---|
366 | static void group_mark_cb(AW_window*, GroupUIdata *data, GroupMarkMode mode) { data->mark_species(mode); } |
---|
367 | |
---|
368 | |
---|
369 | TREE_canvas *NT_get_canvas_showing_tree(const char *tree_name, bool forceDisplay) { |
---|
370 | // search whether any canvas shows the tree 'tree_name'. |
---|
371 | // if yes -> return that canvas |
---|
372 | // if no -> if forceDisplay==false -> return NULp |
---|
373 | // else -> search first visible canvas + switch tree -> return that canvas |
---|
374 | |
---|
375 | TREE_canvas *ntw = NULp; |
---|
376 | { |
---|
377 | TREE_canvas *first_vis = NULp; |
---|
378 | |
---|
379 | for (int ci = 0; ci<MAX_NT_WINDOWS && !ntw; ++ci) { |
---|
380 | TREE_canvas *tc = NT_get_canvas_by_index(ci); |
---|
381 | if (tc && tc->is_shown()) { // @@@ does not detect iconified windows |
---|
382 | if (!first_vis) first_vis = tc; |
---|
383 | if (strcmp(tc->get_awar_tree()->read_char_pntr(), tree_name) == 0) ntw = tc; |
---|
384 | } |
---|
385 | } |
---|
386 | |
---|
387 | if (!ntw && forceDisplay) { |
---|
388 | if (!first_vis) { |
---|
389 | first_vis = NT_get_canvas_by_index(0); |
---|
390 | first_vis->aww->activate(); // popup first (if all windows hidden) |
---|
391 | } |
---|
392 | ntw = first_vis; |
---|
393 | nt_assert(ntw); |
---|
394 | ntw->get_awar_tree()->write_string(tree_name); |
---|
395 | } |
---|
396 | } |
---|
397 | |
---|
398 | return ntw; |
---|
399 | } |
---|
400 | |
---|
401 | static TREE_canvas *get_canvas_able_to_show(GBDATA *gb_group) { |
---|
402 | // detect tree containing group |
---|
403 | // + use NT_get_canvas_showing_tree to force display of that tree |
---|
404 | |
---|
405 | TREE_canvas *ntw = NULp; |
---|
406 | { |
---|
407 | GB_transaction ta(gb_group); |
---|
408 | |
---|
409 | GBDATA *gb_tree = GB_get_father(gb_group); |
---|
410 | char *tree_name = GB_read_key(gb_tree); |
---|
411 | |
---|
412 | ntw = NT_get_canvas_showing_tree(tree_name, true); // forces display of 'tree_name' |
---|
413 | |
---|
414 | free(tree_name); |
---|
415 | } |
---|
416 | |
---|
417 | return ntw; |
---|
418 | } |
---|
419 | |
---|
420 | static bool inside_group_selection = false; |
---|
421 | |
---|
422 | void GroupUIdata::result_list_awar_changed_cb(AW_root *awr) { |
---|
423 | LocallyModify<bool> avoid_recursion(inside_group_selection, true); |
---|
424 | |
---|
425 | GBDATA *gb_group = awr->awar(AWAR_MAYBE_INVALID_GROUP)->read_pointer(); |
---|
426 | TRACE(GBS_global_string("result_list_awar_changed_cb to %p", gb_group)); |
---|
427 | |
---|
428 | if (gb_group) { |
---|
429 | TREE_canvas *ntw = get_canvas_able_to_show(gb_group); |
---|
430 | |
---|
431 | GB_transaction ta(gb_group); |
---|
432 | ntw->get_graphic_tree()->select_group(gb_group); |
---|
433 | |
---|
434 | awr->awar(AWAR_SELECTED_RESULT_GROUP)->rewrite_pointer(get_selected_group()); |
---|
435 | } |
---|
436 | else { |
---|
437 | // @@@ why need extra refresh here? |
---|
438 | // @@@ does not auto-fold |
---|
439 | // both should be handled by deselect_group?! |
---|
440 | TREE_canvas *ntw = NT_get_canvas_by_index(0); // use any canvas here |
---|
441 | AWT_auto_refresh force(ntw); |
---|
442 | ntw->get_graphic_tree()->deselect_group(); |
---|
443 | ntw->request_refresh(); |
---|
444 | |
---|
445 | awr->awar(AWAR_SELECTED_RESULT_GROUP)->rewrite_pointer(NULp); |
---|
446 | } |
---|
447 | } |
---|
448 | |
---|
449 | static void selected_group_changed_by_canvas_cb(AW_root *awr) { |
---|
450 | if (!inside_group_selection) { |
---|
451 | LocallyModify<bool> avoid_recursion(inside_group_selection, true); |
---|
452 | |
---|
453 | GBDATA *gb_group = awr->awar(AWAR_GROUP)->read_pointer(); |
---|
454 | TRACE(GBS_global_string("selected_group_changed_by_canvas_cb to %p", gb_group)); |
---|
455 | if (gb_group) { |
---|
456 | awr->awar(AWAR_MAYBE_INVALID_GROUP)->write_pointer(gb_group); |
---|
457 | } |
---|
458 | } |
---|
459 | } |
---|
460 | |
---|
461 | static bool nameChangedByGroupChange = false; |
---|
462 | |
---|
463 | void GroupUIdata::selected_group_changed_cb(AW_root *awr) { |
---|
464 | GBDATA *gb_group = awr->awar(AWAR_SELECTED_RESULT_GROUP)->read_pointer(); |
---|
465 | TRACE(GBS_global_string("selected_group_changed_cb to %p", gb_group)); |
---|
466 | |
---|
467 | LocallyModify<bool> ignoreNameChange(nameChangedByGroupChange, true); |
---|
468 | |
---|
469 | const char *name = ""; |
---|
470 | if (gb_group) { |
---|
471 | GB_transaction ta(gb_group); |
---|
472 | name = FoundGroup(gb_group).get_name(); |
---|
473 | } |
---|
474 | awr->awar(AWAR_SELECTED_GROUP_NAME)->write_string(name); |
---|
475 | // ensure update of rename-result even if a group-change did NOT change the name (because both groups have same name) |
---|
476 | update_resulting_groupname_cb(awr); |
---|
477 | } |
---|
478 | |
---|
479 | void GroupUIdata::update_resulting_groupname_cb(AW_root *awr) { |
---|
480 | TRACE("update_resulting_groupname_cb"); |
---|
481 | |
---|
482 | char *curr_name = awr->awar(AWAR_SELECTED_GROUP_NAME)->read_string(); |
---|
483 | char *acisrt = awr->awar(AWAR_RENAME_EXPRESSION)->read_string(); |
---|
484 | |
---|
485 | ARB_ERROR error; |
---|
486 | int idx = result_list->get_index_of_selected(); |
---|
487 | char *result = GS_calc_resulting_groupname(gb_main, group_search->get_results(), idx, curr_name, acisrt, error); |
---|
488 | |
---|
489 | if (result) { |
---|
490 | error.expect_no_error(); |
---|
491 | if (!result[0]) { // empty result (will be skipped in batch-rename) |
---|
492 | result = strdup("<empty result> => group would be skipped in batch-rename"); |
---|
493 | } |
---|
494 | } |
---|
495 | else { |
---|
496 | result = strdup(error.deliver()); // show error in result field |
---|
497 | } |
---|
498 | awr->awar(AWAR_RESULTING_GROUP_NAME)->write_string(result); |
---|
499 | |
---|
500 | free(result); |
---|
501 | free(acisrt); |
---|
502 | free(curr_name); |
---|
503 | } |
---|
504 | |
---|
505 | void GroupUIdata::selected_group_name_changed_cb(AW_root *awr) { |
---|
506 | if (!nameChangedByGroupChange) { |
---|
507 | GB_ERROR error = NULp; |
---|
508 | AW_awar *awar_selected_result = awr->awar(AWAR_SELECTED_RESULT_GROUP); |
---|
509 | GBDATA *gb_group = awar_selected_result->read_pointer(); |
---|
510 | |
---|
511 | if (!gb_group) error = "select a group to rename it"; |
---|
512 | else { |
---|
513 | char *new_name = GBS_trim(awr->awar(AWAR_SELECTED_GROUP_NAME)->read_char_pntr()); |
---|
514 | if (!new_name[0]) error = "empty group name not allowed"; |
---|
515 | else { |
---|
516 | GB_transaction ta(gb_group); |
---|
517 | error = GBT_write_name_to_groupData(gb_group, false, new_name, true); |
---|
518 | } |
---|
519 | } |
---|
520 | |
---|
521 | if (error) { |
---|
522 | aw_message(error); |
---|
523 | awar_selected_result->touch(); // refill groupname inputfield |
---|
524 | } |
---|
525 | } |
---|
526 | } |
---|
527 | |
---|
528 | void GroupUIdata::rename_selected_group() { |
---|
529 | int sel = result_list->get_index_of_selected(); |
---|
530 | if (sel != -1) { // group is selected |
---|
531 | const char *acisrt = AW_root::SINGLETON->awar(AWAR_RENAME_EXPRESSION)->read_char_pntr(); |
---|
532 | aw_message_if(group_search->rename_group(sel, acisrt).deliver()); |
---|
533 | // Note: no refresh needed here (triggered by taxonomy callbacks) |
---|
534 | } |
---|
535 | } |
---|
536 | |
---|
537 | void GroupUIdata::rename_listed_groups() { |
---|
538 | if (group_search.isNull()) { |
---|
539 | aw_message("Please rerun group search"); |
---|
540 | return; |
---|
541 | } |
---|
542 | if (group_search->has_results()) { |
---|
543 | const char *acisrt = AW_root::SINGLETON->awar(AWAR_RENAME_EXPRESSION)->read_char_pntr(); |
---|
544 | aw_message_if(group_search->rename_found_groups(acisrt).deliver()); |
---|
545 | // Note: no refresh needed here (triggered by taxonomy callbacks) |
---|
546 | } |
---|
547 | } |
---|
548 | |
---|
549 | void GroupUIdata::toggle_selected_group_folding() { |
---|
550 | int sel = result_list->get_index_of_selected(); |
---|
551 | if (sel != -1) { // group is selected |
---|
552 | ARB_ERROR error = group_search->fold_group(sel, GFM_TOGGLE); |
---|
553 | aw_message_if(error); |
---|
554 | if (!error) AW_root::SINGLETON->awar(AWAR_GROUP)->touch(); // trigger recenter + refresh of changed group |
---|
555 | } |
---|
556 | } |
---|
557 | |
---|
558 | void GroupUIdata::change_listed_groups_folding(GroupFoldingMode foldmode) { |
---|
559 | if (group_search->has_results() || (foldmode & GFM_COLLAPSE_REST)) { |
---|
560 | ARB_ERROR error = group_search->fold_found_groups(foldmode); |
---|
561 | aw_message_if(error); |
---|
562 | if (!error) { |
---|
563 | AW_root::SINGLETON->awar(AWAR_GROUP)->write_pointer(NULp); // deselect group (otherwise wont fold subtree containing selected) |
---|
564 | AW_root::SINGLETON->awar(AWAR_TREE_REFRESH)->touch(); // force expose of all trees (triggers reload if DB-changes) |
---|
565 | } |
---|
566 | } |
---|
567 | } |
---|
568 | |
---|
569 | enum GroupMarkTarget { |
---|
570 | GMT_ALL_SPECIES, // targets all species in DB |
---|
571 | GMT_SELECTED_GROUP, // targets all species contained in SELECTED group |
---|
572 | GMT_ANY_GROUP, // targets all species contained in ANY listed group |
---|
573 | GMT_ALL_GROUPS, // targets all species contained in ALL listed groups |
---|
574 | }; |
---|
575 | |
---|
576 | void GroupUIdata::mark_species(GroupMarkMode mode) { |
---|
577 | GroupMarkTarget target = GroupMarkTarget(AW_root::SINGLETON->awar(AWAR_MARK_TARGET)->read_int()); |
---|
578 | bool anyGroup = false; |
---|
579 | ARB_ERROR error; |
---|
580 | GB_transaction ta(gb_main); |
---|
581 | |
---|
582 | switch (target) { |
---|
583 | case GMT_ALL_SPECIES: |
---|
584 | GBT_mark_all(gb_main, int(mode)); |
---|
585 | break; |
---|
586 | |
---|
587 | case GMT_SELECTED_GROUP: { |
---|
588 | int sel = result_list->get_index_of_selected(); |
---|
589 | if (sel != -1) { |
---|
590 | error = group_search->set_marks_in_group(sel, mode); |
---|
591 | } |
---|
592 | else { |
---|
593 | error = "Please select a group"; |
---|
594 | } |
---|
595 | |
---|
596 | break; |
---|
597 | } |
---|
598 | case GMT_ANY_GROUP: |
---|
599 | anyGroup = true; |
---|
600 | // fall-through |
---|
601 | case GMT_ALL_GROUPS: |
---|
602 | if (group_search->has_results()) { |
---|
603 | error = group_search->set_marks_in_found_groups(mode, anyGroup ? UNITE : INTERSECT); |
---|
604 | } |
---|
605 | else { |
---|
606 | error = "No results listed"; |
---|
607 | } |
---|
608 | break; |
---|
609 | } |
---|
610 | |
---|
611 | error = ta.close(error); |
---|
612 | aw_message_if(error.deliver()); |
---|
613 | } |
---|
614 | |
---|
615 | void create_group_search_awars(AW_root *aw_root, AW_default props) { |
---|
616 | aw_root->awar_pointer(AWAR_MAYBE_INVALID_GROUP, NULp, props); |
---|
617 | aw_root->awar_pointer(AWAR_SELECTED_RESULT_GROUP, NULp, props); |
---|
618 | |
---|
619 | CriterionType deftype[MAX_CRITERIA] = { |
---|
620 | CT_NAME, |
---|
621 | CT_SIZE, |
---|
622 | CT_MARKED_PC, |
---|
623 | }; |
---|
624 | |
---|
625 | for (int crit = 1; crit<=MAX_CRITERIA; ++crit) { |
---|
626 | if (crit>1) { |
---|
627 | aw_root->awar_int(criterion_awar_name(AWARFORMAT_CRIT_OPERATOR, crit), CO_IGNORE, props); |
---|
628 | } |
---|
629 | aw_root->awar_int (criterion_awar_name(AWARFORMAT_CRIT_KEY, crit), deftype[crit-1], props); |
---|
630 | aw_root->awar_int (criterion_awar_name(AWARFORMAT_CRIT_EQUALS, crit), CM_MATCH, props); |
---|
631 | aw_root->awar_string(criterion_awar_name(AWARFORMAT_CRIT_MATCHES, crit), crit == 1 ? "*" : "", props); |
---|
632 | } |
---|
633 | |
---|
634 | aw_root->awar_string(AWAR_SELECTED_GROUP_NAME, "", props); |
---|
635 | aw_root->awar_string(AWAR_RENAME_EXPRESSION, "", props); |
---|
636 | aw_root->awar_string(AWAR_RESULTING_GROUP_NAME, "???", props); |
---|
637 | aw_root->awar_string(AWAR_TREE_SELECTED, "", props); |
---|
638 | |
---|
639 | aw_root->awar_string(AWAR_DUP_EXCLUDED_WORDS, |
---|
640 | "and or" |
---|
641 | " branch group clade subsection" |
---|
642 | " order family genus" |
---|
643 | " incertae sedis" |
---|
644 | " other unknown unclassified miscellaneous" |
---|
645 | , props); |
---|
646 | aw_root->awar_string(AWAR_DUP_WORD_SEPARATORS, ",; /()[]_", props); |
---|
647 | |
---|
648 | aw_root->awar_int(AWAR_GROUP_HIT_COUNT, 0, props); |
---|
649 | aw_root->awar_int(AWAR_SEARCH_WHICH_TREES, SEARCH_CURRENT_TREE, props); |
---|
650 | aw_root->awar_int(AWAR_SEARCH_MODE, GSM_FIND, props); |
---|
651 | aw_root->awar_int(AWAR_MATCH_MODE, GSM_MATCH, props); |
---|
652 | aw_root->awar_int(AWAR_DUPLICATE_MODE, DONT_MIND_DUPLICATES, props); |
---|
653 | aw_root->awar_int(AWAR_DUP_TREE_MODE, DLC_SAME_TREE, props); |
---|
654 | aw_root->awar_int(AWAR_DUP_MIN_CLUSTER_SIZE, 2, props)->set_min(2); |
---|
655 | aw_root->awar_int(AWAR_DUP_NAME_MATCH, DNC_WHOLENAME, props); |
---|
656 | aw_root->awar_int(AWAR_DUP_IGNORE_CASE, 1, props); |
---|
657 | aw_root->awar_int(AWAR_DUP_MIN_WORDS, 2, props)->set_min(1); |
---|
658 | aw_root->awar_int(AWAR_RESULT_ORDER, GSC_NONE, props); |
---|
659 | aw_root->awar_int(AWAR_MARK_TARGET, GMT_SELECTED_GROUP, props); |
---|
660 | |
---|
661 | // perma-callbacks: |
---|
662 | aw_root->awar(AWAR_GROUP) ->add_callback(selected_group_changed_by_canvas_cb); |
---|
663 | // more callbacks are installed above in .@install_callbacks |
---|
664 | } |
---|
665 | |
---|
666 | static AW_window *create_tree_select_window_cb(AW_root *aw_root, GroupUIdata *data) { |
---|
667 | AW_window_simple *aws = new AW_window_simple; |
---|
668 | |
---|
669 | aw_root->awar(AWAR_SEARCH_WHICH_TREES)->write_int(SEARCH_SELECTED_TREES); // switch target-range to 'selected trees' |
---|
670 | |
---|
671 | aws->init(aw_root, "GROUP_TREES", "Select trees to search"); |
---|
672 | aws->load_xfig("group_trees.fig"); |
---|
673 | |
---|
674 | aws->callback(AW_POPDOWN); |
---|
675 | aws->at("close"); |
---|
676 | aws->create_button("CLOSE", "CLOSE", "C"); |
---|
677 | |
---|
678 | aws->callback(makeHelpCallback("group_trees.hlp")); |
---|
679 | aws->at("help"); |
---|
680 | aws->create_button("HELP", "HELP", "H"); |
---|
681 | |
---|
682 | aws->at("list"); |
---|
683 | AW_DB_selection *all_trees = awt_create_TREE_selection_list(data->get_gb_main(), aws, AWAR_TREE_SELECTED); |
---|
684 | AW_selection *selected_trees = awt_create_subset_selection_list(aws, all_trees->get_sellist(), "selected", "add", "sort"); |
---|
685 | |
---|
686 | data->announce_tree_select_list(selected_trees); |
---|
687 | |
---|
688 | return aws; |
---|
689 | } |
---|
690 | |
---|
691 | static AW_window *create_group_rename_window_cb(AW_root *awr, GroupUIdata *data) { |
---|
692 | AW_window_simple *aws = new AW_window_simple; |
---|
693 | aws->init(awr, "RENAME_GROUPS", "Rename taxonomic groups"); |
---|
694 | |
---|
695 | const int PAD = 10; |
---|
696 | |
---|
697 | aws->at(PAD, PAD); |
---|
698 | aws->auto_space(PAD/2, PAD/2); |
---|
699 | |
---|
700 | aws->button_length(7); |
---|
701 | |
---|
702 | aws->callback(AW_POPDOWN); |
---|
703 | aws->create_button("CLOSE", "Close", "C"); |
---|
704 | |
---|
705 | aws->at_shift(450, 0); // defines minimum window width |
---|
706 | |
---|
707 | aws->at_attach(-PAD, 0); |
---|
708 | aws->callback(makeHelpCallback("group_rename.hlp")); |
---|
709 | aws->create_button("HELP", "Help", "H"); |
---|
710 | aws->at_unattach(); |
---|
711 | |
---|
712 | const int IF_YSIZE = 32; // lineheight of attached input field |
---|
713 | |
---|
714 | const int LABEL_LENGTH = 27; |
---|
715 | const int FIELD_LENGTH = 40; // startup-length (chars) |
---|
716 | |
---|
717 | aws->label_length(LABEL_LENGTH); |
---|
718 | |
---|
719 | aws->at_newline(); |
---|
720 | int sy = aws->get_at_yposition(); |
---|
721 | |
---|
722 | aws->at_attach_to(true, false, -PAD, IF_YSIZE); |
---|
723 | aws->label("Selected group name:"); |
---|
724 | aws->create_input_field(AWAR_SELECTED_GROUP_NAME, FIELD_LENGTH); |
---|
725 | |
---|
726 | aws->at_newline(); |
---|
727 | int inputlineHeight = aws->get_at_yposition()-sy; |
---|
728 | |
---|
729 | aws->at_attach_to(true, false, -PAD, IF_YSIZE); |
---|
730 | aws->label("Modify using ACI/SRT:"); |
---|
731 | aws->create_input_field(AWAR_RENAME_EXPRESSION, FIELD_LENGTH); |
---|
732 | aws->at_unattach(); |
---|
733 | |
---|
734 | aws->at_newline(); |
---|
735 | int rx, ry; |
---|
736 | aws->get_at_position(&rx, &ry); |
---|
737 | aws->at_shift(0, inputlineHeight); // reserve space for "Resulting group name" |
---|
738 | |
---|
739 | int by = aws->get_at_yposition(); |
---|
740 | aws->at_attach(0, -PAD); |
---|
741 | aws->callback(makeWindowCallback(rename_selected_group_cb, data)); |
---|
742 | aws->create_autosize_button("SELECTED", "Apply to\nselected group"); |
---|
743 | aws->at_attach(0, -PAD); |
---|
744 | aws->callback(makeWindowCallback(rename_listed_groups_cb, data)); |
---|
745 | aws->create_autosize_button("LISTED", "Apply to all\nlisted groups"); |
---|
746 | aws->at_unattach(); |
---|
747 | |
---|
748 | aws->at_newline(); |
---|
749 | int bheight = aws->get_at_yposition()-by; |
---|
750 | |
---|
751 | aws->at(rx, ry); |
---|
752 | aws->at_attach_to(true, true, -PAD, -(PAD+bheight)); |
---|
753 | aws->label("Resulting group name:"); |
---|
754 | aws->create_button(NULp, AWAR_RESULTING_GROUP_NAME, NULp, "+"); |
---|
755 | aws->at_unattach(); |
---|
756 | |
---|
757 | aws->window_fit(); |
---|
758 | |
---|
759 | return aws; |
---|
760 | } |
---|
761 | |
---|
762 | static AW_window *create_dup_config_window_cb(AW_root *awr) { |
---|
763 | AW_window_simple *aws = new AW_window_simple; |
---|
764 | aws->init(awr, "DUP_CONFIG", "Configure group duplicate search"); |
---|
765 | |
---|
766 | const int PAD = 10; |
---|
767 | const int IF_YSIZE = 32; // lineheight of attached input field |
---|
768 | |
---|
769 | aws->at(PAD, PAD); |
---|
770 | aws->auto_space(PAD/2, PAD/2); |
---|
771 | |
---|
772 | aws->button_length(7); |
---|
773 | |
---|
774 | aws->callback(AW_POPDOWN); |
---|
775 | aws->create_button("CLOSE", "Close", "C"); |
---|
776 | |
---|
777 | const int LONG_LABEL_LENGTH = 30; |
---|
778 | const int SHORT_LABEL_LENGTH = 15; |
---|
779 | const int LONG_FIELD_LENGTH = 40; // used for string input field |
---|
780 | const int SHORT_FIELD_LENGTH = 7; // used for numeric input fields |
---|
781 | |
---|
782 | aws->label_length(LONG_LABEL_LENGTH); |
---|
783 | |
---|
784 | aws->at_newline(); |
---|
785 | aws->label("Min. size of duplicate cluster:"); |
---|
786 | aws->create_input_field(AWAR_DUP_MIN_CLUSTER_SIZE, SHORT_FIELD_LENGTH); |
---|
787 | |
---|
788 | aws->at_newline(); |
---|
789 | aws->label("Search duplicates"); |
---|
790 | aws->create_option_menu(AWAR_DUP_TREE_MODE); |
---|
791 | aws->insert_default_option("inside same tree", "s", DLC_SAME_TREE); |
---|
792 | aws->insert_option ("in different trees", "d", DLC_DIFF_TREE); |
---|
793 | aws->insert_option ("anywhere", "a", DLC_ANYWHERE); |
---|
794 | aws->update_option_menu(); |
---|
795 | |
---|
796 | aws->at_newline(); |
---|
797 | aws->label("Ignore case?"); |
---|
798 | aws->create_toggle(AWAR_DUP_IGNORE_CASE); |
---|
799 | |
---|
800 | aws->at_newline(); |
---|
801 | aws->create_toggle_field(AWAR_DUP_NAME_MATCH, "Duplicates are names that"); |
---|
802 | aws->insert_default_toggle("match whole name", "n", DNC_WHOLENAME); |
---|
803 | aws->insert_toggle ("match wordwise", "w", DNC_WORDWISE); |
---|
804 | aws->update_toggle_field(); |
---|
805 | |
---|
806 | aws->at_newline(); |
---|
807 | aws->label("Min. number of matching words"); |
---|
808 | aws->create_input_field(AWAR_DUP_MIN_WORDS, SHORT_FIELD_LENGTH); |
---|
809 | |
---|
810 | aws->at_newline(); |
---|
811 | aws->label("Word separators"); |
---|
812 | aws->create_input_field(AWAR_DUP_WORD_SEPARATORS, 2*SHORT_FIELD_LENGTH); |
---|
813 | |
---|
814 | aws->at_newline(); |
---|
815 | aws->label_length(SHORT_LABEL_LENGTH); |
---|
816 | aws->at_attach_to(true, false, -PAD, IF_YSIZE); |
---|
817 | aws->label("Ignored words"); |
---|
818 | aws->create_input_field(AWAR_DUP_EXCLUDED_WORDS, LONG_FIELD_LENGTH); |
---|
819 | |
---|
820 | aws->window_fit(); |
---|
821 | return aws; |
---|
822 | } |
---|
823 | |
---|
824 | static AWT_config_mapping_def group_search_config_mapping[] = { |
---|
825 | { AWAR_SEARCH_WHICH_TREES, "searched_trees" }, |
---|
826 | { AWAR_SEARCH_MODE, "search_mode" }, |
---|
827 | { AWAR_MATCH_MODE, "match_mode" }, |
---|
828 | |
---|
829 | { AWAR_DUPLICATE_MODE, "dup_mode" }, |
---|
830 | { AWAR_DUP_TREE_MODE, "dup_tree_mode" }, |
---|
831 | { AWAR_DUP_MIN_CLUSTER_SIZE, "dup_min_size" }, |
---|
832 | { AWAR_DUP_NAME_MATCH, "dup_match_mode" }, |
---|
833 | { AWAR_DUP_IGNORE_CASE, "dup_ignore_case" }, |
---|
834 | { AWAR_DUP_MIN_WORDS, "dup_min_words" }, |
---|
835 | { AWAR_DUP_EXCLUDED_WORDS, "dup_excluded_words" }, |
---|
836 | { AWAR_DUP_WORD_SEPARATORS, "dup_word_separators" }, |
---|
837 | |
---|
838 | { AWAR_MARK_TARGET, "mark_target" }, |
---|
839 | { AWAR_RENAME_EXPRESSION, "rename_script" }, |
---|
840 | |
---|
841 | { NULp, NULp }, |
---|
842 | }; |
---|
843 | |
---|
844 | static void create_search_config_setup_cb(AWT_config_definition& def) { |
---|
845 | def.add(group_search_config_mapping); // fixed parameters |
---|
846 | |
---|
847 | for (int crit = 1; crit<=MAX_CRITERIA; ++crit) { |
---|
848 | if (crit>1) { |
---|
849 | def.add(criterion_awar_name(AWARFORMAT_CRIT_OPERATOR, crit), "op", crit); |
---|
850 | } |
---|
851 | def.add(criterion_awar_name(AWARFORMAT_CRIT_KEY, crit), "sel", crit); |
---|
852 | def.add(criterion_awar_name(AWARFORMAT_CRIT_EQUALS, crit), "eq", crit); |
---|
853 | def.add(criterion_awar_name(AWARFORMAT_CRIT_MATCHES, crit), "expr", crit); |
---|
854 | } |
---|
855 | } |
---|
856 | |
---|
857 | static AWT_predefined_config predefined_group_search[] = { |
---|
858 | { "*tagged_find", "Search expression for \"tagged\" groupnames", "dup_mode='0';eq1='0';expr1='*[*]*';match_mode='0';op2='2';op3='2';sel1='0'" }, |
---|
859 | { "*tags_remove_all", "Expression for batch-rename:\n- removes any prefix(es) in \"[..]\" from groupnames", "rename_script='/\\\\[.*\\\\]//'" }, |
---|
860 | { "*tag_prefix", "Expression for batch-rename:\n- adds prefix \"[TAG] \" to groupnames", "rename_script='\"[TAG] \";dd'" }, |
---|
861 | { "*swap_AND_names", "Batch-rename:\n \"X and Y\" -> \"Y and X\"\n \"X, Y and Z\" -> \"Z, X and Y\" ...", "rename_script=':* and *=*2 and *1:* and *, *=*1, *2 and *3:*, * and *, *=*1, *2, *3 and *4'" }, |
---|
862 | { "*rename_enumerated", "Batch-rename:\n- appends running number to group-name\n (using duplicate search + hitlist order)", "dup_mode='1';rename_script='dd;\"~\";dupidx|merge|srt(~~=~)'" }, |
---|
863 | { "*remove_numeric_suffix", "Batch-rename:\n- removes numeric suffixes \n like \"~1\", \"~003\"", "rename_script='/~[0-9]+$//'" }, |
---|
864 | |
---|
865 | { "*undo_groupXfer_report", "undo group rename applied by \"Move groups\":\n * remove prefix \"XFRD_\"\n * remove penalty suffix", "rename_script='command(\"/^XFRD_//\")|command(\"/\\\\\\\\{penalty.*\\\\\\\\}$//\")'" }, |
---|
866 | |
---|
867 | { NULp, NULp, NULp } |
---|
868 | }; |
---|
869 | |
---|
870 | void popup_group_search_window(AW_window *aw_parent, GBDATA *gb_main) { |
---|
871 | static AW_window *awgs = NULp; |
---|
872 | static GroupUIdata data(gb_main); |
---|
873 | |
---|
874 | data.initialize(); |
---|
875 | |
---|
876 | if (!awgs) { |
---|
877 | AW_window_simple *aws = new AW_window_simple; |
---|
878 | AW_root *aw_root = aw_parent->get_root(); |
---|
879 | |
---|
880 | aws->init(aw_root, "GROUP_SEARCH", "Search taxonomic groups"); |
---|
881 | aws->load_xfig("group_search.fig"); |
---|
882 | |
---|
883 | aws->button_length(7); |
---|
884 | |
---|
885 | aws->at("close"); |
---|
886 | aws->callback(AW_POPDOWN); |
---|
887 | aws->create_button("CLOSE", "Close", "C"); |
---|
888 | |
---|
889 | aws->at("help"); |
---|
890 | aws->callback(makeHelpCallback("group_search.hlp")); |
---|
891 | aws->create_button("HELP", "Help", "H"); |
---|
892 | |
---|
893 | aws->at("config"); |
---|
894 | AWT_insert_config_manager(aws, AW_ROOT_DEFAULT, "group_search", makeConfigSetupCallback(create_search_config_setup_cb), NULp, predefined_group_search); |
---|
895 | |
---|
896 | aws->at("trees"); |
---|
897 | aws->create_option_menu(AWAR_SEARCH_WHICH_TREES); |
---|
898 | aws->insert_default_option("current tree", "c", SEARCH_CURRENT_TREE); |
---|
899 | aws->insert_option ("selected trees", "s", SEARCH_SELECTED_TREES); |
---|
900 | aws->insert_option ("all trees", "a", SEARCH_ALL_TREES); |
---|
901 | aws->update_option_menu(); |
---|
902 | |
---|
903 | aws->at("select"); |
---|
904 | aws->callback(makeCreateWindowCallback(create_tree_select_window_cb, &data)); |
---|
905 | aws->create_autosize_button("select_trees", "(select)"); |
---|
906 | |
---|
907 | aws->at("mode"); |
---|
908 | aws->create_option_menu(AWAR_SEARCH_MODE); |
---|
909 | aws->insert_default_option("list", "l", GSM_FIND); |
---|
910 | aws->insert_option ("add", "a", GSM_ADD); |
---|
911 | aws->insert_option ("keep", "k", GSM_KEEP); |
---|
912 | aws->insert_option ("remove", "r", GSM_REMOVE); |
---|
913 | aws->update_option_menu(); |
---|
914 | |
---|
915 | aws->at("not"); |
---|
916 | aws->create_option_menu(AWAR_MATCH_MODE); |
---|
917 | aws->insert_default_option("match", "m", GSM_MATCH); |
---|
918 | aws->insert_option ("don't match", "d", GSM_MISMATCH); |
---|
919 | aws->update_option_menu(); |
---|
920 | |
---|
921 | aws->at("dups"); |
---|
922 | aws->create_option_menu(AWAR_DUPLICATE_MODE); |
---|
923 | aws->insert_default_option("No", "n", DONT_MIND_DUPLICATES); |
---|
924 | aws->insert_option ("duplicate groups only", "d", ONLY_DUPLICATES); |
---|
925 | aws->insert_option ("unique groups only", "u", ONLY_UNIQUE); |
---|
926 | aws->update_option_menu(); |
---|
927 | |
---|
928 | aws->at("dupconf"); |
---|
929 | aws->callback(create_dup_config_window_cb); |
---|
930 | aws->create_autosize_button("config_dup", "Configure"); |
---|
931 | |
---|
932 | WindowCallback search_wcb = makeWindowCallback(runGroupSearch_cb, &data); |
---|
933 | |
---|
934 | for (int crit = 1; crit<=MAX_CRITERIA; ++crit) { |
---|
935 | if (crit>1) { |
---|
936 | aws->at(GBS_global_string("op%i", crit)); |
---|
937 | aws->create_option_menu(criterion_awar_name(AWARFORMAT_CRIT_OPERATOR, crit)); |
---|
938 | aws->insert_option ("and", "a", CO_AND); |
---|
939 | aws->insert_option ("or", "o", CO_OR); |
---|
940 | aws->insert_default_option("ign", "i", CO_IGNORE); |
---|
941 | aws->update_option_menu(); |
---|
942 | } |
---|
943 | |
---|
944 | aws->at(GBS_global_string("crit%i", crit)); |
---|
945 | aws->create_option_menu(criterion_awar_name(AWARFORMAT_CRIT_KEY, crit)); |
---|
946 | aws->insert_default_option("groupname", "g", CT_NAME); |
---|
947 | aws->insert_option ("parent", "p", CT_PARENT_DIRECT); |
---|
948 | aws->insert_option ("parent (any)", "a", CT_PARENT_ANY); |
---|
949 | aws->insert_option ("parent (all)", "l", CT_PARENT_ALL); |
---|
950 | aws->insert_option ("nesting", "n", CT_NESTING_LEVEL); |
---|
951 | aws->insert_option ("folded", "f", CT_FOLDED); |
---|
952 | aws->insert_option ("size", "s", CT_SIZE); |
---|
953 | aws->insert_option ("marked", "m", CT_MARKED); |
---|
954 | aws->insert_option ("marked%", "%", CT_MARKED_PC); |
---|
955 | aws->insert_option ("zombies", "z", CT_ZOMBIES); |
---|
956 | aws->insert_option ("AID", "A", CT_AID); |
---|
957 | aws->insert_option ("keeled", "k", CT_KEELED); |
---|
958 | aws->update_option_menu(); |
---|
959 | |
---|
960 | aws->at(GBS_global_string("eq%i", crit)); |
---|
961 | aws->create_toggle(criterion_awar_name(AWARFORMAT_CRIT_EQUALS, crit), "#equal.xpm", "#notEqual.xpm"); |
---|
962 | |
---|
963 | aws->at(GBS_global_string("content%i", crit)); |
---|
964 | aws->d_callback(search_wcb); // ENTER in search field |
---|
965 | aws->create_input_field(criterion_awar_name(AWARFORMAT_CRIT_MATCHES, crit)); |
---|
966 | } |
---|
967 | |
---|
968 | aws->button_length(18); |
---|
969 | |
---|
970 | aws->at("doquery"); |
---|
971 | aws->callback(search_wcb); |
---|
972 | aws->create_button("SEARCH", "Search", "S", "+"); |
---|
973 | |
---|
974 | aws->button_length(13); |
---|
975 | |
---|
976 | aws->at("count"); |
---|
977 | aws->label("Hits:"); |
---|
978 | aws->create_button(NULp, AWAR_GROUP_HIT_COUNT, NULp, "+"); |
---|
979 | |
---|
980 | aws->at("result"); |
---|
981 | aws->d_callback(makeWindowCallback(double_click_group_cb, &data)); |
---|
982 | AW_selection_list *result_list = aws->create_selection_list(AWAR_MAYBE_INVALID_GROUP); |
---|
983 | data.announce_result_list(result_list); |
---|
984 | data.clear_result_list(); |
---|
985 | |
---|
986 | aws->at("order"); |
---|
987 | aws->create_option_menu(AWAR_RESULT_ORDER); |
---|
988 | aws->insert_default_option("unsorted", "u", GSC_NONE); |
---|
989 | aws->insert_option ("by name", "n", GSC_NAME); |
---|
990 | aws->insert_option ("by nesting", "g", GSC_NESTING); |
---|
991 | aws->insert_option ("by size", "s", GSC_SIZE); |
---|
992 | aws->insert_option ("by marked", "m", GSC_MARKED); |
---|
993 | aws->insert_option ("by marked%", "%", GSC_MARKED_PC); |
---|
994 | aws->insert_option ("by treename", "t", GSC_TREENAME); |
---|
995 | aws->insert_option ("by treeorder", "o", GSC_TREEORDER); |
---|
996 | aws->insert_option ("by hit", "h", GSC_HIT_REASON); |
---|
997 | aws->insert_option ("by cluster", "c", GSC_CLUSTER); |
---|
998 | aws->insert_option ("by AID", "A", GSC_AID); |
---|
999 | aws->insert_option ("by keeled", "k", GSC_KEELED); |
---|
1000 | aws->insert_option ("reverse", "r", GSC_REVERSE); |
---|
1001 | aws->update_option_menu(); |
---|
1002 | |
---|
1003 | // actions (results): |
---|
1004 | aws->button_length(6); |
---|
1005 | |
---|
1006 | aws->at("remhit"); |
---|
1007 | aws->callback(makeWindowCallback(remove_hit_cb, &data)); |
---|
1008 | aws->create_button("rm_sel", "Remove"); |
---|
1009 | |
---|
1010 | aws->at("clear"); |
---|
1011 | aws->callback(makeWindowCallback(clear_results_cb, &data)); |
---|
1012 | aws->create_button("clear", "Clear"); |
---|
1013 | |
---|
1014 | // actions (groups): |
---|
1015 | // ..... rename |
---|
1016 | aws->at("rename"); |
---|
1017 | aws->callback(makeCreateWindowCallback(create_group_rename_window_cb, &data)); |
---|
1018 | aws->create_button("rename", "Rename ..."); |
---|
1019 | |
---|
1020 | // ..... expand/collapse |
---|
1021 | aws->at("expand"); |
---|
1022 | aws->callback(makeWindowCallback(listed_groups_folding_cb, &data, GFM_EXPANDREC)); |
---|
1023 | aws->create_button("expand_listed", "Expand listed"); |
---|
1024 | |
---|
1025 | aws->at("expcol"); |
---|
1026 | aws->callback(makeWindowCallback(listed_groups_folding_cb, &data, GFM_EXPANDREC_COLLREST)); |
---|
1027 | aws->create_button("explst_collrest", "Expand listed\ncollapse rest"); |
---|
1028 | |
---|
1029 | aws->at("expparent"); |
---|
1030 | aws->callback(makeWindowCallback(listed_groups_folding_cb, &data, GFM_EXPANDPARENTS)); |
---|
1031 | aws->create_button("expand_parents", "Expand parents"); |
---|
1032 | |
---|
1033 | aws->at("collapse"); |
---|
1034 | aws->callback(makeWindowCallback(listed_groups_folding_cb, &data, GFM_COLLAPSE)); |
---|
1035 | aws->create_button("collapse_listed", "Collapse listed"); |
---|
1036 | |
---|
1037 | // ..... delete |
---|
1038 | aws->at("delete"); |
---|
1039 | aws->callback(makeWindowCallback(delete_selected_group_cb, &data)); |
---|
1040 | aws->create_button("del_sel", "Destroy\nselected group"); |
---|
1041 | |
---|
1042 | aws->at("dellisted"); |
---|
1043 | aws->callback(makeWindowCallback(delete_listed_groups_cb, &data)); |
---|
1044 | aws->create_button("del_listed", "Destroy all\nlisted groups"); |
---|
1045 | |
---|
1046 | // ..... mark/unmark |
---|
1047 | aws->at("mark"); |
---|
1048 | aws->callback(makeWindowCallback(group_mark_cb, &data, GMM_MARK)); |
---|
1049 | aws->create_button("mark", "Mark"); |
---|
1050 | aws->at("unmark"); |
---|
1051 | aws->callback(makeWindowCallback(group_mark_cb, &data, GMM_UNMARK)); |
---|
1052 | aws->create_button("unmark", "Unmark"); |
---|
1053 | aws->at("inv"); |
---|
1054 | aws->callback(makeWindowCallback(group_mark_cb, &data, GMM_INVERT)); |
---|
1055 | aws->create_button("invert", "Inv"); |
---|
1056 | |
---|
1057 | aws->at("mwhich"); // needs to be created AFTER delete buttons |
---|
1058 | aws->create_option_menu(AWAR_MARK_TARGET); |
---|
1059 | aws->insert_default_option("selected", "s", GMT_SELECTED_GROUP); |
---|
1060 | aws->insert_option ("any listed", "l", GMT_ANY_GROUP); |
---|
1061 | aws->insert_option ("all listed", "a", GMT_ALL_GROUPS); |
---|
1062 | aws->insert_option ("database", "d", GMT_ALL_SPECIES); |
---|
1063 | aws->update_option_menu(); |
---|
1064 | |
---|
1065 | // trigger cleanup on popdown: |
---|
1066 | aws->on_hide(makeWindowCallback(popdown_search_window_cb, &data)); |
---|
1067 | awgs = aws; |
---|
1068 | } |
---|
1069 | |
---|
1070 | awgs->activate(); |
---|
1071 | } |
---|
1072 | |
---|