ui/gtk/list.c

changeset 109
c3dfcb8f0be7
parent 108
77254bd6dccb
child 110
c00e968d018b
equal deleted inserted replaced
108:77254bd6dccb 109:c3dfcb8f0be7
37 37
38 #include <cx/array_list.h> 38 #include <cx/array_list.h>
39 #include <cx/linked_list.h> 39 #include <cx/linked_list.h>
40 40
41 #include "list.h" 41 #include "list.h"
42 #include "button.h"
42 #include "icon.h" 43 #include "icon.h"
43 #include "menu.h" 44 #include "menu.h"
44 #include "dnd.h" 45 #include "dnd.h"
45 46
46 47
47 void* ui_strmodel_getvalue(void *elm, int column) { 48 static void* getvalue_wrapper(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
48 return column == 0 ? elm : NULL; 49 ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
49 } 50 return getvalue(elm, col);
50 51 }
51 static void* model_getvalue(UiModel *model, UiList *list, void *elm, int row, int col, UiBool *freeResult) { 52
52 if(model->getvalue2) { 53 static void* str_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
53 return model->getvalue2(list, elm, row, col, model->getvalue2data, freeResult); 54 return elm;
54 } else if(model->getvalue) { 55 }
55 return model->getvalue(elm, col); 56
56 } 57 static void* null_getvalue(UiList *list, void *elm, int row, int col, void *userdata, UiBool *freeResult) {
57 return NULL; 58 return NULL;
58 } 59 }
59 60
60 /* 61 /*
61 static GtkTargetEntry targetentries[] = 62 static GtkTargetEntry targetentries[] =
70 listview->elements = calloc(nelm, sizeof(char*)); 71 listview->elements = calloc(nelm, sizeof(char*));
71 listview->nelm = nelm; 72 listview->nelm = nelm;
72 for(int i=0;i<nelm;i++) { 73 for(int i=0;i<nelm;i++) {
73 listview->elements[i] = strdup(elm[i]); 74 listview->elements[i] = strdup(elm[i]);
74 } 75 }
76 }
77
78 static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
79 UiListView *tableview = malloc(sizeof(UiListView));
80 memset(tableview, 0, sizeof(UiListView));
81 tableview->obj = obj;
82 tableview->model = args->model;
83 tableview->onactivate = args->onactivate;
84 tableview->onactivatedata = args->onactivatedata;
85 tableview->onselection = args->onselection;
86 tableview->onselectiondata = args->onselectiondata;
87 tableview->ondragstart = args->ondragstart;
88 tableview->ondragstartdata = args->ondragstartdata;
89 tableview->ondragcomplete = args->ondragcomplete;
90 tableview->ondragcompletedata = args->ondragcompletedata;
91 tableview->ondrop = args->ondrop;
92 tableview->ondropdata = args->ondropdata;
93 tableview->selection.count = 0;
94 tableview->selection.rows = NULL;
95
96 if(args->getvalue2) {
97 tableview->getvalue = args->getvalue2;
98 tableview->getvaluedata = args->getvalue2data;
99 } else if(args->getvalue) {
100 tableview->getvalue = getvalue_wrapper;
101 tableview->getvaluedata = (void*)args->getvalue;
102 } else {
103 tableview->getvalue = null_getvalue;
104 }
105
106 return tableview;
75 } 107 }
76 108
77 #if GTK_CHECK_VERSION(4, 10, 0) 109 #if GTK_CHECK_VERSION(4, 10, 0)
78 110
79 111
129 gtk_label_set_xalign(GTK_LABEL(label), 0); 161 gtk_label_set_xalign(GTK_LABEL(label), 0);
130 gtk_list_item_set_child(item, label); 162 gtk_list_item_set_child(item, label);
131 } 163 }
132 } 164 }
133 165
134 static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { 166 static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
135 UiColData *col = userdata; 167 UiColData *col = userdata;
136 UiList *list = col->listview->var ? col->listview->var->value : NULL; 168 UiList *list = col->listview->var ? col->listview->var->value : NULL;
169 UiListView *listview = col->listview;
137 170
138 ObjWrapper *obj = gtk_list_item_get_item(item); 171 ObjWrapper *obj = gtk_list_item_get_item(item);
139 UiModel *model = col->listview->model; 172 UiModel *model = col->listview->model;
140 UiModelType type = model->types[col->model_column]; 173 UiModelType type = model->types[col->model_column];
141 174
142 UiBool freevalue = FALSE; 175 UiBool freevalue = FALSE;
143 void *data = model_getvalue(model, list, obj->data, obj->i, col->data_column, &freevalue); 176 void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
144 GtkWidget *child = gtk_list_item_get_child(item); 177 GtkWidget *child = gtk_list_item_get_child(item);
145 178
146 switch(type) { 179 switch(type) {
147 case UI_STRING_FREE: { 180 case UI_STRING_FREE: {
148 freevalue = TRUE; 181 freevalue = TRUE;
170 } 203 }
171 case UI_ICON_TEXT: { 204 case UI_ICON_TEXT: {
172 205
173 } 206 }
174 case UI_ICON_TEXT_FREE: { 207 case UI_ICON_TEXT_FREE: {
175 void *data2 = model_getvalue(model, list, obj->data, obj->i, col->data_column+1, &freevalue); 208 void *data2 = listview->getvalue(list, obj->data, obj->i, col->data_column+1, listview->getvaluedata, &freevalue);
176 if(type == UI_ICON_TEXT_FREE) { 209 if(type == UI_ICON_TEXT_FREE) {
177 freevalue = TRUE; 210 freevalue = TRUE;
178 } 211 }
179 GtkWidget *image = g_object_get_data(G_OBJECT(child), "image"); 212 GtkWidget *image = g_object_get_data(G_OBJECT(child), "image");
180 GtkWidget *label = g_object_get_data(G_OBJECT(child), "label"); 213 GtkWidget *label = g_object_get_data(G_OBJECT(child), "label");
197 GtkSelectionModel *selection_model; 230 GtkSelectionModel *selection_model;
198 if(multiselection) { 231 if(multiselection) {
199 selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore))); 232 selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore)));
200 } else { 233 } else {
201 selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore))); 234 selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(liststore)));
235 gtk_single_selection_set_can_unselect(GTK_SINGLE_SELECTION(selection_model), TRUE);
236 gtk_single_selection_set_autoselect(GTK_SINGLE_SELECTION(selection_model), FALSE);
202 } 237 }
203 g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview); 238 g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), listview);
204 return selection_model; 239 return selection_model;
205 }
206
207 static UiListView* create_listview(UiObject *obj, UiListArgs *args) {
208 UiListView *tableview = malloc(sizeof(UiListView));
209 memset(tableview, 0, sizeof(UiListView));
210 tableview->obj = obj;
211 tableview->model = args->model;
212 tableview->onactivate = args->onactivate;
213 tableview->onactivatedata = args->onactivatedata;
214 tableview->onselection = args->onselection;
215 tableview->onselectiondata = args->onselectiondata;
216 tableview->ondragstart = args->ondragstart;
217 tableview->ondragstartdata = args->ondragstartdata;
218 tableview->ondragcomplete = args->ondragcomplete;
219 tableview->ondragcompletedata = args->ondragcompletedata;
220 tableview->ondrop = args->ondrop;
221 tableview->ondropdata = args->ondropdata;
222 tableview->selection.count = 0;
223 tableview->selection.rows = NULL;
224 return tableview;
225 } 240 }
226 241
227 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) { 242 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
228 UiObject* current = uic_current_obj(obj); 243 UiObject* current = uic_current_obj(obj);
229 244
230 // to simplify things and share code with ui_table_create, we also 245 // to simplify things and share code with ui_table_create, we also
231 // use a UiModel for the listview 246 // use a UiModel for the listview
232 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 247 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
233 if(args->getvalue2) {
234 model->getvalue2 = args->getvalue2;
235 model->getvalue2data = args->getvalue2data;
236 } else if(args->getvalue) {
237 model->getvalue = args->getvalue;
238 } else {
239 model->getvalue = ui_strmodel_getvalue;
240 }
241 args->model = model; 248 args->model = model;
242 249
243 GListStore *ls = g_list_store_new(G_TYPE_OBJECT); 250 GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
244 UiListView *listview = create_listview(obj, args); 251 UiListView *listview = create_listview(obj, args);
252 if(!args->getvalue && !args->getvalue2) {
253 listview->getvalue = str_getvalue;
254 }
245 255
246 listview->columns = malloc(sizeof(UiColData)); 256 listview->columns = malloc(sizeof(UiColData));
247 listview->columns->listview = listview; 257 listview->columns->listview = listview;
248 listview->columns->data_column = 0; 258 listview->columns->data_column = 0;
249 listview->columns->model_column = 0; 259 listview->columns->model_column = 0;
250 260
251 GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); 261 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
252 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); 262 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
253 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); 263 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
254 264
255 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection); 265 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
278 list->setselection = ui_listview_setselection2; 288 list->setselection = ui_listview_setselection2;
279 289
280 ui_update_liststore(ls, list); 290 ui_update_liststore(ls, list);
281 } else if (args->static_elements && args->static_nelm > 0) { 291 } else if (args->static_elements && args->static_nelm > 0) {
282 listview_copy_static_elements(listview, args->static_elements, args->static_nelm); 292 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
283 listview->model->getvalue = ui_strmodel_getvalue; // force strmodel 293 listview->getvalue = str_getvalue; // force string values
284 ui_update_liststore_static(ls, listview->elements, listview->nelm); 294 ui_update_liststore_static(ls, listview->elements, listview->nelm);
285 } 295 }
286 296
287 // event handling 297 // event handling
288 if(args->onactivate) { 298 if(args->onactivate) {
318 UiObject* current = uic_current_obj(obj); 328 UiObject* current = uic_current_obj(obj);
319 329
320 // to simplify things and share code with ui_tableview_create, we also 330 // to simplify things and share code with ui_tableview_create, we also
321 // use a UiModel for the listview 331 // use a UiModel for the listview
322 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 332 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
323 if(args->getvalue2) {
324 model->getvalue2 = args->getvalue2;
325 model->getvalue2data = args->getvalue2data;
326 } else if(args->getvalue) {
327 model->getvalue = args->getvalue;
328 } else {
329 model->getvalue = ui_strmodel_getvalue;
330 }
331 args->model = model; 333 args->model = model;
332 334
333 GListStore *ls = g_list_store_new(G_TYPE_OBJECT); 335 GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
334 UiListView *listview = create_listview(obj, args); 336 UiListView *listview = create_listview(obj, args);
337
338 if(!args->getvalue && !args->getvalue2) {
339 listview->getvalue = str_getvalue;
340 }
335 341
336 listview->columns = malloc(sizeof(UiColData)); 342 listview->columns = malloc(sizeof(UiColData));
337 listview->columns->listview = listview; 343 listview->columns->listview = listview;
338 listview->columns->data_column = 0; 344 listview->columns->data_column = 0;
339 listview->columns->model_column = 0; 345 listview->columns->model_column = 0;
368 list->setselection = ui_combobox_setselection; 374 list->setselection = ui_combobox_setselection;
369 375
370 ui_update_liststore(ls, list); 376 ui_update_liststore(ls, list);
371 } else if (args->static_elements && args->static_nelm > 0) { 377 } else if (args->static_elements && args->static_nelm > 0) {
372 listview_copy_static_elements(listview, args->static_elements, args->static_nelm); 378 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
373 listview->model->getvalue = ui_strmodel_getvalue; // force strmodel 379 listview->getvalue = str_getvalue; // force string values
374 ui_update_liststore_static(ls, listview->elements, listview->nelm); 380 ui_update_liststore_static(ls, listview->elements, listview->nelm);
375 } 381 }
376 382
377 // event handling 383 // event handling
378 if(args->onactivate) { 384 if(args->onactivate) {
624 ui_update_liststore(view->liststore, list); 630 ui_update_liststore(view->liststore, list);
625 } else { 631 } else {
626 void *value = list->get(list, i); 632 void *value = list->get(list, i);
627 if(value) { 633 if(value) {
628 ObjWrapper *obj = obj_wrapper_new(value, i); 634 ObjWrapper *obj = obj_wrapper_new(value, i);
635 UiListSelection sel = list->getselection(list);
629 // TODO: if index i is selected, the selection is lost 636 // TODO: if index i is selected, the selection is lost
630 // is it possible to update the item without removing it? 637 // is it possible to update the item without removing it?
638 // workaround: save selection and reapply it
631 int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore)); 639 int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
632 if(count <= i) { 640 if(count <= i) {
633 g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1); 641 g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1);
634 } else { 642 } else {
635 g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1); 643 g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1);
636 } 644 }
645 if(sel.count > 0) {
646 list->setselection(list, sel);
647 }
648 ui_listselection_free(sel);
637 } 649 }
638 } 650 }
639 } 651 }
640 652
641 UiListSelection ui_listview_getselection2(UiList *list) { 653 UiListSelection ui_listview_getselection2(UiList *list) {
693 ui_setop_enable(FALSE); 705 ui_setop_enable(FALSE);
694 } 706 }
695 707
696 #else 708 #else
697 709
698 static void update_list_row(GtkListStore *store, GtkTreeIter *iter, UiModel *model, UiList *list, void *elm, int row) { 710 static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) {
711 UiModel *model = listview->model;
699 // set column values 712 // set column values
700 int c = 0; 713 int c = 0;
701 for(int i=0;i<model->columns;i++,c++) { 714 for(int i=0;i<model->columns;i++,c++) {
702 UiBool freevalue = FALSE; 715 UiBool freevalue = FALSE;
703 void *data = model_getvalue(model, list, elm, row, c, &freevalue); 716 void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
704 717
705 GValue value = G_VALUE_INIT; 718 GValue value = G_VALUE_INIT;
706 switch(model->types[i]) { 719 switch(model->types[i]) {
707 case UI_STRING_FREE: { 720 case UI_STRING_FREE: {
708 freevalue = TRUE; 721 freevalue = TRUE;
763 } 776 }
764 #endif 777 #endif
765 c++; 778 c++;
766 779
767 freevalue = FALSE; 780 freevalue = FALSE;
768 char *str = model_getvalue(model, list, elm, row, c, &freevalue); 781 char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
769 g_value_init(&value, G_TYPE_STRING); 782 g_value_init(&value, G_TYPE_STRING);
770 g_value_set_string(&value, str); 783 g_value_set_string(&value, str);
771 if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) { 784 if(model->types[i] == UI_ICON_TEXT_FREE || freevalue) {
772 free(str); 785 free(str);
773 } 786 }
777 790
778 gtk_list_store_set_value(store, iter, c, &value); 791 gtk_list_store_set_value(store, iter, c, &value);
779 } 792 }
780 } 793 }
781 794
782 static GtkListStore* create_list_store(UiList *list, UiModel *model) { 795 static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
796 UiModel *model = listview->model;
783 int columns = model->columns; 797 int columns = model->columns;
784 GType types[2*columns]; 798 GType types[2*columns];
785 int c = 0; 799 int c = 0;
786 for(int i=0;i<columns;i++,c++) { 800 for(int i=0;i<columns;i++,c++) {
787 switch(model->types[i]) { 801 switch(model->types[i]) {
805 while(elm) { 819 while(elm) {
806 // insert new row 820 // insert new row
807 GtkTreeIter iter; 821 GtkTreeIter iter;
808 gtk_list_store_insert (store, &iter, -1); 822 gtk_list_store_insert (store, &iter, -1);
809 823
810 update_list_row(store, &iter, model, list, elm, i++); 824 update_list_row(listview, store, &iter, list, elm, i++);
811 825
812 // next row 826 // next row
813 elm = list->next(list); 827 elm = list->next(list);
814 } 828 }
815 } 829 }
839 #else 853 #else
840 // TODO: implement for gtk2 854 // TODO: implement for gtk2
841 #endif 855 #endif
842 856
843 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 857 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
844 if(args->getvalue2) { 858
845 model->getvalue2 = args->getvalue2; 859 UiListView *listview = create_listview(obj, args);
846 model->getvalue2data = args->getvalue2data; 860 if(!args->getvalue && !args->getvalue2) {
847 } else if(args->getvalue) { 861 listview->getvalue = str_getvalue;
848 model->getvalue = args->getvalue; 862 }
849 } else {
850 model->getvalue = ui_strmodel_getvalue;
851 }
852
853 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
854
855 UiList *list = var ? var->value : NULL;
856 GtkListStore *listmodel = create_list_store(list, model);
857 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
858 g_object_unref(listmodel);
859
860 UiListView *listview = malloc(sizeof(UiListView));
861 memset(listview, 0, sizeof(UiListView));
862 listview->obj = obj;
863 listview->widget = view;
864 listview->var = var;
865 listview->model = model; 863 listview->model = model;
866 listview->selection.count = 0;
867 listview->selection.rows = NULL;
868 g_signal_connect( 864 g_signal_connect(
869 view, 865 view,
870 "destroy", 866 "destroy",
871 G_CALLBACK(ui_listview_destroy), 867 G_CALLBACK(ui_listview_destroy),
872 listview); 868 listview);
869
870 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
871
872 // init listview
873 listview->widget = view;
874 listview->var = var;
875
876 UiList *list = var ? var->value : NULL;
877 GtkListStore *listmodel = create_list_store(listview, list);
878 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
879 g_object_unref(listmodel);
873 880
874 // bind var 881 // bind var
875 list->update = ui_listview_update; 882 list->update = ui_listview_update;
876 list->getselection = ui_listview_getselection; 883 list->getselection = ui_listview_getselection;
877 list->setselection = ui_listview_setselection; 884 list->setselection = ui_listview_setselection;
919 GTK_POLICY_AUTOMATIC, 926 GTK_POLICY_AUTOMATIC,
920 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS 927 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
921 SCROLLEDWINDOW_SET_CHILD(scroll_area, view); 928 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
922 929
923 UI_APPLY_LAYOUT2(current, args); 930 UI_APPLY_LAYOUT2(current, args);
924 current->container->add(current->container, scroll_area, FALSE); 931 current->container->add(current->container, scroll_area);
925 932
926 // ct->current should point to view, not scroll_area, to make it possible 933 // ct->current should point to view, not scroll_area, to make it possible
927 // to add a context menu 934 // to add a context menu
928 current->container->current = view; 935 current->container->current = view;
929 936
1004 1011
1005 #endif 1012 #endif
1006 1013
1007 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST); 1014 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
1008 1015
1009 UiList *list = var ? var->value : NULL;
1010 GtkListStore *listmodel = create_list_store(list, model);
1011 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
1012 g_object_unref(listmodel);
1013
1014 //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL); 1016 //g_signal_connect(view, "drag-begin", G_CALLBACK(drag_begin), NULL);
1015 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); 1017 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL);
1016 1018
1017 // add TreeView as observer to the UiList to update the TreeView if the 1019 // add TreeView as observer to the UiList to update the TreeView if the
1018 // data changes 1020 // data changes
1019 UiListView *tableview = malloc(sizeof(UiListView)); 1021 UiListView *tableview = create_listview(obj, args);
1020 memset(tableview, 0, sizeof(UiListView));
1021 tableview->obj = obj;
1022 tableview->widget = view;
1023 tableview->var = var;
1024 tableview->model = model;
1025 tableview->ondragstart = args->ondragstart;
1026 tableview->ondragstartdata = args->ondragstartdata;
1027 tableview->ondragcomplete = args->ondragcomplete;
1028 tableview->ondragcompletedata = args->ondragcompletedata;
1029 tableview->ondrop = args->ondrop;
1030 tableview->ondropdata = args->ondropdata;
1031 tableview->selection.count = 0;
1032 tableview->selection.rows = NULL;
1033 g_signal_connect( 1022 g_signal_connect(
1034 view, 1023 view,
1035 "destroy", 1024 "destroy",
1036 G_CALLBACK(ui_listview_destroy), 1025 G_CALLBACK(ui_listview_destroy),
1037 tableview); 1026 tableview);
1027
1028 UiList *list = var ? var->value : NULL;
1029 GtkListStore *listmodel = create_list_store(tableview, list);
1030 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(listmodel));
1031 g_object_unref(listmodel);
1038 1032
1039 // bind var 1033 // bind var
1040 list->update = ui_listview_update; 1034 list->update = ui_listview_update;
1041 list->getselection = ui_listview_getselection; 1035 list->getselection = ui_listview_getselection;
1042 list->setselection = ui_listview_setselection; 1036 list->setselection = ui_listview_setselection;
1096 ui_widget_set_contextmenu(view, menu); 1090 ui_widget_set_contextmenu(view, menu);
1097 #endif 1091 #endif
1098 } 1092 }
1099 1093
1100 UI_APPLY_LAYOUT2(current, args); 1094 UI_APPLY_LAYOUT2(current, args);
1101 current->container->add(current->container, scroll_area, FALSE); 1095 current->container->add(current->container, scroll_area);
1102 1096
1103 // ct->current should point to view, not scroll_area, to make it possible 1097 // ct->current should point to view, not scroll_area, to make it possible
1104 // to add a context menu 1098 // to add a context menu
1105 current->container->current = view; 1099 current->container->current = view;
1106 1100
1110 1104
1111 1105
1112 void ui_listview_update(UiList *list, int i) { 1106 void ui_listview_update(UiList *list, int i) {
1113 UiListView *view = list->obj; 1107 UiListView *view = list->obj;
1114 if(i < 0) { 1108 if(i < 0) {
1115 GtkListStore *store = create_list_store(list, view->model); 1109 GtkListStore *store = create_list_store(view, list);
1116 gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store)); 1110 gtk_tree_view_set_model(GTK_TREE_VIEW(view->widget), GTK_TREE_MODEL(store));
1117 g_object_unref(G_OBJECT(store)); 1111 g_object_unref(G_OBJECT(store));
1118 } else { 1112 } else {
1119 void *elm = list->get(list, i); 1113 void *elm = list->get(list, i);
1120 GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget)); 1114 GtkTreeModel *store = gtk_tree_view_get_model(GTK_TREE_VIEW(view->widget));
1121 GtkTreeIter iter; 1115 GtkTreeIter iter;
1122 if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) { 1116 if(gtk_tree_model_iter_nth_child(store, &iter, NULL, i)) {
1123 update_list_row(GTK_LIST_STORE(store), &iter, view->model, list, elm, i); 1117 update_list_row(view, GTK_LIST_STORE(store), &iter, list, elm, i);
1124 } 1118 }
1125 } 1119 }
1126 } 1120 }
1127 1121
1128 UiListSelection ui_listview_getselection(UiList *list) { 1122 UiListSelection ui_listview_getselection(UiList *list) {
1148 /* --------------------------- ComboBox --------------------------- */ 1142 /* --------------------------- ComboBox --------------------------- */
1149 1143
1150 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) { 1144 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs *args) {
1151 UiObject* current = uic_current_obj(obj); 1145 UiObject* current = uic_current_obj(obj);
1152 1146
1153 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 1147 GtkWidget *combobox = gtk_combo_box_new();
1154 if(args->getvalue2) { 1148
1155 model->getvalue2 = args->getvalue2;
1156 model->getvalue2data = args->getvalue2data;
1157 } else if(args->getvalue) {
1158 model->getvalue = args->getvalue;
1159 } else {
1160 model->getvalue = ui_strmodel_getvalue;
1161 }
1162
1163 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
1164
1165 GtkWidget *combobox = ui_create_combobox(obj, model, var, args->static_elements, args->static_nelm, args->onactivate, args->onactivatedata);
1166 ui_set_name_and_style(combobox, args->name, args->style_class); 1149 ui_set_name_and_style(combobox, args->name, args->style_class);
1167 ui_set_widget_groups(obj->ctx, combobox, args->groups); 1150 ui_set_widget_groups(obj->ctx, combobox, args->groups);
1168 UI_APPLY_LAYOUT2(current, args); 1151 UI_APPLY_LAYOUT2(current, args);
1169 current->container->add(current->container, combobox, FALSE); 1152 current->container->add(current->container, combobox);
1170 current->container->current = combobox; 1153 current->container->current = combobox;
1171 return combobox; 1154
1172 } 1155 UiListView *listview = create_listview(obj, args);
1173 1156 listview->widget = combobox;
1174 GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, char **elm, size_t nelm, ui_callback f, void *udata) { 1157 listview->model = ui_model(obj->ctx, UI_STRING, "", -1);
1175 GtkWidget *combobox = gtk_combo_box_new(); 1158 g_signal_connect(
1176 1159 combobox,
1177 UiListView *uicbox = malloc(sizeof(UiListView)); 1160 "destroy",
1178 memset(uicbox, 0, sizeof(UiListView)); 1161 G_CALLBACK(ui_listview_destroy),
1179 uicbox->obj = obj; 1162 listview);
1180 uicbox->widget = combobox; 1163
1181 1164 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args->list, args->varname, UI_VAR_LIST);
1182 UiList *list = var ? var->value : NULL; 1165 UiList *list = var ? var->value : NULL;
1183 GtkListStore *listmodel = create_list_store(list, model); 1166 GtkListStore *listmodel = create_list_store(listview, list);
1184 1167 if(var) {
1185 if(!list && elm && nelm > 0) { 1168 listview->var = var;
1186 listview_copy_static_elements(uicbox, elm, nelm); 1169 list->update = ui_combobox_modelupdate;
1187 for(int i=0;i<nelm;i++) { 1170 list->getselection = ui_combobox_getselection;
1171 list->setselection = ui_combobox_setselection;
1172 list->obj = listview;
1173 list->update(list, -1);
1174 } else if(args->static_nelm > 0) {
1175 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
1176 for(int i=0;i<args->static_nelm;i++) {
1188 GtkTreeIter iter; 1177 GtkTreeIter iter;
1189 GValue value = G_VALUE_INIT; 1178 GValue value = G_VALUE_INIT;
1190 gtk_list_store_insert(listmodel, &iter, -1); 1179 gtk_list_store_insert(listmodel, &iter, -1);
1191 g_value_init(&value, G_TYPE_STRING); 1180 g_value_init(&value, G_TYPE_STRING);
1192 g_value_set_string(&value, uicbox->elements[i]); 1181 g_value_set_string(&value, listview->elements[i]);
1193 gtk_list_store_set_value(listmodel, &iter, 0, &value); 1182 gtk_list_store_set_value(listmodel, &iter, 0, &value);
1194 } 1183 }
1195 } 1184 }
1196 1185
1197 if(listmodel) { 1186 if(listmodel) {
1198 gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel)); 1187 gtk_combo_box_set_model(GTK_COMBO_BOX(combobox), GTK_TREE_MODEL(listmodel));
1199 g_object_unref(listmodel); 1188 g_object_unref(listmodel);
1200 }
1201
1202 uicbox->var = var;
1203 uicbox->model = model;
1204
1205 g_signal_connect(
1206 combobox,
1207 "destroy",
1208 G_CALLBACK(ui_combobox_destroy),
1209 uicbox);
1210
1211 // bind var
1212 if(list) {
1213 list->update = ui_combobox_modelupdate;
1214 list->getselection = ui_combobox_getselection;
1215 list->setselection = ui_combobox_setselection;
1216 list->obj = uicbox;
1217 } 1189 }
1218 1190
1219 GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); 1191 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1220 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE); 1192 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combobox), renderer, TRUE);
1221 gtk_cell_layout_set_attributes( 1193 gtk_cell_layout_set_attributes(
1225 0, 1197 0,
1226 NULL); 1198 NULL);
1227 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0); 1199 gtk_combo_box_set_active(GTK_COMBO_BOX(combobox), 0);
1228 1200
1229 // add callback 1201 // add callback
1230 if(f) { 1202 if(args->onactivate) {
1231 UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData)); 1203 UiEventData *event = ui_malloc(obj->ctx, sizeof(UiEventData));
1232 event->obj = obj; 1204 event->obj = obj;
1233 event->userdata = udata; 1205 event->userdata = args->onactivatedata;
1234 event->callback = f; 1206 event->callback = args->onactivate;
1235 event->value = 0; 1207 event->value = 0;
1236 event->customdata = uicbox; 1208 event->customdata = listview;
1237 1209
1238 g_signal_connect( 1210 g_signal_connect(
1239 combobox, 1211 combobox,
1240 "changed", 1212 "changed",
1241 G_CALLBACK(ui_combobox_change_event), 1213 G_CALLBACK(ui_combobox_change_event),
1266 e->callback(&event, e->userdata); 1238 e->callback(&event, e->userdata);
1267 } 1239 }
1268 1240
1269 void ui_combobox_modelupdate(UiList *list, int i) { 1241 void ui_combobox_modelupdate(UiList *list, int i) {
1270 UiListView *view = list->obj; 1242 UiListView *view = list->obj;
1271 GtkListStore *store = create_list_store(view->var->value, view->model); 1243 GtkListStore *store = create_list_store(view, list);
1272 gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store)); 1244 gtk_combo_box_set_model(GTK_COMBO_BOX(view->widget), GTK_TREE_MODEL(store));
1273 g_object_unref(store); 1245 g_object_unref(store);
1274 } 1246 }
1275 1247
1276 UiListSelection ui_combobox_getselection(UiList *list) { 1248 UiListSelection ui_combobox_getselection(UiList *list) {
1707 #endif 1679 #endif
1708 free(v->selection.rows); 1680 free(v->selection.rows);
1709 free(v); 1681 free(v);
1710 } 1682 }
1711 1683
1712 void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
1713 if(v->var) {
1714 ui_destroy_boundvar(v->obj->ctx, v->var);
1715 }
1716 if(v->elements) {
1717 for(int i=0;i<v->nelm;i++) {
1718 free(v->elements[i]);
1719 }
1720 free(v->elements);
1721 }
1722 free(v);
1723 }
1724
1725 1684
1726 /* ------------------------------ Source List ------------------------------ */ 1685 /* ------------------------------ Source List ------------------------------ */
1727 1686
1728 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { 1687 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
1729 cxListFree(v->sublists); 1688 cxListFree(v->sublists);
1799 uisublist.separator = sublist->separator; 1758 uisublist.separator = sublist->separator;
1800 uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS); 1759 uisublist.widgets = cxLinkedListCreateSimple(CX_STORE_POINTERS);
1801 uisublist.listbox = uilistbox; 1760 uisublist.listbox = uilistbox;
1802 uisublist.userdata = sublist->userdata; 1761 uisublist.userdata = sublist->userdata;
1803 uisublist.index = cxListSize(sublists); 1762 uisublist.index = cxListSize(sublists);
1804 1763 uisublist.startpos = 0;
1764 cxListAdd(sublists, &uisublist);
1765
1805 // bind UiList 1766 // bind UiList
1806 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1); 1767 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)-1);
1807 UiList *list = uisublist.var->value; 1768 if(uisublist.var && uisublist.var->value) {
1808 if(list) { 1769 UiList *list = uisublist.var->value;
1809 list->obj = sublist_ptr; 1770 list->obj = sublist_ptr;
1810 list->update = ui_listbox_list_update; 1771 list->update = ui_listbox_list_update;
1811 } 1772 }
1812
1813 cxListAdd(sublists, &uisublist);
1814 } 1773 }
1815 1774
1816 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) { 1775 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
1817 UiObject* current = uic_current_obj(obj); 1776 UiObject* current = uic_current_obj(obj);
1818 1777
1881 // register uilistbox for both widgets, so it doesn't matter which 1840 // register uilistbox for both widgets, so it doesn't matter which
1882 // widget is used later 1841 // widget is used later
1883 g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox); 1842 g_object_set_data(G_OBJECT(scroll_area), "ui_listbox", uilistbox);
1884 g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox); 1843 g_object_set_data(G_OBJECT(listbox), "ui_listbox", uilistbox);
1885 1844
1845 if(args->contextmenu) {
1846 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox);
1847 ui_widget_set_contextmenu(listbox, menu);
1848 }
1849
1886 // signals 1850 // signals
1887 g_signal_connect( 1851 g_signal_connect(
1888 listbox, 1852 listbox,
1889 "destroy", 1853 "destroy",
1890 G_CALLBACK(ui_destroy_sourcelist), 1854 G_CALLBACK(ui_destroy_sourcelist),
1950 if(i.index > to) { 1914 if(i.index > to) {
1951 break; 1915 break;
1952 } 1916 }
1953 1917
1954 // reload sublist 1918 // reload sublist
1919 sublist->startpos = pos;
1955 ui_listbox_update_sublist(listbox, sublist, pos); 1920 ui_listbox_update_sublist(listbox, sublist, pos);
1956 pos += sublist->numitems; 1921 pos += sublist->numitems;
1922 }
1923 }
1924
1925 static void listbox_button_clicked(GtkWidget *widget, UiEventDataExt *data) {
1926 UiListBoxSubList *sublist = data->customdata0;
1927
1928 UiSubListEventData eventdata;
1929 eventdata.list = sublist->var->value;
1930 eventdata.sublist_index = sublist->index;
1931 eventdata.row_index = data->value0;
1932 eventdata.sublist_userdata = sublist->userdata;
1933 eventdata.row_data = eventdata.list->get(eventdata.list, eventdata.row_index);
1934 eventdata.event_data = data->customdata2;
1935
1936 UiEvent event;
1937 event.obj = data->obj;
1938 event.window = event.obj->window;
1939 event.document = event.obj->ctx->document;
1940 event.eventdata = &eventdata;
1941 event.eventdatatype = UI_EVENT_DATA_SUBLIST;
1942 event.intval = data->value0;
1943 event.set = ui_get_setop();
1944
1945 if(data->callback2) {
1946 data->callback2(&event, data->userdata2);
1957 } 1947 }
1958 } 1948 }
1959 1949
1960 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { 1950 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
1961 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); 1951 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
1964 BOX_ADD(hbox, icon); 1954 BOX_ADD(hbox, icon);
1965 } 1955 }
1966 GtkWidget *label = gtk_label_new(item->label); 1956 GtkWidget *label = gtk_label_new(item->label);
1967 gtk_widget_set_halign(label, GTK_ALIGN_START); 1957 gtk_widget_set_halign(label, GTK_ALIGN_START);
1968 BOX_ADD_EXPAND(hbox, label); 1958 BOX_ADD_EXPAND(hbox, label);
1969 // TODO: badge, button 1959 if(item->badge) {
1960
1961 }
1970 GtkWidget *row = gtk_list_box_row_new(); 1962 GtkWidget *row = gtk_list_box_row_new();
1971 LISTBOX_ROW_SET_CHILD(row, hbox); 1963 LISTBOX_ROW_SET_CHILD(row, hbox);
1972 1964
1973 // signals 1965 // signals
1974 UiEventDataExt *event = malloc(sizeof(UiEventDataExt)); 1966 UiEventDataExt *event = malloc(sizeof(UiEventDataExt));
1989 G_CALLBACK(ui_destroy_userdata), 1981 G_CALLBACK(ui_destroy_userdata),
1990 event); 1982 event);
1991 1983
1992 g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event); 1984 g_object_set_data(G_OBJECT(row), "ui-listbox-row-eventdata", event);
1993 1985
1986 // badge
1987 if(item->badge) {
1988 GtkWidget *badge = gtk_label_new(item->badge);
1989 WIDGET_ADD_CSS_CLASS(badge, "ui-badge");
1990 #if GTK_CHECK_VERSION(4, 0, 0)
1991 gtk_widget_set_valign(badge, GTK_ALIGN_CENTER);
1992 BOX_ADD(hbox, badge);
1993 #else
1994 GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
1995 gtk_container_add(GTK_CONTAINER(align), badge);
1996 BOX_ADD(hbox, align);
1997 #endif
1998 }
1999 // button
2000 if(item->button_icon || item->button_label) {
2001 GtkWidget *button = gtk_button_new();
2002 gtk_button_set_label(GTK_BUTTON(button), item->button_label);
2003 ui_button_set_icon_name(button, item->button_icon);
2004 WIDGET_ADD_CSS_CLASS(button, "flat");
2005 BOX_ADD(hbox, button);
2006 g_signal_connect(
2007 button,
2008 "clicked",
2009 G_CALLBACK(listbox_button_clicked),
2010 event
2011 );
2012 }
2013
1994 return row; 2014 return row;
1995 } 2015 }
1996 2016
1997 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { 2017 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
1998 // clear sublist 2018 // clear sublist
2003 cxListClear(sublist->widgets); 2023 cxListClear(sublist->widgets);
2004 2024
2005 sublist->numitems = 0; 2025 sublist->numitems = 0;
2006 2026
2007 // create items for each UiList element 2027 // create items for each UiList element
2028 if(!sublist->var) {
2029 return;
2030 }
2008 UiList *list = sublist->var->value; 2031 UiList *list = sublist->var->value;
2009 if(!list) { 2032 if(!list) {
2010 return; 2033 return;
2011 } 2034 }
2012 2035
2013 size_t index = 0; 2036 size_t index = 0;
2014 void *elm = list->first(list); 2037 void *elm = list->first(list);
2038
2039 if(!elm && sublist->header) {
2040 // empty row for header
2041 GtkWidget *row = gtk_list_box_row_new();
2042 cxListAdd(sublist->widgets, row);
2043 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
2044 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
2045 intptr_t rowindex = listbox_insert_index + index;
2046 g_object_set_data(G_OBJECT(row), "ui_listbox_row_index", (gpointer)rowindex);
2047 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
2048 sublist->numitems = 1;
2049 return;
2050 }
2051
2015 while(elm) { 2052 while(elm) {
2016 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL }; 2053 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
2017 if(listbox->getvalue) { 2054 if(listbox->getvalue) {
2018 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata); 2055 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
2019 } else { 2056 } else {
2053 sublist->numitems = cxListSize(sublist->widgets); 2090 sublist->numitems = cxListSize(sublist->widgets);
2054 } 2091 }
2055 2092
2056 void ui_listbox_list_update(UiList *list, int i) { 2093 void ui_listbox_list_update(UiList *list, int i) {
2057 UiListBoxSubList *sublist = list->obj; 2094 UiListBoxSubList *sublist = list->obj;
2095 ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
2096 size_t pos = 0;
2097 CxIterator it = cxListIterator(sublist->listbox->sublists);
2098 cx_foreach(UiListBoxSubList *, ls, it) {
2099 ls->startpos = pos;
2100 pos += sublist->numitems;
2101 }
2102
2058 } 2103 }
2059 2104
2060 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { 2105 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
2061 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); 2106 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
2062 if(!data) { 2107 if(!data) {

mercurial