source: tags/arb-6.0/WINDOW/AW_select.cxx

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