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 "icon.h"
43 #include "menu.h"
44 #include "dnd.h"
45
46
47 void* ui_strmodel_getvalue(
void *elm,
int column) {
48 return column ==
0 ? elm :
NULL;
49 }
50
51
52
53
54
55
56
57
58
59
60 #if GTK_CHECK_VERSION(
4,
10,
0)
61
62
63
64
65 typedef struct _ObjWrapper {
66 GObject parent_instance;
67 void *data;
68 } ObjWrapper;
69
70 typedef struct _ObjWrapperClass {
71 GObjectClass parent_class;
72 } ObjWrapperClass;
73
74 G_DEFINE_TYPE(ObjWrapper, obj_wrapper,
G_TYPE_OBJECT)
75
76 static void obj_wrapper_class_init(ObjWrapperClass *klass) {
77
78 }
79
80 static void obj_wrapper_init(ObjWrapper *self) {
81 self->data =
NULL;
82 }
83
84 ObjWrapper* obj_wrapper_new(
void* data) {
85 ObjWrapper *obj = g_object_new(obj_wrapper_get_type(),
NULL);
86 obj->data = data;
87 return obj;
88 }
89
90
91
92 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
93 UiColData *col = userdata;
94 UiModel *model = col->listview->model;
95 UiModelType type = model->types[col->model_column];
96 if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
97 GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
6);
98 GtkWidget *image = gtk_image_new();
99 GtkWidget *label = gtk_label_new(
NULL);
100 BOX_ADD(hbox, image);
101 BOX_ADD(hbox, label);
102 gtk_list_item_set_child(item, hbox);
103 g_object_set_data(
G_OBJECT(hbox),
"image", image);
104 g_object_set_data(
G_OBJECT(hbox),
"label", label);
105 }
else if(type ==
UI_ICON) {
106 GtkWidget *image = gtk_image_new();
107 gtk_list_item_set_child(item, image);
108 }
else {
109 GtkWidget *label = gtk_label_new(
NULL);
110 gtk_label_set_xalign(
GTK_LABEL(label),
0);
111 gtk_list_item_set_child(item, label);
112 }
113 }
114
115 static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
116 UiColData *col = userdata;
117
118 ObjWrapper *obj = gtk_list_item_get_item(item);
119 UiModel *model = col->listview->model;
120 UiModelType type = model->types[col->model_column];
121
122 void *data = model->getvalue(obj->data, col->data_column);
123 GtkWidget *child = gtk_list_item_get_child(item);
124
125 bool freevalue =
TRUE;
126 switch(type) {
127 case UI_STRING: {
128 freevalue =
FALSE;
129 }
130 case UI_STRING_FREE: {
131 gtk_label_set_label(
GTK_LABEL(child), data);
132 if(freevalue) {
133 free(data);
134 }
135 break;
136 }
137 case UI_INTEGER: {
138 intptr_t intvalue = (
intptr_t)data;
139 char buf[
32];
140 snprintf(buf,
32,
"%d", (
int)intvalue);
141 gtk_label_set_label(
GTK_LABEL(child), buf);
142 break;
143 }
144 case UI_ICON: {
145 UiIcon *icon = data;
146 if(icon) {
147 gtk_image_set_from_paintable(
GTK_IMAGE(child),
GDK_PAINTABLE(icon->info));
148 }
149 break;
150 }
151 case UI_ICON_TEXT: {
152 freevalue =
FALSE;
153 }
154 case UI_ICON_TEXT_FREE: {
155 void *data2 = model->getvalue(obj->data, col->data_column+
1);
156 GtkWidget *image = g_object_get_data(
G_OBJECT(child),
"image");
157 GtkWidget *label = g_object_get_data(
G_OBJECT(child),
"label");
158 if(data && image) {
159 UiIcon *icon = data;
160 gtk_image_set_from_paintable(
GTK_IMAGE(image),
GDK_PAINTABLE(icon->info));
161 }
162 if(data2 && label) {
163 gtk_label_set_label(
GTK_LABEL(label), data2);
164 }
165 if(freevalue) {
166 free(data2);
167 }
168 break;
169 }
170 }
171 }
172
173 static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
174 GtkSelectionModel *selection_model;
175 if(multiselection) {
176 selection_model =
GTK_SELECTION_MODEL(gtk_multi_selection_new(
G_LIST_MODEL(liststore)));
177 }
else {
178 selection_model =
GTK_SELECTION_MODEL(gtk_single_selection_new(
G_LIST_MODEL(liststore)));
179 }
180 g_signal_connect(selection_model,
"selection-changed",
G_CALLBACK(ui_listview_selection_changed), listview);
181 return selection_model;
182 }
183
184 static UiListView* create_listview(UiObject *obj, UiListArgs args) {
185 UiListView *tableview = malloc(
sizeof(UiListView));
186 memset(tableview,
0,
sizeof(UiListView));
187 tableview->obj = obj;
188 tableview->model = args.model;
189 tableview->onactivate = args.onactivate;
190 tableview->onactivatedata = args.onactivatedata;
191 tableview->onselection = args.onselection;
192 tableview->onselectiondata = args.onselectiondata;
193 tableview->ondragstart = args.ondragstart;
194 tableview->ondragstartdata = args.ondragstartdata;
195 tableview->ondragcomplete = args.ondragcomplete;
196 tableview->ondragcompletedata = args.ondragcompletedata;
197 tableview->ondrop = args.ondrop;
198 tableview->ondropdata = args.ondropsdata;
199 tableview->selection.count =
0;
200 tableview->selection.rows =
NULL;
201 return tableview;
202 }
203
204 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
205 UiObject* current = uic_current_obj(obj);
206
207
208
209 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
210 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
211 args.model = model;
212
213 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
214 UiListView *listview = create_listview(obj, args);
215
216 listview->columns = malloc(
sizeof(UiColData));
217 listview->columns->listview = listview;
218 listview->columns->data_column =
0;
219 listview->columns->model_column =
0;
220
221 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
222 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), listview->columns);
223 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), listview->columns);
224
225 GtkSelectionModel *selection_model = create_selection_model(listview, ls, args.multiselection);
226 GtkWidget *view = gtk_list_view_new(
GTK_SELECTION_MODEL(selection_model), factory);
227
228 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
229
230
231 listview->widget = view;
232 listview->var = var;
233 listview->liststore = ls;
234 listview->selectionmodel = selection_model;
235 g_signal_connect(
236 view,
237 "destroy",
238 G_CALLBACK(ui_listview_destroy),
239 listview);
240
241
242 if(var && var->value) {
243 UiList *list = var->value;
244
245 list->obj = listview;
246 list->update = ui_listview_update2;
247 list->getselection = ui_listview_getselection2;
248 list->setselection = ui_listview_setselection2;
249
250 ui_update_liststore(ls, list);
251 }
252
253
254 if(args.onactivate) {
255
256
257
258 g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), listview);
259 }
260
261
262 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
263 gtk_scrolled_window_set_policy(
264 GTK_SCROLLED_WINDOW(scroll_area),
265 GTK_POLICY_AUTOMATIC,
266 GTK_POLICY_AUTOMATIC);
267 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
268
269 UI_APPLY_LAYOUT1(current, args);
270 current->container->add(current->container, scroll_area,
FALSE);
271
272
273
274 current->container->current = view;
275
276 return scroll_area;
277 }
278
279 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
280 UiObject* current = uic_current_obj(obj);
281
282
283
284 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
285 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
286 args.model = model;
287
288 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
289 UiListView *listview = create_listview(obj, args);
290
291 listview->columns = malloc(
sizeof(UiColData));
292 listview->columns->listview = listview;
293 listview->columns->data_column =
0;
294 listview->columns->model_column =
0;
295
296 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
297 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), listview->columns);
298 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), listview->columns);
299
300 GtkWidget *view = gtk_drop_down_new(
G_LIST_MODEL(ls),
NULL);
301 gtk_drop_down_set_factory(
GTK_DROP_DOWN(view), factory);
302
303 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
304
305
306 listview->widget = view;
307 listview->var = var;
308 listview->liststore = ls;
309 listview->selectionmodel =
NULL;
310 g_signal_connect(
311 view,
312 "destroy",
313 G_CALLBACK(ui_listview_destroy),
314 listview);
315
316
317 if(var && var->value) {
318 UiList *list = var->value;
319
320 list->obj = listview;
321 list->update = ui_listview_update2;
322 list->getselection = ui_combobox_getselection;
323 list->setselection = ui_combobox_setselection;
324
325 ui_update_liststore(ls, list);
326 }
327
328
329 if(args.onactivate) {
330 g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), listview);
331 }
332
333
334 UI_APPLY_LAYOUT1(current, args);
335 current->container->add(current->container, view,
FALSE);
336 return view;
337 }
338
339 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
340 UiObject* current = uic_current_obj(obj);
341
342 GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
343
344
345
346
347 UiListView *tableview = create_listview(obj, args);
348
349 GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args.multiselection);
350 GtkWidget *view = gtk_column_view_new(
GTK_SELECTION_MODEL(selection_model));
351
352 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
353
354
355 tableview->widget = view;
356 tableview->var = var;
357 tableview->liststore = ls;
358 tableview->selectionmodel = selection_model;
359 g_signal_connect(
360 view,
361 "destroy",
362 G_CALLBACK(ui_listview_destroy),
363 tableview);
364
365
366
367 UiModel *model = args.model;
368 int columns = model ? model->columns :
0;
369
370 tableview->columns = calloc(columns,
sizeof(UiColData));
371
372 int addi =
0;
373 for(
int i=
0;i<columns;i++) {
374 tableview->columns[i].listview = tableview;
375 tableview->columns[i].model_column = i;
376 tableview->columns[i].data_column = i+addi;
377
378 if(model->types[i] ==
UI_ICON_TEXT || model->types[i] ==
UI_ICON_TEXT_FREE) {
379
380 addi++;
381 }
382
383 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
384 UiColData *col = &tableview->columns[i];
385 g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), col);
386 g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), col);
387
388 GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
389 gtk_column_view_column_set_resizable(column, true);
390 gtk_column_view_append_column(
GTK_COLUMN_VIEW(view), column);
391 }
392
393
394 if(var && var->value) {
395 UiList *list = var->value;
396
397 list->obj = tableview;
398 list->update = ui_listview_update2;
399 list->getselection = ui_listview_getselection2;
400 list->setselection = ui_listview_setselection2;
401
402 ui_update_liststore(ls, list);
403 }
404
405
406 if(args.onactivate) {
407 g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), tableview);
408 }
409
410
411 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
412 gtk_scrolled_window_set_policy(
413 GTK_SCROLLED_WINDOW(scroll_area),
414 GTK_POLICY_AUTOMATIC,
415 GTK_POLICY_AUTOMATIC);
416 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
417
418 UI_APPLY_LAYOUT1(current, args);
419 current->container->add(current->container, scroll_area,
FALSE);
420
421
422
423 current->container->current = view;
424
425 return scroll_area;
426 }
427
428 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
429 UiListSelection sel = {
0,
NULL };
430 GtkBitset *bitset = gtk_selection_model_get_selection(model);
431 int n = gtk_bitset_get_size(bitset);
432 printf(
"bitset %d\n", n);
433
434 gtk_bitset_unref(bitset);
435 return sel;
436 }
437
438 static void listview_event(ui_callback cb,
void *cbdata, UiListView *view) {
439 UiEvent event;
440 event.obj = view->obj;
441 event.document = event.obj->ctx->document;
442 event.window = event.obj->window;
443 event.intval = view->selection.count;
444 event.eventdata = &view->selection;
445 if(cb) {
446 cb(&event, cbdata);
447 }
448 }
449
450 void ui_columnview_activate(
void *ignore, guint position, gpointer userdata) {
451 UiListView *view = userdata;
452 listview_event(view->onactivate, view->onactivatedata, view);
453 }
454
455 void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) {
456 UiListView *view = userdata;
457 free(view->selection.rows);
458 view->selection.count =
0;
459 view->selection.rows =
NULL;
460
461 CX_ARRAY_DECLARE(
int, newselection);
462 cx_array_initialize(newselection,
8);
463
464 size_t nitems = g_list_model_get_n_items(
G_LIST_MODEL(view->liststore));
465
466 for(
size_t i=
0;i<nitems;i++) {
467 if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
468 int s = (
int)i;
469 cx_array_simple_add(newselection, s);
470 }
471 }
472
473 if(newselection_size >
0) {
474 view->selection.count = newselection_size;
475 view->selection.rows = newselection;
476 }
else {
477 free(newselection);
478 }
479
480 listview_event(view->onselection, view->onselectiondata, view);
481 }
482
483 void ui_dropdown_activate(GtkDropDown* self, gpointer userdata) {
484 UiListView *view = userdata;
485 guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
486 UiListSelection sel = {
0,
NULL };
487 int sel2 = (
int)selection;
488 if(selection !=
GTK_INVALID_LIST_POSITION) {
489 sel.count =
1;
490 sel.rows = &sel2;
491 }
492
493 if(view->onactivate) {
494 UiEvent event;
495 event.obj = view->obj;
496 event.document = event.obj->ctx->document;
497 event.window = event.obj->window;
498 event.intval = view->selection.count;
499 event.eventdata = &view->selection;
500 view->onactivate(&event, view->onactivatedata);
501 }
502 }
503
504 void ui_update_liststore(GListStore *liststore, UiList *list) {
505 g_list_store_remove_all(liststore);
506 void *elm = list->first(list);
507 while(elm) {
508 ObjWrapper *obj = obj_wrapper_new(elm);
509 g_list_store_append(liststore, obj);
510 elm = list->next(list);
511 }
512 }
513
514 void ui_listview_update2(UiList *list,
int i) {
515 UiListView *view = list->obj;
516 ui_update_liststore(view->liststore, view->var->value);
517 }
518
519 UiListSelection ui_listview_getselection2(UiList *list) {
520 UiListView *view = list->obj;
521 UiListSelection selection;
522 selection.count = view->selection.count;
523 selection.rows = calloc(selection.count,
sizeof(
int));
524 memcpy(selection.rows, view->selection.rows, selection.count*
sizeof(
int));
525 return selection;
526 }
527
528 void ui_listview_setselection2(UiList *list, UiListSelection selection) {
529 UiListView *view = list->obj;
530 UiListSelection newselection;
531 newselection.count = view->selection.count;
532 if(selection.count >
0) {
533 newselection.rows = calloc(newselection.count,
sizeof(
int));
534 memcpy(newselection.rows, selection.rows, selection.count*
sizeof(
int));
535 }
else {
536 newselection.rows =
NULL;
537 }
538 free(view->selection.rows);
539 view->selection = newselection;
540
541 gtk_selection_model_unselect_all(view->selectionmodel);
542 if(selection.count >
0) {
543 for(
int i=
0;i<selection.count;i++) {
544 gtk_selection_model_select_item(view->selectionmodel, selection.rows[i],
FALSE);
545 }
546 }
547 }
548
549 UiListSelection ui_combobox_getselection(UiList *list) {
550 UiListView *view = list->obj;
551 guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
552 UiListSelection sel = {
0,
NULL };
553 if(selection !=
GTK_INVALID_LIST_POSITION) {
554 sel.count =
1;
555 sel.rows = malloc(
sizeof(
int));
556 sel.rows[
0] = (
int)selection;
557 }
558 return sel;
559 }
560
561 void ui_combobox_setselection(UiList *list, UiListSelection selection) {
562 UiListView *view = list->obj;
563 if(selection.count >
0) {
564 gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget), selection.rows[
0]);
565 }
else {
566 gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget),
GTK_INVALID_LIST_POSITION);
567 }
568 }
569
570 #else
571
572 static GtkListStore* create_list_store(UiList *list, UiModel *model) {
573 int columns = model->columns;
574 GType types[
2*columns];
575 int c =
0;
576 for(
int i=
0;i<columns;i++,c++) {
577 switch(model->types[i]) {
578 case UI_STRING:
579 case UI_STRING_FREE: types[c] =
G_TYPE_STRING;
break;
580 case UI_INTEGER: types[c] =
G_TYPE_INT;
break;
581 case UI_ICON: types[c] =
G_TYPE_OBJECT;
break;
582 case UI_ICON_TEXT:
583 case UI_ICON_TEXT_FREE: {
584 types[c] =
G_TYPE_OBJECT;
585 types[++c] =
G_TYPE_STRING;
586 }
587 }
588 }
589
590 GtkListStore *store = gtk_list_store_newv(c, types);
591
592 if(list) {
593 void *elm = list->first(list);
594 while(elm) {
595
596 GtkTreeIter iter;
597 gtk_list_store_insert (store, &iter, -
1);
598
599
600 int c =
0;
601 for(
int i=
0;i<columns;i++,c++) {
602 void *data = model->getvalue(elm, c);
603
604 GValue value =
G_VALUE_INIT;
605 switch(model->types[i]) {
606 case UI_STRING:
607 case UI_STRING_FREE: {
608 g_value_init(&value,
G_TYPE_STRING);
609 g_value_set_string(&value, data);
610 if(model->types[i] ==
UI_STRING_FREE) {
611 free(data);
612 }
613 break;
614 }
615 case UI_INTEGER: {
616 g_value_init(&value,
G_TYPE_INT);
617 int *intptr = data;
618 g_value_set_int(&value, *intptr);
619 break;
620 }
621 case UI_ICON: {
622 g_value_init(&value,
G_TYPE_OBJECT);
623 UiIcon *icon = data;
624 #if GTK_MAJOR_VERSION >=
4
625 g_value_set_object(&value, icon->info);
626 #else
627 if(!icon->pixbuf && icon->info) {
628 GError *error =
NULL;
629 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
630 icon->pixbuf = pixbuf;
631 }
632
633 if(icon->pixbuf) {
634 g_value_set_object(&value, icon->pixbuf);
635 }
636 #endif
637 break;
638 }
639 case UI_ICON_TEXT:
640 case UI_ICON_TEXT_FREE: {
641 UiIcon *icon = data;
642 #if GTK_MAJOR_VERSION >=
4
643 if(icon) {
644 GValue iconvalue =
G_VALUE_INIT;
645 g_value_init(&iconvalue,
G_TYPE_OBJECT);
646 g_value_set_object(&iconvalue, ui_icon_pixbuf(icon));
647 gtk_list_store_set_value(store, &iter, c, &iconvalue);
648 }
649 #else
650 GValue pixbufvalue =
G_VALUE_INIT;
651 if(icon) {
652 if(!icon->pixbuf && icon->info) {
653 GError *error =
NULL;
654 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
655 icon->pixbuf = pixbuf;
656 }
657 g_value_init(&pixbufvalue,
G_TYPE_OBJECT);
658 g_value_set_object(&pixbufvalue, icon->pixbuf);
659 gtk_list_store_set_value(store, &iter, c, &pixbufvalue);
660 }
661 #endif
662 c++;
663
664 char *str = model->getvalue(elm, c);
665 g_value_init(&value,
G_TYPE_STRING);
666 g_value_set_string(&value, str);
667 if(model->types[i] ==
UI_ICON_TEXT_FREE) {
668 free(str);
669 }
670 break;
671 }
672 }
673
674 gtk_list_store_set_value(store, &iter, c, &value);
675 }
676
677
678 elm = list->next(list);
679 }
680 }
681
682 return store;
683 }
684
685
686 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
687 UiObject* current = uic_current_obj(obj);
688
689
690 GtkWidget *view = gtk_tree_view_new();
691 ui_set_name_and_style(view, args.name, args.style_class);
692 ui_set_widget_groups(obj->ctx, view, args.groups);
693 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
694 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
NULL, renderer,
"text",
0,
NULL);
695 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
696
697 gtk_tree_view_set_headers_visible(
GTK_TREE_VIEW(view),
FALSE);
698 #ifdef UI_GTK3
699 #if GTK_MINOR_VERSION >=
8
700
701 #else
702
703 #endif
704 #else
705
706 #endif
707
708 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
709 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
710
711 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
712
713 UiList *list = var ? var->value :
NULL;
714 GtkListStore *listmodel = create_list_store(list, model);
715 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
716 g_object_unref(listmodel);
717
718 UiListView *listview = malloc(
sizeof(UiListView));
719 listview->obj = obj;
720 listview->widget = view;
721 listview->var = var;
722 listview->model = model;
723 listview->selection.count =
0;
724 listview->selection.rows =
NULL;
725 g_signal_connect(
726 view,
727 "destroy",
728 G_CALLBACK(ui_listview_destroy),
729 listview);
730
731
732 list->update = ui_listview_update;
733 list->getselection = ui_listview_getselection;
734 list->setselection = ui_listview_setselection;
735 list->obj = listview;
736
737
738 UiTreeEventData *event = malloc(
sizeof(UiTreeEventData));
739 event->obj = obj;
740 event->activate = args.onactivate;
741 event->activatedata = args.onactivatedata;
742 event->selection = args.onselection;
743 event->selectiondata = args.onselectiondata;
744 g_signal_connect(
745 view,
746 "destroy",
747 G_CALLBACK(ui_destroy_userdata),
748 event);
749
750 if(args.onactivate) {
751 g_signal_connect(
752 view,
753 "row-activated",
754 G_CALLBACK(ui_listview_activate_event),
755 event);
756 }
757 if(args.onselection) {
758 GtkTreeSelection *selection = gtk_tree_view_get_selection(
759 GTK_TREE_VIEW(view));
760 g_signal_connect(
761 selection,
762 "changed",
763 G_CALLBACK(ui_listview_selection_event),
764 event);
765 }
766 if(args.contextmenu) {
767 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view);
768 ui_widget_set_contextmenu(view, menu);
769 }
770
771
772
773 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
774 gtk_scrolled_window_set_policy(
775 GTK_SCROLLED_WINDOW(scroll_area),
776 GTK_POLICY_AUTOMATIC,
777 GTK_POLICY_AUTOMATIC);
778 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
779
780 UI_APPLY_LAYOUT1(current, args);
781 current->container->add(current->container, scroll_area,
FALSE);
782
783
784
785 current->container->current = view;
786
787 return scroll_area;
788 }
789
790 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
791 UiObject* current = uic_current_obj(obj);
792
793
794 GtkWidget *view = gtk_tree_view_new();
795
796 UiModel *model = args.model;
797 int columns = model ? model->columns :
0;
798
799 int addi =
0;
800 for(
int i=
0;i<columns;i++) {
801 GtkTreeViewColumn *column =
NULL;
802 if(model->types[i] ==
UI_ICON_TEXT) {
803 column = gtk_tree_view_column_new();
804 gtk_tree_view_column_set_title(column, model->titles[i]);
805
806 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
807 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
808
809 gtk_tree_view_column_pack_end(column, textrenderer,
TRUE);
810 gtk_tree_view_column_pack_start(column, iconrenderer,
FALSE);
811
812
813 gtk_tree_view_column_add_attribute(column, iconrenderer,
"pixbuf", i);
814 gtk_tree_view_column_add_attribute(column, textrenderer,
"text", i+
1);
815
816 addi++;
817 }
else if (model->types[i] ==
UI_ICON) {
818 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
819 column = gtk_tree_view_column_new_with_attributes(
820 model->titles[i],
821 iconrenderer,
822 "pixbuf",
823 i + addi,
824 NULL);
825 }
else {
826 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
827 column = gtk_tree_view_column_new_with_attributes(
828 model->titles[i],
829 renderer,
830 "text",
831 i + addi,
832 NULL);
833 }
834
835 int colsz = model->columnsize[i];
836 if(colsz >
0) {
837 gtk_tree_view_column_set_fixed_width(column, colsz);
838 }
else if(colsz <
0) {
839 gtk_tree_view_column_set_expand(column,
TRUE);
840 }
841
842 gtk_tree_view_column_set_resizable(column,
TRUE);
843 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
844 }
845
846
847 #ifdef UI_GTK3
848
849 #else
850
851 #endif
852
853 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
854
855 UiList *list = var ? var->value :
NULL;
856 GtkListStore *listmodel = create_list_store(list, model);
857 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
858 g_object_unref(listmodel);
859
860
861
862
863
864
865 UiListView *tableview = malloc(
sizeof(UiListView));
866 tableview->obj = obj;
867 tableview->widget = view;
868 tableview->var = var;
869 tableview->model = model;
870 tableview->ondragstart = args.ondragstart;
871 tableview->ondragstartdata = args.ondragstartdata;
872 tableview->ondragcomplete = args.ondragcomplete;
873 tableview->ondragcompletedata = args.ondragcompletedata;
874 tableview->ondrop = args.ondrop;
875 tableview->ondropdata = args.ondropsdata;
876 tableview->selection.count =
0;
877 tableview->selection.rows =
NULL;
878 g_signal_connect(
879 view,
880 "destroy",
881 G_CALLBACK(ui_listview_destroy),
882 tableview);
883
884
885 list->update = ui_listview_update;
886 list->getselection = ui_listview_getselection;
887 list->setselection = ui_listview_setselection;
888 list->obj = tableview;
889
890
891 UiTreeEventData *event = ui_malloc(obj->ctx,
sizeof(UiTreeEventData));
892 event->obj = obj;
893 event->activate = args.onactivate;
894 event->selection = args.onselection;
895 event->activatedata = args.onactivatedata;
896 event->selectiondata = args.onselectiondata;
897 if(args.onactivate) {
898 g_signal_connect(
899 view,
900 "row-activated",
901 G_CALLBACK(ui_listview_activate_event),
902 event);
903 }
904 if(args.onselection) {
905 GtkTreeSelection *selection = gtk_tree_view_get_selection(
906 GTK_TREE_VIEW(view));
907 g_signal_connect(
908 selection,
909 "changed",
910 G_CALLBACK(ui_listview_selection_event),
911 event);
912 }
913
914
915
916 if(args.ondragstart) {
917 ui_listview_add_dnd(tableview, &args);
918 }
919 if(args.ondrop) {
920 ui_listview_enable_drop(tableview, &args);
921 }
922
923 GtkTreeSelection *selection = gtk_tree_view_get_selection (
GTK_TREE_VIEW(view));
924 if(args.multiselection) {
925 gtk_tree_selection_set_mode(selection,
GTK_SELECTION_MULTIPLE);
926 }
927
928
929 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
930 gtk_scrolled_window_set_policy(
931 GTK_SCROLLED_WINDOW(scroll_area),
932 GTK_POLICY_AUTOMATIC,
933 GTK_POLICY_AUTOMATIC);
934 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
935
936 if(args.contextmenu) {
937 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area);
938 #if GTK_MAJOR_VERSION >=
4
939 ui_widget_set_contextmenu(scroll_area, menu);
940 #else
941 ui_widget_set_contextmenu(view, menu);
942 #endif
943 }
944
945 UI_APPLY_LAYOUT1(current, args);
946 current->container->add(current->container, scroll_area,
FALSE);
947
948
949
950 current->container->current = view;
951
952 return scroll_area;
953 }
954
955
956
957 void ui_listview_update(UiList *list,
int i) {
958 UiListView *view = list->obj;
959 GtkListStore *store = create_list_store(list, view->model);
960 gtk_tree_view_set_model(
GTK_TREE_VIEW(view->widget),
GTK_TREE_MODEL(store));
961 g_object_unref(
G_OBJECT(store));
962 }
963
964 UiListSelection ui_listview_getselection(UiList *list) {
965 UiListView *view = list->obj;
966 UiListSelection selection = ui_listview_selection(
967 gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget)),
968 NULL);
969 return selection;
970 }
971
972 void ui_listview_setselection(UiList *list, UiListSelection selection) {
973 UiListView *view = list->obj;
974 GtkTreeSelection *sel = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget));
975 GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
976 gtk_tree_selection_select_path(sel, path);
977
978 }
979
980
981
982
983
984 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
985 UiObject* current = uic_current_obj(obj);
986
987 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
988 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
989
990 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
991
992 GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata);
993 ui_set_name_and_style(combobox, args.name, args.style_class);
994 ui_set_widget_groups(obj->ctx, combobox, args.groups);
995 UI_APPLY_LAYOUT1(current, args);
996 current->container->add(current->container, combobox,
FALSE);
997 current->container->current = combobox;
998 return combobox;
999 }
1000
1001 GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f,
void *udata) {
1002 GtkWidget *combobox = gtk_combo_box_new();
1003
1004 UiListView *uicbox = malloc(
sizeof(UiListView));
1005 uicbox->obj = obj;
1006 uicbox->widget = combobox;
1007
1008 UiList *list = var ? var->value :
NULL;
1009 GtkListStore *listmodel = create_list_store(list, model);
1010
1011 if(listmodel) {
1012 gtk_combo_box_set_model(
GTK_COMBO_BOX(combobox),
GTK_TREE_MODEL(listmodel));
1013 g_object_unref(listmodel);
1014 }
1015
1016 uicbox->var = var;
1017 uicbox->model = model;
1018
1019 g_signal_connect(
1020 combobox,
1021 "destroy",
1022 G_CALLBACK(ui_combobox_destroy),
1023 uicbox);
1024
1025
1026 if(list) {
1027 list->update = ui_combobox_modelupdate;
1028 list->getselection = ui_combobox_getselection;
1029 list->setselection = ui_combobox_setselection;
1030 list->obj = uicbox;
1031 }
1032
1033 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
1034 gtk_cell_layout_pack_start(
GTK_CELL_LAYOUT(combobox), renderer,
TRUE);
1035 gtk_cell_layout_set_attributes(
1036 GTK_CELL_LAYOUT(combobox),
1037 renderer,
1038 "text",
1039 0,
1040 NULL);
1041 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox),
0);
1042
1043
1044 if(f) {
1045 UiEventData *event = ui_malloc(obj->ctx,
sizeof(UiEventData));
1046 event->obj = obj;
1047 event->userdata = udata;
1048 event->callback = f;
1049 event->value =
0;
1050 event->customdata =
NULL;
1051
1052 g_signal_connect(
1053 combobox,
1054 "changed",
1055 G_CALLBACK(ui_combobox_change_event),
1056 event);
1057 }
1058
1059 return combobox;
1060 }
1061
1062 void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) {
1063 UiEvent event;
1064 event.obj = e->obj;
1065 event.window = event.obj->window;
1066 event.document = event.obj->ctx->document;
1067 event.eventdata =
NULL;
1068 event.intval = gtk_combo_box_get_active(widget);
1069 e->callback(&event, e->userdata);
1070 }
1071
1072 void ui_combobox_modelupdate(UiList *list,
int i) {
1073 UiListView *view = list->obj;
1074 GtkListStore *store = create_list_store(view->var->value, view->model);
1075 gtk_combo_box_set_model(
GTK_COMBO_BOX(view->widget),
GTK_TREE_MODEL(store));
1076 g_object_unref(store);
1077 }
1078
1079 UiListSelection ui_combobox_getselection(UiList *list) {
1080 UiListView *combobox = list->obj;
1081 UiListSelection ret;
1082 ret.rows = malloc(
sizeof(
int*));
1083 ret.count =
1;
1084 ret.rows[
0] = gtk_combo_box_get_active(
GTK_COMBO_BOX(combobox->widget));
1085 return ret;
1086 }
1087
1088 void ui_combobox_setselection(UiList *list, UiListSelection selection) {
1089 UiListView *combobox = list->obj;
1090 if(selection.count >
0) {
1091 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox->widget), selection.rows[
0]);
1092 }
1093 }
1094
1095
1096
1097
1098 void ui_listview_activate_event(
1099 GtkTreeView *treeview,
1100 GtkTreePath *path,
1101 GtkTreeViewColumn *column,
1102 UiTreeEventData *event)
1103 {
1104 UiListSelection selection = ui_listview_selection(
1105 gtk_tree_view_get_selection(treeview),
1106 event);
1107
1108 UiEvent e;
1109 e.obj = event->obj;
1110 e.window = event->obj->window;
1111 e.document = event->obj->ctx->document;
1112 e.eventdata = &selection;
1113 e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
1114 event->activate(&e, event->activatedata);
1115
1116 if(selection.count >
0) {
1117 free(selection.rows);
1118 }
1119 }
1120
1121 void ui_listview_selection_event(
1122 GtkTreeSelection *treeselection,
1123 UiTreeEventData *event)
1124 {
1125 UiListSelection selection = ui_listview_selection(treeselection, event);
1126
1127 UiEvent e;
1128 e.obj = event->obj;
1129 e.window = event->obj->window;
1130 e.document = event->obj->ctx->document;
1131 e.eventdata = &selection;
1132 e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
1133 event->selection(&e, event->selectiondata);
1134
1135 if(selection.count >
0) {
1136 free(selection.rows);
1137 }
1138 }
1139
1140 UiListSelection ui_listview_selection(
1141 GtkTreeSelection *selection,
1142 UiTreeEventData *event)
1143 {
1144 GList *rows = gtk_tree_selection_get_selected_rows(selection,
NULL);
1145
1146 UiListSelection ls;
1147 ls.count = g_list_length(rows);
1148 ls.rows = calloc(ls.count,
sizeof(
int));
1149 GList *r = rows;
1150 int i =
0;
1151 while(r) {
1152 GtkTreePath *path = r->data;
1153 ls.rows[i] = ui_tree_path_list_index(path);
1154 r = r->next;
1155 i++;
1156 }
1157 return ls;
1158 }
1159
1160 int ui_tree_path_list_index(GtkTreePath *path) {
1161 int depth = gtk_tree_path_get_depth(path);
1162 if(depth ==
0) {
1163 fprintf(stderr,
"UiError: treeview selection: depth == 0\n");
1164 return -
1;
1165 }
1166 int *indices = gtk_tree_path_get_indices(path);
1167 return indices[depth -
1];
1168 }
1169
1170
1171 #endif
1172
1173
1174 #if GTK_MAJOR_VERSION >=
4
1175
1176 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source,
double x,
double y,
void *data) {
1177
1178 UiListView *listview = data;
1179
1180 UiDnD *dnd = ui_create_dnd();
1181 GdkContentProvider *provider =
NULL;
1182
1183
1184 if(listview->ondragstart) {
1185 UiEvent event;
1186 event.obj = listview->obj;
1187 event.window = event.obj->window;
1188 event.document = event.obj->ctx->document;
1189 event.eventdata = dnd;
1190 event.intval =
0;
1191 listview->ondragstart(&event, listview->ondragstartdata);
1192 }
1193
1194 size_t numproviders = cxListSize(dnd->providers);
1195 if(numproviders >
0) {
1196 GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers,
0);
1197 provider = gdk_content_provider_new_union(providers, numproviders);
1198 }
1199 ui_dnd_free(dnd);
1200
1201 return provider;
1202 }
1203
1204 static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) {
1205
1206 }
1207
1208 static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) {
1209
1210 UiListView *listview = user_data;
1211 if(listview->ondragcomplete) {
1212 UiDnD dnd;
1213 dnd.target =
NULL;
1214 dnd.value =
NULL;
1215 dnd.providers =
NULL;
1216 dnd.selected_action = gdk_drag_get_selected_action(drag);
1217 dnd.delete = delete_data;
1218 dnd.accept =
FALSE;
1219
1220 UiEvent event;
1221 event.obj = listview->obj;
1222 event.window = event.obj->window;
1223 event.document = event.obj->ctx->document;
1224 event.eventdata = &dnd;
1225 event.intval =
0;
1226 listview->ondragcomplete(&event, listview->ondragcompletedata);
1227 }
1228 }
1229
1230 static gboolean ui_listview_drop(
1231 GtkDropTarget *target,
1232 const GValue* value,
1233 gdouble x,
1234 gdouble y,
1235 gpointer user_data)
1236 {
1237 UiListView *listview = user_data;
1238 UiDnD dnd;
1239 dnd.providers =
NULL;
1240 dnd.target = target;
1241 dnd.value = value;
1242 dnd.selected_action =
0;
1243 dnd.delete =
FALSE;
1244 dnd.accept =
FALSE;
1245
1246 if(listview->ondrop) {
1247 dnd.accept =
TRUE;
1248 UiEvent event;
1249 event.obj = listview->obj;
1250 event.window = event.obj->window;
1251 event.document = event.obj->ctx->document;
1252 event.eventdata = &dnd;
1253 event.intval =
0;
1254 listview->ondrop(&event, listview->ondropdata);
1255 }
1256
1257 return dnd.accept;
1258 }
1259
1260 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
1261 GtkDragSource *dragsource = gtk_drag_source_new();
1262 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(dragsource));
1263 g_signal_connect (dragsource,
"prepare",
G_CALLBACK (ui_listview_dnd_prepare), listview);
1264 g_signal_connect(
1265 dragsource,
1266 "drag-begin",
1267 G_CALLBACK(ui_listview_drag_begin),
1268 listview);
1269 g_signal_connect(
1270 dragsource,
1271 "drag-end",
1272 G_CALLBACK(ui_listview_drag_end),
1273 listview);
1274 }
1275
1276 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
1277 GtkDropTarget *target = gtk_drop_target_new(
G_TYPE_INVALID,
GDK_ACTION_COPY);
1278 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(target));
1279 GType default_types[
2] = {
GDK_TYPE_FILE_LIST,
G_TYPE_STRING };
1280 gtk_drop_target_set_gtypes(target, default_types,
2);
1281 g_signal_connect(target,
"drop",
G_CALLBACK(ui_listview_drop), listview);
1282 }
1283
1284 #else
1285
1286 static GtkTargetEntry targetentries[] =
1287 {
1288 {
"STRING",
0,
0 },
1289 {
"text/plain",
0,
1 },
1290 {
"text/uri-list",
0,
2 },
1291 };
1292
1293 static void ui_listview_drag_getdata(
1294 GtkWidget* self,
1295 GdkDragContext* context,
1296 GtkSelectionData* data,
1297 guint info,
1298 guint time,
1299 gpointer user_data)
1300 {
1301 UiListView *listview = user_data;
1302 UiDnD dnd;
1303 dnd.context = context;
1304 dnd.data = data;
1305 dnd.selected_action =
0;
1306 dnd.delete =
FALSE;
1307 dnd.accept =
FALSE;
1308
1309 if(listview->ondragstart) {
1310 UiEvent event;
1311 event.obj = listview->obj;
1312 event.window = event.obj->window;
1313 event.document = event.obj->ctx->document;
1314 event.eventdata = &dnd;
1315 event.intval =
0;
1316 listview->ondragstart(&event, listview->ondragstartdata);
1317 }
1318 }
1319
1320 static void ui_listview_drag_end(
1321 GtkWidget *widget,
1322 GdkDragContext *context,
1323 guint time,
1324 gpointer user_data)
1325 {
1326 UiListView *listview = user_data;
1327 UiDnD dnd;
1328 dnd.context = context;
1329 dnd.data =
NULL;
1330 dnd.selected_action = gdk_drag_context_get_selected_action(context);
1331 dnd.delete = dnd.selected_action ==
UI_DND_ACTION_MOVE ?
TRUE :
FALSE;
1332 dnd.accept =
FALSE;
1333 if(listview->ondragcomplete) {
1334 UiEvent event;
1335 event.obj = listview->obj;
1336 event.window = event.obj->window;
1337 event.document = event.obj->ctx->document;
1338 event.eventdata = &dnd;
1339 event.intval =
0;
1340 listview->ondragcomplete(&event, listview->ondragcompletedata);
1341 }
1342 }
1343
1344 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
1345 gtk_tree_view_enable_model_drag_source(
1346 GTK_TREE_VIEW(listview->widget),
1347 GDK_BUTTON1_MASK,
1348 targetentries,
1349 2,
1350 GDK_ACTION_COPY);
1351
1352 g_signal_connect(listview->widget,
"drag-data-get",
G_CALLBACK(ui_listview_drag_getdata), listview);
1353 g_signal_connect(listview->widget,
"drag-end",
G_CALLBACK(ui_listview_drag_end), listview);
1354 }
1355
1356
1357
1358
1359 static void ui_listview_drag_data_received(
1360 GtkWidget *self,
1361 GdkDragContext *context,
1362 gint x,
1363 gint y,
1364 GtkSelectionData *data,
1365 guint info,
1366 guint time,
1367 gpointer user_data)
1368 {
1369 UiListView *listview = user_data;
1370 UiDnD dnd;
1371 dnd.context = context;
1372 dnd.data = data;
1373 dnd.selected_action =
0;
1374 dnd.delete =
FALSE;
1375 dnd.accept =
FALSE;
1376
1377 if(listview->ondrop) {
1378 dnd.accept =
TRUE;
1379 UiEvent event;
1380 event.obj = listview->obj;
1381 event.window = event.obj->window;
1382 event.document = event.obj->ctx->document;
1383 event.eventdata = &dnd;
1384 event.intval =
0;
1385 listview->ondrop(&event, listview->ondropdata);
1386 }
1387 }
1388
1389 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
1390 gtk_tree_view_enable_model_drag_dest(
1391 GTK_TREE_VIEW(listview->widget),
1392 targetentries,
1393 3,
1394 GDK_ACTION_COPY);
1395 if(listview->ondrop) {
1396 g_signal_connect(listview->widget,
"drag_data_received",
G_CALLBACK(ui_listview_drag_data_received), listview);
1397 }
1398 }
1399
1400 #endif
1401
1402
1403 GtkWidget* ui_get_tree_widget(
UIWIDGET widget) {
1404 return SCROLLEDWINDOW_GET_CHILD(widget);
1405 }
1406
1407 static char** targets2array(
char *target0, va_list ap,
int *nelm) {
1408 int al =
16;
1409 char **targets = calloc(
16,
sizeof(
char*));
1410 targets[
0] = target0;
1411
1412 int i =
1;
1413 char *target;
1414 while((target = va_arg(ap,
char*)) !=
NULL) {
1415 if(i >= al) {
1416 al *=
2;
1417 targets = realloc(targets, al*
sizeof(
char*));
1418 }
1419 targets[i] = target;
1420 i++;
1421 }
1422
1423 *nelm = i;
1424 return targets;
1425 }
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437 void ui_table_dragsource(
UIWIDGET tablewidget,
int actions,
char *target0, ...) {
1438 va_list ap;
1439 va_start(ap, target0);
1440 int nelm;
1441 char **targets = targets2array(target0, ap, &nelm);
1442 va_end(ap);
1443
1444
1445
1446
1447 free(targets);
1448 }
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
1485
1486 ui_destroy_boundvar(v->obj->ctx, v->var);
1487 #if GTK_CHECK_VERSION(
4,
10,
0)
1488 free(v->columns);
1489 #endif
1490 free(v->selection.rows);
1491 free(v);
1492 }
1493
1494 void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
1495 ui_destroy_boundvar(v->obj->ctx, v->var);
1496 free(v);
1497 }
1498
1499
1500
1501
1502 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
1503 cxListFree(v->sublists);
1504 free(v);
1505 }
1506
1507 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
1508 free(sublist->header);
1509 ui_destroy_boundvar(obj->ctx, sublist->var);
1510 cxListFree(sublist->widgets);
1511 }
1512
1513 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
1514
1515 UiListBox *listbox = g_object_get_data(
G_OBJECT(row),
"ui_listbox");
1516 if(!listbox) {
1517 return;
1518 }
1519
1520 UiListBoxSubList *sublist = g_object_get_data(
G_OBJECT(row),
"ui_listbox_sublist");
1521 if(!sublist) {
1522 return;
1523 }
1524
1525 if(sublist->separator) {
1526 GtkWidget *separator = gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL);
1527 gtk_list_box_row_set_header(row, separator);
1528 }
else if(sublist->header) {
1529 GtkWidget *header = gtk_label_new(sublist->header);
1530 gtk_widget_set_halign(header,
GTK_ALIGN_START);
1531 if(row == listbox->first_row) {
1532 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header-first");
1533 }
else {
1534 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header");
1535 }
1536 gtk_list_box_row_set_header(row, header);
1537 }
1538 }
1539
1540 #ifdef UI_GTK3
1541 typedef struct _UiSidebarListBoxClass {
1542 GtkListBoxClass parent_class;
1543 } UiSidebarListBoxClass;
1544
1545 typedef struct _UiSidebarListBox {
1546 GtkListBox parent_instance;
1547 } UiSidebarListBox;
1548
1549 G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box,
GTK_TYPE_LIST_BOX)
1550
1551
1552 static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) {
1553 GtkWidgetClass *widget_class =
GTK_WIDGET_CLASS(klass);
1554 gtk_widget_class_set_css_name (widget_class,
"placessidebar");
1555 }
1556
1557 static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
1558
1559 }
1560 #endif
1561
1562 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
1563 UiObject* current = uic_current_obj(obj);
1564
1565 #ifdef UI_GTK3
1566 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(),
NULL);
1567 #else
1568 GtkWidget *listbox = gtk_list_box_new();
1569 #endif
1570 if(!args.style_class) {
1571 #if GTK_MAJOR_VERSION >=
4
1572 WIDGET_ADD_CSS_CLASS(listbox,
"navigation-sidebar");
1573 #else
1574 WIDGET_ADD_CSS_CLASS(listbox,
"sidebar");
1575 #endif
1576 }
1577 gtk_list_box_set_header_func(
GTK_LIST_BOX(listbox), listbox_create_header,
NULL,
NULL);
1578 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
1579 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
1580
1581 ui_set_name_and_style(listbox, args.name, args.style_class);
1582 ui_set_widget_groups(obj->ctx, listbox, args.groups);
1583 UI_APPLY_LAYOUT1(current, args);
1584 current->container->add(current->container, scroll_area,
TRUE);
1585
1586 UiListBox *uilistbox = malloc(
sizeof(UiListBox));
1587 uilistbox->obj = obj;
1588 uilistbox->listbox =
GTK_LIST_BOX(listbox);
1589 uilistbox->getvalue = args.getvalue;
1590 uilistbox->onactivate = args.onactivate;
1591 uilistbox->onactivatedata = args.onactivatedata;
1592 uilistbox->onbuttonclick = args.onbuttonclick;
1593 uilistbox->onbuttonclickdata = args.onbuttonclickdata;
1594 uilistbox->sublists = cxArrayListCreateSimple(
sizeof(UiListBoxSubList),
4);
1595 uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
1596 uilistbox->sublists->collection.destructor_data = obj;
1597 uilistbox->first_row =
NULL;
1598
1599 if(args.numsublists ==
0 && args.sublists) {
1600 args.numsublists =
INT_MAX;
1601 }
1602 for(
int i=
0;i<args.numsublists;i++) {
1603 UiSubList sublist = args.sublists[i];
1604 if(!sublist.varname && !sublist.value) {
1605 break;
1606 }
1607
1608 UiListBoxSubList uisublist;
1609 uisublist.var = uic_widget_var(
1610 obj->ctx,
1611 current->ctx,
1612 sublist.value,
1613 sublist.varname,
1614 UI_VAR_LIST);
1615 uisublist.numitems =
0;
1616 uisublist.header = sublist.header ? strdup(sublist.header) :
NULL;
1617 uisublist.separator = sublist.separator;
1618 uisublist.widgets = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
1619 uisublist.listbox = uilistbox;
1620 uisublist.userdata = sublist.userdata;
1621 uisublist.index = i;
1622
1623 cxListAdd(uilistbox->sublists, &uisublist);
1624
1625
1626 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-
1);
1627 UiList *list = uisublist.var->value;
1628 if(list) {
1629 list->obj = sublist_ptr;
1630 list->update = ui_listbox_list_update;
1631 }
1632 }
1633
1634 ui_listbox_update(uilistbox,
0, cxListSize(uilistbox->sublists));
1635
1636
1637
1638 g_object_set_data(
G_OBJECT(scroll_area),
"ui_listbox", uilistbox);
1639 g_object_set_data(
G_OBJECT(listbox),
"ui_listbox", uilistbox);
1640
1641
1642 g_signal_connect(
1643 listbox,
1644 "destroy",
1645 G_CALLBACK(ui_destroy_sourcelist),
1646 uilistbox);
1647
1648 if(args.onactivate) {
1649 g_signal_connect(
1650 listbox,
1651 "row-activated",
1652 G_CALLBACK(ui_listbox_row_activate),
1653 NULL);
1654 }
1655
1656 return scroll_area;
1657 }
1658
1659 void ui_listbox_update(UiListBox *listbox,
int from,
int to) {
1660 CxIterator i = cxListIterator(listbox->sublists);
1661 size_t pos =
0;
1662 cx_foreach(UiListBoxSubList *, sublist, i) {
1663 if(i.index < from) {
1664 pos += sublist->numitems;
1665 continue;
1666 }
1667 if(i.index > to) {
1668 break;
1669 }
1670
1671
1672 ui_listbox_update_sublist(listbox, sublist, pos);
1673 pos += sublist->numitems;
1674 }
1675 }
1676
1677 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item,
int index) {
1678 GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
10);
1679 if(item->icon) {
1680 GtkWidget *icon =
ICON_IMAGE(item->icon);
1681 BOX_ADD(hbox, icon);
1682 }
1683 GtkWidget *label = gtk_label_new(item->label);
1684 gtk_widget_set_halign(label,
GTK_ALIGN_START);
1685 BOX_ADD_EXPAND(hbox, label);
1686
1687 GtkWidget *row = gtk_list_box_row_new();
1688 LISTBOX_ROW_SET_CHILD(row, hbox);
1689
1690
1691 UiEventDataExt *event = malloc(
sizeof(UiEventDataExt));
1692 memset(event,
0,
sizeof(UiEventDataExt));
1693 event->obj = listbox->obj;
1694 event->customdata0 = sublist;
1695 event->customdata1 = sublist->var;
1696 event->customdata2 = item->eventdata;
1697 event->callback = listbox->onactivate;
1698 event->userdata = listbox->onactivatedata;
1699 event->callback2 = listbox->onbuttonclick;
1700 event->userdata2 = listbox->onbuttonclickdata;
1701 event->value0 = index;
1702
1703 g_signal_connect(
1704 row,
1705 "destroy",
1706 G_CALLBACK(ui_destroy_userdata),
1707 event);
1708
1709 g_object_set_data(
G_OBJECT(row),
"ui-listbox-row-eventdata", event);
1710
1711 return row;
1712 }
1713
1714 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist,
size_t listbox_insert_index) {
1715
1716 CxIterator r = cxListIterator(sublist->widgets);
1717 cx_foreach(GtkWidget*, widget, r) {
1718 LISTBOX_REMOVE(listbox->listbox, widget);
1719 }
1720 cxListClear(sublist->widgets);
1721
1722 sublist->numitems =
0;
1723
1724
1725 UiList *list = sublist->var->value;
1726 if(!list) {
1727 return;
1728 }
1729
1730 size_t index =
0;
1731 void *elm = list->first(list);
1732 while(elm) {
1733 UiSubListItem item = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
1734 listbox->getvalue(sublist->userdata, elm, index, &item);
1735
1736
1737 GtkWidget *row = create_listbox_row(listbox, sublist, &item, (
int)index);
1738 if(index ==
0) {
1739
1740
1741 g_object_set_data(
G_OBJECT(row),
"ui_listbox", listbox);
1742 g_object_set_data(
G_OBJECT(row),
"ui_listbox_sublist", sublist);
1743
1744 if(listbox_insert_index ==
0) {
1745
1746 listbox->first_row =
GTK_LIST_BOX_ROW(row);
1747 }
1748 }
1749 intptr_t rowindex = listbox_insert_index + index;
1750 g_object_set_data(
G_OBJECT(row),
"ui_listbox_row_index", (gpointer)rowindex);
1751 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
1752 cxListAdd(sublist->widgets, row);
1753
1754
1755 free(item.label);
1756 free(item.icon);
1757 free(item.button_label);
1758 free(item.button_icon);
1759 free(item.badge);
1760
1761
1762 elm = list->next(list);
1763 index++;
1764 }
1765
1766 sublist->numitems = cxListSize(sublist->widgets);
1767 }
1768
1769 void ui_listbox_list_update(UiList *list,
int i) {
1770 UiListBoxSubList *sublist = list->obj;
1771 }
1772
1773 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
1774 UiEventDataExt *data = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-eventdata");
1775 if(!data) {
1776 return;
1777 }
1778 UiListBoxSubList *sublist = data->customdata0;
1779
1780 UiEvent event;
1781 event.obj = data->obj;
1782 event.window = event.obj->window;
1783 event.document = event.obj->ctx->document;
1784 event.eventdata = data->customdata2;
1785 event.intval = data->value0;
1786
1787 if(data->callback) {
1788 data->callback(&event, data->userdata);
1789 }
1790 }
1791