1 | // ================================================================ // |
---|
2 | // // |
---|
3 | // File : AW_select.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Coded by Ralf Westram (coder@reallysoft.de) in February 2010 // |
---|
7 | // Institute of Microbiology (Technical University Munich) // |
---|
8 | // http://www.arb-home.de/ // |
---|
9 | // // |
---|
10 | // ================================================================ // |
---|
11 | |
---|
12 | #include "aw_select.hxx" |
---|
13 | #include "aw_window_Xm.hxx" |
---|
14 | #include "aw_root.hxx" |
---|
15 | #include "aw_awar.hxx" |
---|
16 | |
---|
17 | #include <arb_strarray.h> |
---|
18 | #include <arb_strbuf.h> |
---|
19 | #include <arb_str.h> |
---|
20 | #include <arb_sort.h> |
---|
21 | #include <ad_cb.h> |
---|
22 | |
---|
23 | #include <Xm/List.h> |
---|
24 | |
---|
25 | __ATTR__NORETURN inline void selection_type_mismatch(const char *triedType) { type_mismatch(triedType, "selection-list"); } |
---|
26 | |
---|
27 | // -------------------------- |
---|
28 | // AW_selection_list |
---|
29 | |
---|
30 | |
---|
31 | AW_selection_list::AW_selection_list(const char *variable_name_, int variable_type_, Widget select_list_widget_) |
---|
32 | : variable_name(nulldup(variable_name_)), |
---|
33 | variable_type(AW_VARIABLE_TYPE(variable_type_)), |
---|
34 | update_cb(NULL), |
---|
35 | cl_update(0), |
---|
36 | select_list_widget(select_list_widget_), |
---|
37 | list_table(NULL), |
---|
38 | last_of_list_table(NULL), |
---|
39 | default_select(NULL), |
---|
40 | next(NULL) |
---|
41 | {} |
---|
42 | |
---|
43 | AW_selection_list::~AW_selection_list() { |
---|
44 | clear(); |
---|
45 | free(variable_name); |
---|
46 | } |
---|
47 | |
---|
48 | inline XmString XmStringCreateSimple_wrapper(const char *text) { |
---|
49 | return XmStringCreateSimple((char*)text); |
---|
50 | } |
---|
51 | |
---|
52 | void AW_selection_list::update() { |
---|
53 | // Warning: |
---|
54 | // update() will not set the connected awar to the default value |
---|
55 | // if it contains a value which is not associated with a list entry! |
---|
56 | |
---|
57 | size_t count = size(); |
---|
58 | if (default_select) count++; |
---|
59 | |
---|
60 | XmString *strtab = new XmString[count]; |
---|
61 | |
---|
62 | count = 0; |
---|
63 | for (AW_selection_list_entry *lt = list_table; lt; lt = lt->next) { |
---|
64 | const char *s2 = lt->get_displayed(); |
---|
65 | if (!s2[0]) s2 = " "; |
---|
66 | strtab[count] = XmStringCreateSimple_wrapper(s2); |
---|
67 | count++; |
---|
68 | } |
---|
69 | |
---|
70 | if (default_select) { |
---|
71 | const char *s2 = default_select->get_displayed(); |
---|
72 | if (!strlen(s2)) s2 = " "; |
---|
73 | strtab[count] = XmStringCreateSimple_wrapper(s2); |
---|
74 | count++; |
---|
75 | } |
---|
76 | if (!count) { |
---|
77 | strtab[count] = XmStringCreateSimple_wrapper(" "); |
---|
78 | count ++; |
---|
79 | } |
---|
80 | |
---|
81 | XtVaSetValues(select_list_widget, XmNitemCount, count, XmNitems, strtab, NULL); |
---|
82 | |
---|
83 | refresh(); |
---|
84 | |
---|
85 | for (size_t i=0; i<count; i++) XmStringFree(strtab[i]); |
---|
86 | delete [] strtab; |
---|
87 | |
---|
88 | if (update_cb) update_cb(this, cl_update); |
---|
89 | } |
---|
90 | |
---|
91 | void AW_selection_list::set_update_callback(sellist_update_cb ucb, AW_CL cl_user) { |
---|
92 | aw_assert(!update_cb || !ucb); // overwrite allowed only with NULL! |
---|
93 | |
---|
94 | update_cb = ucb; |
---|
95 | cl_update = cl_user; |
---|
96 | } |
---|
97 | |
---|
98 | void AW_selection_list::refresh() { |
---|
99 | if (!variable_name) return; // not connected to awar |
---|
100 | |
---|
101 | AW_root *root = AW_root::SINGLETON; |
---|
102 | bool found = false; |
---|
103 | int pos = 0; |
---|
104 | AW_awar *awar = root->awar(variable_name); |
---|
105 | |
---|
106 | AW_selection_list_entry *lt; |
---|
107 | |
---|
108 | switch (variable_type) { |
---|
109 | case AW_STRING: { |
---|
110 | char *var_value = awar->read_string(); |
---|
111 | for (lt = list_table; lt; lt = lt->next) { |
---|
112 | if (strcmp(var_value, lt->value.get_string()) == 0) { |
---|
113 | found = true; |
---|
114 | break; |
---|
115 | } |
---|
116 | pos++; |
---|
117 | } |
---|
118 | free(var_value); |
---|
119 | break; |
---|
120 | } |
---|
121 | case AW_INT: { |
---|
122 | int var_value = awar->read_int(); |
---|
123 | for (lt = list_table; lt; lt = lt->next) { |
---|
124 | if (var_value == lt->value.get_int()) { |
---|
125 | found = true; |
---|
126 | break; |
---|
127 | } |
---|
128 | pos++; |
---|
129 | } |
---|
130 | break; |
---|
131 | } |
---|
132 | case AW_FLOAT: { |
---|
133 | float var_value = awar->read_float(); |
---|
134 | for (lt = list_table; lt; lt = lt->next) { |
---|
135 | if (var_value == lt->value.get_float()) { |
---|
136 | found = true; |
---|
137 | break; |
---|
138 | } |
---|
139 | pos++; |
---|
140 | } |
---|
141 | break; |
---|
142 | } |
---|
143 | case AW_POINTER: { |
---|
144 | GBDATA *var_value = awar->read_pointer(); |
---|
145 | for (lt = list_table; lt; lt = lt->next) { |
---|
146 | if (var_value == lt->value.get_pointer()) { |
---|
147 | found = true; |
---|
148 | break; |
---|
149 | } |
---|
150 | pos++; |
---|
151 | } |
---|
152 | break; |
---|
153 | } |
---|
154 | default: |
---|
155 | aw_assert(0); |
---|
156 | GB_warning("Unknown AWAR type"); |
---|
157 | break; |
---|
158 | } |
---|
159 | |
---|
160 | if (found || default_select) { |
---|
161 | pos++; |
---|
162 | int top; |
---|
163 | int vis; |
---|
164 | XtVaGetValues(select_list_widget, |
---|
165 | XmNvisibleItemCount, &vis, |
---|
166 | XmNtopItemPosition, &top, |
---|
167 | NULL); |
---|
168 | XmListSelectPos(select_list_widget, pos, False); |
---|
169 | |
---|
170 | if (pos < top) { |
---|
171 | if (pos > 1) pos --; |
---|
172 | XmListSetPos(select_list_widget, pos); |
---|
173 | } |
---|
174 | if (pos >= top + vis) { |
---|
175 | XmListSetBottomPos(select_list_widget, pos + 1); |
---|
176 | } |
---|
177 | } |
---|
178 | else { |
---|
179 | GBK_terminatef("Selection list '%s' has no default selection", variable_name); |
---|
180 | } |
---|
181 | } |
---|
182 | |
---|
183 | void AW_selection::refresh() { |
---|
184 | get_sellist()->clear(); |
---|
185 | fill(); |
---|
186 | get_sellist()->update(); |
---|
187 | } |
---|
188 | |
---|
189 | void AW_selection_list::clear() { |
---|
190 | /** Remove all items from the list. Default item is removed as well.*/ |
---|
191 | while (list_table) { |
---|
192 | AW_selection_list_entry *nextEntry = list_table->next; |
---|
193 | delete list_table; |
---|
194 | list_table = nextEntry; |
---|
195 | } |
---|
196 | list_table = NULL; |
---|
197 | last_of_list_table = NULL; |
---|
198 | |
---|
199 | delete_default(); |
---|
200 | } |
---|
201 | |
---|
202 | bool AW_selection_list::default_is_selected() const { |
---|
203 | const char *sel = get_selected_value(); |
---|
204 | if (!sel) { |
---|
205 | // (case should be impossible in gtk port) |
---|
206 | return true; // handle "nothing" like default |
---|
207 | } |
---|
208 | |
---|
209 | const char *def = get_default_value(); |
---|
210 | return def && (strcmp(sel, def) == 0); |
---|
211 | } |
---|
212 | |
---|
213 | const char *AW_selection_list::get_selected_value() const { |
---|
214 | int i; |
---|
215 | AW_selection_list_entry *lt; |
---|
216 | AW_selection_list_entry *found = 0; |
---|
217 | |
---|
218 | aw_assert(select_list_widget); |
---|
219 | |
---|
220 | for (i=1, lt = list_table; lt; i++, lt = lt->next) { |
---|
221 | lt->is_selected = XmListPosSelected(select_list_widget, i); |
---|
222 | if (lt->is_selected && !found) found = lt; |
---|
223 | } |
---|
224 | |
---|
225 | if (default_select) { |
---|
226 | default_select->is_selected = XmListPosSelected(select_list_widget, i); |
---|
227 | if (default_select->is_selected && !found) found = default_select; |
---|
228 | } |
---|
229 | return found ? found->value.get_string() : NULL; |
---|
230 | } |
---|
231 | |
---|
232 | AW_selection_list_entry *AW_selection_list::get_entry_at(int index) const { |
---|
233 | AW_selection_list_entry *entry = list_table; |
---|
234 | while (index && entry) { |
---|
235 | entry = entry->next; |
---|
236 | index--; |
---|
237 | } |
---|
238 | return entry; |
---|
239 | } |
---|
240 | |
---|
241 | |
---|
242 | void AW_selection_list::select_default() { |
---|
243 | set_awar_value(get_default_value()); |
---|
244 | } |
---|
245 | |
---|
246 | void AW_selection_list::delete_element_at(const int index) { |
---|
247 | if (index<0) return; |
---|
248 | |
---|
249 | AW_selection_list_entry *prev = NULL; |
---|
250 | if (index>0) { |
---|
251 | prev = get_entry_at(index-1); |
---|
252 | if (!prev) return; // invalid index |
---|
253 | } |
---|
254 | |
---|
255 | int selected_index = get_index_of_selected(); |
---|
256 | if (index == selected_index) select_default(); |
---|
257 | |
---|
258 | AW_selection_list_entry *toDel = prev ? prev->next : list_table; |
---|
259 | aw_assert(toDel != default_select); |
---|
260 | |
---|
261 | (prev ? prev->next : list_table) = toDel->next; |
---|
262 | delete toDel; |
---|
263 | |
---|
264 | if (last_of_list_table == toDel) last_of_list_table = prev; |
---|
265 | } |
---|
266 | |
---|
267 | void AW_selection_list::delete_value(const char *value) { |
---|
268 | int index = get_index_of(value); |
---|
269 | delete_element_at(index); |
---|
270 | } |
---|
271 | |
---|
272 | const char *AW_selection_list::get_awar_value() const { |
---|
273 | AW_awar *awar = AW_root::SINGLETON->awar(variable_name); |
---|
274 | return awar->read_char_pntr(); |
---|
275 | } |
---|
276 | |
---|
277 | char *AW_selection_list::get_content_as_string(long number_of_lines) { |
---|
278 | // number_of_lines == 0 -> print all |
---|
279 | |
---|
280 | AW_selection_list_entry *lt; |
---|
281 | GBS_strstruct *fd = GBS_stropen(10000); |
---|
282 | |
---|
283 | for (lt = list_table; lt; lt = lt->next) { |
---|
284 | number_of_lines--; |
---|
285 | GBS_strcat(fd, lt->get_displayed()); |
---|
286 | GBS_chrcat(fd, '\n'); |
---|
287 | if (!number_of_lines) break; |
---|
288 | } |
---|
289 | return GBS_strclose(fd); |
---|
290 | } |
---|
291 | |
---|
292 | const char *AW_selection_list::get_default_display() const { |
---|
293 | return default_select ? default_select->get_displayed() : NULL; |
---|
294 | } |
---|
295 | |
---|
296 | const char *AW_selection_list::get_default_value() const { |
---|
297 | return default_select ? default_select->value.get_string() : NULL; |
---|
298 | } |
---|
299 | |
---|
300 | int AW_selection_list::get_index_of(const char *searched_value) { |
---|
301 | /*! get index of an entry in the selection list |
---|
302 | * @return 0..n-1 index of matching element (or -1) |
---|
303 | */ |
---|
304 | int element_index = 0; |
---|
305 | for (AW_selection_list_iterator entry(this); entry; ++entry) { |
---|
306 | if (strcmp(entry.get_value(), searched_value) == 0) return element_index; |
---|
307 | ++element_index; |
---|
308 | } |
---|
309 | return -1; |
---|
310 | } |
---|
311 | |
---|
312 | int AW_selection_list::get_index_of_selected() { |
---|
313 | // returns index of element (or index of default) |
---|
314 | const char *awar_value = get_awar_value(); |
---|
315 | return get_index_of(awar_value); |
---|
316 | } |
---|
317 | |
---|
318 | void AW_selection_list::init_from_array(const CharPtrArray& entries, const char *default_displayed, const char *default_value) { |
---|
319 | // update selection list with contents of NULL-terminated array 'entries' |
---|
320 | // |
---|
321 | // 'default_displayed' and 'default_value' are used as default selection. |
---|
322 | // To position the default selection, add 'default_value' to 'entries' as well. |
---|
323 | // |
---|
324 | // awar value will be changed to 'defaultEntry' if it does not match any other entry |
---|
325 | // |
---|
326 | // Note: This works only with selection lists bound to AW_STRING awars. |
---|
327 | |
---|
328 | aw_assert(default_displayed); |
---|
329 | aw_assert(default_value); |
---|
330 | |
---|
331 | // use copies (just in case default_* points to a value free'd by clear()) |
---|
332 | char *defaultDispCopy = strdup(default_displayed); |
---|
333 | char *defaultValueCopy = strdup(default_value); |
---|
334 | |
---|
335 | bool defInserted = false; |
---|
336 | |
---|
337 | clear(); |
---|
338 | for (int i = 0; entries[i]; ++i) { |
---|
339 | if (!defInserted && strcmp(entries[i], defaultValueCopy) == 0) { |
---|
340 | insert_default(defaultDispCopy, defaultValueCopy); |
---|
341 | defInserted = true; |
---|
342 | } |
---|
343 | else { |
---|
344 | insert(entries[i], entries[i]); |
---|
345 | } |
---|
346 | } |
---|
347 | if (!defInserted) insert_default(defaultDispCopy, defaultValueCopy); |
---|
348 | update(); |
---|
349 | |
---|
350 | const char *selected = get_selected_value(); |
---|
351 | if (selected) set_awar_value(selected); |
---|
352 | |
---|
353 | free(defaultValueCopy); |
---|
354 | free(defaultDispCopy); |
---|
355 | } |
---|
356 | |
---|
357 | void AW_selection_list::insert(const char *displayed, const char *value) { |
---|
358 | if (variable_type != AW_STRING) { |
---|
359 | selection_type_mismatch("string"); |
---|
360 | return; |
---|
361 | } |
---|
362 | |
---|
363 | if (list_table) { |
---|
364 | last_of_list_table->next = new AW_selection_list_entry(displayed, value); |
---|
365 | last_of_list_table = last_of_list_table->next; |
---|
366 | last_of_list_table->next = NULL; |
---|
367 | } |
---|
368 | else { |
---|
369 | last_of_list_table = list_table = new AW_selection_list_entry(displayed, value); |
---|
370 | } |
---|
371 | } |
---|
372 | |
---|
373 | void AW_selection_list::delete_default() { |
---|
374 | /** Removes the default entry from the list*/ |
---|
375 | if (default_select) { |
---|
376 | delete default_select; |
---|
377 | default_select = NULL; |
---|
378 | } |
---|
379 | } |
---|
380 | |
---|
381 | void AW_selection_list::insert_default(const char *displayed, const char *value) { |
---|
382 | if (variable_type != AW_STRING) { |
---|
383 | selection_type_mismatch("string"); |
---|
384 | return; |
---|
385 | } |
---|
386 | if (default_select) delete_default(); |
---|
387 | default_select = new AW_selection_list_entry(displayed, value); |
---|
388 | } |
---|
389 | |
---|
390 | void AW_selection_list::insert(const char *displayed, int32_t value) { |
---|
391 | if (variable_type != AW_INT) { |
---|
392 | selection_type_mismatch("int"); |
---|
393 | return; |
---|
394 | } |
---|
395 | if (list_table) { |
---|
396 | last_of_list_table->next = new AW_selection_list_entry(displayed, value); |
---|
397 | last_of_list_table = last_of_list_table->next; |
---|
398 | last_of_list_table->next = NULL; |
---|
399 | } |
---|
400 | else { |
---|
401 | last_of_list_table = list_table = new AW_selection_list_entry(displayed, value); |
---|
402 | } |
---|
403 | } |
---|
404 | |
---|
405 | void AW_selection_list::insert_default(const char *displayed, int32_t value) { |
---|
406 | if (variable_type != AW_INT) { |
---|
407 | selection_type_mismatch("int"); |
---|
408 | return; |
---|
409 | } |
---|
410 | if (default_select) delete_default(); |
---|
411 | default_select = new AW_selection_list_entry(displayed, value); |
---|
412 | } |
---|
413 | |
---|
414 | |
---|
415 | void AW_selection_list::insert(const char *displayed, GBDATA *pointer) { |
---|
416 | if (variable_type != AW_POINTER) { |
---|
417 | selection_type_mismatch("pointer"); |
---|
418 | return; |
---|
419 | } |
---|
420 | if (list_table) { |
---|
421 | last_of_list_table->next = new AW_selection_list_entry(displayed, pointer); |
---|
422 | last_of_list_table = last_of_list_table->next; |
---|
423 | last_of_list_table->next = NULL; |
---|
424 | } |
---|
425 | else { |
---|
426 | last_of_list_table = list_table = new AW_selection_list_entry(displayed, pointer); |
---|
427 | } |
---|
428 | } |
---|
429 | |
---|
430 | void AW_selection_list::insert_default(const char *displayed, GBDATA *pointer) { |
---|
431 | if (variable_type != AW_POINTER) { |
---|
432 | selection_type_mismatch("pointer"); |
---|
433 | return; |
---|
434 | } |
---|
435 | if (default_select) delete_default(); |
---|
436 | default_select = new AW_selection_list_entry(displayed, pointer); |
---|
437 | } |
---|
438 | |
---|
439 | |
---|
440 | void AW_selection_list::move_content_to(AW_selection_list *target_list) { |
---|
441 | //! move all entries (despite default entry) to another AW_selection_list |
---|
442 | |
---|
443 | if (default_select) { |
---|
444 | char *defDisp = strdup(default_select->get_displayed()); |
---|
445 | char *defVal = strdup(default_select->value.get_string()); |
---|
446 | |
---|
447 | delete_default(); |
---|
448 | move_content_to(target_list); |
---|
449 | insert_default(defDisp, defVal); |
---|
450 | |
---|
451 | free(defVal); |
---|
452 | free(defDisp); |
---|
453 | } |
---|
454 | else { |
---|
455 | AW_selection_list_entry *entry = list_table; |
---|
456 | while (entry) { |
---|
457 | if (!target_list->list_table) { |
---|
458 | target_list->last_of_list_table = target_list->list_table = new AW_selection_list_entry(entry->get_displayed(), entry->value.get_string()); |
---|
459 | } |
---|
460 | else { |
---|
461 | target_list->last_of_list_table->next = new AW_selection_list_entry(entry->get_displayed(), entry->value.get_string()); |
---|
462 | target_list->last_of_list_table = target_list->last_of_list_table->next; |
---|
463 | target_list->last_of_list_table->next = NULL; |
---|
464 | } |
---|
465 | entry = entry->next; |
---|
466 | } |
---|
467 | clear(); |
---|
468 | } |
---|
469 | } |
---|
470 | |
---|
471 | void AW_selection_list::move_selection(int offset) { |
---|
472 | /*! move selection 'offset' position |
---|
473 | * offset == 1 -> select next element |
---|
474 | * offset == -1 -> select previous element |
---|
475 | */ |
---|
476 | |
---|
477 | int index = get_index_of_selected(); |
---|
478 | select_element_at(index+offset); |
---|
479 | } |
---|
480 | |
---|
481 | const char *AW_selection_list::get_value_at(int index) { |
---|
482 | // get value of the entry at position 'index' [0..n-1] of the 'selection_list' |
---|
483 | // returns NULL if index is out of bounds |
---|
484 | AW_selection_list_entry *entry = get_entry_at(index); |
---|
485 | return entry ? entry->value.get_string() : NULL; |
---|
486 | } |
---|
487 | |
---|
488 | void AW_selection_list::select_element_at(int wanted_index) { |
---|
489 | const char *wanted_value = get_value_at(wanted_index); |
---|
490 | |
---|
491 | if (!wanted_value) { |
---|
492 | wanted_value = get_default_value(); |
---|
493 | if (!wanted_value) wanted_value = ""; |
---|
494 | } |
---|
495 | |
---|
496 | set_awar_value(wanted_value); |
---|
497 | } |
---|
498 | |
---|
499 | void AW_selection_list::set_awar_value(const char *new_value) { |
---|
500 | AW_awar *awar = AW_root::SINGLETON->awar(variable_name); |
---|
501 | awar->write_string(new_value); |
---|
502 | } |
---|
503 | |
---|
504 | void AW_selection_list::set_file_suffix(const char *suffix) { |
---|
505 | AW_root *aw_root = AW_root::SINGLETON; |
---|
506 | char filter[200]; |
---|
507 | sprintf(filter, "tmp/save_box_sel_%li/filter", (long)this); |
---|
508 | aw_root->awar_string(filter, suffix); |
---|
509 | sprintf(filter, "tmp/load_box_sel_%li/filter", (long)this); |
---|
510 | aw_root->awar_string(filter, suffix); |
---|
511 | } |
---|
512 | |
---|
513 | size_t AW_selection_list::size() { |
---|
514 | AW_selection_list_entry *lt = list_table; |
---|
515 | size_t count = 0; |
---|
516 | |
---|
517 | while (lt) { |
---|
518 | ++count; |
---|
519 | lt = lt->next; |
---|
520 | } |
---|
521 | return count; |
---|
522 | } |
---|
523 | |
---|
524 | static int sel_sort_backward(const char *d1, const char *d2) { return strcmp(d2, d1); } |
---|
525 | static int sel_isort_backward(const char *d1, const char *d2) { return ARB_stricmp(d2, d1); } |
---|
526 | static int gb_compare_function__2__sellist_cmp_fun(const void *t1, const void *t2, void *v_selcmp) { |
---|
527 | sellist_cmp_fun selcmp = (sellist_cmp_fun)v_selcmp; |
---|
528 | return selcmp(static_cast<const AW_selection_list_entry*>(t1)->get_displayed(), |
---|
529 | static_cast<const AW_selection_list_entry*>(t2)->get_displayed()); |
---|
530 | } |
---|
531 | |
---|
532 | void AW_selection_list::sortCustom(sellist_cmp_fun cmp) { |
---|
533 | // WARNING: function behaves different in motif and gtk: |
---|
534 | // gtk also sorts default-element, motif always places default-element @ bottom |
---|
535 | size_t count = size(); |
---|
536 | if (count) { |
---|
537 | AW_selection_list_entry **tables = new AW_selection_list_entry *[count]; |
---|
538 | count = 0; |
---|
539 | for (AW_selection_list_entry *lt = list_table; lt; lt = lt->next) { |
---|
540 | tables[count++] = lt; |
---|
541 | } |
---|
542 | |
---|
543 | GB_sort((void**)tables, 0, count, gb_compare_function__2__sellist_cmp_fun, (void*)cmp); |
---|
544 | |
---|
545 | size_t i; |
---|
546 | for (i=0; i<count-1; i++) { |
---|
547 | tables[i]->next = tables[i+1]; |
---|
548 | } |
---|
549 | tables[i]->next = 0; |
---|
550 | list_table = tables[0]; |
---|
551 | last_of_list_table = tables[i]; |
---|
552 | |
---|
553 | delete [] tables; |
---|
554 | } |
---|
555 | } |
---|
556 | |
---|
557 | void AW_selection_list::sort(bool backward, bool case_sensitive) { |
---|
558 | // WARNING: function behaves different in motif and gtk: |
---|
559 | // gtk also sorts default-element, motif always places default-element @ bottom |
---|
560 | sellist_cmp_fun cmp; |
---|
561 | if (backward) { |
---|
562 | if (case_sensitive) cmp = sel_sort_backward; |
---|
563 | else cmp = sel_isort_backward; |
---|
564 | } |
---|
565 | else { |
---|
566 | if (case_sensitive) cmp = strcmp; |
---|
567 | else cmp = ARB_stricmp; |
---|
568 | } |
---|
569 | sortCustom(cmp); |
---|
570 | } |
---|
571 | |
---|
572 | void AW_selection_list::to_array(StrArray& array, bool values) { |
---|
573 | /*! read contents of selection list into an array. |
---|
574 | * @param values true->read values, false->read displayed strings |
---|
575 | * Use GBT_free_names() to free the result. |
---|
576 | * |
---|
577 | * Note: if 'values' is true, this function only works for string selection lists! |
---|
578 | */ |
---|
579 | |
---|
580 | array.reserve(size()); |
---|
581 | |
---|
582 | for (AW_selection_list_entry *lt = list_table; lt; lt = lt->next) { |
---|
583 | array.put(strdup(values ? lt->value.get_string() : lt->get_displayed())); |
---|
584 | } |
---|
585 | aw_assert(array.size() == size()); |
---|
586 | } |
---|
587 | |
---|
588 | GB_HASH *AW_selection_list::to_hash(bool case_sens) { |
---|
589 | // creates a hash (key = value of selection list, value = display string from selection list) |
---|
590 | // (Warning: changing the selection list will render the hash invalid!) |
---|
591 | |
---|
592 | GB_HASH *hash = GBS_create_hash(size(), case_sens ? GB_MIND_CASE : GB_IGNORE_CASE); |
---|
593 | |
---|
594 | for (AW_selection_list_entry *lt = list_table; lt; lt = lt->next) { |
---|
595 | GBS_write_hash(hash, lt->value.get_string(), (long)lt->get_displayed()); |
---|
596 | } |
---|
597 | |
---|
598 | return hash; |
---|
599 | } |
---|
600 | |
---|
601 | char *AW_selection_list_entry::copy_string_for_display(const char *str) { |
---|
602 | size_t len = strlen(str); |
---|
603 | bool tooLong = len>MAX_DISPLAY_LENGTH; |
---|
604 | char *out; |
---|
605 | if (tooLong) { |
---|
606 | out = GB_strndup(str, MAX_DISPLAY_LENGTH); |
---|
607 | { // add message about truncation |
---|
608 | char *truncated = GBS_global_string_copy(" <truncated - original contains %zu byte>", len); |
---|
609 | size_t tlen = strlen(truncated); |
---|
610 | aw_assert(MAX_DISPLAY_LENGTH>tlen); |
---|
611 | memcpy(out+MAX_DISPLAY_LENGTH-tlen, truncated, tlen); |
---|
612 | } |
---|
613 | len = MAX_DISPLAY_LENGTH; |
---|
614 | } |
---|
615 | else { |
---|
616 | out = GB_strduplen(str, len); |
---|
617 | } |
---|
618 | |
---|
619 | for (size_t i = 0; i<len; ++i) { |
---|
620 | switch (out[i]) { |
---|
621 | case ',': out[i] = ';'; break; |
---|
622 | case '\n': out[i] = '#'; break; |
---|
623 | } |
---|
624 | } |
---|
625 | return out; |
---|
626 | } |
---|
627 | |
---|
628 | // ------------------------- |
---|
629 | // AW_DB_selection |
---|
630 | |
---|
631 | static void AW_DB_selection_refresh_cb(GBDATA *, AW_DB_selection *selection) { |
---|
632 | selection->refresh(); |
---|
633 | } |
---|
634 | |
---|
635 | AW_DB_selection::AW_DB_selection(AW_selection_list *sellist_, GBDATA *gbd_) |
---|
636 | : AW_selection(sellist_) |
---|
637 | , gbd(gbd_) |
---|
638 | { |
---|
639 | GB_transaction ta(gbd); |
---|
640 | GB_add_callback(gbd, GB_CB_CHANGED, makeDatabaseCallback(AW_DB_selection_refresh_cb, this)); |
---|
641 | } |
---|
642 | |
---|
643 | AW_DB_selection::~AW_DB_selection() { |
---|
644 | GB_transaction ta(gbd); |
---|
645 | GB_remove_callback(gbd, GB_CB_CHANGED, makeDatabaseCallback(AW_DB_selection_refresh_cb, this)); |
---|
646 | } |
---|
647 | |
---|
648 | GBDATA *AW_DB_selection::get_gb_main() { |
---|
649 | return GB_get_root(gbd); |
---|
650 | } |
---|
651 | |
---|
652 | // -------------------------------------------------------------------------------- |
---|
653 | |
---|
654 | #ifdef UNIT_TESTS |
---|
655 | #ifndef TEST_UNIT_H |
---|
656 | #include <test_unit.h> |
---|
657 | #endif |
---|
658 | |
---|
659 | #define TEST_LIST_CONTENT(list,values,expected) do { \ |
---|
660 | StrArray a; \ |
---|
661 | (list).to_array(a, values); \ |
---|
662 | char *str = GBT_join_strings(a, ';'); \ |
---|
663 | TEST_EXPECT_EQUAL(str, expected); \ |
---|
664 | free(str); \ |
---|
665 | } while(0) |
---|
666 | |
---|
667 | #define TEST_GET_LIST_CONTENT(list,expected) do { \ |
---|
668 | char *str = (list).get_content_as_string(10); \ |
---|
669 | TEST_EXPECT_EQUAL(str, expected); \ |
---|
670 | free(str); \ |
---|
671 | } while(0) |
---|
672 | |
---|
673 | void TEST_selection_list_access() { |
---|
674 | #if defined(ARB_GTK) |
---|
675 | AW_selection_list list0(NULL, false); |
---|
676 | AW_selection_list list1(NULL, false); |
---|
677 | AW_selection_list list2(NULL, false); |
---|
678 | #else // ARB_MOTIF |
---|
679 | AW_selection_list list0("bla", GB_STRING, NULL); |
---|
680 | AW_selection_list list1("bla", GB_STRING, NULL); |
---|
681 | AW_selection_list list2("alb", GB_STRING, NULL); |
---|
682 | #endif |
---|
683 | |
---|
684 | list0.insert_default("First", "1st"); |
---|
685 | list0.insert("Second", "2nd"); |
---|
686 | list0.insert("Third", "3rd"); |
---|
687 | |
---|
688 | list1.insert("First", "1st"); |
---|
689 | list1.insert_default("Second", "2nd"); |
---|
690 | list1.insert("Third", "3rd"); |
---|
691 | |
---|
692 | list2.insert("First", "1st"); |
---|
693 | list2.insert("Second", "2nd"); |
---|
694 | list2.insert("Third", "3rd"); |
---|
695 | list2.insert_default("Default", ""); |
---|
696 | |
---|
697 | TEST_EXPECT_EQUAL(list0.size(), 2); |
---|
698 | TEST_EXPECT_EQUAL(list1.size(), 2); |
---|
699 | TEST_EXPECT_EQUAL(list2.size(), 3); |
---|
700 | |
---|
701 | TEST_EXPECT_EQUAL(list1.get_default_value(), "2nd"); |
---|
702 | TEST_EXPECT_EQUAL(list1.get_default_display(), "Second"); |
---|
703 | |
---|
704 | TEST_EXPECT_EQUAL(list0.get_index_of("1st"), -1); // default value is not indexed |
---|
705 | TEST_EXPECT_EQUAL(list0.get_index_of("2nd"), 0); |
---|
706 | TEST_EXPECT_EQUAL(list0.get_index_of("3rd"), 1); // = second non-default entry |
---|
707 | |
---|
708 | TEST_EXPECT_EQUAL(list1.get_index_of("1st"), 0); |
---|
709 | TEST_EXPECT_EQUAL(list1.get_index_of("2nd"), -1); // default value is not indexed |
---|
710 | TEST_EXPECT_EQUAL(list1.get_index_of("3rd"), 1); // = second non-default entry |
---|
711 | |
---|
712 | TEST_EXPECT_EQUAL(list2.get_index_of("1st"), 0); |
---|
713 | TEST_EXPECT_EQUAL(list2.get_index_of("2nd"), 1); |
---|
714 | TEST_EXPECT_EQUAL(list2.get_index_of("3rd"), 2); |
---|
715 | |
---|
716 | |
---|
717 | TEST_EXPECT_EQUAL(list0.get_value_at(0), "2nd"); |
---|
718 | TEST_EXPECT_EQUAL(list0.get_value_at(1), "3rd"); |
---|
719 | TEST_EXPECT_NULL(list0.get_value_at(2)); |
---|
720 | |
---|
721 | TEST_EXPECT_EQUAL(list1.get_value_at(0), "1st"); |
---|
722 | TEST_EXPECT_EQUAL(list1.get_value_at(1), "3rd"); |
---|
723 | TEST_EXPECT_NULL(list1.get_value_at(2)); |
---|
724 | |
---|
725 | TEST_EXPECT_EQUAL(list2.get_value_at(0), "1st"); |
---|
726 | TEST_EXPECT_EQUAL(list2.get_value_at(1), "2nd"); |
---|
727 | TEST_EXPECT_EQUAL(list2.get_value_at(2), "3rd"); |
---|
728 | TEST_EXPECT_NULL(list2.get_value_at(3)); |
---|
729 | |
---|
730 | TEST_LIST_CONTENT(list1, true, "1st;3rd"); |
---|
731 | TEST_LIST_CONTENT(list1, false, "First;Third"); |
---|
732 | TEST_GET_LIST_CONTENT(list1, "First\nThird\n"); |
---|
733 | |
---|
734 | TEST_LIST_CONTENT(list2, true, "1st;2nd;3rd"); |
---|
735 | TEST_LIST_CONTENT(list2, false, "First;Second;Third"); |
---|
736 | TEST_GET_LIST_CONTENT(list2, "First\nSecond\nThird\n"); |
---|
737 | |
---|
738 | { |
---|
739 | AW_selection_list_iterator iter1(&list1); |
---|
740 | AW_selection_list_iterator iter2(&list2); |
---|
741 | |
---|
742 | TEST_EXPECT(bool(iter1)); |
---|
743 | TEST_EXPECT(bool(iter2)); |
---|
744 | |
---|
745 | TEST_EXPECT_EQUAL(iter1.get_displayed(), "First"); |
---|
746 | TEST_EXPECT_EQUAL(iter2.get_displayed(), "First"); |
---|
747 | |
---|
748 | TEST_EXPECT_EQUAL(iter1.get_value(), "1st"); |
---|
749 | TEST_EXPECT_EQUAL(iter2.get_value(), "1st"); |
---|
750 | |
---|
751 | ++iter1; |
---|
752 | ++iter2; |
---|
753 | |
---|
754 | TEST_EXPECT(bool(iter1)); |
---|
755 | TEST_EXPECT(bool(iter2)); |
---|
756 | |
---|
757 | TEST_EXPECT_EQUAL(iter1.get_displayed(), "Third"); |
---|
758 | TEST_EXPECT_EQUAL(iter2.get_displayed(), "Second"); |
---|
759 | |
---|
760 | TEST_EXPECT_EQUAL(iter1.get_value(), "3rd"); |
---|
761 | TEST_EXPECT_EQUAL(iter2.get_value(), "2nd"); |
---|
762 | |
---|
763 | ++iter1; |
---|
764 | ++iter2; |
---|
765 | |
---|
766 | TEST_REJECT(bool(iter1)); |
---|
767 | TEST_EXPECT(bool(iter2)); |
---|
768 | } |
---|
769 | |
---|
770 | { |
---|
771 | #if defined(ARB_GTK) |
---|
772 | AW_selection_list copy1(NULL, false); |
---|
773 | AW_selection_list copy2(NULL, false); |
---|
774 | #else // ARB_MOTIF |
---|
775 | AW_selection_list copy1("c1", GB_STRING, NULL); |
---|
776 | AW_selection_list copy2("c2", GB_STRING, NULL); |
---|
777 | #endif |
---|
778 | |
---|
779 | list1.move_content_to(©1); |
---|
780 | list2.move_content_to(©2); |
---|
781 | |
---|
782 | TEST_EXPECT_EQUAL(list1.size(), 0); |
---|
783 | TEST_EXPECT_EQUAL(list2.size(), 0); |
---|
784 | |
---|
785 | TEST_LIST_CONTENT(copy1, true, "1st;3rd"); |
---|
786 | TEST_LIST_CONTENT(copy2, true, "1st;2nd;3rd"); |
---|
787 | } |
---|
788 | } |
---|
789 | TEST_PUBLISH(TEST_selection_list_access); |
---|
790 | |
---|
791 | #endif // UNIT_TESTS |
---|
792 | |
---|
793 | // -------------------------------------------------------------------------------- |
---|
794 | |
---|