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