ui/gtk/list.c

changeset 110
c00e968d018b
parent 109
c3dfcb8f0be7
child 112
c3f2f16fa4b8
equal deleted inserted replaced
109:c3dfcb8f0be7 110:c00e968d018b
90 tableview->ondragcompletedata = args->ondragcompletedata; 90 tableview->ondragcompletedata = args->ondragcompletedata;
91 tableview->ondrop = args->ondrop; 91 tableview->ondrop = args->ondrop;
92 tableview->ondropdata = args->ondropdata; 92 tableview->ondropdata = args->ondropdata;
93 tableview->selection.count = 0; 93 tableview->selection.count = 0;
94 tableview->selection.rows = NULL; 94 tableview->selection.rows = NULL;
95 tableview->current_row = -1;
96 tableview->getstyle = args->getstyle;
97 tableview->getstyledata = args->getstyledata;
98 tableview->onsave = args->onsave;
99 tableview->onsavedata = args->onsavedata;
95 100
96 if(args->getvalue2) { 101 if(args->getvalue2) {
97 tableview->getvalue = args->getvalue2; 102 tableview->getvalue = args->getvalue2;
98 tableview->getvaluedata = args->getvalue2data; 103 tableview->getvaluedata = args->getvalue2data;
99 } else if(args->getvalue) { 104 } else if(args->getvalue) {
100 tableview->getvalue = getvalue_wrapper; 105 tableview->getvalue = getvalue_wrapper;
101 tableview->getvaluedata = (void*)args->getvalue; 106 tableview->getvaluedata = (void*)args->getvalue;
102 } else { 107 } else {
103 tableview->getvalue = null_getvalue; 108 tableview->getvalue = null_getvalue;
104 } 109 }
105 110
106 return tableview; 111 return tableview;
107 } 112 }
108 113
109 #if GTK_CHECK_VERSION(4, 10, 0) 114 #if GTK_CHECK_VERSION(4, 10, 0)
110 115
137 obj->i = i; 142 obj->i = i;
138 return obj; 143 return obj;
139 } 144 }
140 145
141 /* END GObject wrapper for generic pointers */ 146 /* END GObject wrapper for generic pointers */
147
148 typedef struct UiCellEntry {
149 GtkEntry *entry;
150 UiListView *listview;
151 char *previous_value;
152 int row;
153 int col;
154 } UiCellEntry;
155
156 static void cell_save_value(UiCellEntry *data, int restore) {
157 if(data->listview && data->listview->onsave) {
158 UiVar *var = data->listview->var;
159 UiList *list = var ? var->value : NULL;
160 const char *str = ENTRY_GET_TEXT(data->entry);
161 UiCellValue value;
162 value.string = str;
163 value.type = UI_STRING_EDITABLE;
164 if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) {
165 free(data->previous_value);
166 data->previous_value = strdup(str);
167 } else if(restore) {
168 ENTRY_SET_TEXT(data->entry, data->previous_value);
169 }
170 }
171 }
172
173 static void cell_entry_leave_focus(
174 GtkEventControllerFocus *self,
175 UiCellEntry *data)
176 {
177 // TODO: use a different singal to track focus
178 // we only want to call cell_save_value, when another entry is selected,
179 // not when the window loses focus or something like that
180 cell_save_value(data, TRUE);
181 }
182
183 static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) {
184 free(data->previous_value);
185 free(data);
186 }
187
188 static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) {
189 const char *text = ENTRY_GET_TEXT(w);
190 cell_save_value(data, FALSE);
191 }
192
193 static void cell_entry_activate(
194 GtkEntry *self,
195 UiCellEntry *data)
196 {
197 cell_save_value(data, TRUE);
198 }
142 199
143 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { 200 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
144 UiColData *col = userdata; 201 UiColData *col = userdata;
145 UiModel *model = col->listview->model; 202 UiModel *model = col->listview->model;
146 UiModelType type = model->types[col->model_column]; 203 UiModelType type = model->types[col->model_column];
154 g_object_set_data(G_OBJECT(hbox), "image", image); 211 g_object_set_data(G_OBJECT(hbox), "image", image);
155 g_object_set_data(G_OBJECT(hbox), "label", label); 212 g_object_set_data(G_OBJECT(hbox), "label", label);
156 } else if(type == UI_ICON) { 213 } else if(type == UI_ICON) {
157 GtkWidget *image = gtk_image_new(); 214 GtkWidget *image = gtk_image_new();
158 gtk_list_item_set_child(item, image); 215 gtk_list_item_set_child(item, image);
216 } else if(type == UI_STRING_EDITABLE) {
217 GtkWidget *textfield = gtk_entry_new();
218 gtk_widget_add_css_class(textfield, "ui-table-entry");
219 gtk_list_item_set_child(item, textfield);
220
221 UiCellEntry *entry_data = malloc(sizeof(UiCellEntry));
222 entry_data->entry = GTK_ENTRY(textfield);
223 entry_data->listview = NULL;
224 entry_data->previous_value = NULL;
225 entry_data->col = 0;
226 entry_data->row = 0;
227 g_object_set_data(G_OBJECT(textfield), "ui_entry_data", entry_data);
228
229 g_signal_connect(
230 textfield,
231 "destroy",
232 G_CALLBACK(cell_entry_destroy),
233 entry_data);
234 g_signal_connect(
235 textfield,
236 "activate",
237 G_CALLBACK(cell_entry_activate),
238 entry_data);
239 g_signal_connect(
240 textfield,
241 "unmap",
242 G_CALLBACK(cell_entry_unmap),
243 entry_data);
244
245 GtkEventController *focus_controller = gtk_event_controller_focus_new();
246 g_signal_connect(focus_controller, "leave", G_CALLBACK(cell_entry_leave_focus), entry_data);
247 gtk_widget_add_controller(textfield, focus_controller);
159 } else { 248 } else {
160 GtkWidget *label = gtk_label_new(NULL); 249 GtkWidget *label = gtk_label_new(NULL);
161 gtk_label_set_xalign(GTK_LABEL(label), 0); 250 gtk_label_set_xalign(GTK_LABEL(label), 0);
162 gtk_list_item_set_child(item, label); 251 gtk_list_item_set_child(item, label);
163 } 252 }
164 } 253 }
165 254
166 static void column_factory_bind(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) { 255 PangoAttrList* textstyle2pangoattributes(UiTextStyle style) {
256 PangoAttrList *attr = pango_attr_list_new();
257
258 if(style.text_style & UI_TEXT_STYLE_BOLD) {
259 pango_attr_list_insert(attr, pango_attr_weight_new(PANGO_WEIGHT_BOLD));
260 }
261 if(style.text_style & UI_TEXT_STYLE_ITALIC) {
262 pango_attr_list_insert(attr, pango_attr_style_new(PANGO_STYLE_ITALIC));
263 }
264 if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
265 pango_attr_list_insert(attr, pango_attr_underline_new(PANGO_UNDERLINE_SINGLE));
266 }
267
268 // foreground color, convert from 8bit to 16bit
269 guint16 r = (guint16)style.fg.red * 257;
270 guint16 g = (guint16)style.fg.green * 257;
271 guint16 b = (guint16)style.fg.blue * 257;
272 pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b));
273
274 return attr;
275 }
276
277 static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) {
167 UiColData *col = userdata; 278 UiColData *col = userdata;
168 UiList *list = col->listview->var ? col->listview->var->value : NULL; 279 UiList *list = col->listview->var ? col->listview->var->value : NULL;
169 UiListView *listview = col->listview; 280 UiListView *listview = col->listview;
170 281
171 ObjWrapper *obj = gtk_list_item_get_item(item); 282 ObjWrapper *obj = gtk_list_item_get_item(item);
172 UiModel *model = col->listview->model; 283 UiModel *model = col->listview->model;
173 UiModelType type = model->types[col->model_column]; 284 UiModelType type = model->types[col->model_column];
174 285
286 // cache the GtkListItem
287 CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
288 UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
289 if(row) {
290 if(row->items[col->model_column] == NULL) {
291 row->bound++;
292 }
293 } else {
294 row = calloc(1, sizeof(UiRowItems) + listview->numcolumns * sizeof(GtkListItem*));
295 cxMapPut(listview->bound_rows, row_key, row);
296 row->bound = 1;
297 }
298 row->items[col->model_column] = item;
299
175 UiBool freevalue = FALSE; 300 UiBool freevalue = FALSE;
176 void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue); 301 void *data = listview->getvalue(list, obj->data, obj->i, col->data_column, listview->getvaluedata, &freevalue);
177 GtkWidget *child = gtk_list_item_get_child(item); 302 GtkWidget *child = gtk_list_item_get_child(item);
303
304 PangoAttrList *attributes = NULL;
305 UiTextStyle style = { 0, 0 };
306 if(listview->getstyle) {
307 // query current row style, if it wasn't already queried
308 if(obj->i != listview->current_row) {
309 listview->current_row = obj->i;
310 listview->row_style = (UiTextStyle){ 0, 0 };
311 listview->apply_row_style = listview->getstyle(list, obj->data, obj->i, -1, listview->getstyledata, &listview->row_style);
312 style = listview->row_style;
313 if(listview->apply_row_style) {
314 pango_attr_list_unref(listview->current_row_attributes);
315 listview->current_row_attributes = textstyle2pangoattributes(style);
316 }
317 }
318
319 int style_col = col->data_column;
320 if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
321 style_col++; // col->data_column is the icon, we need the next col for the label
322 }
323
324 // get the column style
325 if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) {
326 attributes = textstyle2pangoattributes(style);
327 } else if(listview->apply_row_style) {
328 attributes = listview->current_row_attributes;
329 }
330 }
178 331
179 switch(type) { 332 switch(type) {
180 case UI_STRING_FREE: { 333 case UI_STRING_FREE: {
181 freevalue = TRUE; 334 freevalue = TRUE;
182 } 335 }
183 case UI_STRING: { 336 case UI_STRING: {
184 gtk_label_set_label(GTK_LABEL(child), data); 337 gtk_label_set_label(GTK_LABEL(child), data);
185 if(freevalue) { 338 if(freevalue) {
186 free(data); 339 free(data);
187 } 340 }
341 gtk_label_set_attributes(GTK_LABEL(child), attributes);
188 break; 342 break;
189 } 343 }
190 case UI_INTEGER: { 344 case UI_INTEGER: {
191 intptr_t intvalue = (intptr_t)data; 345 intptr_t intvalue = (intptr_t)data;
192 char buf[32]; 346 char buf[32];
193 snprintf(buf, 32, "%d", (int)intvalue); 347 snprintf(buf, 32, "%d", (int)intvalue);
194 gtk_label_set_label(GTK_LABEL(child), buf); 348 gtk_label_set_label(GTK_LABEL(child), buf);
349 gtk_label_set_attributes(GTK_LABEL(child), attributes);
195 break; 350 break;
196 } 351 }
197 case UI_ICON: { 352 case UI_ICON: {
198 UiIcon *icon = data; 353 UiIcon *icon = data;
199 if(icon) { 354 if(icon) {
215 UiIcon *icon = data; 370 UiIcon *icon = data;
216 gtk_image_set_from_paintable(GTK_IMAGE(image), GDK_PAINTABLE(icon->info)); 371 gtk_image_set_from_paintable(GTK_IMAGE(image), GDK_PAINTABLE(icon->info));
217 } 372 }
218 if(data2 && label) { 373 if(data2 && label) {
219 gtk_label_set_label(GTK_LABEL(label), data2); 374 gtk_label_set_label(GTK_LABEL(label), data2);
375 gtk_label_set_attributes(GTK_LABEL(label), attributes);
220 } 376 }
221 if(freevalue) { 377 if(freevalue) {
222 free(data2); 378 free(data2);
223 } 379 }
224 break; 380 break;
225 } 381 }
226 } 382 case UI_STRING_EDITABLE: {
227 } 383 UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
384 if(entry) {
385 entry->listview = col->listview;
386 entry->row = obj->i;
387 entry->col = col->data_column;
388 entry->previous_value = strdup(data);
389 }
390 ENTRY_SET_TEXT(child, data);
391 break;
392 }
393 }
394
395 if(attributes != listview->current_row_attributes) {
396 pango_attr_list_unref(attributes);
397 }
398 }
399
400 static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
401 ObjWrapper *obj = gtk_list_item_get_item(item);
402 UiListView *listview = col->listview;
403 CxHashKey row_key = cx_hash_key(&obj->i, sizeof(int));
404 UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
405 if(row) {
406 row->items[col->model_column] = NULL;
407 row->bound--;
408 if(row->bound == 0) {
409 cxMapRemove(listview->bound_rows, row_key);
410 }
411 } // else: should not happen
412
413 GtkWidget *child = gtk_list_item_get_child(item);
414 UiCellEntry *entry = g_object_get_data(G_OBJECT(child), "ui_entry_data");
415 if(entry) {
416 cell_save_value(entry, FALSE);
417 entry->listview = NULL;
418 free(entry->previous_value);
419 entry->previous_value = NULL;
420 }
421 }
422
228 423
229 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) { 424 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
230 GtkSelectionModel *selection_model; 425 GtkSelectionModel *selection_model;
231 if(multiselection) { 426 if(multiselection) {
232 selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore))); 427 selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(liststore)));
251 UiListView *listview = create_listview(obj, args); 446 UiListView *listview = create_listview(obj, args);
252 if(!args->getvalue && !args->getvalue2) { 447 if(!args->getvalue && !args->getvalue2) {
253 listview->getvalue = str_getvalue; 448 listview->getvalue = str_getvalue;
254 } 449 }
255 450
451 listview->numcolumns = 1;
256 listview->columns = malloc(sizeof(UiColData)); 452 listview->columns = malloc(sizeof(UiColData));
257 listview->columns->listview = listview; 453 listview->columns->listview = listview;
258 listview->columns->data_column = 0; 454 listview->columns->data_column = 0;
259 listview->columns->model_column = 0; 455 listview->columns->model_column = 0;
456
457 listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
458 listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
260 459
261 GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); 460 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
262 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); 461 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
263 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); 462 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
264 463
337 536
338 if(!args->getvalue && !args->getvalue2) { 537 if(!args->getvalue && !args->getvalue2) {
339 listview->getvalue = str_getvalue; 538 listview->getvalue = str_getvalue;
340 } 539 }
341 540
541 listview->numcolumns = 1;
342 listview->columns = malloc(sizeof(UiColData)); 542 listview->columns = malloc(sizeof(UiColData));
343 listview->columns->listview = listview; 543 listview->columns->listview = listview;
344 listview->columns->data_column = 0; 544 listview->columns->data_column = 0;
345 listview->columns->model_column = 0; 545 listview->columns->model_column = 0;
546
547 listview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
548 listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
346 549
347 GtkListItemFactory *factory = gtk_signal_list_item_factory_new(); 550 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
348 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns); 551 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), listview->columns);
349 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns); 552 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), listview->columns);
350 553
430 // create columns from UiModel 633 // create columns from UiModel
431 UiModel *model = args->model; 634 UiModel *model = args->model;
432 int columns = model ? model->columns : 0; 635 int columns = model ? model->columns : 0;
433 636
434 tableview->columns = calloc(columns, sizeof(UiColData)); 637 tableview->columns = calloc(columns, sizeof(UiColData));
638 tableview->numcolumns = columns;
639
640 tableview->bound_rows = cxHashMapCreate(NULL, CX_STORE_POINTERS, 128);
641 tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
435 642
436 int addi = 0; 643 int addi = 0;
437 for(int i=0;i<columns;i++) { 644 for(int i=0;i<columns;i++) {
438 tableview->columns[i].listview = tableview; 645 tableview->columns[i].listview = tableview;
439 tableview->columns[i].model_column = i; 646 tableview->columns[i].model_column = i;
629 if(i < 0) { 836 if(i < 0) {
630 ui_update_liststore(view->liststore, list); 837 ui_update_liststore(view->liststore, list);
631 } else { 838 } else {
632 void *value = list->get(list, i); 839 void *value = list->get(list, i);
633 if(value) { 840 if(value) {
634 ObjWrapper *obj = obj_wrapper_new(value, i); 841 ObjWrapper *obj = g_list_model_get_item(G_LIST_MODEL(view->liststore), i);
635 UiListSelection sel = list->getselection(list); 842 if(obj) {
636 // TODO: if index i is selected, the selection is lost 843 obj->data = value;
637 // is it possible to update the item without removing it?
638 // workaround: save selection and reapply it
639 int count = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
640 if(count <= i) {
641 g_list_store_splice(view->liststore, i, 0, (void **)&obj, 1);
642 } else {
643 g_list_store_splice(view->liststore, i, 1, (void **)&obj, 1);
644 } 844 }
645 if(sel.count > 0) { 845
646 list->setselection(list, sel); 846 CxHashKey row_key = cx_hash_key(&i, sizeof(int));
847 UiRowItems *row = cxMapGet(view->bound_rows, row_key);
848 if(row) {
849 for(int c=0;c<view->numcolumns;c++) {
850 if(row->items[c] != NULL) {
851 column_factory_bind(NULL, row->items[c], &view->columns[c]);
852 }
853 }
647 } 854 }
648 ui_listselection_free(sel);
649 } 855 }
650 } 856 }
651 } 857 }
652 858
653 UiListSelection ui_listview_getselection2(UiList *list) { 859 UiListSelection ui_listview_getselection2(UiList *list) {
707 913
708 #else 914 #else
709 915
710 static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) { 916 static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list, void *elm, int row) {
711 UiModel *model = listview->model; 917 UiModel *model = listview->model;
918 ui_getstylefunc getstyle = listview->getstyle;
919
920 // get the row style
921 UiBool style_set = FALSE;
922 UiTextStyle style = { 0, 0 };
923 if(getstyle) {
924 style_set = getstyle(list, elm, row, -1, listview->getstyledata, &style);
925 }
926
712 // set column values 927 // set column values
713 int c = 0; 928 int c = 0;
714 for(int i=0;i<model->columns;i++,c++) { 929 for(int i=0;i<model->columns;i++,c++) {
715 UiBool freevalue = FALSE; 930 UiBool freevalue = FALSE;
716 void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue); 931 void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
932
933 UiModelType type = model->types[i];
934
935 if(getstyle) {
936 // in case the column is icon+text, only get a style for the text column
937 int style_col = c;
938 if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
939 style_col++;
940 }
941
942 // Get the individual column style
943 // The column style overrides the row style, however if no column style
944 // is provided, we stick with the row style
945 if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
946 style_set = TRUE;
947 }
948 }
717 949
718 GValue value = G_VALUE_INIT; 950 GValue value = G_VALUE_INIT;
719 switch(model->types[i]) { 951 switch(type) {
720 case UI_STRING_FREE: { 952 case UI_STRING_FREE: {
721 freevalue = TRUE; 953 freevalue = TRUE;
722 } 954 }
723 case UI_STRING: { 955 case UI_STRING: {
724 g_value_init(&value, G_TYPE_STRING); 956 g_value_init(&value, G_TYPE_STRING);
787 break; 1019 break;
788 } 1020 }
789 } 1021 }
790 1022
791 gtk_list_store_set_value(store, iter, c, &value); 1023 gtk_list_store_set_value(store, iter, c, &value);
1024
1025 if(style_set) {
1026 int soff = listview->style_offset + i*6;
1027
1028 GValue style_set_value = G_VALUE_INIT;
1029 g_value_init(&style_set_value, G_TYPE_BOOLEAN);
1030 g_value_set_boolean(&style_set_value, TRUE);
1031 gtk_list_store_set_value(store, iter, soff, &style_set_value);
1032
1033 GValue style_weight_value = G_VALUE_INIT;
1034 g_value_init(&style_weight_value, G_TYPE_INT);
1035 if(style.text_style & UI_TEXT_STYLE_BOLD) {
1036 g_value_set_int(&style_weight_value, 600);
1037 } else {
1038 g_value_set_int(&style_weight_value, 400);
1039 }
1040 gtk_list_store_set_value(store, iter, soff + 1, &style_weight_value);
1041
1042 GValue style_underline_value = G_VALUE_INIT;
1043 g_value_init(&style_underline_value, G_TYPE_INT);
1044 if(style.text_style & UI_TEXT_STYLE_UNDERLINE) {
1045 g_value_set_int(&style_underline_value, PANGO_UNDERLINE_SINGLE);
1046 } else {
1047 g_value_set_int(&style_underline_value, PANGO_UNDERLINE_NONE);
1048 }
1049 gtk_list_store_set_value(store, iter, soff + 2, &style_underline_value);
1050
1051 GValue style_italic_value = G_VALUE_INIT;
1052 g_value_init(&style_italic_value, G_TYPE_INT);
1053 if(style.text_style & UI_TEXT_STYLE_ITALIC) {
1054 g_value_set_int(&style_italic_value, PANGO_STYLE_ITALIC);
1055 } else {
1056 g_value_set_int(&style_italic_value, PANGO_STYLE_NORMAL);
1057 }
1058 gtk_list_store_set_value(store, iter, soff + 3, &style_italic_value);
1059
1060 GValue style_fgset_value = G_VALUE_INIT;
1061 g_value_init(&style_fgset_value, G_TYPE_BOOLEAN);
1062 g_value_set_boolean(&style_fgset_value, style.fg_set);
1063 gtk_list_store_set_value(store, iter, soff + 4, &style_fgset_value);
1064
1065 if(style.fg_set) {
1066 char buf[8];
1067 snprintf(buf, 8, "#%02X%02X%02X", (int)style.fg.red, (int)style.fg.green, (int)style.fg.blue);
1068
1069 GValue style_fg_value = G_VALUE_INIT;
1070 g_value_init(&style_fg_value, G_TYPE_STRING);
1071 g_value_set_string(&style_fg_value, buf);
1072 gtk_list_store_set_value(store, iter, soff + 5, &style_fg_value);
1073 }
1074 }
792 } 1075 }
793 } 1076 }
794 1077
795 static GtkListStore* create_list_store(UiListView *listview, UiList *list) { 1078 static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
796 UiModel *model = listview->model; 1079 UiModel *model = listview->model;
797 int columns = model->columns; 1080 int columns = model->columns;
798 GType types[2*columns]; 1081 GType *types = calloc(columns*8, sizeof(GType));
799 int c = 0; 1082 int c = 0;
800 for(int i=0;i<columns;i++,c++) { 1083 for(int i=0;i<columns;i++,c++) {
801 switch(model->types[i]) { 1084 switch(model->types[i]) {
802 case UI_STRING: 1085 case UI_STRING:
803 case UI_STRING_FREE: types[c] = G_TYPE_STRING; break; 1086 case UI_STRING_FREE: types[c] = G_TYPE_STRING; break;
808 types[c] = G_TYPE_OBJECT; 1091 types[c] = G_TYPE_OBJECT;
809 types[++c] = G_TYPE_STRING; 1092 types[++c] = G_TYPE_STRING;
810 } 1093 }
811 } 1094 }
812 } 1095 }
813 1096 int s = 0;
814 GtkListStore *store = gtk_list_store_newv(c, types); 1097 for(int i=0;i<columns;i++) {
1098 types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // *-set
1099 types[listview->style_offset+s] = G_TYPE_INT; s++; // weight
1100 types[listview->style_offset+s] = G_TYPE_INT; s++; // underline
1101 types[listview->style_offset+s] = G_TYPE_INT; s++; // style
1102 types[listview->style_offset+s] = G_TYPE_BOOLEAN; s++; // foreground-set
1103 types[listview->style_offset+s] = G_TYPE_STRING; s++; // foreground
1104 }
1105
1106 GtkListStore *store = gtk_list_store_newv(c+s, types);
1107 free(types);
815 1108
816 if(list) { 1109 if(list) {
817 void *elm = list->first(list); 1110 void *elm = list->first(list);
818 int i = 0; 1111 int i = 0;
819 while(elm) { 1112 while(elm) {
855 #endif 1148 #endif
856 1149
857 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1); 1150 UiModel *model = ui_model(obj->ctx, UI_STRING, "", -1);
858 1151
859 UiListView *listview = create_listview(obj, args); 1152 UiListView *listview = create_listview(obj, args);
1153 listview->style_offset = 1;
860 if(!args->getvalue && !args->getvalue2) { 1154 if(!args->getvalue && !args->getvalue2) {
861 listview->getvalue = str_getvalue; 1155 listview->getvalue = str_getvalue;
862 } 1156 }
863 listview->model = model; 1157 listview->model = model;
864 g_signal_connect( 1158 g_signal_connect(
955 GtkWidget *view = gtk_tree_view_new(); 1249 GtkWidget *view = gtk_tree_view_new();
956 1250
957 UiModel *model = args->model; 1251 UiModel *model = args->model;
958 int columns = model ? model->columns : 0; 1252 int columns = model ? model->columns : 0;
959 1253
1254 // find the last data column index
960 int addi = 0; 1255 int addi = 0;
961 for(int i=0;i<columns;i++) { 1256 int style_offset = 0;
1257 int i = 0;
1258 for(;i<columns;i++) {
1259 if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
1260 addi++;
1261 }
1262 }
1263 style_offset = i+addi;
1264
1265 // create columns and init cell renderers
1266 addi = 0;
1267 for(i=0;i<columns;i++) {
962 GtkTreeViewColumn *column = NULL; 1268 GtkTreeViewColumn *column = NULL;
963 if(model->types[i] == UI_ICON_TEXT) { 1269 if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
964 column = gtk_tree_view_column_new(); 1270 column = gtk_tree_view_column_new();
965 gtk_tree_view_column_set_title(column, model->titles[i]); 1271 gtk_tree_view_column_set_title(column, model->titles[i]);
966 1272
967 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); 1273 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
968 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new(); 1274 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
969 1275
970 gtk_tree_view_column_pack_end(column, textrenderer, TRUE); 1276 gtk_tree_view_column_pack_end(column, textrenderer, TRUE);
971 gtk_tree_view_column_pack_start(column, iconrenderer, FALSE); 1277 gtk_tree_view_column_pack_start(column, iconrenderer, FALSE);
972 1278
973 1279
974 gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", i); 1280 gtk_tree_view_column_add_attribute(column, iconrenderer, "pixbuf", addi + i);
975 gtk_tree_view_column_add_attribute(column, textrenderer, "text", i+1); 1281 gtk_tree_view_column_add_attribute(column, textrenderer, "text", addi + i+1);
1282
1283 if(args->getstyle) {
1284 int soff = style_offset + i*6;
1285 gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
1286 gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
1287 gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
1288
1289 gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
1290 gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
1291 gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
1292 gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
1293 gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
1294 }
976 1295
977 addi++; 1296 addi++;
978 } else if (model->types[i] == UI_ICON) { 1297 } else if (model->types[i] == UI_ICON) {
979 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new(); 1298 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
980 column = gtk_tree_view_column_new_with_attributes( 1299 column = gtk_tree_view_column_new_with_attributes(
982 iconrenderer, 1301 iconrenderer,
983 "pixbuf", 1302 "pixbuf",
984 i + addi, 1303 i + addi,
985 NULL); 1304 NULL);
986 } else { 1305 } else {
987 GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); 1306 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
988 column = gtk_tree_view_column_new_with_attributes( 1307 column = gtk_tree_view_column_new_with_attributes(
989 model->titles[i], 1308 model->titles[i],
990 renderer, 1309 textrenderer,
991 "text", 1310 "text",
992 i + addi, 1311 i + addi,
993 NULL); 1312 NULL);
1313
1314 if(args->getstyle) {
1315 int soff = style_offset + i*6;
1316 gtk_tree_view_column_add_attribute(column, textrenderer, "weight-set", soff);
1317 gtk_tree_view_column_add_attribute(column, textrenderer, "underline-set", soff);
1318 gtk_tree_view_column_add_attribute(column, textrenderer, "style-set", soff);
1319
1320 gtk_tree_view_column_add_attribute(column, textrenderer, "weight", soff + 1);
1321 gtk_tree_view_column_add_attribute(column, textrenderer, "underline", soff + 2);
1322 gtk_tree_view_column_add_attribute(column, textrenderer, "style", soff + 3);
1323 gtk_tree_view_column_add_attribute(column, textrenderer, "foreground-set", soff + 4);
1324 gtk_tree_view_column_add_attribute(column, textrenderer, "foreground", soff + 5);
1325 }
994 } 1326 }
995 1327
996 int colsz = model->columnsize[i]; 1328 int colsz = model->columnsize[i];
997 if(colsz > 0) { 1329 if(colsz > 0) {
998 gtk_tree_view_column_set_fixed_width(column, colsz); 1330 gtk_tree_view_column_set_fixed_width(column, colsz);
1017 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL); 1349 //g_signal_connect(view, "drag-end", G_CALLBACK(drag_end), NULL);
1018 1350
1019 // add TreeView as observer to the UiList to update the TreeView if the 1351 // add TreeView as observer to the UiList to update the TreeView if the
1020 // data changes 1352 // data changes
1021 UiListView *tableview = create_listview(obj, args); 1353 UiListView *tableview = create_listview(obj, args);
1354 tableview->widget = view;
1355 tableview->style_offset = style_offset;
1022 g_signal_connect( 1356 g_signal_connect(
1023 view, 1357 view,
1024 "destroy", 1358 "destroy",
1025 G_CALLBACK(ui_listview_destroy), 1359 G_CALLBACK(ui_listview_destroy),
1026 tableview); 1360 tableview);
1152 current->container->add(current->container, combobox); 1486 current->container->add(current->container, combobox);
1153 current->container->current = combobox; 1487 current->container->current = combobox;
1154 1488
1155 UiListView *listview = create_listview(obj, args); 1489 UiListView *listview = create_listview(obj, args);
1156 listview->widget = combobox; 1490 listview->widget = combobox;
1491 listview->style_offset = 1;
1157 listview->model = ui_model(obj->ctx, UI_STRING, "", -1); 1492 listview->model = ui_model(obj->ctx, UI_STRING, "", -1);
1158 g_signal_connect( 1493 g_signal_connect(
1159 combobox, 1494 combobox,
1160 "destroy", 1495 "destroy",
1161 G_CALLBACK(ui_listview_destroy), 1496 G_CALLBACK(ui_listview_destroy),
1674 } 2009 }
1675 free(v->elements); 2010 free(v->elements);
1676 } 2011 }
1677 #if GTK_CHECK_VERSION(4, 10, 0) 2012 #if GTK_CHECK_VERSION(4, 10, 0)
1678 free(v->columns); 2013 free(v->columns);
2014 pango_attr_list_unref(v->current_row_attributes);
2015 cxMapFree(v->bound_rows);
1679 #endif 2016 #endif
1680 free(v->selection.rows); 2017 free(v->selection.rows);
1681 free(v); 2018 free(v);
1682 } 2019 }
1683 2020
1945 if(data->callback2) { 2282 if(data->callback2) {
1946 data->callback2(&event, data->userdata2); 2283 data->callback2(&event, data->userdata2);
1947 } 2284 }
1948 } 2285 }
1949 2286
1950 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item, int index) { 2287 static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item, int index) {
1951 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10); 2288 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 10);
1952 if(item->icon) { 2289 if(item->icon) {
1953 GtkWidget *icon = ICON_IMAGE(item->icon); 2290 GtkWidget *icon = ICON_IMAGE(item->icon);
1954 BOX_ADD(hbox, icon); 2291 BOX_ADD(hbox, icon);
1955 } 2292 }
1957 gtk_widget_set_halign(label, GTK_ALIGN_START); 2294 gtk_widget_set_halign(label, GTK_ALIGN_START);
1958 BOX_ADD_EXPAND(hbox, label); 2295 BOX_ADD_EXPAND(hbox, label);
1959 if(item->badge) { 2296 if(item->badge) {
1960 2297
1961 } 2298 }
1962 GtkWidget *row = gtk_list_box_row_new();
1963 LISTBOX_ROW_SET_CHILD(row, hbox); 2299 LISTBOX_ROW_SET_CHILD(row, hbox);
1964 2300
1965 // signals 2301 // signals
1966 UiEventDataExt *event = malloc(sizeof(UiEventDataExt)); 2302 UiEventDataExt *event = malloc(sizeof(UiEventDataExt));
1967 memset(event, 0, sizeof(UiEventDataExt)); 2303 memset(event, 0, sizeof(UiEventDataExt));
1985 2321
1986 // badge 2322 // badge
1987 if(item->badge) { 2323 if(item->badge) {
1988 GtkWidget *badge = gtk_label_new(item->badge); 2324 GtkWidget *badge = gtk_label_new(item->badge);
1989 WIDGET_ADD_CSS_CLASS(badge, "ui-badge"); 2325 WIDGET_ADD_CSS_CLASS(badge, "ui-badge");
1990 #if GTK_CHECK_VERSION(4, 0, 0) 2326 #if GTK_CHECK_VERSION(3, 14, 0)
1991 gtk_widget_set_valign(badge, GTK_ALIGN_CENTER); 2327 gtk_widget_set_valign(badge, GTK_ALIGN_CENTER);
1992 BOX_ADD(hbox, badge); 2328 BOX_ADD(hbox, badge);
1993 #else 2329 #else
1994 GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0); 2330 GtkWidget *align = gtk_alignment_new(0.5, 0.5, 0, 0);
1995 gtk_container_add(GTK_CONTAINER(align), badge); 2331 gtk_container_add(GTK_CONTAINER(align), badge);
2008 "clicked", 2344 "clicked",
2009 G_CALLBACK(listbox_button_clicked), 2345 G_CALLBACK(listbox_button_clicked),
2010 event 2346 event
2011 ); 2347 );
2012 } 2348 }
2013 2349 }
2014 return row; 2350
2351 static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist, int index) {
2352 GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index);
2353 if(!row) {
2354 return;
2355 }
2356 UiList *list = sublist->var->value;
2357 if(!list) {
2358 return;
2359 }
2360
2361 void *elm = list->get(list, index);
2362 UiSubListItem item = { NULL, NULL, NULL, NULL, NULL, NULL };
2363 if(listbox->getvalue) {
2364 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
2365 } else {
2366 item.label = strdup(elm);
2367 }
2368
2369 LISTBOX_ROW_REMOVE_CHILD(row);
2370
2371 listbox_fill_row(listbox, GTK_WIDGET(row), sublist, &item, index);
2372
2373 // cleanup
2374 free(item.label);
2375 free(item.icon);
2376 free(item.button_label);
2377 free(item.button_icon);
2378 free(item.badge);
2015 } 2379 }
2016 2380
2017 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) { 2381 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist, size_t listbox_insert_index) {
2018 // clear sublist 2382 // clear sublist
2019 CxIterator r = cxListIterator(sublist->widgets); 2383 CxIterator r = cxListIterator(sublist->widgets);
2056 } else { 2420 } else {
2057 item.label = strdup(elm); 2421 item.label = strdup(elm);
2058 } 2422 }
2059 2423
2060 // create listbox item 2424 // create listbox item
2061 GtkWidget *row = create_listbox_row(listbox, sublist, &item, (int)index); 2425 GtkWidget *row = gtk_list_box_row_new();
2426 listbox_fill_row(listbox, row, sublist, &item, (int)index);
2062 if(index == 0) { 2427 if(index == 0) {
2063 // first row in the sublist, set ui_listbox data to the row 2428 // first row in the sublist, set ui_listbox data to the row
2064 // which is then used by the headerfunc 2429 // which is then used by the headerfunc
2065 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox); 2430 g_object_set_data(G_OBJECT(row), "ui_listbox", listbox);
2066 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist); 2431 g_object_set_data(G_OBJECT(row), "ui_listbox_sublist", sublist);
2090 sublist->numitems = cxListSize(sublist->widgets); 2455 sublist->numitems = cxListSize(sublist->widgets);
2091 } 2456 }
2092 2457
2093 void ui_listbox_list_update(UiList *list, int i) { 2458 void ui_listbox_list_update(UiList *list, int i) {
2094 UiListBoxSubList *sublist = list->obj; 2459 UiListBoxSubList *sublist = list->obj;
2095 ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos); 2460 if(i < 0) {
2096 size_t pos = 0; 2461 ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
2097 CxIterator it = cxListIterator(sublist->listbox->sublists); 2462 size_t pos = 0;
2098 cx_foreach(UiListBoxSubList *, ls, it) { 2463 CxIterator it = cxListIterator(sublist->listbox->sublists);
2099 ls->startpos = pos; 2464 cx_foreach(UiListBoxSubList *, ls, it) {
2100 pos += sublist->numitems; 2465 ls->startpos = pos;
2101 } 2466 pos += ls->numitems;
2102 2467 }
2468 } else {
2469 update_sublist_item(sublist->listbox, sublist, i);
2470 }
2103 } 2471 }
2104 2472
2105 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) { 2473 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
2106 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata"); 2474 UiEventDataExt *data = g_object_get_data(G_OBJECT(row), "ui-listbox-row-eventdata");
2107 if(!data) { 2475 if(!data) {

mercurial