1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdarg.h>
33
34 #include "../common/context.h"
35 #include "../common/object.h"
36 #include "container.h"
37
38 #include <cx/array_list.h>
39 #include <cx/linked_list.h>
40
41 #include "list.h"
42 #include "button.h"
43 #include "icon.h"
44 #include "menu.h"
45 #include "dnd.h"
46
47
48 static void* getvalue_wrapper(UiList *list,
void *elm,
int row,
int col,
void *userdata, UiBool *freeResult) {
49 ui_getvaluefunc getvalue = (ui_getvaluefunc)userdata;
50 return getvalue(elm, col);
51 }
52
53 static void* str_getvalue(UiList *list,
void *elm,
int row,
int col,
void *userdata, UiBool *freeResult) {
54 return elm;
55 }
56
57 static void* null_getvalue(UiList *list,
void *elm,
int row,
int col,
void *userdata, UiBool *freeResult) {
58 return NULL;
59 }
60
61
62
63
64
65
66
67
68
69
70 static void listview_copy_static_elements(UiListView *listview,
char **elm,
size_t nelm) {
71 listview->elements = calloc(nelm,
sizeof(
char*));
72 listview->nelm = nelm;
73 for(
int i=
0;i<nelm;i++) {
74 listview->elements[i] = strdup(elm[i]);
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->multiselection = args->multiselection;
84 tableview->onactivate = args->onactivate;
85 tableview->onactivatedata = args->onactivatedata;
86 tableview->onselection = args->onselection;
87 tableview->onselectiondata = args->onselectiondata;
88 tableview->ondragstart = args->ondragstart;
89 tableview->ondragstartdata = args->ondragstartdata;
90 tableview->ondragcomplete = args->ondragcomplete;
91 tableview->ondragcompletedata = args->ondragcompletedata;
92 tableview->ondrop = args->ondrop;
93 tableview->ondropdata = args->ondropdata;
94 tableview->selection.count =
0;
95 tableview->selection.rows =
NULL;
96 tableview->current_row =
-1;
97 tableview->getstyle = args->getstyle;
98 tableview->getstyledata = args->getstyledata;
99 tableview->onsave = args->onsave;
100 tableview->onsavedata = args->onsavedata;
101
102 #if GTK_CHECK_VERSION(
4,
0,
0)
103 tableview->coldata.listview = tableview;
104 tableview->coldata.column =
0;
105 #endif
106
107 if(args->getvalue2) {
108 tableview->getvalue = args->getvalue2;
109 tableview->getvaluedata = args->getvalue2data;
110 }
else if(args->getvalue) {
111 tableview->getvalue = getvalue_wrapper;
112 tableview->getvaluedata = (
void*)args->getvalue;
113 }
else {
114 tableview->getvalue = null_getvalue;
115 }
116
117 return tableview;
118 }
119
120 #if GTK_CHECK_VERSION(
4,
10,
0)
121
122
123
124
125 typedef struct _ObjWrapper {
126 GObject parent_instance;
127 void *data;
128 int i;
129 } ObjWrapper;
130
131 typedef struct _ObjWrapperClass {
132 GObjectClass parent_class;
133 } ObjWrapperClass;
134
135 G_DEFINE_TYPE(ObjWrapper, obj_wrapper,
G_TYPE_OBJECT)
136
137 static void obj_wrapper_class_init(ObjWrapperClass *klass) {
138
139 }
140
141 static void obj_wrapper_init(ObjWrapper *self) {
142 self->data =
NULL;
143 }
144
145 ObjWrapper* obj_wrapper_new(
void* data,
int i) {
146 ObjWrapper *obj = g_object_new(obj_wrapper_get_type(),
NULL);
147 obj->data = data;
148 obj->i = i;
149 return obj;
150 }
151
152
153
154 typedef struct UiCellEntry {
155 GtkEntry *entry;
156 UiListView *listview;
157 char *previous_value;
158 int row;
159 int col;
160 } UiCellEntry;
161
162 static void cell_save_value(UiCellEntry *data,
int restore) {
163 if(data->listview && data->listview->onsave) {
164 UiVar *var = data->listview->var;
165 UiList *list = var ? var->value :
NULL;
166 const char *str =
ENTRY_GET_TEXT(data->entry);
167 UiCellValue value;
168 value.string = str;
169 value.type =
UI_STRING_EDITABLE;
170 if(data->listview->onsave(list, data->row, data->col, &value, data->listview->onsavedata)) {
171 free(data->previous_value);
172 data->previous_value = strdup(str);
173 }
else if(restore) {
174 ENTRY_SET_TEXT(data->entry, data->previous_value);
175 }
176 }
177 }
178
179 static void cell_entry_leave_focus(
180 GtkEventControllerFocus *self,
181 UiCellEntry *data)
182 {
183
184
185
186 cell_save_value(data,
TRUE);
187 }
188
189 static void cell_entry_destroy(GtkWidget *object, UiCellEntry *data) {
190 free(data->previous_value);
191 free(data);
192 }
193
194 static void cell_entry_unmap(GtkWidget *w, UiCellEntry *data) {
195 const char *text =
ENTRY_GET_TEXT(w);
196 cell_save_value(data,
FALSE);
197 }
198
199 static void cell_entry_activate(
200 GtkEntry *self,
201 UiCellEntry *data)
202 {
203 cell_save_value(data,
TRUE);
204 }
205
206 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
207 UiColData *col = userdata;
208 UiModel *model = col->listview->model;
209 UiModelType type = model->types[col->column];
210 if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
211 GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
6);
212 GtkWidget *image = gtk_image_new();
213 GtkWidget *label = gtk_label_new(
NULL);
214 BOX_ADD(hbox, image);
215 BOX_ADD(hbox, label);
216 gtk_list_item_set_child(item, hbox);
217 g_object_set_data(
G_OBJECT(hbox),
"image", image);
218 g_object_set_data(
G_OBJECT(hbox),
"label", label);
219 }
else if(type ==
UI_ICON) {
220 GtkWidget *image = gtk_image_new();
221 gtk_list_item_set_child(item, image);
222 }
else if(type ==
UI_STRING_EDITABLE) {
223 GtkWidget *textfield = gtk_entry_new();
224 gtk_widget_add_css_class(textfield,
"ui-table-entry");
225 gtk_list_item_set_child(item, textfield);
226
227 UiCellEntry *entry_data = malloc(
sizeof(UiCellEntry));
228 entry_data->entry =
GTK_ENTRY(textfield);
229 entry_data->listview =
NULL;
230 entry_data->previous_value =
NULL;
231 entry_data->col =
0;
232 entry_data->row =
0;
233 g_object_set_data(
G_OBJECT(textfield),
"ui_entry_data", entry_data);
234
235 g_signal_connect(
236 textfield,
237 "destroy",
238 G_CALLBACK(cell_entry_destroy),
239 entry_data);
240 g_signal_connect(
241 textfield,
242 "activate",
243 G_CALLBACK(cell_entry_activate),
244 entry_data);
245 g_signal_connect(
246 textfield,
247 "unmap",
248 G_CALLBACK(cell_entry_unmap),
249 entry_data);
250
251 GtkEventController *focus_controller = gtk_event_controller_focus_new();
252 g_signal_connect(focus_controller,
"leave",
G_CALLBACK(cell_entry_leave_focus), entry_data);
253 gtk_widget_add_controller(textfield, focus_controller);
254 }
else if(type ==
UI_BOOL_EDITABLE) {
255 GtkWidget *checkbox = gtk_check_button_new();
256 gtk_list_item_set_child(item, checkbox);
257 }
else {
258 GtkWidget *label = gtk_label_new(
NULL);
259 gtk_label_set_xalign(
GTK_LABEL(label),
0);
260 gtk_list_item_set_child(item, label);
261 }
262 }
263
264 PangoAttrList* textstyle2pangoattributes(UiTextStyle style) {
265 PangoAttrList *attr = pango_attr_list_new();
266
267 if(style.text_style &
UI_TEXT_STYLE_BOLD) {
268 pango_attr_list_insert(attr, pango_attr_weight_new(
PANGO_WEIGHT_BOLD));
269 }
270 if(style.text_style &
UI_TEXT_STYLE_ITALIC) {
271 pango_attr_list_insert(attr, pango_attr_style_new(
PANGO_STYLE_ITALIC));
272 }
273 if(style.text_style &
UI_TEXT_STYLE_UNDERLINE) {
274 pango_attr_list_insert(attr, pango_attr_underline_new(
PANGO_UNDERLINE_SINGLE));
275 }
276
277
278 guint16 r = (guint16)style.fg.red *
257;
279 guint16 g = (guint16)style.fg.green *
257;
280 guint16 b = (guint16)style.fg.blue *
257;
281 pango_attr_list_insert(attr, pango_attr_foreground_new(r, g, b));
282
283 return attr;
284 }
285
286 static void column_factory_bind(GtkListItemFactory *unused, GtkListItem *item, gpointer userdata) {
287 UiColData *col = userdata;
288 UiList *list = col->listview->var ? col->listview->var->value :
NULL;
289 UiListView *listview = col->listview;
290 int datacolumn = listview->columns[col->column];
291
292 ObjWrapper *obj = gtk_list_item_get_item(item);
293 UiModel *model = col->listview->model;
294 UiModelType type = model->types[col->column];
295
296
297 CxHashKey row_key = cx_hash_key(&obj->i,
sizeof(
int));
298 UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
299 if(row) {
300 if(row->items[col->column] ==
NULL) {
301 row->bound++;
302 }
303 }
else {
304 row = calloc(
1,
sizeof(UiRowItems) + listview->numcolumns *
sizeof(GtkListItem*));
305 cxMapPut(listview->bound_rows, row_key, row);
306 row->bound =
1;
307 }
308 row->items[col->column] = item;
309
310 UiBool freevalue =
FALSE;
311 void *data = listview->getvalue(list, obj->data, obj->i, datacolumn, listview->getvaluedata, &freevalue);
312 GtkWidget *child = gtk_list_item_get_child(item);
313
314 PangoAttrList *attributes =
NULL;
315 UiTextStyle style = {
0,
0 };
316 if(listview->getstyle) {
317
318 if(obj->i != listview->current_row) {
319 listview->current_row = obj->i;
320 listview->row_style = (UiTextStyle){
0,
0 };
321 listview->apply_row_style = listview->getstyle(list, obj->data, obj->i,
-1, listview->getstyledata, &listview->row_style);
322 style = listview->row_style;
323 if(listview->apply_row_style) {
324 pango_attr_list_unref(listview->current_row_attributes);
325 listview->current_row_attributes = textstyle2pangoattributes(style);
326 }
327 }
328
329 int style_col = datacolumn;
330 if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
331 style_col++;
332 }
333
334
335 if(listview->getstyle(list, obj->data, obj->i, style_col, listview->getstyledata, &style)) {
336 attributes = textstyle2pangoattributes(style);
337 }
else if(listview->apply_row_style) {
338 attributes = listview->current_row_attributes;
339 }
340 }
341
342 switch(type) {
343 case UI_STRING_FREE: {
344 freevalue =
TRUE;
345 }
346 case UI_STRING: {
347 gtk_label_set_label(
GTK_LABEL(child), data);
348 if(freevalue) {
349 free(data);
350 }
351 gtk_label_set_attributes(
GTK_LABEL(child), attributes);
352 break;
353 }
354 case UI_INTEGER: {
355 intptr_t intvalue = (
intptr_t)data;
356 char buf[
32];
357 snprintf(buf,
32,
"%d", (
int)intvalue);
358 gtk_label_set_label(
GTK_LABEL(child), buf);
359 gtk_label_set_attributes(
GTK_LABEL(child), attributes);
360 break;
361 }
362 case UI_ICON: {
363 UiIcon *icon = data;
364 if(icon) {
365 gtk_image_set_from_paintable(
GTK_IMAGE(child),
GDK_PAINTABLE(icon->info));
366 }
367 break;
368 }
369 case UI_ICON_TEXT: {
370
371 }
372 case UI_ICON_TEXT_FREE: {
373 void *data2 = listview->getvalue(list, obj->data, obj->i, datacolumn
+1, listview->getvaluedata, &freevalue);
374 if(type ==
UI_ICON_TEXT_FREE) {
375 freevalue =
TRUE;
376 }
377 GtkWidget *image = g_object_get_data(
G_OBJECT(child),
"image");
378 GtkWidget *label = g_object_get_data(
G_OBJECT(child),
"label");
379 if(data && image) {
380 UiIcon *icon = data;
381 gtk_image_set_from_paintable(
GTK_IMAGE(image),
GDK_PAINTABLE(icon->info));
382 }
383 if(data2 && label) {
384 gtk_label_set_label(
GTK_LABEL(label), data2);
385 gtk_label_set_attributes(
GTK_LABEL(label), attributes);
386 }
387 if(freevalue) {
388 free(data2);
389 }
390 break;
391 }
392 case UI_STRING_EDITABLE: {
393 UiCellEntry *entry = g_object_get_data(
G_OBJECT(child),
"ui_entry_data");
394 if(entry) {
395 entry->listview = col->listview;
396 entry->row = obj->i;
397 entry->col = datacolumn;
398 entry->previous_value = strdup(data);
399 }
400 ENTRY_SET_TEXT(child, data);
401 break;
402 }
403 case UI_BOOL_EDITABLE: {
404 intptr_t i = (
intptr_t)data;
405 gtk_check_button_set_active(
GTK_CHECK_BUTTON(child), (gboolean)i);
406 break;
407 }
408 }
409
410 if(attributes != listview->current_row_attributes) {
411 pango_attr_list_unref(attributes);
412 }
413 }
414
415 static void column_factory_unbind(GtkSignalListItemFactory *self, GtkListItem *item, UiColData *col) {
416 ObjWrapper *obj = gtk_list_item_get_item(item);
417 UiListView *listview = col->listview;
418 CxHashKey row_key = cx_hash_key(&obj->i,
sizeof(
int));
419 UiRowItems *row = cxMapGet(listview->bound_rows, row_key);
420 if(row) {
421 row->items[col->column] =
NULL;
422 row->bound--;
423 if(row->bound ==
0) {
424 cxMapRemove(listview->bound_rows, row_key);
425 }
426 }
427
428 GtkWidget *child = gtk_list_item_get_child(item);
429 UiCellEntry *entry = g_object_get_data(
G_OBJECT(child),
"ui_entry_data");
430 if(entry) {
431 cell_save_value(entry,
FALSE);
432 entry->listview =
NULL;
433 free(entry->previous_value);
434 entry->previous_value =
NULL;
435 }
else if(
GTK_IS_CHECK_BUTTON(child)) {
436
437 }
438 }
439
440
441 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
442 GtkSelectionModel *selection_model;
443 if(multiselection) {
444 selection_model =
GTK_SELECTION_MODEL(gtk_multi_selection_new(
G_LIST_MODEL(liststore)));
445 }
else {
446 selection_model =
GTK_SELECTION_MODEL(gtk_single_selection_new(
G_LIST_MODEL(liststore)));
447 gtk_single_selection_set_can_unselect(
GTK_SINGLE_SELECTION(selection_model),
TRUE);
448 gtk_single_selection_set_autoselect(
GTK_SINGLE_SELECTION(selection_model),
FALSE);
449 }
450 g_signal_connect(selection_model,
"selection-changed",
G_CALLBACK(ui_listview_selection_changed), listview);
451 return selection_model;
452 }
453
454 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
455
456
457 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"",
-1);
458 args->model = model;
459
460 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
461 UiListView *listview = create_listview(obj, args);
462 if(!args->getvalue && !args->getvalue2) {
463 listview->getvalue = str_getvalue;
464 }
465
466 listview->numcolumns =
1;
467 listview->columns = malloc(
sizeof(
int));
468 listview->columns[
0] =
0;
469
470 listview->bound_rows = cxHashMapCreate(
NULL,
CX_STORE_POINTERS,
128);
471 listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
472
473 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
474 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), &listview->coldata);
475 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), &listview->coldata);
476 g_signal_connect(factory,
"unbind",
G_CALLBACK(column_factory_unbind), &listview->coldata);
477
478 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args->multiselection);
479 GtkWidget *view = gtk_list_view_new(
GTK_SELECTION_MODEL(selection_model), factory);
480
481 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
482
483
484 listview->widget = view;
485 listview->var = var;
486 listview->liststore = ls;
487 listview->selectionmodel = selection_model;
488 g_signal_connect(
489 view,
490 "destroy",
491 G_CALLBACK(ui_listview_destroy),
492 listview);
493
494
495 if(var && var->value) {
496 UiList *list = var->value;
497
498 list->obj = listview;
499 list->update = ui_listview_update2;
500 list->getselection = ui_listview_getselection2;
501 list->setselection = ui_listview_setselection2;
502
503 ui_update_liststore(ls, list);
504 }
else if (args->static_elements && args->static_nelm >
0) {
505 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
506 listview->getvalue = str_getvalue;
507 ui_update_liststore_static(ls, listview->elements, listview->nelm);
508 }
509
510
511 if(args->onactivate) {
512
513
514
515 g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), listview);
516 }
517 if(args->contextmenu) {
518 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
519 ui_widget_set_contextmenu(view, menu);
520 }
521
522
523 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
524 gtk_scrolled_window_set_policy(
525 GTK_SCROLLED_WINDOW(scroll_area),
526 GTK_POLICY_AUTOMATIC,
527 GTK_POLICY_AUTOMATIC);
528 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
529
530 if(args->width >
0 || args->height >
0) {
531 int width = args->width;
532 int height = args->height;
533 if(width ==
0) {
534 width =
-1;
535 }
536 if(height ==
0) {
537 height =
-1;
538 }
539 gtk_widget_set_size_request(scroll_area, width, height);
540 }
541
542 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
543 UiLayout layout =
UI_ARGS2LAYOUT(args);
544 ct->add(ct, scroll_area, &layout);
545
546 return scroll_area;
547 }
548
549 UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
550
551
552 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"",
-1);
553 args->model = model;
554
555 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
556 UiListView *listview = create_listview(obj, args);
557
558 if(!args->getvalue && !args->getvalue2) {
559 listview->getvalue = str_getvalue;
560 }
561
562 listview->numcolumns =
1;
563 listview->columns = malloc(
sizeof(
int));
564 listview->columns[
0] =
0;
565
566 listview->bound_rows = cxHashMapCreate(
NULL,
CX_STORE_POINTERS,
128);
567 listview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
568
569 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
570 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), &listview->coldata);
571 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), &listview->coldata);
572
573 GtkWidget *view = gtk_drop_down_new(
G_LIST_MODEL(ls),
NULL);
574 gtk_drop_down_set_factory(
GTK_DROP_DOWN(view), factory);
575 if(args->width >
0) {
576 gtk_widget_set_size_request(view, args->width,
-1);
577 }
578
579 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
580
581
582 listview->widget = view;
583 listview->var = var;
584 listview->liststore = ls;
585 listview->selectionmodel =
NULL;
586 g_signal_connect(
587 view,
588 "destroy",
589 G_CALLBACK(ui_listview_destroy),
590 listview);
591
592
593 if(var && var->value) {
594 UiList *list = var->value;
595
596 list->obj = listview;
597 list->update = ui_listview_update2;
598 list->getselection = ui_dropdown_getselection;
599 list->setselection = ui_dropdown_setselection;
600
601 ui_update_liststore(ls, list);
602 }
else if (args->static_elements && args->static_nelm >
0) {
603 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
604 listview->getvalue = str_getvalue;
605 ui_update_liststore_static(ls, listview->elements, listview->nelm);
606 }
607
608
609 if(args->onactivate) {
610 g_signal_connect(view,
"notify::selected",
G_CALLBACK(ui_dropdown_notify), listview);
611 }
612
613
614 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
615 UiLayout layout =
UI_ARGS2LAYOUT(args);
616 ct->add(ct, view, &layout);
617
618 return view;
619 }
620
621 void ui_listview_select(
UIWIDGET listview,
int index) {
622 GtkSelectionModel *model = gtk_list_view_get_model(
GTK_LIST_VIEW(listview));
623 gtk_selection_model_select_item(model, index,
TRUE);
624 }
625
626 void ui_dropdown_select(
UIWIDGET dropdown,
int index) {
627 gtk_drop_down_set_selected(
GTK_DROP_DOWN(dropdown), index);
628 }
629
630 static void add_column(UiListView *tableview,
int index) {
631 UiModel *model = tableview->model;
632
633 UiColData *col = malloc(
sizeof(UiColData));
634 col->listview = tableview;
635 col->column = index;
636
637 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
638 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), col);
639 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), col);
640 g_object_set_data_full(
G_OBJECT(factory),
"coldata", col, (GDestroyNotify)free);
641
642 GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[index], factory);
643 gtk_column_view_column_set_resizable(column, true);
644 gtk_column_view_insert_column(
GTK_COLUMN_VIEW(tableview->widget), index, column);
645
646 int size = model->columnsize[index];
647 if(size >
0) {
648 gtk_column_view_column_set_fixed_width(column, size);
649 }
else if(size <
0) {
650 gtk_column_view_column_set_expand(column,
TRUE);
651 }
652 }
653
654 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
655 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
656
657
658
659
660 UiListView *tableview = create_listview(obj, args);
661
662 GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args->multiselection);
663 GtkWidget *view = gtk_column_view_new(
GTK_SELECTION_MODEL(selection_model));
664
665 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
666
667
668 tableview->widget = view;
669 tableview->var = var;
670 tableview->liststore = ls;
671 tableview->selectionmodel = selection_model;
672 g_signal_connect(
673 view,
674 "destroy",
675 G_CALLBACK(ui_listview_destroy),
676 tableview);
677
678
679
680 UiModel *model = args->model;
681 int columns =
0;
682 if(model) {
683 columns = model->columns;
684 ui_model_add_observer(model, ui_listview_update_model, tableview);
685 }
686
687 tableview->columns = calloc(columns,
sizeof(
int));
688 tableview->numcolumns = columns;
689
690 tableview->bound_rows = cxHashMapCreate(
NULL,
CX_STORE_POINTERS,
128);
691 tableview->bound_rows->collection.simple_destructor = (cx_destructor_func)free;
692
693 int addi =
0;
694 for(
int i=
0;i<columns;i++) {
695 tableview->columns[i] = i+addi;
696
697 if(model->types[i] ==
UI_ICON_TEXT || model->types[i] ==
UI_ICON_TEXT_FREE) {
698
699 addi++;
700 }
701
702 add_column(tableview, i);
703 }
704
705
706 if(var && var->value) {
707 UiList *list = var->value;
708
709 list->obj = tableview;
710 list->update = ui_listview_update2;
711 list->getselection = ui_listview_getselection2;
712 list->setselection = ui_listview_setselection2;
713
714 ui_update_liststore(ls, list);
715 }
716
717
718 if(args->onactivate) {
719 g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), tableview);
720 }
721 if(args->contextmenu) {
722 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
723 ui_widget_set_contextmenu(view, menu);
724 }
725
726
727 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
728 gtk_scrolled_window_set_policy(
729 GTK_SCROLLED_WINDOW(scroll_area),
730 GTK_POLICY_AUTOMATIC,
731 GTK_POLICY_AUTOMATIC);
732 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
733
734 if(args->width >
0 || args->height >
0) {
735 int width = args->width;
736 int height = args->height;
737 if(width ==
0) {
738 width =
-1;
739 }
740 if(height ==
0) {
741 height =
-1;
742 }
743 gtk_widget_set_size_request(scroll_area, width, height);
744 }
745
746 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
747 UiLayout layout =
UI_ARGS2LAYOUT(args);
748 ct->add(ct, scroll_area, &layout);
749
750 return scroll_area;
751 }
752
753 void ui_listview_update_model(UiModel *model,
void *userdata,
int insert_index,
int delete_index) {
754 UiListView *listview = userdata;
755 if(insert_index >= listview->numcolumns) {
756 listview->numcolumns = insert_index
+1;
757 listview->columns = realloc(listview->columns, listview->numcolumns *
sizeof(UiColData));
758 }
759
760 gtk_column_view_set_model(
GTK_COLUMN_VIEW(listview->widget),
NULL);
761 cxMapClear(listview->bound_rows);
762
763 if(insert_index) {
764 int prev =
0;
765 if(insert_index >
0) {
766 prev = listview->columns[insert_index
-1];
767 }
768 listview->columns[insert_index] = prev
+1;
769 add_column(listview, insert_index);
770
771 if(insert_index
+1 < listview->numcolumns) {
772
773 UiModelType type = model->types[insert_index];
774 int add =
1;
775 if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
776 add++;
777 }
778 for(
int i=insert_index
+1;i<listview->numcolumns;i++) {
779 listview->columns[i] += add;
780 }
781 }
782 }
783
784 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
785 GtkSelectionModel *selection_model = create_selection_model(listview, ls, listview->multiselection);
786 gtk_column_view_set_model(
GTK_COLUMN_VIEW(listview->widget), selection_model);
787 listview->selectionmodel = selection_model;
788 listview->liststore = ls;
789
790 if(listview->var) {
791 UiList *list = listview->var->value;
792 ui_list_update(list);
793 }
794 }
795
796 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
797 UiListSelection sel = {
0,
NULL };
798 GtkBitset *bitset = gtk_selection_model_get_selection(model);
799 int n = gtk_bitset_get_size(bitset);
800 printf(
"bitset %d\n", n);
801
802 gtk_bitset_unref(bitset);
803 return sel;
804 }
805
806 static void listview_event(ui_callback cb,
void *cbdata, UiListView *view) {
807 UiEvent event;
808 event.obj = view->obj;
809 event.document = event.obj->ctx->document;
810 event.window = event.obj->window;
811 event.intval = view->selection.count;
812 event.eventdata = &view->selection;
813 event.eventdatatype =
UI_EVENT_DATA_LIST_SELECTION;
814 event.set = ui_get_setop();
815 if(cb) {
816 cb(&event, cbdata);
817 }
818 }
819
820 static void listview_update_selection(UiListView *view) {
821 free(view->selection.rows);
822 view->selection.count =
0;
823 view->selection.rows =
NULL;
824
825 CX_ARRAY_DECLARE(
int, newselection);
826 cx_array_initialize(newselection,
8);
827
828 size_t nitems = g_list_model_get_n_items(
G_LIST_MODEL(view->liststore));
829
830 for(
size_t i=
0;i<nitems;i++) {
831 if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
832 int s = (
int)i;
833 cx_array_simple_add(newselection, s);
834 }
835 }
836
837 if(newselection_size >
0) {
838 view->selection.count = newselection_size;
839 view->selection.rows = newselection;
840 }
else {
841 free(newselection);
842 }
843 }
844
845 void ui_dropdown_notify(GtkWidget *dropdown, GObject *pspec, gpointer userdata) {
846 UiListView *view = userdata;
847 guint index = gtk_drop_down_get_selected(
GTK_DROP_DOWN(dropdown));
848 GObject *item = gtk_drop_down_get_selected_item(
GTK_DROP_DOWN(dropdown));
849 if(item && view->onactivate) {
850 ObjWrapper *eventdata = (ObjWrapper*)item;
851 UiEvent event;
852 event.obj = view->obj;
853 event.document = event.obj->ctx->document;
854 event.window = event.obj->window;
855 event.intval = index;
856 event.eventdata = eventdata->data;
857 event.eventdatatype =
UI_EVENT_DATA_LIST_ELM;
858 event.set = ui_get_setop();
859 view->onactivate(&event, view->onactivatedata);
860 }
861 }
862
863
864 void ui_columnview_activate(
void *ignore, guint position, gpointer userdata) {
865 UiListView *view = userdata;
866 if(view->selection.count ==
0) {
867 listview_update_selection(view);
868 }
869 listview_event(view->onactivate, view->onactivatedata, view);
870 }
871
872 void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) {
873 UiListView *view = userdata;
874 listview_update_selection(view);
875 listview_event(view->onselection, view->onselectiondata, view);
876 }
877
878 void ui_dropdown_activate(GtkDropDown* self, gpointer userdata) {
879 UiListView *view = userdata;
880 guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
881 UiListSelection sel = {
0,
NULL };
882 int sel2 = (
int)selection;
883 if(selection !=
GTK_INVALID_LIST_POSITION) {
884 sel.count =
1;
885 sel.rows = &sel2;
886 }
887
888 if(view->onactivate) {
889 UiEvent event;
890 event.obj = view->obj;
891 event.document = event.obj->ctx->document;
892 event.window = event.obj->window;
893 event.intval = view->selection.count;
894 event.eventdata = &view->selection;
895 event.eventdatatype =
UI_EVENT_DATA_LIST_SELECTION;
896 event.set = ui_get_setop();
897 view->onactivate(&event, view->onactivatedata);
898 }
899 }
900
901 void ui_update_liststore(GListStore *liststore, UiList *list) {
902 g_list_store_remove_all(liststore);
903 int i =
0;
904 void *elm = list->first(list);
905 while(elm) {
906 ObjWrapper *obj = obj_wrapper_new(elm, i++);
907 g_list_store_append(liststore, obj);
908 elm = list->next(list);
909 }
910 }
911
912 void ui_update_liststore_static(GListStore *liststore,
char **elm,
size_t nelm) {
913 g_list_store_remove_all(liststore);
914 for(
int i=
0;i<nelm;i++) {
915 ObjWrapper *obj = obj_wrapper_new(elm[i], i);
916 g_list_store_append(liststore, obj);
917 }
918 }
919
920 void ui_listview_update2(UiList *list,
int i) {
921 UiListView *view = list->obj;
922 view->current_row =
-1;
923 if(i <
0) {
924 cxMapClear(view->bound_rows);
925 ui_update_liststore(view->liststore, list);
926 }
else {
927 void *value = list->get(list, i);
928 if(value) {
929 ObjWrapper *obj = g_list_model_get_item(
G_LIST_MODEL(view->liststore), i);
930 if(obj) {
931 obj->data = value;
932 }
933
934 CxHashKey row_key = cx_hash_key(&i,
sizeof(
int));
935 UiRowItems *row = cxMapGet(view->bound_rows, row_key);
936 if(row) {
937 UiColData coldata;
938 coldata.listview = view;
939 for(
int c=
0;c<view->numcolumns;c++) {
940 if(row->items[c] !=
NULL) {
941 coldata.column = c;
942 column_factory_bind(
NULL, row->items[c], &coldata);
943 }
944 }
945 }
946 }
947 }
948 }
949
950 UiListSelection ui_listview_getselection2(UiList *list) {
951 UiListView *view = list->obj;
952 UiListSelection selection;
953 selection.count = view->selection.count;
954 selection.rows = calloc(selection.count,
sizeof(
int));
955 memcpy(selection.rows, view->selection.rows, selection.count*
sizeof(
int));
956 return selection;
957 }
958
959 void ui_listview_setselection2(UiList *list, UiListSelection selection) {
960 ui_setop_enable(
TRUE);
961 UiListView *view = list->obj;
962 UiListSelection newselection;
963 newselection.count = view->selection.count;
964 if(selection.count >
0) {
965 newselection.rows = calloc(newselection.count,
sizeof(
int));
966 memcpy(newselection.rows, selection.rows, selection.count*
sizeof(
int));
967 }
else {
968 newselection.rows =
NULL;
969 }
970 free(view->selection.rows);
971 view->selection = newselection;
972
973 gtk_selection_model_unselect_all(view->selectionmodel);
974 if(selection.count >
0) {
975 for(
int i=
0;i<selection.count;i++) {
976 gtk_selection_model_select_item(view->selectionmodel, selection.rows[i],
FALSE);
977 }
978 }
979 ui_setop_enable(
FALSE);
980 }
981
982 UiListSelection ui_dropdown_getselection(UiList *list) {
983 UiListView *view = list->obj;
984 guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
985 UiListSelection sel = {
0,
NULL };
986 if(selection !=
GTK_INVALID_LIST_POSITION) {
987 sel.count =
1;
988 sel.rows = malloc(
sizeof(
int));
989 sel.rows[
0] = (
int)selection;
990 }
991 return sel;
992 }
993
994 void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
995 ui_setop_enable(
TRUE);
996 UiListView *view = list->obj;
997 if(selection.count >
0) {
998 gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget), selection.rows[
0]);
999 }
else {
1000 gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget),
GTK_INVALID_LIST_POSITION);
1001 }
1002 ui_setop_enable(
FALSE);
1003 }
1004
1005 #else
1006
1007 static void update_list_row(UiListView *listview, GtkListStore *store, GtkTreeIter *iter, UiList *list,
void *elm,
int row) {
1008 UiModel *model = listview->model;
1009 ui_getstylefunc getstyle = listview->getstyle;
1010
1011
1012 UiBool style_set =
FALSE;
1013 UiTextStyle style = {
0,
0 };
1014 if(getstyle) {
1015 style_set = getstyle(list, elm, row,
-1, listview->getstyledata, &style);
1016 }
1017
1018
1019 int c =
0;
1020 for(
int i=
0;i<model->columns;i++,c++) {
1021 UiBool freevalue =
FALSE;
1022 void *data = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
1023
1024 UiModelType type = model->types[i];
1025
1026 if(getstyle) {
1027
1028 int style_col = c;
1029 if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
1030 style_col++;
1031 }
1032
1033
1034
1035
1036 if(getstyle(list, elm, row, style_col, listview->getstyledata, &style)) {
1037 style_set =
TRUE;
1038 }
1039 }
1040
1041 GValue value =
G_VALUE_INIT;
1042 switch(type) {
1043 case UI_STRING_FREE: {
1044 freevalue =
TRUE;
1045 }
1046 case UI_STRING: {
1047 g_value_init(&value,
G_TYPE_STRING);
1048 g_value_set_string(&value, data);
1049 if(freevalue) {
1050 free(data);
1051 }
1052 break;
1053 }
1054 case UI_INTEGER: {
1055 g_value_init(&value,
G_TYPE_INT);
1056 intptr_t intptr = (
intptr_t)data;
1057 g_value_set_int(&value, (
int)intptr);
1058 break;
1059 }
1060 case UI_ICON: {
1061 g_value_init(&value,
G_TYPE_OBJECT);
1062 UiIcon *icon = data;
1063 #if GTK_MAJOR_VERSION >=
4
1064 g_value_set_object(&value, icon->info);
1065 #else
1066 if(!icon->pixbuf && icon->info) {
1067 GError *error =
NULL;
1068 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
1069 icon->pixbuf = pixbuf;
1070 }
1071
1072 if(icon->pixbuf) {
1073 g_value_set_object(&value, icon->pixbuf);
1074 }
1075 #endif
1076 break;
1077 }
1078 case UI_ICON_TEXT:
1079 case UI_ICON_TEXT_FREE: {
1080 UiIcon *icon = data;
1081 #if GTK_MAJOR_VERSION >=
4
1082 if(icon) {
1083 GValue iconvalue =
G_VALUE_INIT;
1084 g_value_init(&iconvalue,
G_TYPE_OBJECT);
1085 g_value_set_object(&iconvalue, ui_icon_pixbuf(icon));
1086 gtk_list_store_set_value(store, &iter, c, &iconvalue);
1087 }
1088 #else
1089 GValue pixbufvalue =
G_VALUE_INIT;
1090 if(icon) {
1091 if(!icon->pixbuf && icon->info) {
1092 GError *error =
NULL;
1093 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
1094 icon->pixbuf = pixbuf;
1095 }
1096 g_value_init(&pixbufvalue,
G_TYPE_OBJECT);
1097 g_value_set_object(&pixbufvalue, icon->pixbuf);
1098 gtk_list_store_set_value(store, iter, c, &pixbufvalue);
1099 }
1100 #endif
1101 c++;
1102
1103 freevalue =
FALSE;
1104 char *str = listview->getvalue(list, elm, row, c, listview->getvaluedata, &freevalue);
1105 g_value_init(&value,
G_TYPE_STRING);
1106 g_value_set_string(&value, str);
1107 if(model->types[i] ==
UI_ICON_TEXT_FREE || freevalue) {
1108 free(str);
1109 }
1110 break;
1111 }
1112 }
1113
1114 gtk_list_store_set_value(store, iter, c, &value);
1115
1116 if(style_set) {
1117 int soff = listview->style_offset + i*
6;
1118
1119 GValue style_set_value =
G_VALUE_INIT;
1120 g_value_init(&style_set_value,
G_TYPE_BOOLEAN);
1121 g_value_set_boolean(&style_set_value,
TRUE);
1122 gtk_list_store_set_value(store, iter, soff, &style_set_value);
1123
1124 GValue style_weight_value =
G_VALUE_INIT;
1125 g_value_init(&style_weight_value,
G_TYPE_INT);
1126 if(style.text_style &
UI_TEXT_STYLE_BOLD) {
1127 g_value_set_int(&style_weight_value,
600);
1128 }
else {
1129 g_value_set_int(&style_weight_value,
400);
1130 }
1131 gtk_list_store_set_value(store, iter, soff +
1, &style_weight_value);
1132
1133 GValue style_underline_value =
G_VALUE_INIT;
1134 g_value_init(&style_underline_value,
G_TYPE_INT);
1135 if(style.text_style &
UI_TEXT_STYLE_UNDERLINE) {
1136 g_value_set_int(&style_underline_value,
PANGO_UNDERLINE_SINGLE);
1137 }
else {
1138 g_value_set_int(&style_underline_value,
PANGO_UNDERLINE_NONE);
1139 }
1140 gtk_list_store_set_value(store, iter, soff +
2, &style_underline_value);
1141
1142 GValue style_italic_value =
G_VALUE_INIT;
1143 g_value_init(&style_italic_value,
G_TYPE_INT);
1144 if(style.text_style &
UI_TEXT_STYLE_ITALIC) {
1145 g_value_set_int(&style_italic_value,
PANGO_STYLE_ITALIC);
1146 }
else {
1147 g_value_set_int(&style_italic_value,
PANGO_STYLE_NORMAL);
1148 }
1149 gtk_list_store_set_value(store, iter, soff +
3, &style_italic_value);
1150
1151 GValue style_fgset_value =
G_VALUE_INIT;
1152 g_value_init(&style_fgset_value,
G_TYPE_BOOLEAN);
1153 g_value_set_boolean(&style_fgset_value, style.fg_set);
1154 gtk_list_store_set_value(store, iter, soff +
4, &style_fgset_value);
1155
1156 if(style.fg_set) {
1157 char buf[
8];
1158 snprintf(buf,
8,
"#%02X%02X%02X", (
int)style.fg.red, (
int)style.fg.green, (
int)style.fg.blue);
1159
1160 GValue style_fg_value =
G_VALUE_INIT;
1161 g_value_init(&style_fg_value,
G_TYPE_STRING);
1162 g_value_set_string(&style_fg_value, buf);
1163 gtk_list_store_set_value(store, iter, soff +
5, &style_fg_value);
1164 }
1165 }
1166 }
1167 }
1168
1169 static GtkListStore* create_list_store(UiListView *listview, UiList *list) {
1170 UiModel *model = listview->model;
1171 int columns = model->columns;
1172 GType *types = calloc(columns*
8,
sizeof(GType));
1173 int c =
0;
1174 for(
int i=
0;i<columns;i++,c++) {
1175 switch(model->types[i]) {
1176 case UI_STRING:
1177 case UI_STRING_FREE: types[c] =
G_TYPE_STRING;
break;
1178 case UI_INTEGER: types[c] =
G_TYPE_INT;
break;
1179 case UI_ICON: types[c] =
G_TYPE_OBJECT;
break;
1180 case UI_ICON_TEXT:
1181 case UI_ICON_TEXT_FREE: {
1182 types[c] =
G_TYPE_OBJECT;
1183 types[++c] =
G_TYPE_STRING;
1184 }
1185 }
1186 }
1187 int s =
0;
1188 for(
int i=
0;i<columns;i++) {
1189 types[listview->style_offset+s] =
G_TYPE_BOOLEAN; s++;
1190 types[listview->style_offset+s] =
G_TYPE_INT; s++;
1191 types[listview->style_offset+s] =
G_TYPE_INT; s++;
1192 types[listview->style_offset+s] =
G_TYPE_INT; s++;
1193 types[listview->style_offset+s] =
G_TYPE_BOOLEAN; s++;
1194 types[listview->style_offset+s] =
G_TYPE_STRING; s++;
1195 }
1196
1197 GtkListStore *store = gtk_list_store_newv(c+s, types);
1198 free(types);
1199
1200 if(list) {
1201 void *elm = list->first(list);
1202 int i =
0;
1203 while(elm) {
1204
1205 GtkTreeIter iter;
1206 gtk_list_store_insert (store, &iter,
-1);
1207
1208 update_list_row(listview, store, &iter, list, elm, i++);
1209
1210
1211 elm = list->next(list);
1212 }
1213 }
1214
1215 return store;
1216 }
1217
1218
1219 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs *args) {
1220
1221 GtkWidget *view = gtk_tree_view_new();
1222 ui_set_name_and_style(view, args->name, args->style_class);
1223 ui_set_widget_states(obj->ctx, view, args->states);
1224 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1225 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
NULL, renderer,
"text",
0,
NULL);
1226 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
1227
1228 gtk_tree_view_set_headers_visible(
GTK_TREE_VIEW(view),
FALSE);
1229 #ifdef UI_GTK3
1230 #if GTK_MINOR_VERSION >=
8
1231
1232 #else
1233
1234 #endif
1235 #else
1236
1237 #endif
1238
1239 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"",
-1);
1240
1241 UiListView *listview = create_listview(obj, args);
1242 listview->style_offset =
1;
1243 if(!args->getvalue && !args->getvalue2) {
1244 listview->getvalue = str_getvalue;
1245 }
1246 listview->model = model;
1247 g_signal_connect(
1248 view,
1249 "destroy",
1250 G_CALLBACK(ui_listview_destroy),
1251 listview);
1252
1253 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
1254
1255
1256 listview->widget = view;
1257 listview->var = var;
1258
1259 UiList *list = var ? var->value :
NULL;
1260 GtkListStore *listmodel = create_list_store(listview, list);
1261 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
1262 g_object_unref(listmodel);
1263
1264
1265 list->update = ui_listview_update;
1266 list->getselection = ui_listview_getselection;
1267 list->setselection = ui_listview_setselection;
1268 list->obj = listview;
1269
1270
1271 UiTreeEventData *event = malloc(
sizeof(UiTreeEventData));
1272 event->obj = obj;
1273 event->activate = args->onactivate;
1274 event->activatedata = args->onactivatedata;
1275 event->selection = args->onselection;
1276 event->selectiondata = args->onselectiondata;
1277 g_signal_connect(
1278 view,
1279 "destroy",
1280 G_CALLBACK(ui_destroy_userdata),
1281 event);
1282
1283 if(args->onactivate) {
1284 g_signal_connect(
1285 view,
1286 "row-activated",
1287 G_CALLBACK(ui_listview_activate_event),
1288 event);
1289 }
1290 if(args->onselection) {
1291 GtkTreeSelection *selection = gtk_tree_view_get_selection(
1292 GTK_TREE_VIEW(view));
1293 g_signal_connect(
1294 selection,
1295 "changed",
1296 G_CALLBACK(ui_listview_selection_event),
1297 event);
1298 }
1299 if(args->contextmenu) {
1300 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, view);
1301 ui_widget_set_contextmenu(view, menu);
1302 }
1303
1304
1305
1306 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
1307 gtk_scrolled_window_set_policy(
1308 GTK_SCROLLED_WINDOW(scroll_area),
1309 GTK_POLICY_AUTOMATIC,
1310 GTK_POLICY_AUTOMATIC);
1311 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
1312
1313 if(args->width >
0 || args->height >
0) {
1314 int width = args->width;
1315 int height = args->height;
1316 if(width ==
0) {
1317 width =
-1;
1318 }
1319 if(height ==
0) {
1320 height =
-1;
1321 }
1322 gtk_widget_set_size_request(scroll_area, width, height);
1323 }
1324
1325 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1326 UiLayout layout =
UI_ARGS2LAYOUT(args);
1327 ct->add(ct, scroll_area, &layout);
1328
1329 return scroll_area;
1330 }
1331
1332 void ui_listview_select(
UIWIDGET listview,
int index) {
1333 GtkTreeSelection *sel = gtk_tree_view_get_selection(
GTK_TREE_VIEW(listview));
1334 GtkTreePath *path = gtk_tree_path_new_from_indicesv(&index,
1);
1335 gtk_tree_selection_select_path(sel, path);
1336
1337 }
1338
1339 void ui_dropdown_select(
UIWIDGET dropdown,
int index) {
1340 gtk_combo_box_set_active(
GTK_COMBO_BOX(dropdown), index);
1341 }
1342
1343 UIWIDGET ui_table_create(UiObject *obj, UiListArgs *args) {
1344
1345 GtkWidget *view = gtk_tree_view_new();
1346
1347 UiModel *model = args->model;
1348 int columns = model ? model->columns :
0;
1349
1350
1351 int addi =
0;
1352 int style_offset =
0;
1353 int i =
0;
1354 for(;i<columns;i++) {
1355 if(model->types[i] ==
UI_ICON_TEXT || model->types[i] ==
UI_ICON_TEXT_FREE) {
1356 addi++;
1357 }
1358 }
1359 style_offset = i+addi;
1360
1361
1362 addi =
0;
1363 for(i=
0;i<columns;i++) {
1364 GtkTreeViewColumn *column =
NULL;
1365 if(model->types[i] ==
UI_ICON_TEXT || model->types[i] ==
UI_ICON_TEXT_FREE) {
1366 column = gtk_tree_view_column_new();
1367 gtk_tree_view_column_set_title(column, model->titles[i]);
1368
1369 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
1370 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
1371
1372 gtk_tree_view_column_pack_end(column, textrenderer,
TRUE);
1373 gtk_tree_view_column_pack_start(column, iconrenderer,
FALSE);
1374
1375
1376 gtk_tree_view_column_add_attribute(column, iconrenderer,
"pixbuf", addi + i);
1377 gtk_tree_view_column_add_attribute(column, textrenderer,
"text", addi + i
+1);
1378
1379 if(args->getstyle) {
1380 int soff = style_offset + i*
6;
1381 gtk_tree_view_column_add_attribute(column, textrenderer,
"weight-set", soff);
1382 gtk_tree_view_column_add_attribute(column, textrenderer,
"underline-set", soff);
1383 gtk_tree_view_column_add_attribute(column, textrenderer,
"style-set", soff);
1384
1385 gtk_tree_view_column_add_attribute(column, textrenderer,
"weight", soff +
1);
1386 gtk_tree_view_column_add_attribute(column, textrenderer,
"underline", soff +
2);
1387 gtk_tree_view_column_add_attribute(column, textrenderer,
"style", soff +
3);
1388 gtk_tree_view_column_add_attribute(column, textrenderer,
"foreground-set", soff +
4);
1389 gtk_tree_view_column_add_attribute(column, textrenderer,
"foreground", soff +
5);
1390 }
1391
1392 addi++;
1393 }
else if (model->types[i] ==
UI_ICON) {
1394 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
1395 column = gtk_tree_view_column_new_with_attributes(
1396 model->titles[i],
1397 iconrenderer,
1398 "pixbuf",
1399 i + addi,
1400 NULL);
1401 }
else {
1402 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
1403 column = gtk_tree_view_column_new_with_attributes(
1404 model->titles[i],
1405 textrenderer,
1406 "text",
1407 i + addi,
1408 NULL);
1409
1410 if(args->getstyle) {
1411 int soff = style_offset + i*
6;
1412 gtk_tree_view_column_add_attribute(column, textrenderer,
"weight-set", soff);
1413 gtk_tree_view_column_add_attribute(column, textrenderer,
"underline-set", soff);
1414 gtk_tree_view_column_add_attribute(column, textrenderer,
"style-set", soff);
1415
1416 gtk_tree_view_column_add_attribute(column, textrenderer,
"weight", soff +
1);
1417 gtk_tree_view_column_add_attribute(column, textrenderer,
"underline", soff +
2);
1418 gtk_tree_view_column_add_attribute(column, textrenderer,
"style", soff +
3);
1419 gtk_tree_view_column_add_attribute(column, textrenderer,
"foreground-set", soff +
4);
1420 gtk_tree_view_column_add_attribute(column, textrenderer,
"foreground", soff +
5);
1421 }
1422 }
1423
1424 int colsz = model->columnsize[i];
1425 if(colsz >
0) {
1426 gtk_tree_view_column_set_fixed_width(column, colsz);
1427 }
else if(colsz <
0) {
1428 gtk_tree_view_column_set_expand(column,
TRUE);
1429 }
1430
1431 gtk_tree_view_column_set_resizable(column,
TRUE);
1432 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
1433 }
1434
1435
1436 #ifdef UI_GTK3
1437
1438 #else
1439
1440 #endif
1441
1442 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
1443
1444
1445
1446
1447
1448
1449 UiListView *tableview = create_listview(obj, args);
1450 tableview->widget = view;
1451 tableview->style_offset = style_offset;
1452 g_signal_connect(
1453 view,
1454 "destroy",
1455 G_CALLBACK(ui_listview_destroy),
1456 tableview);
1457
1458 UiList *list = var ? var->value :
NULL;
1459 GtkListStore *listmodel = create_list_store(tableview, list);
1460 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
1461 g_object_unref(listmodel);
1462
1463
1464 list->update = ui_listview_update;
1465 list->getselection = ui_listview_getselection;
1466 list->setselection = ui_listview_setselection;
1467 list->obj = tableview;
1468
1469
1470 UiTreeEventData *event = ui_malloc(obj->ctx,
sizeof(UiTreeEventData));
1471 event->obj = obj;
1472 event->activate = args->onactivate;
1473 event->selection = args->onselection;
1474 event->activatedata = args->onactivatedata;
1475 event->selectiondata = args->onselectiondata;
1476 if(args->onactivate) {
1477 g_signal_connect(
1478 view,
1479 "row-activated",
1480 G_CALLBACK(ui_listview_activate_event),
1481 event);
1482 }
1483 if(args->onselection) {
1484 GtkTreeSelection *selection = gtk_tree_view_get_selection(
1485 GTK_TREE_VIEW(view));
1486 g_signal_connect(
1487 selection,
1488 "changed",
1489 G_CALLBACK(ui_listview_selection_event),
1490 event);
1491 }
1492
1493
1494
1495 if(args->ondragstart) {
1496 ui_listview_add_dnd(tableview, args);
1497 }
1498 if(args->ondrop) {
1499 ui_listview_enable_drop(tableview, args);
1500 }
1501
1502 GtkTreeSelection *selection = gtk_tree_view_get_selection (
GTK_TREE_VIEW(view));
1503 if(args->multiselection) {
1504 gtk_tree_selection_set_mode(selection,
GTK_SELECTION_MULTIPLE);
1505 }
1506
1507
1508 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
1509 gtk_scrolled_window_set_policy(
1510 GTK_SCROLLED_WINDOW(scroll_area),
1511 GTK_POLICY_AUTOMATIC,
1512 GTK_POLICY_AUTOMATIC);
1513 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
1514
1515 if(args->width >
0 || args->height >
0) {
1516 int width = args->width;
1517 int height = args->height;
1518 if(width ==
0) {
1519 width =
-1;
1520 }
1521 if(height ==
0) {
1522 height =
-1;
1523 }
1524 gtk_widget_set_size_request(scroll_area, width, height);
1525 }
1526
1527 if(args->contextmenu) {
1528 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, scroll_area);
1529 #if GTK_MAJOR_VERSION >=
4
1530 ui_widget_set_contextmenu(scroll_area, menu);
1531 #else
1532 ui_widget_set_contextmenu(view, menu);
1533 #endif
1534 }
1535
1536 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1537 UiLayout layout =
UI_ARGS2LAYOUT(args);
1538 ct->add(ct, scroll_area, &layout);
1539
1540 return scroll_area;
1541 }
1542
1543
1544
1545 void ui_listview_update(UiList *list,
int i) {
1546 UiListView *view = list->obj;
1547 if(i <
0) {
1548 GtkListStore *store = create_list_store(view, list);
1549 gtk_tree_view_set_model(
GTK_TREE_VIEW(view->widget),
GTK_TREE_MODEL(store));
1550 g_object_unref(
G_OBJECT(store));
1551 }
else {
1552 void *elm = list->get(list, i);
1553 GtkTreeModel *store = gtk_tree_view_get_model(
GTK_TREE_VIEW(view->widget));
1554 GtkTreeIter iter;
1555 if(gtk_tree_model_iter_nth_child(store, &iter,
NULL, i)) {
1556 update_list_row(view,
GTK_LIST_STORE(store), &iter, list, elm, i);
1557 }
1558 }
1559 }
1560
1561 UiListSelection ui_listview_getselection(UiList *list) {
1562 UiListView *view = list->obj;
1563 UiListSelection selection = ui_listview_selection(
1564 gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget)),
1565 NULL);
1566 return selection;
1567 }
1568
1569 void ui_listview_setselection(UiList *list, UiListSelection selection) {
1570 ui_setop_enable(
TRUE);
1571 UiListView *view = list->obj;
1572 GtkTreeSelection *sel = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget));
1573 GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
1574 gtk_tree_selection_select_path(sel, path);
1575
1576 ui_setop_enable(
FALSE);
1577 }
1578
1579
1580
1581
1582
1583 UIWIDGET ui_dropdown_create(UiObject *obj, UiListArgs *args) {
1584 GtkWidget *combobox = gtk_combo_box_new();
1585 if(args->width >
0) {
1586 gtk_widget_set_size_request(combobox, args->width,
-1);
1587 }
1588
1589 ui_set_name_and_style(combobox, args->name, args->style_class);
1590 ui_set_widget_states(obj->ctx, combobox, args->states);
1591 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
1592 UiLayout layout =
UI_ARGS2LAYOUT(args);
1593 ct->add(ct, combobox, &layout);
1594
1595 UiListView *listview = create_listview(obj, args);
1596 listview->widget = combobox;
1597 listview->style_offset =
1;
1598 listview->model = ui_model(obj->ctx,
UI_STRING,
"",
-1);
1599 g_signal_connect(
1600 combobox,
1601 "destroy",
1602 G_CALLBACK(ui_listview_destroy),
1603 listview);
1604
1605 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->list, args->varname,
UI_VAR_LIST);
1606 UiList *list = var ? var->value :
NULL;
1607 GtkListStore *listmodel = create_list_store(listview, list);
1608 if(var) {
1609 listview->var = var;
1610 list->update = ui_combobox_modelupdate;
1611 list->getselection = ui_dropdown_getselection;
1612 list->setselection = ui_dropdown_setselection;
1613 list->obj = listview;
1614 list->update(list,
-1);
1615 }
else if(args->static_nelm >
0) {
1616 listview_copy_static_elements(listview, args->static_elements, args->static_nelm);
1617 for(
int i=
0;i<args->static_nelm;i++) {
1618 GtkTreeIter iter;
1619 GValue value =
G_VALUE_INIT;
1620 gtk_list_store_insert(listmodel, &iter,
-1);
1621 g_value_init(&value,
G_TYPE_STRING);
1622 g_value_set_string(&value, listview->elements[i]);
1623 gtk_list_store_set_value(listmodel, &iter,
0, &value);
1624 }
1625 }
1626
1627 if(listmodel) {
1628 gtk_combo_box_set_model(
GTK_COMBO_BOX(combobox),
GTK_TREE_MODEL(listmodel));
1629 g_object_unref(listmodel);
1630 }
1631
1632 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1633 gtk_cell_layout_pack_start(
GTK_CELL_LAYOUT(combobox), renderer,
TRUE);
1634 gtk_cell_layout_set_attributes(
1635 GTK_CELL_LAYOUT(combobox),
1636 renderer,
1637 "text",
1638 0,
1639 NULL);
1640 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox),
0);
1641
1642
1643 if(args->onactivate) {
1644 UiEventData *event = ui_malloc(obj->ctx,
sizeof(UiEventData));
1645 event->obj = obj;
1646 event->userdata = args->onactivatedata;
1647 event->callback = args->onactivate;
1648 event->value =
0;
1649 event->customdata = listview;
1650
1651 g_signal_connect(
1652 combobox,
1653 "changed",
1654 G_CALLBACK(ui_combobox_change_event),
1655 event);
1656 }
1657
1658 return combobox;
1659 }
1660
1661 void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) {
1662 int index = gtk_combo_box_get_active(widget);
1663 UiListView *listview = e->customdata;
1664 void *eventdata =
NULL;
1665 if(listview->var && listview->var->value) {
1666 UiList *list = listview->var->value;
1667 eventdata = ui_list_get(list, index);
1668 }
else if(listview->elements && listview->nelm > index) {
1669 eventdata = listview->elements[index];
1670 }
1671
1672 UiEvent event;
1673 event.obj = e->obj;
1674 event.window = event.obj->window;
1675 event.document = event.obj->ctx->document;
1676 event.eventdata = eventdata;
1677 event.intval = index;
1678 event.set = ui_get_setop();
1679 e->callback(&event, e->userdata);
1680 }
1681
1682 void ui_combobox_modelupdate(UiList *list,
int i) {
1683 UiListView *view = list->obj;
1684 GtkListStore *store = create_list_store(view, list);
1685 gtk_combo_box_set_model(
GTK_COMBO_BOX(view->widget),
GTK_TREE_MODEL(store));
1686 g_object_unref(store);
1687 }
1688
1689 UiListSelection ui_dropdown_getselection(UiList *list) {
1690 UiListView *combobox = list->obj;
1691 UiListSelection ret;
1692 ret.rows = malloc(
sizeof(
int*));
1693 ret.count =
1;
1694 ret.rows[
0] = gtk_combo_box_get_active(
GTK_COMBO_BOX(combobox->widget));
1695 return ret;
1696 }
1697
1698 void ui_dropdown_setselection(UiList *list, UiListSelection selection) {
1699 ui_setop_enable(
TRUE);
1700 UiListView *combobox = list->obj;
1701 if(selection.count >
0) {
1702 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox->widget), selection.rows[
0]);
1703 }
1704 ui_setop_enable(
FALSE);
1705 }
1706
1707
1708
1709
1710 void ui_listview_activate_event(
1711 GtkTreeView *treeview,
1712 GtkTreePath *path,
1713 GtkTreeViewColumn *column,
1714 UiTreeEventData *event)
1715 {
1716 UiListSelection selection = ui_listview_selection(
1717 gtk_tree_view_get_selection(treeview),
1718 event);
1719
1720 UiEvent e;
1721 e.obj = event->obj;
1722 e.window = event->obj->window;
1723 e.document = event->obj->ctx->document;
1724 e.eventdata = &selection;
1725 e.intval = selection.count >
0 ? selection.rows[
0] :
-1;
1726 e.set = ui_get_setop();
1727 event->activate(&e, event->activatedata);
1728
1729 if(selection.count >
0) {
1730 free(selection.rows);
1731 }
1732 }
1733
1734 void ui_listview_selection_event(
1735 GtkTreeSelection *treeselection,
1736 UiTreeEventData *event)
1737 {
1738 UiListSelection selection = ui_listview_selection(treeselection, event);
1739
1740 UiEvent e;
1741 e.obj = event->obj;
1742 e.window = event->obj->window;
1743 e.document = event->obj->ctx->document;
1744 e.eventdata = &selection;
1745 e.intval = selection.count >
0 ? selection.rows[
0] :
-1;
1746 e.set = ui_get_setop();
1747 event->selection(&e, event->selectiondata);
1748
1749 if(selection.count >
0) {
1750 free(selection.rows);
1751 }
1752 }
1753
1754 UiListSelection ui_listview_selection(
1755 GtkTreeSelection *selection,
1756 UiTreeEventData *event)
1757 {
1758 GList *rows = gtk_tree_selection_get_selected_rows(selection,
NULL);
1759
1760 UiListSelection ls;
1761 ls.count = g_list_length(rows);
1762 ls.rows = calloc(ls.count,
sizeof(
int));
1763 GList *r = rows;
1764 int i =
0;
1765 while(r) {
1766 GtkTreePath *path = r->data;
1767 ls.rows[i] = ui_tree_path_list_index(path);
1768 r = r->next;
1769 i++;
1770 }
1771 return ls;
1772 }
1773
1774 int ui_tree_path_list_index(GtkTreePath *path) {
1775 int depth = gtk_tree_path_get_depth(path);
1776 if(depth ==
0) {
1777 fprintf(stderr,
"UiError: treeview selection: depth == 0\n");
1778 return -1;
1779 }
1780 int *indices = gtk_tree_path_get_indices(path);
1781 return indices[depth -
1];
1782 }
1783
1784
1785 #endif
1786
1787
1788 #if GTK_MAJOR_VERSION >=
4
1789
1790 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source,
double x,
double y,
void *data) {
1791
1792 UiListView *listview = data;
1793
1794 UiDnD *dnd = ui_create_dnd();
1795 GdkContentProvider *provider =
NULL;
1796
1797
1798 if(listview->ondragstart) {
1799 UiEvent event;
1800 event.obj = listview->obj;
1801 event.window = event.obj->window;
1802 event.document = event.obj->ctx->document;
1803 event.eventdata = dnd;
1804 event.eventdatatype =
UI_EVENT_DATA_DND;
1805 event.intval =
0;
1806 event.set = ui_get_setop();
1807 listview->ondragstart(&event, listview->ondragstartdata);
1808 }
1809
1810 size_t numproviders = cxListSize(dnd->providers);
1811 if(numproviders >
0) {
1812 GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers,
0);
1813 provider = gdk_content_provider_new_union(providers, numproviders);
1814 }
1815 ui_dnd_free(dnd);
1816
1817 return provider;
1818 }
1819
1820 static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) {
1821
1822 }
1823
1824 static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) {
1825
1826 UiListView *listview = user_data;
1827 if(listview->ondragcomplete) {
1828 UiDnD dnd;
1829 dnd.target =
NULL;
1830 dnd.value =
NULL;
1831 dnd.providers =
NULL;
1832 dnd.selected_action = gdk_drag_get_selected_action(drag);
1833 dnd.delete = delete_data;
1834 dnd.accept =
FALSE;
1835
1836 UiEvent event;
1837 event.obj = listview->obj;
1838 event.window = event.obj->window;
1839 event.document = event.obj->ctx->document;
1840 event.eventdata = &dnd;
1841 event.eventdatatype =
UI_EVENT_DATA_DND;
1842 event.intval =
0;
1843 event.set = ui_get_setop();
1844 listview->ondragcomplete(&event, listview->ondragcompletedata);
1845 }
1846 }
1847
1848 static gboolean ui_listview_drop(
1849 GtkDropTarget *target,
1850 const GValue* value,
1851 gdouble x,
1852 gdouble y,
1853 gpointer user_data)
1854 {
1855 UiListView *listview = user_data;
1856 UiDnD dnd;
1857 dnd.providers =
NULL;
1858 dnd.target = target;
1859 dnd.value = value;
1860 dnd.selected_action =
0;
1861 dnd.delete =
FALSE;
1862 dnd.accept =
FALSE;
1863
1864 if(listview->ondrop) {
1865 dnd.accept =
TRUE;
1866 UiEvent event;
1867 event.obj = listview->obj;
1868 event.window = event.obj->window;
1869 event.document = event.obj->ctx->document;
1870 event.eventdata = &dnd;
1871 event.eventdatatype =
UI_EVENT_DATA_DND;
1872 event.intval =
0;
1873 event.set = ui_get_setop();
1874 listview->ondrop(&event, listview->ondropdata);
1875 }
1876
1877 return dnd.accept;
1878 }
1879
1880 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
1881 GtkDragSource *dragsource = gtk_drag_source_new();
1882 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(dragsource));
1883 g_signal_connect (dragsource,
"prepare",
G_CALLBACK (ui_listview_dnd_prepare), listview);
1884 g_signal_connect(
1885 dragsource,
1886 "drag-begin",
1887 G_CALLBACK(ui_listview_drag_begin),
1888 listview);
1889 g_signal_connect(
1890 dragsource,
1891 "drag-end",
1892 G_CALLBACK(ui_listview_drag_end),
1893 listview);
1894 }
1895
1896 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
1897 GtkDropTarget *target = gtk_drop_target_new(
G_TYPE_INVALID,
GDK_ACTION_COPY);
1898 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(target));
1899 GType default_types[
2] = {
GDK_TYPE_FILE_LIST,
G_TYPE_STRING };
1900 gtk_drop_target_set_gtypes(target, default_types,
2);
1901 g_signal_connect(target,
"drop",
G_CALLBACK(ui_listview_drop), listview);
1902 }
1903
1904 #else
1905
1906 static GtkTargetEntry targetentries[] =
1907 {
1908 {
"STRING",
0,
0 },
1909 {
"text/plain",
0,
1 },
1910 {
"text/uri-list",
0,
2 },
1911 };
1912
1913 static void ui_listview_drag_getdata(
1914 GtkWidget* self,
1915 GdkDragContext* context,
1916 GtkSelectionData* data,
1917 guint info,
1918 guint time,
1919 gpointer user_data)
1920 {
1921 UiListView *listview = user_data;
1922 UiDnD dnd;
1923 dnd.context = context;
1924 dnd.data = data;
1925 dnd.selected_action =
0;
1926 dnd.delete =
FALSE;
1927 dnd.accept =
FALSE;
1928
1929 if(listview->ondragstart) {
1930 UiEvent event;
1931 event.obj = listview->obj;
1932 event.window = event.obj->window;
1933 event.document = event.obj->ctx->document;
1934 event.eventdata = &dnd;
1935 event.intval =
0;
1936 event.set = ui_get_setop();
1937 listview->ondragstart(&event, listview->ondragstartdata);
1938 }
1939 }
1940
1941 static void ui_listview_drag_end(
1942 GtkWidget *widget,
1943 GdkDragContext *context,
1944 guint time,
1945 gpointer user_data)
1946 {
1947 UiListView *listview = user_data;
1948 UiDnD dnd;
1949 dnd.context = context;
1950 dnd.data =
NULL;
1951 dnd.selected_action = gdk_drag_context_get_selected_action(context);
1952 dnd.delete = dnd.selected_action ==
UI_DND_ACTION_MOVE ?
TRUE :
FALSE;
1953 dnd.accept =
FALSE;
1954 if(listview->ondragcomplete) {
1955 UiEvent event;
1956 event.obj = listview->obj;
1957 event.window = event.obj->window;
1958 event.document = event.obj->ctx->document;
1959 event.eventdata = &dnd;
1960 event.intval =
0;
1961 event.set = ui_get_setop();
1962 listview->ondragcomplete(&event, listview->ondragcompletedata);
1963 }
1964 }
1965
1966 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
1967 gtk_tree_view_enable_model_drag_source(
1968 GTK_TREE_VIEW(listview->widget),
1969 GDK_BUTTON1_MASK,
1970 targetentries,
1971 2,
1972 GDK_ACTION_COPY);
1973
1974 g_signal_connect(listview->widget,
"drag-data-get",
G_CALLBACK(ui_listview_drag_getdata), listview);
1975 g_signal_connect(listview->widget,
"drag-end",
G_CALLBACK(ui_listview_drag_end), listview);
1976 }
1977
1978
1979
1980
1981 static void ui_listview_drag_data_received(
1982 GtkWidget *self,
1983 GdkDragContext *context,
1984 gint x,
1985 gint y,
1986 GtkSelectionData *data,
1987 guint info,
1988 guint time,
1989 gpointer user_data)
1990 {
1991 UiListView *listview = user_data;
1992 UiDnD dnd;
1993 dnd.context = context;
1994 dnd.data = data;
1995 dnd.selected_action =
0;
1996 dnd.delete =
FALSE;
1997 dnd.accept =
FALSE;
1998
1999 if(listview->ondrop) {
2000 dnd.accept =
TRUE;
2001 UiEvent event;
2002 event.obj = listview->obj;
2003 event.window = event.obj->window;
2004 event.document = event.obj->ctx->document;
2005 event.eventdata = &dnd;
2006 event.intval =
0;
2007 event.set = ui_get_setop();
2008 listview->ondrop(&event, listview->ondropdata);
2009 }
2010 }
2011
2012 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
2013 gtk_tree_view_enable_model_drag_dest(
2014 GTK_TREE_VIEW(listview->widget),
2015 targetentries,
2016 3,
2017 GDK_ACTION_COPY);
2018 if(listview->ondrop) {
2019 g_signal_connect(listview->widget,
"drag_data_received",
G_CALLBACK(ui_listview_drag_data_received), listview);
2020 }
2021 }
2022
2023 #endif
2024
2025
2026 GtkWidget* ui_get_tree_widget(
UIWIDGET widget) {
2027 return SCROLLEDWINDOW_GET_CHILD(widget);
2028 }
2029
2030 static char** targets2array(
char *target0, va_list ap,
int *nelm) {
2031 int al =
16;
2032 char **targets = calloc(
16,
sizeof(
char*));
2033 targets[
0] = target0;
2034
2035 int i =
1;
2036 char *target;
2037 while((target = va_arg(ap,
char*)) !=
NULL) {
2038 if(i >= al) {
2039 al *=
2;
2040 targets = realloc(targets, al*
sizeof(
char*));
2041 }
2042 targets[i] = target;
2043 i++;
2044 }
2045
2046 *nelm = i;
2047 return targets;
2048 }
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060 void ui_table_dragsource(
UIWIDGET tablewidget,
int actions,
char *target0, ...) {
2061 va_list ap;
2062 va_start(ap, target0);
2063 int nelm;
2064 char **targets = targets2array(target0, ap, &nelm);
2065 va_end(ap);
2066
2067
2068
2069
2070 free(targets);
2071 }
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
2108
2109 if(v->var) {
2110 ui_destroy_boundvar(v->obj->ctx, v->var);
2111 }
2112 if(v->model) {
2113 ui_model_remove_observer(v->model, v);
2114 ui_model_unref(v->model);
2115 }
2116 if(v->elements) {
2117 for(
int i=
0;i<v->nelm;i++) {
2118 free(v->elements[i]);
2119 }
2120 free(v->elements);
2121 }
2122 #if GTK_CHECK_VERSION(
4,
10,
0)
2123 free(v->columns);
2124 pango_attr_list_unref(v->current_row_attributes);
2125 cxMapFree(v->bound_rows);
2126 #endif
2127 free(v->selection.rows);
2128 free(v);
2129 }
2130
2131
2132
2133
2134 static ui_sourcelist_update_func sourcelist_update_finished_callback;
2135
2136 void ui_sourcelist_set_update_callback(ui_sourcelist_update_func cb) {
2137 sourcelist_update_finished_callback = cb;
2138 }
2139
2140 static void ui_sourcelist_update_finished(
void) {
2141 if(sourcelist_update_finished_callback) {
2142 sourcelist_update_finished_callback();
2143 }
2144 }
2145
2146 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
2147 cxListFree(v->sublists);
2148 free(v);
2149 }
2150
2151 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
2152 free(sublist->header);
2153 ui_destroy_boundvar(obj->ctx, sublist->var);
2154 cxListFree(sublist->widgets);
2155 }
2156
2157 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
2158
2159 UiListBox *listbox = g_object_get_data(
G_OBJECT(row),
"ui_listbox");
2160 if(!listbox) {
2161 return;
2162 }
2163
2164 UiListBoxSubList *sublist = g_object_get_data(
G_OBJECT(row),
"ui_listbox_sublist");
2165 if(!sublist) {
2166 return;
2167 }
2168
2169 if(sublist->separator) {
2170 GtkWidget *separator = gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL);
2171 gtk_list_box_row_set_header(row, separator);
2172 }
else if(sublist->header && !listbox->header_is_item) {
2173 GtkWidget *header = gtk_label_new(sublist->header);
2174 gtk_widget_set_halign(header,
GTK_ALIGN_START);
2175 if(row == listbox->first_row) {
2176 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header-first");
2177 }
else {
2178 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header");
2179 }
2180 gtk_list_box_row_set_header(row, header);
2181 }
2182 }
2183
2184 #ifdef UI_GTK3
2185 typedef struct _UiSidebarListBoxClass {
2186 GtkListBoxClass parent_class;
2187 } UiSidebarListBoxClass;
2188
2189 typedef struct _UiSidebarListBox {
2190 GtkListBox parent_instance;
2191 } UiSidebarListBox;
2192
2193 G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box,
GTK_TYPE_LIST_BOX)
2194
2195
2196 static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) {
2197 GtkWidgetClass *widget_class =
GTK_WIDGET_CLASS(klass);
2198 gtk_widget_class_set_css_name (widget_class,
"placessidebar");
2199 }
2200
2201 static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
2202
2203 }
2204 #endif
2205
2206
2207 static void add_sublist(UiListBox *uilistbox, CxList *sublists, UiSubList *sublist) {
2208 UiListBoxSubList uisublist;
2209 uisublist.var = uic_widget_var(
2210 uilistbox->obj->ctx,
2211 uilistbox->obj->ctx,
2212 sublist->value,
2213 sublist->varname,
2214 UI_VAR_LIST);
2215 uisublist.numitems =
0;
2216 uisublist.header = sublist->header ? strdup(sublist->header) :
NULL;
2217 uisublist.separator = sublist->separator;
2218 uisublist.widgets = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
2219 uisublist.listbox = uilistbox;
2220 uisublist.userdata = sublist->userdata;
2221 uisublist.index = cxListSize(sublists);
2222 uisublist.startpos =
0;
2223 cxListAdd(sublists, &uisublist);
2224
2225
2226 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(sublists)
-1);
2227 if(uisublist.var && uisublist.var->value) {
2228 UiList *list = uisublist.var->value;
2229 list->obj = sublist_ptr;
2230 list->update = ui_listbox_list_update;
2231 list->getselection = ui_listbox_list_getselection;
2232 list->setselection = ui_listbox_list_setselection;
2233 }
2234 }
2235
2236 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs *args) {
2237 #ifdef UI_GTK3
2238 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(),
NULL);
2239 #else
2240 GtkWidget *listbox = gtk_list_box_new();
2241 #endif
2242 if(!args->style_class) {
2243 #if GTK_MAJOR_VERSION >=
4
2244 WIDGET_ADD_CSS_CLASS(listbox,
"navigation-sidebar");
2245 #else
2246 WIDGET_ADD_CSS_CLASS(listbox,
"sidebar");
2247 #endif
2248 }
2249 gtk_list_box_set_header_func(
GTK_LIST_BOX(listbox), listbox_create_header,
NULL,
NULL);
2250 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
2251 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
2252
2253 ui_set_name_and_style(listbox, args->name, args->style_class);
2254 ui_set_widget_states(obj->ctx, listbox, args->states);
2255 UiContainerPrivate *ct = (UiContainerPrivate*)obj->container_end;
2256 UiLayout layout =
UI_ARGS2LAYOUT(args);
2257 ct->add(ct, scroll_area, &layout);
2258
2259 UiListBox *uilistbox = malloc(
sizeof(UiListBox));
2260 uilistbox->obj = obj;
2261 uilistbox->listbox =
GTK_LIST_BOX(listbox);
2262 uilistbox->header_is_item = args->header_is_item;
2263 uilistbox->getvalue = args->getvalue;
2264 uilistbox->getvaluedata = args->getvaluedata;
2265 uilistbox->onactivate = args->onactivate;
2266 uilistbox->onactivatedata = args->onactivatedata;
2267 uilistbox->onbuttonclick = args->onbuttonclick;
2268 uilistbox->onbuttonclickdata = args->onbuttonclickdata;
2269 uilistbox->sublists = cxArrayListCreateSimple(
sizeof(UiListBoxSubList),
4);
2270 uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
2271 uilistbox->sublists->collection.destructor_data = obj;
2272 uilistbox->first_row =
NULL;
2273
2274 if(args->sublists) {
2275
2276 if(args->numsublists ==
0 && args->sublists) {
2277 args->numsublists =
INT_MAX;
2278 }
2279 for(
int i=
0;i<args->numsublists;i++) {
2280 UiSubList sublist = args->sublists[i];
2281 if(!sublist.varname && !sublist.value) {
2282 break;
2283 }
2284
2285 add_sublist(uilistbox, uilistbox->sublists, &sublist);
2286 }
2287
2288
2289 ui_listbox_update(uilistbox,
0, cxListSize(uilistbox->sublists));
2290 }
else {
2291 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args->dynamic_sublist, args->varname,
UI_VAR_LIST);
2292 if(var) {
2293 UiList *list = var->value;
2294 list->obj = uilistbox;
2295 list->update = ui_listbox_dynamic_update;
2296 list->getselection = ui_listbox_dynamic_getselection;
2297 list->setselection = ui_listbox_dynamic_setselection;
2298
2299 ui_listbox_dynamic_update(list,
-1);
2300 }
2301 }
2302
2303
2304
2305 g_object_set_data(
G_OBJECT(scroll_area),
"ui_listbox", uilistbox);
2306 g_object_set_data(
G_OBJECT(listbox),
"ui_listbox", uilistbox);
2307
2308 if(args->contextmenu) {
2309 UIMENU menu = ui_contextmenu_create(args->contextmenu, obj, listbox);
2310 ui_widget_set_contextmenu(listbox, menu);
2311 }
2312
2313
2314 g_signal_connect(
2315 listbox,
2316 "destroy",
2317 G_CALLBACK(ui_destroy_sourcelist),
2318 uilistbox);
2319
2320 if(args->onactivate) {
2321 g_signal_connect(
2322 listbox,
2323 "row-activated",
2324 G_CALLBACK(ui_listbox_row_activate),
2325 NULL);
2326 }
2327
2328 return scroll_area;
2329 }
2330
2331 void ui_listbox_dynamic_update(UiList *list,
int x) {
2332 UiListBox *uilistbox = list->obj;
2333
2334
2335 CxIterator i = cxListIterator(uilistbox->sublists);
2336 cx_foreach(UiListBoxSubList *, s, i) {
2337
2338
2339
2340
2341 CxIterator r = cxListIterator(s->widgets);
2342 cx_foreach(GtkWidget*, widget, r) {
2343 LISTBOX_REMOVE(uilistbox->listbox, widget);
2344 }
2345
2346 if(s->var) {
2347 UiList *sl = s->var->value;
2348 sl->obj =
NULL;
2349 sl->update =
NULL;
2350 if(s->var->type ==
UI_VAR_SPECIAL) {
2351 ui_free(s->var->from_ctx, s->var);
2352 }
2353 }
2354 }
2355
2356 cxListFree(uilistbox->sublists);
2357 CxList *new_sublists = cxArrayListCreateSimple(
sizeof(UiListBoxSubList), list->count(list));
2358 uilistbox->sublists = new_sublists;
2359
2360 UiSubList *sublist = list->first(list);
2361 while(sublist) {
2362 add_sublist(uilistbox, new_sublists, sublist);
2363 sublist = list->next(list);
2364 }
2365
2366 ui_listbox_update(uilistbox,
0, cxListSize(uilistbox->sublists));
2367 }
2368
2369 void ui_listbox_dynamic_setselection(UiList *list, UiListSelection sel) {
2370 UiListBox *uilistbox = list->obj;
2371 gtk_list_box_unselect_all(uilistbox->listbox);
2372 if(sel.count >
0) {
2373 int index = sel.rows[
0];
2374 if(index >=
0) {
2375 GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, index);
2376 if(row) {
2377 gtk_list_box_select_row(uilistbox->listbox, row);
2378 }
2379 }
2380 }
2381 }
2382
2383 UiListSelection ui_listbox_dynamic_getselection(UiList *list) {
2384 UiListSelection sel = {
0,
NULL };
2385 UiListBox *uilistbox = list->obj;
2386 GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox);
2387 if(row) {
2388 sel.count =
1;
2389 sel.rows = malloc(
sizeof(
int));
2390 sel.rows[
0] = gtk_list_box_row_get_index(row);
2391 }
2392 return sel;
2393 }
2394
2395 void ui_listbox_update(UiListBox *listbox,
int from,
int to) {
2396 CxIterator i = cxListIterator(listbox->sublists);
2397 size_t pos =
0;
2398 cx_foreach(UiListBoxSubList *, sublist, i) {
2399 if(i.index < from) {
2400 pos += sublist->numitems;
2401 continue;
2402 }
2403 if(i.index > to) {
2404 break;
2405 }
2406
2407
2408 sublist->startpos = pos;
2409 ui_listbox_update_sublist(listbox, sublist, pos);
2410 pos += sublist->numitems;
2411 }
2412
2413 ui_sourcelist_update_finished();
2414 }
2415
2416 static void listbox_button_clicked(GtkWidget *button, UiEventDataExt *data) {
2417 UiListBoxSubList *sublist = data->customdata0;
2418
2419 UiSubListEventData *eventdata = &sublist->listbox->current_eventdata;
2420 eventdata->list = sublist->var->value;
2421 eventdata->sublist_index = sublist->index;
2422 eventdata->row_index = data->value0;
2423 eventdata->sublist_userdata = sublist->userdata;
2424 eventdata->row_data = eventdata->list->get(eventdata->list, eventdata->row_index);
2425 eventdata->event_data = data->customdata2;
2426
2427 UiEvent event;
2428 event.obj = data->obj;
2429 event.window = event.obj->window;
2430 event.document = event.obj->ctx->document;
2431 event.eventdata = eventdata;
2432 event.eventdatatype =
UI_EVENT_DATA_SUBLIST;
2433 event.intval = data->value0;
2434 event.set = ui_get_setop();
2435
2436 if(data->callback2) {
2437 data->callback2(&event, data->userdata2);
2438 }
2439
2440 if(data->customdata3) {
2441 uic_set_tmp_eventdata(eventdata,
UI_EVENT_DATA_SUBLIST);
2442
2443 UIMENU menu = data->customdata3;
2444 g_object_set_data(
G_OBJECT(button),
"ui-button-popup", menu);
2445 gtk_popover_popup(
GTK_POPOVER(menu));
2446 }
2447 }
2448
2449 #if GTK_CHECK_VERSION(
4,
0,
0)
2450 static void button_popover_closed(GtkPopover *popover, GtkWidget *button) {
2451 g_object_set_data(
G_OBJECT(button),
"ui-button-popup",
NULL);
2452 if(g_object_get_data(
G_OBJECT(button),
"ui-button-invisible")) {
2453 g_object_set_data(
G_OBJECT(button),
"ui-button-invisible",
NULL);
2454 gtk_widget_set_visible(button,
FALSE);
2455 }
2456 }
2457 #else
2458 static void popup_hide(GtkWidget *self, GtkWidget *button) {
2459 g_object_set_data(
G_OBJECT(button),
"ui-button-popup",
NULL);
2460 if(g_object_get_data(
G_OBJECT(button),
"ui-button-invisible")) {
2461 g_object_set_data(
G_OBJECT(button),
"ui-button-invisible",
NULL);
2462 gtk_widget_set_visible(button,
FALSE);
2463 }
2464 }
2465 #endif
2466
2467 static void listbox_fill_row(UiListBox *listbox, GtkWidget *row, UiListBoxSubList *sublist, UiSubListItem *item,
int index) {
2468 UiBool is_header = index <
0;
2469
2470 GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
10);
2471 if(item->icon) {
2472 GtkWidget *icon =
ICON_IMAGE(item->icon);
2473 BOX_ADD(hbox, icon);
2474 }
2475 GtkWidget *label = gtk_label_new(item->label);
2476 if(is_header) {
2477 WIDGET_ADD_CSS_CLASS(label,
"ui-listbox-header-row");
2478 }
2479 gtk_widget_set_halign(label,
GTK_ALIGN_START);
2480 BOX_ADD_EXPAND(hbox, label);
2481 if(item->badge) {
2482
2483 }
2484 LISTBOX_ROW_SET_CHILD(row, hbox);
2485
2486
2487 UiEventDataExt *event = malloc(
sizeof(UiEventDataExt));
2488 memset(event,
0,
sizeof(UiEventDataExt));
2489 event->obj = listbox->obj;
2490 event->customdata0 = sublist;
2491 event->customdata1 = sublist->var;
2492 event->customdata2 = item->eventdata;
2493 event->callback = listbox->onactivate;
2494 event->userdata = listbox->onactivatedata;
2495 event->callback2 = listbox->onbuttonclick;
2496 event->userdata2 = listbox->onbuttonclickdata;
2497 event->value0 = index;
2498
2499
2500
2501
2502 g_signal_connect(
2503 row,
2504 "destroy",
2505 G_CALLBACK(ui_destroy_userdata),
2506 event);
2507
2508 g_object_set_data(
G_OBJECT(row),
"ui-listbox-row-eventdata", event);
2509
2510
2511 if(item->badge) {
2512 GtkWidget *badge = gtk_label_new(item->badge);
2513 WIDGET_ADD_CSS_CLASS(badge,
"ui-badge");
2514 #if GTK_CHECK_VERSION(
3,
14,
0)
2515 gtk_widget_set_valign(badge,
GTK_ALIGN_CENTER);
2516 BOX_ADD(hbox, badge);
2517 #else
2518 GtkWidget *align = gtk_alignment_new(
0.5,
0.5,
0,
0);
2519 gtk_container_add(
GTK_CONTAINER(align), badge);
2520 BOX_ADD(hbox, align);
2521 #endif
2522 }
2523
2524 if(item->button_icon || item->button_label) {
2525 GtkWidget *button = gtk_button_new();
2526 gtk_button_set_label(
GTK_BUTTON(button), item->button_label);
2527 ui_button_set_icon_name(button, item->button_icon);
2528 WIDGET_ADD_CSS_CLASS(button,
"flat");
2529 BOX_ADD(hbox, button);
2530 g_signal_connect(
2531 button,
2532 "clicked",
2533 G_CALLBACK(listbox_button_clicked),
2534 event
2535 );
2536 gtk_widget_set_visible(button,
FALSE);
2537
2538 g_object_set_data(
G_OBJECT(row),
"ui-listbox-row-button", button);
2539
2540
2541 if(item->button_menu) {
2542 UIMENU menu = ui_contextmenu_create(item->button_menu, listbox->obj, button);
2543 event->customdata3 = menu;
2544 #if GTK_CHECK_VERSION(
4,
0,
0)
2545 g_signal_connect(menu,
"closed",
G_CALLBACK(button_popover_closed), button);
2546 #else
2547 g_signal_connect(menu,
"hide",
G_CALLBACK(popup_hide), button);
2548 #endif
2549 ui_menubuilder_unref(item->button_menu);
2550 }
2551 }
2552 }
2553
2554 static void update_sublist_item(UiListBox *listbox, UiListBoxSubList *sublist,
int index) {
2555 int header_row = listbox->header_is_item && sublist->header ?
1 :
0;
2556 GtkListBoxRow *row = gtk_list_box_get_row_at_index(listbox->listbox, sublist->startpos + index + header_row);
2557 if(!row) {
2558 return;
2559 }
2560 UiList *list = sublist->var->value;
2561 if(!list) {
2562 return;
2563 }
2564
2565 void *elm = list->get(list, index);
2566 UiSubListItem item = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
2567 if(listbox->getvalue) {
2568 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
2569 }
else {
2570 item.label = strdup(elm);
2571 }
2572
2573 LISTBOX_ROW_REMOVE_CHILD(row);
2574
2575 listbox_fill_row(listbox,
GTK_WIDGET(row), sublist, &item, index);
2576
2577
2578 free(item.label);
2579 free(item.icon);
2580 free(item.button_label);
2581 free(item.button_icon);
2582 free(item.badge);
2583 }
2584
2585 static void listbox_row_on_enter(GtkWidget *row) {
2586 GtkWidget *button = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-button");
2587 if(button) {
2588 gtk_widget_set_visible(button,
TRUE);
2589 }
2590 }
2591
2592 static void listbox_row_on_leave(GtkWidget *row) {
2593 GtkWidget *button = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-button");
2594 if(button) {
2595 if(!g_object_get_data(
G_OBJECT(button),
"ui-button-popup")) {
2596 gtk_widget_set_visible(button,
FALSE);
2597 }
else {
2598 g_object_set_data(
G_OBJECT(button),
"ui-button-invisible", (
void*)
1);
2599 }
2600 }
2601 }
2602
2603 #if GTK_CHECK_VERSION(
4,
0,
0)
2604 static void listbox_row_enter(
2605 GtkEventControllerMotion* self,
2606 gdouble x,
2607 gdouble y,
2608 GtkWidget *row)
2609 {
2610 listbox_row_on_enter(row);
2611 }
2612
2613 static void listbox_row_leave(
2614 GtkEventControllerMotion* self,
2615 GtkWidget *row)
2616 {
2617 listbox_row_on_leave(row);
2618 }
2619 #else
2620 static gboolean listbox_row_enter(
2621 GtkWidget *row,
2622 GdkEventCrossing event,
2623 gpointer user_data)
2624 {
2625 listbox_row_on_enter(row);
2626 return FALSE;
2627 }
2628
2629
2630 static gboolean listbox_row_leave(
2631 GtkWidget *row,
2632 GdkEventCrossing *event,
2633 gpointer user_data)
2634 {
2635 listbox_row_on_leave(row);
2636 return FALSE;
2637 }
2638
2639 #endif
2640
2641 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist,
size_t listbox_insert_index) {
2642
2643 CxIterator r = cxListIterator(sublist->widgets);
2644 cx_foreach(GtkWidget*, widget, r) {
2645 LISTBOX_REMOVE(listbox->listbox, widget);
2646 }
2647 cxListClear(sublist->widgets);
2648
2649 sublist->numitems =
0;
2650
2651
2652 if(!sublist->var) {
2653 return;
2654 }
2655 UiList *list = sublist->var->value;
2656 if(!list) {
2657 return;
2658 }
2659
2660 int index =
0;
2661 void *elm = list->first(list);
2662 void *first = elm;
2663
2664 if(sublist->header && !listbox->header_is_item && !elm) {
2665
2666 GtkWidget *row = gtk_list_box_row_new();
2667 cxListAdd(sublist->widgets, row);
2668 g_object_set_data(
G_OBJECT(row),
"ui_listbox", listbox);
2669 g_object_set_data(
G_OBJECT(row),
"ui_listbox_sublist", sublist);
2670
2671
2672 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
2673 sublist->numitems =
1;
2674 return;
2675 }
2676
2677 int first_index =
0;
2678 int header_row =
0;
2679 if(listbox->header_is_item && sublist->header) {
2680 index =
-1;
2681 first_index =
-1;
2682 header_row =
1;
2683 elm = sublist->header;
2684 }
2685
2686 while(elm) {
2687 UiSubListItem item = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
2688 if(listbox->getvalue) {
2689 listbox->getvalue(list, sublist->userdata, elm, index, &item, listbox->getvaluedata);
2690 }
else {
2691 item.label = strdup(elm);
2692 }
2693
2694 if(item.label ==
NULL && index ==
-1 && sublist->header) {
2695 item.label = strdup(sublist->header);
2696 }
2697
2698
2699 GtkWidget *row = gtk_list_box_row_new();
2700 #if GTK_CHECK_VERSION(
4,
0,
0)
2701 GtkEventController *motion_controller = gtk_event_controller_motion_new();
2702 gtk_widget_add_controller(
GTK_WIDGET(row), motion_controller);
2703 g_signal_connect(motion_controller,
"enter",
G_CALLBACK(listbox_row_enter), row);
2704 g_signal_connect(motion_controller,
"leave",
G_CALLBACK(listbox_row_leave), row);
2705 #else
2706 gtk_widget_set_events(
GTK_WIDGET(row),
GDK_POINTER_MOTION_MASK |
GDK_ENTER_NOTIFY_MASK |
GDK_LEAVE_NOTIFY_MASK);
2707 g_signal_connect(row,
"enter-notify-event",
G_CALLBACK(listbox_row_enter),
NULL);
2708 g_signal_connect(row,
"leave-notify-event",
G_CALLBACK(listbox_row_leave),
NULL);
2709 #endif
2710
2711 listbox_fill_row(listbox, row, sublist, &item, index);
2712 if(index == first_index) {
2713
2714
2715 g_object_set_data(
G_OBJECT(row),
"ui_listbox", listbox);
2716 g_object_set_data(
G_OBJECT(row),
"ui_listbox_sublist", sublist);
2717
2718 if(listbox_insert_index ==
0) {
2719
2720 listbox->first_row =
GTK_LIST_BOX_ROW(row);
2721 }
2722 }
2723
2724
2725 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index + header_row);
2726 cxListAdd(sublist->widgets, row);
2727
2728
2729 free(item.label);
2730 free(item.icon);
2731 free(item.button_label);
2732 free(item.button_icon);
2733 free(item.badge);
2734
2735
2736 elm = index >=
0 ? list->next(list) : first;
2737 index++;
2738 }
2739
2740 sublist->numitems = cxListSize(sublist->widgets);
2741 }
2742
2743 void ui_listbox_list_update(UiList *list,
int i) {
2744 UiListBoxSubList *sublist = list->obj;
2745 if(i <
0) {
2746 ui_listbox_update_sublist(sublist->listbox, sublist, sublist->startpos);
2747 size_t pos =
0;
2748 CxIterator it = cxListIterator(sublist->listbox->sublists);
2749 cx_foreach(UiListBoxSubList *, ls, it) {
2750 ls->startpos = pos;
2751 pos += ls->numitems;
2752 }
2753 }
else {
2754 update_sublist_item(sublist->listbox, sublist, i);
2755 }
2756
2757 ui_sourcelist_update_finished();
2758 }
2759
2760 void ui_listbox_list_setselection(UiList *list, UiListSelection sel) {
2761 UiListBoxSubList *sublist = list->obj;
2762 UiListBox *uilistbox = sublist->listbox;
2763 gtk_list_box_unselect_all(uilistbox->listbox);
2764 if(sel.count >
0) {
2765 int index = sel.rows[
0];
2766 if(index >=
0 && index < sublist->numitems) {
2767 int global_index = sublist->startpos + index;
2768 GtkListBoxRow *row = gtk_list_box_get_row_at_index(uilistbox->listbox, global_index);
2769 if(row) {
2770 gtk_list_box_select_row(uilistbox->listbox, row);
2771 }
2772 }
2773 }
2774 }
2775
2776 UiListSelection ui_listbox_list_getselection(UiList *list) {
2777 UiListSelection sel = {
0,
NULL };
2778 UiListBoxSubList *sublist = list->obj;
2779 UiListBox *uilistbox = sublist->listbox;
2780 GtkListBoxRow *row = gtk_list_box_get_selected_row(uilistbox->listbox);
2781 if(row) {
2782 int index = gtk_list_box_row_get_index(row);
2783 size_t startpos = sublist->startpos;
2784 if(index >= startpos && index < startpos+sublist->numitems) {
2785 sel.count =
1;
2786 sel.rows = malloc(
sizeof(
int));
2787 sel.rows[
0] = index - startpos;
2788 }
2789 }
2790 return sel;
2791 }
2792
2793 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
2794 UiEventDataExt *data = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-eventdata");
2795 if(!data) {
2796 return;
2797 }
2798 UiListBoxSubList *sublist = data->customdata0;
2799
2800 UiSubListEventData eventdata;
2801 eventdata.list = sublist->var->value;
2802 eventdata.sublist_index = sublist->index;
2803 eventdata.row_index = data->value0;
2804 eventdata.sublist_userdata = sublist->userdata;
2805 eventdata.row_data = eventdata.row_index >=
0 ? eventdata.list->get(eventdata.list, eventdata.row_index) :
NULL;
2806 eventdata.event_data = data->customdata2;
2807
2808 UiEvent event;
2809 event.obj = data->obj;
2810 event.window = event.obj->window;
2811 event.document = event.obj->ctx->document;
2812 event.eventdata = &eventdata;
2813 event.eventdatatype =
UI_EVENT_DATA_SUBLIST;
2814 event.intval = data->value0;
2815 event.set = ui_get_setop();
2816
2817 if(data->callback) {
2818 data->callback(&event, data->userdata);
2819 }
2820 }
2821