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 static GtkListStore* create_list_store(UiList *list, UiModel *model) {
52 int columns = model->columns;
53 GType types[
2*columns];
54 int c =
0;
55 for(
int i=
0;i<columns;i++,c++) {
56 switch(model->types[i]) {
57 case UI_STRING:
58 case UI_STRING_FREE: types[c] =
G_TYPE_STRING;
break;
59 case UI_INTEGER: types[c] =
G_TYPE_INT;
break;
60 case UI_ICON: types[c] =
G_TYPE_OBJECT;
break;
61 case UI_ICON_TEXT:
62 case UI_ICON_TEXT_FREE: {
63 types[c] =
G_TYPE_OBJECT;
64 types[++c] =
G_TYPE_STRING;
65 }
66 }
67 }
68
69 GtkListStore *store = gtk_list_store_newv(c, types);
70
71 if(list) {
72 void *elm = list->first(list);
73 while(elm) {
74
75 GtkTreeIter iter;
76 gtk_list_store_insert (store, &iter, -
1);
77
78
79 int c =
0;
80 for(
int i=
0;i<columns;i++,c++) {
81 void *data = model->getvalue(elm, c);
82
83 GValue value =
G_VALUE_INIT;
84 switch(model->types[i]) {
85 case UI_STRING:
86 case UI_STRING_FREE: {
87 g_value_init(&value,
G_TYPE_STRING);
88 g_value_set_string(&value, data);
89 if(model->types[i] ==
UI_STRING_FREE) {
90 free(data);
91 }
92 break;
93 }
94 case UI_INTEGER: {
95 g_value_init(&value,
G_TYPE_INT);
96 int *intptr = data;
97 g_value_set_int(&value, *intptr);
98 break;
99 }
100 case UI_ICON: {
101 g_value_init(&value,
G_TYPE_OBJECT);
102 UiIcon *icon = data;
103 #if GTK_MAJOR_VERSION >=
4
104 g_value_set_object(&value, icon->info);
105 #else
106 if(!icon->pixbuf && icon->info) {
107 GError *error =
NULL;
108 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
109 icon->pixbuf = pixbuf;
110 }
111
112 if(icon->pixbuf) {
113 g_value_set_object(&value, icon->pixbuf);
114 }
115 #endif
116 break;
117 }
118 case UI_ICON_TEXT:
119 case UI_ICON_TEXT_FREE: {
120 UiIcon *icon = data;
121 #if GTK_MAJOR_VERSION >=
4
122 if(icon) {
123 GValue iconvalue =
G_VALUE_INIT;
124 g_value_init(&iconvalue,
G_TYPE_OBJECT);
125 g_value_set_object(&iconvalue, ui_icon_pixbuf(icon));
126 gtk_list_store_set_value(store, &iter, c, &iconvalue);
127 }
128 #else
129 GValue pixbufvalue =
G_VALUE_INIT;
130 if(icon) {
131 if(!icon->pixbuf && icon->info) {
132 GError *error =
NULL;
133 GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
134 icon->pixbuf = pixbuf;
135 }
136 g_value_init(&pixbufvalue,
G_TYPE_OBJECT);
137 g_value_set_object(&pixbufvalue, icon->pixbuf);
138 gtk_list_store_set_value(store, &iter, c, &pixbufvalue);
139 }
140 #endif
141 c++;
142
143 char *str = model->getvalue(elm, c);
144 g_value_init(&value,
G_TYPE_STRING);
145 g_value_set_string(&value, str);
146 if(model->types[i] ==
UI_ICON_TEXT_FREE) {
147 free(str);
148 }
149 break;
150 }
151 }
152
153 gtk_list_store_set_value(store, &iter, c, &value);
154 }
155
156
157 elm = list->next(list);
158 }
159 }
160
161 return store;
162 }
163
164
165 UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
166 UiObject* current = uic_current_obj(obj);
167
168
169 GtkWidget *view = gtk_tree_view_new();
170 ui_set_name_and_style(view, args.name, args.style_class);
171 ui_set_widget_groups(obj->ctx, view, args.groups);
172 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
173 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
NULL, renderer,
"text",
0,
NULL);
174 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
175
176 gtk_tree_view_set_headers_visible(
GTK_TREE_VIEW(view),
FALSE);
177 #ifdef UI_GTK3
178 #if GTK_MINOR_VERSION >=
8
179
180 #else
181
182 #endif
183 #else
184
185 #endif
186
187 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
188 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
189
190 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
191
192 UiList *list = var ? var->value :
NULL;
193 GtkListStore *listmodel = create_list_store(list, model);
194 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
195 g_object_unref(listmodel);
196
197 UiListView *listview = malloc(
sizeof(UiListView));
198 listview->obj = obj;
199 listview->widget = view;
200 listview->var = var;
201 listview->model = model;
202 g_signal_connect(
203 view,
204 "destroy",
205 G_CALLBACK(ui_listview_destroy),
206 listview);
207
208
209 list->update = ui_listview_update;
210 list->getselection = ui_listview_getselection;
211 list->setselection = ui_listview_setselection;
212 list->obj = listview;
213
214
215 UiTreeEventData *event = malloc(
sizeof(UiTreeEventData));
216 event->obj = obj;
217 event->activate = args.onactivate;
218 event->activatedata = args.onactivatedata;
219 event->selection = args.onselection;
220 event->selectiondata = args.onselectiondata;
221 g_signal_connect(
222 view,
223 "destroy",
224 G_CALLBACK(ui_destroy_userdata),
225 event);
226
227 if(args.onactivate) {
228 g_signal_connect(
229 view,
230 "row-activated",
231 G_CALLBACK(ui_listview_activate_event),
232 event);
233 }
234 if(args.onselection) {
235 GtkTreeSelection *selection = gtk_tree_view_get_selection(
236 GTK_TREE_VIEW(view));
237 g_signal_connect(
238 selection,
239 "changed",
240 G_CALLBACK(ui_listview_selection_event),
241 event);
242 }
243 if(args.contextmenu) {
244 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view);
245 ui_widget_set_contextmenu(view, menu);
246 }
247
248
249
250 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
251 gtk_scrolled_window_set_policy(
252 GTK_SCROLLED_WINDOW(scroll_area),
253 GTK_POLICY_AUTOMATIC,
254 GTK_POLICY_AUTOMATIC);
255 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
256
257 UI_APPLY_LAYOUT1(current, args);
258 current->container->add(current->container, scroll_area,
FALSE);
259
260
261
262 current->container->current = view;
263
264 return scroll_area;
265 }
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
294 UiObject* current = uic_current_obj(obj);
295
296
297 GtkWidget *view = gtk_tree_view_new();
298
299 UiModel *model = args.model;
300 int columns = model ? model->columns :
0;
301
302 int addi =
0;
303 for(
int i=
0;i<columns;i++) {
304 GtkTreeViewColumn *column =
NULL;
305 if(model->types[i] ==
UI_ICON_TEXT) {
306 column = gtk_tree_view_column_new();
307 gtk_tree_view_column_set_title(column, model->titles[i]);
308
309 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
310 GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
311
312 gtk_tree_view_column_pack_end(column, textrenderer,
TRUE);
313 gtk_tree_view_column_pack_start(column, iconrenderer,
FALSE);
314
315
316 gtk_tree_view_column_add_attribute(column, iconrenderer,
"pixbuf", i);
317 gtk_tree_view_column_add_attribute(column, textrenderer,
"text", i+
1);
318
319 addi++;
320 }
else if (model->types[i] ==
UI_ICON) {
321 GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
322 column = gtk_tree_view_column_new_with_attributes(
323 model->titles[i],
324 iconrenderer,
325 "pixbuf",
326 i + addi,
327 NULL);
328 }
else {
329 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
330 column = gtk_tree_view_column_new_with_attributes(
331 model->titles[i],
332 renderer,
333 "text",
334 i + addi,
335 NULL);
336 }
337
338 int colsz = model->columnsize[i];
339 if(colsz >
0) {
340 gtk_tree_view_column_set_fixed_width(column, colsz);
341 }
else if(colsz <
0) {
342 gtk_tree_view_column_set_expand(column,
TRUE);
343 }
344
345 gtk_tree_view_column_set_resizable(column,
TRUE);
346 gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
347 }
348
349
350 #ifdef UI_GTK3
351
352 #else
353
354 #endif
355
356 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
357
358 UiList *list = var ? var->value :
NULL;
359 GtkListStore *listmodel = create_list_store(list, model);
360 gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
361 g_object_unref(listmodel);
362
363
364
365
366
367
368 UiListView *tableview = malloc(
sizeof(UiListView));
369 tableview->obj = obj;
370 tableview->widget = view;
371 tableview->var = var;
372 tableview->model = model;
373 tableview->ondragstart = args.ondragstart;
374 tableview->ondragstartdata = args.ondragstartdata;
375 tableview->ondragcomplete = args.ondragcomplete;
376 tableview->ondragcompletedata = args.ondragcompletedata;
377 tableview->ondrop = args.ondrop;
378 tableview->ondropdata = args.ondropsdata;
379 g_signal_connect(
380 view,
381 "destroy",
382 G_CALLBACK(ui_listview_destroy),
383 tableview);
384
385
386 list->update = ui_listview_update;
387 list->getselection = ui_listview_getselection;
388 list->setselection = ui_listview_setselection;
389 list->obj = tableview;
390
391
392 UiTreeEventData *event = ui_malloc(obj->ctx,
sizeof(UiTreeEventData));
393 event->obj = obj;
394 event->activate = args.onactivate;
395 event->selection = args.onselection;
396 event->activatedata = args.onactivatedata;
397 event->selectiondata = args.onselectiondata;
398 if(args.onactivate) {
399 g_signal_connect(
400 view,
401 "row-activated",
402 G_CALLBACK(ui_listview_activate_event),
403 event);
404 }
405 if(args.onselection) {
406 GtkTreeSelection *selection = gtk_tree_view_get_selection(
407 GTK_TREE_VIEW(view));
408 g_signal_connect(
409 selection,
410 "changed",
411 G_CALLBACK(ui_listview_selection_event),
412 event);
413 }
414
415
416
417 if(args.ondragstart) {
418 ui_listview_add_dnd(tableview, &args);
419 }
420 if(args.ondrop) {
421 ui_listview_enable_drop(tableview, &args);
422 }
423
424 GtkTreeSelection *selection = gtk_tree_view_get_selection (
GTK_TREE_VIEW(view));
425 if(args.multiselection) {
426 gtk_tree_selection_set_mode(selection,
GTK_SELECTION_MULTIPLE);
427 }
428
429
430 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
431 gtk_scrolled_window_set_policy(
432 GTK_SCROLLED_WINDOW(scroll_area),
433 GTK_POLICY_AUTOMATIC,
434 GTK_POLICY_AUTOMATIC);
435 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
436
437 if(args.contextmenu) {
438 UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area);
439 #if GTK_MAJOR_VERSION >=
4
440 ui_widget_set_contextmenu(scroll_area, menu);
441 #else
442 ui_widget_set_contextmenu(view, menu);
443 #endif
444 }
445
446 UI_APPLY_LAYOUT1(current, args);
447 current->container->add(current->container, scroll_area,
FALSE);
448
449
450
451 current->container->current = view;
452
453 return scroll_area;
454 }
455
456 #if GTK_MAJOR_VERSION >=
4
457
458 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source,
double x,
double y,
void *data) {
459
460 UiListView *listview = data;
461
462 UiDnD *dnd = ui_create_dnd();
463 GdkContentProvider *provider =
NULL;
464
465
466 if(listview->ondragstart) {
467 UiEvent event;
468 event.obj = listview->obj;
469 event.window = event.obj->window;
470 event.document = event.obj->ctx->document;
471 event.eventdata = dnd;
472 event.intval =
0;
473 listview->ondragstart(&event, listview->ondragstartdata);
474 }
475
476 size_t numproviders = cxListSize(dnd->providers);
477 if(numproviders >
0) {
478 GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers,
0);
479 provider = gdk_content_provider_new_union(providers, numproviders);
480 }
481 ui_dnd_free(dnd);
482
483 return provider;
484 }
485
486 static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) {
487
488 }
489
490 static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) {
491
492 UiListView *listview = user_data;
493 if(listview->ondragcomplete) {
494 UiDnD dnd;
495 dnd.target =
NULL;
496 dnd.value =
NULL;
497 dnd.providers =
NULL;
498 dnd.selected_action = gdk_drag_get_selected_action(drag);
499 dnd.delete = delete_data;
500 dnd.accept =
FALSE;
501
502 UiEvent event;
503 event.obj = listview->obj;
504 event.window = event.obj->window;
505 event.document = event.obj->ctx->document;
506 event.eventdata = &dnd;
507 event.intval =
0;
508 listview->ondragcomplete(&event, listview->ondragcompletedata);
509 }
510 }
511
512 static gboolean ui_listview_drop(
513 GtkDropTarget *target,
514 const GValue* value,
515 gdouble x,
516 gdouble y,
517 gpointer user_data)
518 {
519 UiListView *listview = user_data;
520 UiDnD dnd;
521 dnd.providers =
NULL;
522 dnd.target = target;
523 dnd.value = value;
524 dnd.selected_action =
0;
525 dnd.delete =
FALSE;
526 dnd.accept =
FALSE;
527
528 if(listview->ondrop) {
529 dnd.accept =
TRUE;
530 UiEvent event;
531 event.obj = listview->obj;
532 event.window = event.obj->window;
533 event.document = event.obj->ctx->document;
534 event.eventdata = &dnd;
535 event.intval =
0;
536 listview->ondrop(&event, listview->ondropdata);
537 }
538
539 return dnd.accept;
540 }
541
542 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
543 GtkDragSource *dragsource = gtk_drag_source_new();
544 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(dragsource));
545 g_signal_connect (dragsource,
"prepare",
G_CALLBACK (ui_listview_dnd_prepare), listview);
546 g_signal_connect(
547 dragsource,
548 "drag-begin",
549 G_CALLBACK(ui_listview_drag_begin),
550 listview);
551 g_signal_connect(
552 dragsource,
553 "drag-end",
554 G_CALLBACK(ui_listview_drag_end),
555 listview);
556 }
557
558 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
559 GtkDropTarget *target = gtk_drop_target_new(
G_TYPE_INVALID,
GDK_ACTION_COPY);
560 gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(target));
561 GType default_types[
2] = {
GDK_TYPE_FILE_LIST,
G_TYPE_STRING };
562 gtk_drop_target_set_gtypes(target, default_types,
2);
563 g_signal_connect(target,
"drop",
G_CALLBACK(ui_listview_drop), listview);
564 }
565
566 #else
567
568 static GtkTargetEntry targetentries[] =
569 {
570 {
"STRING",
0,
0 },
571 {
"text/plain",
0,
1 },
572 {
"text/uri-list",
0,
2 },
573 };
574
575 static void ui_listview_drag_getdata(
576 GtkWidget* self,
577 GdkDragContext* context,
578 GtkSelectionData* data,
579 guint info,
580 guint time,
581 gpointer user_data)
582 {
583 UiListView *listview = user_data;
584 UiDnD dnd;
585 dnd.context = context;
586 dnd.data = data;
587 dnd.selected_action =
0;
588 dnd.delete =
FALSE;
589 dnd.accept =
FALSE;
590
591 if(listview->ondragstart) {
592 UiEvent event;
593 event.obj = listview->obj;
594 event.window = event.obj->window;
595 event.document = event.obj->ctx->document;
596 event.eventdata = &dnd;
597 event.intval =
0;
598 listview->ondragstart(&event, listview->ondragstartdata);
599 }
600 }
601
602 static void ui_listview_drag_end(
603 GtkWidget *widget,
604 GdkDragContext *context,
605 guint time,
606 gpointer user_data)
607 {
608 UiListView *listview = user_data;
609 UiDnD dnd;
610 dnd.context = context;
611 dnd.data =
NULL;
612 dnd.selected_action = gdk_drag_context_get_selected_action(context);
613 dnd.delete = dnd.selected_action ==
UI_DND_ACTION_MOVE ?
TRUE :
FALSE;
614 dnd.accept =
FALSE;
615 if(listview->ondragcomplete) {
616 UiEvent event;
617 event.obj = listview->obj;
618 event.window = event.obj->window;
619 event.document = event.obj->ctx->document;
620 event.eventdata = &dnd;
621 event.intval =
0;
622 listview->ondragcomplete(&event, listview->ondragcompletedata);
623 }
624 }
625
626 void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
627 gtk_tree_view_enable_model_drag_source(
628 GTK_TREE_VIEW(listview->widget),
629 GDK_BUTTON1_MASK,
630 targetentries,
631 2,
632 GDK_ACTION_COPY);
633
634 g_signal_connect(listview->widget,
"drag-data-get",
G_CALLBACK(ui_listview_drag_getdata), listview);
635 g_signal_connect(listview->widget,
"drag-end",
G_CALLBACK(ui_listview_drag_end), listview);
636 }
637
638
639
640
641 static void ui_listview_drag_data_received(
642 GtkWidget *self,
643 GdkDragContext *context,
644 gint x,
645 gint y,
646 GtkSelectionData *data,
647 guint info,
648 guint time,
649 gpointer user_data)
650 {
651 UiListView *listview = user_data;
652 UiDnD dnd;
653 dnd.context = context;
654 dnd.data = data;
655 dnd.selected_action =
0;
656 dnd.delete =
FALSE;
657 dnd.accept =
FALSE;
658
659 if(listview->ondrop) {
660 dnd.accept =
TRUE;
661 UiEvent event;
662 event.obj = listview->obj;
663 event.window = event.obj->window;
664 event.document = event.obj->ctx->document;
665 event.eventdata = &dnd;
666 event.intval =
0;
667 listview->ondrop(&event, listview->ondropdata);
668 }
669 }
670
671 void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
672 gtk_tree_view_enable_model_drag_dest(
673 GTK_TREE_VIEW(listview->widget),
674 targetentries,
675 3,
676 GDK_ACTION_COPY);
677 if(listview->ondrop) {
678 g_signal_connect(listview->widget,
"drag_data_received",
G_CALLBACK(ui_listview_drag_data_received), listview);
679 }
680 }
681
682 #endif
683
684
685 GtkWidget* ui_get_tree_widget(
UIWIDGET widget) {
686 return SCROLLEDWINDOW_GET_CHILD(widget);
687 }
688
689 static char** targets2array(
char *target0, va_list ap,
int *nelm) {
690 int al =
16;
691 char **targets = calloc(
16,
sizeof(
char*));
692 targets[
0] = target0;
693
694 int i =
1;
695 char *target;
696 while((target = va_arg(ap,
char*)) !=
NULL) {
697 if(i >= al) {
698 al *=
2;
699 targets = realloc(targets, al*
sizeof(
char*));
700 }
701 targets[i] = target;
702 i++;
703 }
704
705 *nelm = i;
706 return targets;
707 }
708
709
710
711
712
713
714
715
716
717
718
719 void ui_table_dragsource(
UIWIDGET tablewidget,
int actions,
char *target0, ...) {
720 va_list ap;
721 va_start(ap, target0);
722 int nelm;
723 char **targets = targets2array(target0, ap, &nelm);
724 va_end(ap);
725
726
727
728
729 free(targets);
730 }
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766 void ui_listview_update(UiList *list,
int i) {
767 UiListView *view = list->obj;
768 GtkListStore *store = create_list_store(list, view->model);
769 gtk_tree_view_set_model(
GTK_TREE_VIEW(view->widget),
GTK_TREE_MODEL(store));
770 g_object_unref(
G_OBJECT(store));
771 }
772
773 UiListSelection ui_listview_getselection(UiList *list) {
774 UiListView *view = list->obj;
775 UiListSelection selection = ui_listview_selection(
776 gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget)),
777 NULL);
778 return selection;
779 }
780
781 void ui_listview_setselection(UiList *list, UiListSelection selection) {
782 UiListView *view = list->obj;
783 GtkTreeSelection *sel = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget));
784 GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
785 gtk_tree_selection_select_path(sel, path);
786
787 }
788
789 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
790
791 ui_destroy_boundvar(v->obj->ctx, v->var);
792 free(v);
793 }
794
795 void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
796 ui_destroy_boundvar(v->obj->ctx, v->var);
797 free(v);
798 }
799
800
801 void ui_listview_activate_event(
802 GtkTreeView *treeview,
803 GtkTreePath *path,
804 GtkTreeViewColumn *column,
805 UiTreeEventData *event)
806 {
807 UiListSelection selection = ui_listview_selection(
808 gtk_tree_view_get_selection(treeview),
809 event);
810
811 UiEvent e;
812 e.obj = event->obj;
813 e.window = event->obj->window;
814 e.document = event->obj->ctx->document;
815 e.eventdata = &selection;
816 e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
817 event->activate(&e, event->activatedata);
818
819 if(selection.count >
0) {
820 free(selection.rows);
821 }
822 }
823
824 void ui_listview_selection_event(
825 GtkTreeSelection *treeselection,
826 UiTreeEventData *event)
827 {
828 UiListSelection selection = ui_listview_selection(treeselection, event);
829
830 UiEvent e;
831 e.obj = event->obj;
832 e.window = event->obj->window;
833 e.document = event->obj->ctx->document;
834 e.eventdata = &selection;
835 e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
836 event->selection(&e, event->selectiondata);
837
838 if(selection.count >
0) {
839 free(selection.rows);
840 }
841 }
842
843 UiListSelection ui_listview_selection(
844 GtkTreeSelection *selection,
845 UiTreeEventData *event)
846 {
847 GList *rows = gtk_tree_selection_get_selected_rows(selection,
NULL);
848
849 UiListSelection ls;
850 ls.count = g_list_length(rows);
851 ls.rows = calloc(ls.count,
sizeof(
int));
852 GList *r = rows;
853 int i =
0;
854 while(r) {
855 GtkTreePath *path = r->data;
856 ls.rows[i] = ui_tree_path_list_index(path);
857 r = r->next;
858 i++;
859 }
860 return ls;
861 }
862
863 int ui_tree_path_list_index(GtkTreePath *path) {
864 int depth = gtk_tree_path_get_depth(path);
865 if(depth ==
0) {
866 fprintf(stderr,
"UiError: treeview selection: depth == 0\n");
867 return -
1;
868 }
869 int *indices = gtk_tree_path_get_indices(path);
870 return indices[depth -
1];
871 }
872
873
874
875
876 UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
877 UiObject* current = uic_current_obj(obj);
878
879 UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
880 model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
881
882 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
883
884 GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata);
885 ui_set_name_and_style(combobox, args.name, args.style_class);
886 ui_set_widget_groups(obj->ctx, combobox, args.groups);
887 UI_APPLY_LAYOUT1(current, args);
888 current->container->add(current->container, combobox,
FALSE);
889 current->container->current = combobox;
890 return combobox;
891 }
892
893 GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f,
void *udata) {
894 GtkWidget *combobox = gtk_combo_box_new();
895
896 UiListView *uicbox = malloc(
sizeof(UiListView));
897 uicbox->obj = obj;
898 uicbox->widget = combobox;
899
900 UiList *list = var ? var->value :
NULL;
901 GtkListStore *listmodel = create_list_store(list, model);
902
903 if(listmodel) {
904 gtk_combo_box_set_model(
GTK_COMBO_BOX(combobox),
GTK_TREE_MODEL(listmodel));
905 g_object_unref(listmodel);
906 }
907
908 uicbox->var = var;
909 uicbox->model = model;
910
911 g_signal_connect(
912 combobox,
913 "destroy",
914 G_CALLBACK(ui_combobox_destroy),
915 uicbox);
916
917
918 if(list) {
919 list->update = ui_combobox_modelupdate;
920 list->getselection = ui_combobox_getselection;
921 list->setselection = ui_combobox_setselection;
922 list->obj = uicbox;
923 }
924
925 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
926 gtk_cell_layout_pack_start(
GTK_CELL_LAYOUT(combobox), renderer,
TRUE);
927 gtk_cell_layout_set_attributes(
928 GTK_CELL_LAYOUT(combobox),
929 renderer,
930 "text",
931 0,
932 NULL);
933 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox),
0);
934
935
936 if(f) {
937 UiEventData *event = ui_malloc(obj->ctx,
sizeof(UiEventData));
938 event->obj = obj;
939 event->userdata = udata;
940 event->callback = f;
941 event->value =
0;
942 event->customdata =
NULL;
943
944 g_signal_connect(
945 combobox,
946 "changed",
947 G_CALLBACK(ui_combobox_change_event),
948 event);
949 }
950
951 return combobox;
952 }
953
954 void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) {
955 UiEvent event;
956 event.obj = e->obj;
957 event.window = event.obj->window;
958 event.document = event.obj->ctx->document;
959 event.eventdata =
NULL;
960 event.intval = gtk_combo_box_get_active(widget);
961 e->callback(&event, e->userdata);
962 }
963
964 void ui_combobox_modelupdate(UiList *list,
int i) {
965 UiListView *view = list->obj;
966 GtkListStore *store = create_list_store(view->var->value, view->model);
967 gtk_combo_box_set_model(
GTK_COMBO_BOX(view->widget),
GTK_TREE_MODEL(store));
968 g_object_unref(store);
969 }
970
971 UiListSelection ui_combobox_getselection(UiList *list) {
972 UiListView *combobox = list->obj;
973 UiListSelection ret;
974 ret.rows = malloc(
sizeof(
int*));
975 ret.count =
1;
976 ret.rows[
0] = gtk_combo_box_get_active(
GTK_COMBO_BOX(combobox->widget));
977 return ret;
978 }
979
980 void ui_combobox_setselection(UiList *list, UiListSelection selection) {
981 UiListView *combobox = list->obj;
982 if(selection.count >
0) {
983 gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox->widget), selection.rows[
0]);
984 }
985 }
986
987
988
989
990 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
991 cxListDestroy(v->sublists);
992 free(v);
993 }
994
995 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
996 free(sublist->header);
997 ui_destroy_boundvar(obj->ctx, sublist->var);
998 cxListDestroy(sublist->widgets);
999 }
1000
1001 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
1002
1003 UiListBox *listbox = g_object_get_data(
G_OBJECT(row),
"ui_listbox");
1004 if(!listbox) {
1005 return;
1006 }
1007
1008 UiListBoxSubList *sublist = g_object_get_data(
G_OBJECT(row),
"ui_listbox_sublist");
1009 if(!sublist) {
1010 return;
1011 }
1012
1013 if(sublist->separator) {
1014 GtkWidget *separator = gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL);
1015 gtk_list_box_row_set_header(row, separator);
1016 }
else if(sublist->header) {
1017 GtkWidget *header = gtk_label_new(sublist->header);
1018 gtk_widget_set_halign(header,
GTK_ALIGN_START);
1019 if(row == listbox->first_row) {
1020 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header-first");
1021 }
else {
1022 WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header");
1023 }
1024 gtk_list_box_row_set_header(row, header);
1025 }
1026 }
1027
1028 #ifdef UI_GTK3
1029 typedef struct _UiSidebarListBoxClass {
1030 GtkListBoxClass parent_class;
1031 } UiSidebarListBoxClass;
1032
1033 typedef struct _UiSidebarListBox {
1034 GtkListBox parent_instance;
1035 } UiSidebarListBox;
1036
1037 G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box,
GTK_TYPE_LIST_BOX)
1038
1039
1040 static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) {
1041 GtkWidgetClass *widget_class =
GTK_WIDGET_CLASS(klass);
1042 gtk_widget_class_set_css_name (widget_class,
"placessidebar");
1043 }
1044
1045 static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
1046
1047 }
1048 #endif
1049
1050 UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
1051 UiObject* current = uic_current_obj(obj);
1052
1053 #ifdef UI_GTK3
1054 GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(),
NULL);
1055 #else
1056 GtkWidget *listbox = gtk_list_box_new();
1057 #endif
1058 if(!args.style_class) {
1059 #if GTK_MAJOR_VERSION >=
4
1060 WIDGET_ADD_CSS_CLASS(listbox,
"navigation-sidebar");
1061 #else
1062 WIDGET_ADD_CSS_CLASS(listbox,
"sidebar");
1063 #endif
1064 }
1065 gtk_list_box_set_header_func(
GTK_LIST_BOX(listbox), listbox_create_header,
NULL,
NULL);
1066 GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
1067 SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
1068
1069 ui_set_name_and_style(listbox, args.name, args.style_class);
1070 ui_set_widget_groups(obj->ctx, listbox, args.groups);
1071 UI_APPLY_LAYOUT1(current, args);
1072 current->container->add(current->container, scroll_area,
TRUE);
1073
1074 UiListBox *uilistbox = malloc(
sizeof(UiListBox));
1075 uilistbox->obj = obj;
1076 uilistbox->listbox =
GTK_LIST_BOX(listbox);
1077 uilistbox->getvalue = args.getvalue;
1078 uilistbox->onactivate = args.onactivate;
1079 uilistbox->onactivatedata = args.onactivatedata;
1080 uilistbox->onbuttonclick = args.onbuttonclick;
1081 uilistbox->onbuttonclickdata = args.onbuttonclickdata;
1082 uilistbox->sublists = cxArrayListCreateSimple(
sizeof(UiListBoxSubList),
4);
1083 uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
1084 uilistbox->sublists->collection.destructor_data = obj;
1085 uilistbox->first_row =
NULL;
1086
1087 if(args.numsublists ==
0 && args.sublists) {
1088 args.numsublists =
INT_MAX;
1089 }
1090 for(
int i=
0;i<args.numsublists;i++) {
1091 UiSubList sublist = args.sublists[i];
1092 if(!sublist.varname && !sublist.value) {
1093 break;
1094 }
1095
1096 UiListBoxSubList uisublist;
1097 uisublist.var = uic_widget_var(
1098 obj->ctx,
1099 current->ctx,
1100 sublist.value,
1101 sublist.varname,
1102 UI_VAR_LIST);
1103 uisublist.numitems =
0;
1104 uisublist.header = sublist.header ? strdup(sublist.header) :
NULL;
1105 uisublist.separator = sublist.separator;
1106 uisublist.widgets = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
1107 uisublist.listbox = uilistbox;
1108 uisublist.userdata = sublist.userdata;
1109 uisublist.index = i;
1110
1111 cxListAdd(uilistbox->sublists, &uisublist);
1112
1113
1114 UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-
1);
1115 UiList *list = uisublist.var->value;
1116 if(list) {
1117 list->obj = sublist_ptr;
1118 list->update = ui_listbox_list_update;
1119 }
1120 }
1121
1122 ui_listbox_update(uilistbox,
0, cxListSize(uilistbox->sublists));
1123
1124
1125
1126 g_object_set_data(
G_OBJECT(scroll_area),
"ui_listbox", uilistbox);
1127 g_object_set_data(
G_OBJECT(listbox),
"ui_listbox", uilistbox);
1128
1129
1130 g_signal_connect(
1131 listbox,
1132 "destroy",
1133 G_CALLBACK(ui_destroy_sourcelist),
1134 uilistbox);
1135
1136 if(args.onactivate) {
1137 g_signal_connect(
1138 listbox,
1139 "row-activated",
1140 G_CALLBACK(ui_listbox_row_activate),
1141 NULL);
1142 }
1143
1144 return scroll_area;
1145 }
1146
1147 void ui_listbox_update(UiListBox *listbox,
int from,
int to) {
1148 CxIterator i = cxListIterator(listbox->sublists);
1149 size_t pos =
0;
1150 cx_foreach(UiListBoxSubList *, sublist, i) {
1151 if(i.index < from) {
1152 pos += sublist->numitems;
1153 continue;
1154 }
1155 if(i.index > to) {
1156 break;
1157 }
1158
1159
1160 ui_listbox_update_sublist(listbox, sublist, pos);
1161 pos += sublist->numitems;
1162 }
1163 }
1164
1165 static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item,
int index) {
1166 GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
10);
1167 if(item->icon) {
1168 GtkWidget *icon =
ICON_IMAGE(item->icon);
1169 BOX_ADD(hbox, icon);
1170 }
1171 GtkWidget *label = gtk_label_new(item->label);
1172 gtk_widget_set_halign(label,
GTK_ALIGN_START);
1173 BOX_ADD_EXPAND(hbox, label);
1174
1175 GtkWidget *row = gtk_list_box_row_new();
1176 LISTBOX_ROW_SET_CHILD(row, hbox);
1177
1178
1179 UiEventDataExt *event = malloc(
sizeof(UiEventDataExt));
1180 memset(event,
0,
sizeof(UiEventDataExt));
1181 event->obj = listbox->obj;
1182 event->customdata0 = sublist;
1183 event->customdata1 = sublist->var;
1184 event->customdata2 = item->eventdata;
1185 event->callback = listbox->onactivate;
1186 event->userdata = listbox->onactivatedata;
1187 event->callback2 = listbox->onbuttonclick;
1188 event->userdata2 = listbox->onbuttonclickdata;
1189 event->value0 = index;
1190
1191 g_signal_connect(
1192 row,
1193 "destroy",
1194 G_CALLBACK(ui_destroy_userdata),
1195 event);
1196
1197 g_object_set_data(
G_OBJECT(row),
"ui-listbox-row-eventdata", event);
1198
1199 return row;
1200 }
1201
1202 void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist,
size_t listbox_insert_index) {
1203
1204 CxIterator r = cxListIterator(sublist->widgets);
1205 cx_foreach(GtkWidget*, widget, r) {
1206 LISTBOX_REMOVE(listbox->listbox, widget);
1207 }
1208 cxListClear(sublist->widgets);
1209
1210 sublist->numitems =
0;
1211
1212
1213 UiList *list = sublist->var->value;
1214 if(!list) {
1215 return;
1216 }
1217
1218 size_t index =
0;
1219 void *elm = list->first(list);
1220 while(elm) {
1221 UiSubListItem item = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
1222 listbox->getvalue(sublist->userdata, elm, index, &item);
1223
1224
1225 GtkWidget *row = create_listbox_row(listbox, sublist, &item, (
int)index);
1226 if(index ==
0) {
1227
1228
1229 g_object_set_data(
G_OBJECT(row),
"ui_listbox", listbox);
1230 g_object_set_data(
G_OBJECT(row),
"ui_listbox_sublist", sublist);
1231
1232 if(listbox_insert_index ==
0) {
1233
1234 listbox->first_row =
GTK_LIST_BOX_ROW(row);
1235 }
1236 }
1237 intptr_t rowindex = listbox_insert_index + index;
1238 g_object_set_data(
G_OBJECT(row),
"ui_listbox_row_index", (gpointer)rowindex);
1239 gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
1240 cxListAdd(sublist->widgets, row);
1241
1242
1243 free(item.label);
1244 free(item.icon);
1245 free(item.button_label);
1246 free(item.button_icon);
1247 free(item.badge);
1248
1249
1250 elm = list->next(list);
1251 index++;
1252 }
1253
1254 sublist->numitems = cxListSize(sublist->widgets);
1255 }
1256
1257 void ui_listbox_list_update(UiList *list,
int i) {
1258 UiListBoxSubList *sublist = list->obj;
1259 }
1260
1261 void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
1262 UiEventDataExt *data = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-eventdata");
1263 if(!data) {
1264 return;
1265 }
1266 UiListBoxSubList *sublist = data->customdata0;
1267
1268 UiEvent event;
1269 event.obj = data->obj;
1270 event.window = event.obj->window;
1271 event.document = event.obj->ctx->document;
1272 event.eventdata = data->customdata2;
1273 event.intval = data->value0;
1274
1275 if(data->callback) {
1276 data->callback(&event, data->userdata);
1277 }
1278 }
1279