#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "../common/context.h"
#include "../common/object.h"
#include "container.h"
#include <cx/array_list.h>
#include <cx/linked_list.h>
#include "list.h"
#include "icon.h"
#include "menu.h"
#include "dnd.h"
void* ui_strmodel_getvalue(
void *elm,
int column) {
return column ==
0 ? elm :
NULL;
}
#if GTK_CHECK_VERSION(
4,
10,
0)
typedef struct _ObjWrapper {
GObject parent_instance;
void *data;
} ObjWrapper;
typedef struct _ObjWrapperClass {
GObjectClass parent_class;
} ObjWrapperClass;
G_DEFINE_TYPE(ObjWrapper, obj_wrapper,
G_TYPE_OBJECT)
static void obj_wrapper_class_init(ObjWrapperClass *klass) {
}
static void obj_wrapper_init(ObjWrapper *self) {
self->data =
NULL;
}
ObjWrapper* obj_wrapper_new(
void* data) {
ObjWrapper *obj = g_object_new(obj_wrapper_get_type(),
NULL);
obj->data = data;
return obj;
}
static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
UiColData *col = userdata;
UiModel *model = col->listview->model;
UiModelType type = model->types[col->model_column];
if(type ==
UI_ICON_TEXT || type ==
UI_ICON_TEXT_FREE) {
GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
6);
GtkWidget *image = gtk_image_new();
GtkWidget *label = gtk_label_new(
NULL);
BOX_ADD(hbox, image);
BOX_ADD(hbox, label);
gtk_list_item_set_child(item, hbox);
g_object_set_data(
G_OBJECT(hbox),
"image", image);
g_object_set_data(
G_OBJECT(hbox),
"label", label);
}
else if(type ==
UI_ICON) {
GtkWidget *image = gtk_image_new();
gtk_list_item_set_child(item, image);
}
else {
GtkWidget *label = gtk_label_new(
NULL);
gtk_label_set_xalign(
GTK_LABEL(label),
0);
gtk_list_item_set_child(item, label);
}
}
static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
UiColData *col = userdata;
ObjWrapper *obj = gtk_list_item_get_item(item);
UiModel *model = col->listview->model;
UiModelType type = model->types[col->model_column];
void *data = model->getvalue(obj->data, col->data_column);
GtkWidget *child = gtk_list_item_get_child(item);
bool freevalue =
TRUE;
switch(type) {
case UI_STRING: {
freevalue =
FALSE;
}
case UI_STRING_FREE: {
gtk_label_set_label(
GTK_LABEL(child), data);
if(freevalue) {
free(data);
}
break;
}
case UI_INTEGER: {
intptr_t intvalue = (
intptr_t)data;
char buf[
32];
snprintf(buf,
32,
"%d", (
int)intvalue);
gtk_label_set_label(
GTK_LABEL(child), buf);
break;
}
case UI_ICON: {
UiIcon *icon = data;
if(icon) {
gtk_image_set_from_paintable(
GTK_IMAGE(child),
GDK_PAINTABLE(icon->info));
}
break;
}
case UI_ICON_TEXT: {
freevalue =
FALSE;
}
case UI_ICON_TEXT_FREE: {
void *data2 = model->getvalue(obj->data, col->data_column+
1);
GtkWidget *image = g_object_get_data(
G_OBJECT(child),
"image");
GtkWidget *label = g_object_get_data(
G_OBJECT(child),
"label");
if(data && image) {
UiIcon *icon = data;
gtk_image_set_from_paintable(
GTK_IMAGE(image),
GDK_PAINTABLE(icon->info));
}
if(data2 && label) {
gtk_label_set_label(
GTK_LABEL(label), data2);
}
if(freevalue) {
free(data2);
}
break;
}
}
}
static GtkSelectionModel* create_selection_model(UiListView *listview, GListStore *liststore, bool multiselection) {
GtkSelectionModel *selection_model;
if(multiselection) {
selection_model =
GTK_SELECTION_MODEL(gtk_multi_selection_new(
G_LIST_MODEL(liststore)));
}
else {
selection_model =
GTK_SELECTION_MODEL(gtk_single_selection_new(
G_LIST_MODEL(liststore)));
}
g_signal_connect(selection_model,
"selection-changed",
G_CALLBACK(ui_listview_selection_changed), listview);
return selection_model;
}
static UiListView* create_listview(UiObject *obj, UiListArgs args) {
UiListView *tableview = malloc(
sizeof(UiListView));
memset(tableview,
0,
sizeof(UiListView));
tableview->obj = obj;
tableview->model = args.model;
tableview->onactivate = args.onactivate;
tableview->onactivatedata = args.onactivatedata;
tableview->onselection = args.onselection;
tableview->onselectiondata = args.onselectiondata;
tableview->ondragstart = args.ondragstart;
tableview->ondragstartdata = args.ondragstartdata;
tableview->ondragcomplete = args.ondragcomplete;
tableview->ondragcompletedata = args.ondragcompletedata;
tableview->ondrop = args.ondrop;
tableview->ondropdata = args.ondropsdata;
tableview->selection.count =
0;
tableview->selection.rows =
NULL;
return tableview;
}
UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
args.model = model;
GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
UiListView *listview = create_listview(obj, args);
listview->columns = malloc(
sizeof(UiColData));
listview->columns->listview = listview;
listview->columns->data_column =
0;
listview->columns->model_column =
0;
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), listview->columns);
g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), listview->columns);
GtkSelectionModel *selection_model = create_selection_model(listview, ls, args.multiselection);
GtkWidget *view = gtk_list_view_new(
GTK_SELECTION_MODEL(selection_model), factory);
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
listview->widget = view;
listview->var = var;
listview->liststore = ls;
listview->selectionmodel = selection_model;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_listview_destroy),
listview);
if(var && var->value) {
UiList *list = var->value;
list->obj = listview;
list->update = ui_listview_update2;
list->getselection = ui_listview_getselection2;
list->setselection = ui_listview_setselection2;
ui_update_liststore(ls, list);
}
if(args.onactivate) {
g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), listview);
}
GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(scroll_area),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, scroll_area,
FALSE);
current->container->current = view;
return scroll_area;
}
UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
args.model = model;
GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
UiListView *listview = create_listview(obj, args);
listview->columns = malloc(
sizeof(UiColData));
listview->columns->listview = listview;
listview->columns->data_column =
0;
listview->columns->model_column =
0;
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), listview->columns);
g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), listview->columns);
GtkWidget *view = gtk_drop_down_new(
G_LIST_MODEL(ls),
NULL);
gtk_drop_down_set_factory(
GTK_DROP_DOWN(view), factory);
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
listview->widget = view;
listview->var = var;
listview->liststore = ls;
listview->selectionmodel =
NULL;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_listview_destroy),
listview);
if(var && var->value) {
UiList *list = var->value;
list->obj = listview;
list->update = ui_listview_update2;
list->getselection = ui_combobox_getselection;
list->setselection = ui_combobox_setselection;
ui_update_liststore(ls, list);
}
if(args.onactivate) {
g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), listview);
}
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, view,
FALSE);
return view;
}
UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
GListStore *ls = g_list_store_new(
G_TYPE_OBJECT);
UiListView *tableview = create_listview(obj, args);
GtkSelectionModel *selection_model = create_selection_model(tableview, ls, args.multiselection);
GtkWidget *view = gtk_column_view_new(
GTK_SELECTION_MODEL(selection_model));
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
tableview->widget = view;
tableview->var = var;
tableview->liststore = ls;
tableview->selectionmodel = selection_model;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_listview_destroy),
tableview);
UiModel *model = args.model;
int columns = model ? model->columns :
0;
tableview->columns = calloc(columns,
sizeof(UiColData));
int addi =
0;
for(
int i=
0;i<columns;i++) {
tableview->columns[i].listview = tableview;
tableview->columns[i].model_column = i;
tableview->columns[i].data_column = i+addi;
if(model->types[i] ==
UI_ICON_TEXT || model->types[i] ==
UI_ICON_TEXT_FREE) {
addi++;
}
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
UiColData *col = &tableview->columns[i];
g_signal_connect(factory,
"setup",
G_CALLBACK(column_factory_setup), col);
g_signal_connect(factory,
"bind",
G_CALLBACK(column_factory_bind), col);
GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
gtk_column_view_column_set_resizable(column, true);
gtk_column_view_append_column(
GTK_COLUMN_VIEW(view), column);
}
if(var && var->value) {
UiList *list = var->value;
list->obj = tableview;
list->update = ui_listview_update2;
list->getselection = ui_listview_getselection2;
list->setselection = ui_listview_setselection2;
ui_update_liststore(ls, list);
}
if(args.onactivate) {
g_signal_connect(view,
"activate",
G_CALLBACK(ui_columnview_activate), tableview);
}
GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(scroll_area),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, scroll_area,
FALSE);
current->container->current = view;
return scroll_area;
}
static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
UiListSelection sel = {
0,
NULL };
GtkBitset *bitset = gtk_selection_model_get_selection(model);
int n = gtk_bitset_get_size(bitset);
printf(
"bitset %d\n", n);
gtk_bitset_unref(bitset);
return sel;
}
static void listview_event(ui_callback cb,
void *cbdata, UiListView *view) {
UiEvent event;
event.obj = view->obj;
event.document = event.obj->ctx->document;
event.window = event.obj->window;
event.intval = view->selection.count;
event.eventdata = &view->selection;
if(cb) {
cb(&event, cbdata);
}
}
void ui_columnview_activate(
void *ignore, guint position, gpointer userdata) {
UiListView *view = userdata;
listview_event(view->onactivate, view->onactivatedata, view);
}
void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) {
UiListView *view = userdata;
free(view->selection.rows);
view->selection.count =
0;
view->selection.rows =
NULL;
CX_ARRAY_DECLARE(
int, newselection);
cx_array_initialize(newselection,
8);
size_t nitems = g_list_model_get_n_items(
G_LIST_MODEL(view->liststore));
for(
size_t i=
0;i<nitems;i++) {
if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
int s = (
int)i;
cx_array_simple_add(newselection, s);
}
}
if(newselection_size >
0) {
view->selection.count = newselection_size;
view->selection.rows = newselection;
}
else {
free(newselection);
}
listview_event(view->onselection, view->onselectiondata, view);
}
void ui_dropdown_activate(GtkDropDown* self, gpointer userdata) {
UiListView *view = userdata;
guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
UiListSelection sel = {
0,
NULL };
int sel2 = (
int)selection;
if(selection !=
GTK_INVALID_LIST_POSITION) {
sel.count =
1;
sel.rows = &sel2;
}
if(view->onactivate) {
UiEvent event;
event.obj = view->obj;
event.document = event.obj->ctx->document;
event.window = event.obj->window;
event.intval = view->selection.count;
event.eventdata = &view->selection;
view->onactivate(&event, view->onactivatedata);
}
}
void ui_update_liststore(GListStore *liststore, UiList *list) {
g_list_store_remove_all(liststore);
void *elm = list->first(list);
while(elm) {
ObjWrapper *obj = obj_wrapper_new(elm);
g_list_store_append(liststore, obj);
elm = list->next(list);
}
}
void ui_listview_update2(UiList *list,
int i) {
UiListView *view = list->obj;
ui_update_liststore(view->liststore, view->var->value);
}
UiListSelection ui_listview_getselection2(UiList *list) {
UiListView *view = list->obj;
UiListSelection selection;
selection.count = view->selection.count;
selection.rows = calloc(selection.count,
sizeof(
int));
memcpy(selection.rows, view->selection.rows, selection.count*
sizeof(
int));
return selection;
}
void ui_listview_setselection2(UiList *list, UiListSelection selection) {
UiListView *view = list->obj;
UiListSelection newselection;
newselection.count = view->selection.count;
if(selection.count >
0) {
newselection.rows = calloc(newselection.count,
sizeof(
int));
memcpy(newselection.rows, selection.rows, selection.count*
sizeof(
int));
}
else {
newselection.rows =
NULL;
}
free(view->selection.rows);
view->selection = newselection;
gtk_selection_model_unselect_all(view->selectionmodel);
if(selection.count >
0) {
for(
int i=
0;i<selection.count;i++) {
gtk_selection_model_select_item(view->selectionmodel, selection.rows[i],
FALSE);
}
}
}
UiListSelection ui_combobox_getselection(UiList *list) {
UiListView *view = list->obj;
guint selection = gtk_drop_down_get_selected(
GTK_DROP_DOWN(view->widget));
UiListSelection sel = {
0,
NULL };
if(selection !=
GTK_INVALID_LIST_POSITION) {
sel.count =
1;
sel.rows = malloc(
sizeof(
int));
sel.rows[
0] = (
int)selection;
}
return sel;
}
void ui_combobox_setselection(UiList *list, UiListSelection selection) {
UiListView *view = list->obj;
if(selection.count >
0) {
gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget), selection.rows[
0]);
}
else {
gtk_drop_down_set_selected(
GTK_DROP_DOWN(view->widget),
GTK_INVALID_LIST_POSITION);
}
}
#else
static GtkListStore* create_list_store(UiList *list, UiModel *model) {
int columns = model->columns;
GType types[
2*columns];
int c =
0;
for(
int i=
0;i<columns;i++,c++) {
switch(model->types[i]) {
case UI_STRING:
case UI_STRING_FREE: types[c] =
G_TYPE_STRING;
break;
case UI_INTEGER: types[c] =
G_TYPE_INT;
break;
case UI_ICON: types[c] =
G_TYPE_OBJECT;
break;
case UI_ICON_TEXT:
case UI_ICON_TEXT_FREE: {
types[c] =
G_TYPE_OBJECT;
types[++c] =
G_TYPE_STRING;
}
}
}
GtkListStore *store = gtk_list_store_newv(c, types);
if(list) {
void *elm = list->first(list);
while(elm) {
GtkTreeIter iter;
gtk_list_store_insert (store, &iter, -
1);
int c =
0;
for(
int i=
0;i<columns;i++,c++) {
void *data = model->getvalue(elm, c);
GValue value =
G_VALUE_INIT;
switch(model->types[i]) {
case UI_STRING:
case UI_STRING_FREE: {
g_value_init(&value,
G_TYPE_STRING);
g_value_set_string(&value, data);
if(model->types[i] ==
UI_STRING_FREE) {
free(data);
}
break;
}
case UI_INTEGER: {
g_value_init(&value,
G_TYPE_INT);
int *intptr = data;
g_value_set_int(&value, *intptr);
break;
}
case UI_ICON: {
g_value_init(&value,
G_TYPE_OBJECT);
UiIcon *icon = data;
#if GTK_MAJOR_VERSION >=
4
g_value_set_object(&value, icon->info);
#else
if(!icon->pixbuf && icon->info) {
GError *error =
NULL;
GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
icon->pixbuf = pixbuf;
}
if(icon->pixbuf) {
g_value_set_object(&value, icon->pixbuf);
}
#endif
break;
}
case UI_ICON_TEXT:
case UI_ICON_TEXT_FREE: {
UiIcon *icon = data;
#if GTK_MAJOR_VERSION >=
4
if(icon) {
GValue iconvalue =
G_VALUE_INIT;
g_value_init(&iconvalue,
G_TYPE_OBJECT);
g_value_set_object(&iconvalue, ui_icon_pixbuf(icon));
gtk_list_store_set_value(store, &iter, c, &iconvalue);
}
#else
GValue pixbufvalue =
G_VALUE_INIT;
if(icon) {
if(!icon->pixbuf && icon->info) {
GError *error =
NULL;
GdkPixbuf *pixbuf = gtk_icon_info_load_icon(icon->info, &error);
icon->pixbuf = pixbuf;
}
g_value_init(&pixbufvalue,
G_TYPE_OBJECT);
g_value_set_object(&pixbufvalue, icon->pixbuf);
gtk_list_store_set_value(store, &iter, c, &pixbufvalue);
}
#endif
c++;
char *str = model->getvalue(elm, c);
g_value_init(&value,
G_TYPE_STRING);
g_value_set_string(&value, str);
if(model->types[i] ==
UI_ICON_TEXT_FREE) {
free(str);
}
break;
}
}
gtk_list_store_set_value(store, &iter, c, &value);
}
elm = list->next(list);
}
}
return store;
}
UIWIDGET ui_listview_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
GtkWidget *view = gtk_tree_view_new();
ui_set_name_and_style(view, args.name, args.style_class);
ui_set_widget_groups(obj->ctx, view, args.groups);
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
NULL, renderer,
"text",
0,
NULL);
gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
gtk_tree_view_set_headers_visible(
GTK_TREE_VIEW(view),
FALSE);
#ifdef UI_GTK3
#if GTK_MINOR_VERSION >=
8
#else
#endif
#else
#endif
UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
UiList *list = var ? var->value :
NULL;
GtkListStore *listmodel = create_list_store(list, model);
gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
g_object_unref(listmodel);
UiListView *listview = malloc(
sizeof(UiListView));
listview->obj = obj;
listview->widget = view;
listview->var = var;
listview->model = model;
listview->selection.count =
0;
listview->selection.rows =
NULL;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_listview_destroy),
listview);
list->update = ui_listview_update;
list->getselection = ui_listview_getselection;
list->setselection = ui_listview_setselection;
list->obj = listview;
UiTreeEventData *event = malloc(
sizeof(UiTreeEventData));
event->obj = obj;
event->activate = args.onactivate;
event->activatedata = args.onactivatedata;
event->selection = args.onselection;
event->selectiondata = args.onselectiondata;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_destroy_userdata),
event);
if(args.onactivate) {
g_signal_connect(
view,
"row-activated",
G_CALLBACK(ui_listview_activate_event),
event);
}
if(args.onselection) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view));
g_signal_connect(
selection,
"changed",
G_CALLBACK(ui_listview_selection_event),
event);
}
if(args.contextmenu) {
UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, view);
ui_widget_set_contextmenu(view, menu);
}
GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(scroll_area),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, scroll_area,
FALSE);
current->container->current = view;
return scroll_area;
}
UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
GtkWidget *view = gtk_tree_view_new();
UiModel *model = args.model;
int columns = model ? model->columns :
0;
int addi =
0;
for(
int i=
0;i<columns;i++) {
GtkTreeViewColumn *column =
NULL;
if(model->types[i] ==
UI_ICON_TEXT) {
column = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(column, model->titles[i]);
GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
GtkCellRenderer *textrenderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_end(column, textrenderer,
TRUE);
gtk_tree_view_column_pack_start(column, iconrenderer,
FALSE);
gtk_tree_view_column_add_attribute(column, iconrenderer,
"pixbuf", i);
gtk_tree_view_column_add_attribute(column, textrenderer,
"text", i+
1);
addi++;
}
else if (model->types[i] ==
UI_ICON) {
GtkCellRenderer *iconrenderer = gtk_cell_renderer_pixbuf_new();
column = gtk_tree_view_column_new_with_attributes(
model->titles[i],
iconrenderer,
"pixbuf",
i + addi,
NULL);
}
else {
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
column = gtk_tree_view_column_new_with_attributes(
model->titles[i],
renderer,
"text",
i + addi,
NULL);
}
int colsz = model->columnsize[i];
if(colsz >
0) {
gtk_tree_view_column_set_fixed_width(column, colsz);
}
else if(colsz <
0) {
gtk_tree_view_column_set_expand(column,
TRUE);
}
gtk_tree_view_column_set_resizable(column,
TRUE);
gtk_tree_view_append_column(
GTK_TREE_VIEW(view), column);
}
#ifdef UI_GTK3
#else
#endif
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
UiList *list = var ? var->value :
NULL;
GtkListStore *listmodel = create_list_store(list, model);
gtk_tree_view_set_model(
GTK_TREE_VIEW(view),
GTK_TREE_MODEL(listmodel));
g_object_unref(listmodel);
UiListView *tableview = malloc(
sizeof(UiListView));
tableview->obj = obj;
tableview->widget = view;
tableview->var = var;
tableview->model = model;
tableview->ondragstart = args.ondragstart;
tableview->ondragstartdata = args.ondragstartdata;
tableview->ondragcomplete = args.ondragcomplete;
tableview->ondragcompletedata = args.ondragcompletedata;
tableview->ondrop = args.ondrop;
tableview->ondropdata = args.ondropsdata;
tableview->selection.count =
0;
tableview->selection.rows =
NULL;
g_signal_connect(
view,
"destroy",
G_CALLBACK(ui_listview_destroy),
tableview);
list->update = ui_listview_update;
list->getselection = ui_listview_getselection;
list->setselection = ui_listview_setselection;
list->obj = tableview;
UiTreeEventData *event = ui_malloc(obj->ctx,
sizeof(UiTreeEventData));
event->obj = obj;
event->activate = args.onactivate;
event->selection = args.onselection;
event->activatedata = args.onactivatedata;
event->selectiondata = args.onselectiondata;
if(args.onactivate) {
g_signal_connect(
view,
"row-activated",
G_CALLBACK(ui_listview_activate_event),
event);
}
if(args.onselection) {
GtkTreeSelection *selection = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view));
g_signal_connect(
selection,
"changed",
G_CALLBACK(ui_listview_selection_event),
event);
}
if(args.ondragstart) {
ui_listview_add_dnd(tableview, &args);
}
if(args.ondrop) {
ui_listview_enable_drop(tableview, &args);
}
GtkTreeSelection *selection = gtk_tree_view_get_selection (
GTK_TREE_VIEW(view));
if(args.multiselection) {
gtk_tree_selection_set_mode(selection,
GTK_SELECTION_MULTIPLE);
}
GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
gtk_scrolled_window_set_policy(
GTK_SCROLLED_WINDOW(scroll_area),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
if(args.contextmenu) {
UIMENU menu = ui_contextmenu_create(args.contextmenu, obj, scroll_area);
#if GTK_MAJOR_VERSION >=
4
ui_widget_set_contextmenu(scroll_area, menu);
#else
ui_widget_set_contextmenu(view, menu);
#endif
}
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, scroll_area,
FALSE);
current->container->current = view;
return scroll_area;
}
void ui_listview_update(UiList *list,
int i) {
UiListView *view = list->obj;
GtkListStore *store = create_list_store(list, view->model);
gtk_tree_view_set_model(
GTK_TREE_VIEW(view->widget),
GTK_TREE_MODEL(store));
g_object_unref(
G_OBJECT(store));
}
UiListSelection ui_listview_getselection(UiList *list) {
UiListView *view = list->obj;
UiListSelection selection = ui_listview_selection(
gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget)),
NULL);
return selection;
}
void ui_listview_setselection(UiList *list, UiListSelection selection) {
UiListView *view = list->obj;
GtkTreeSelection *sel = gtk_tree_view_get_selection(
GTK_TREE_VIEW(view->widget));
GtkTreePath *path = gtk_tree_path_new_from_indicesv(selection.rows, selection.count);
gtk_tree_selection_select_path(sel, path);
}
UIWIDGET ui_combobox_create(UiObject *obj, UiListArgs args) {
UiObject* current = uic_current_obj(obj);
UiModel *model = ui_model(obj->ctx,
UI_STRING,
"", -
1);
model->getvalue = args.getvalue ? args.getvalue : ui_strmodel_getvalue;
UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname,
UI_VAR_LIST);
GtkWidget *combobox = ui_create_combobox(obj, model, var, args.onactivate, args.onactivatedata);
ui_set_name_and_style(combobox, args.name, args.style_class);
ui_set_widget_groups(obj->ctx, combobox, args.groups);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, combobox,
FALSE);
current->container->current = combobox;
return combobox;
}
GtkWidget* ui_create_combobox(UiObject *obj, UiModel *model, UiVar *var, ui_callback f,
void *udata) {
GtkWidget *combobox = gtk_combo_box_new();
UiListView *uicbox = malloc(
sizeof(UiListView));
uicbox->obj = obj;
uicbox->widget = combobox;
UiList *list = var ? var->value :
NULL;
GtkListStore *listmodel = create_list_store(list, model);
if(listmodel) {
gtk_combo_box_set_model(
GTK_COMBO_BOX(combobox),
GTK_TREE_MODEL(listmodel));
g_object_unref(listmodel);
}
uicbox->var = var;
uicbox->model = model;
g_signal_connect(
combobox,
"destroy",
G_CALLBACK(ui_combobox_destroy),
uicbox);
if(list) {
list->update = ui_combobox_modelupdate;
list->getselection = ui_combobox_getselection;
list->setselection = ui_combobox_setselection;
list->obj = uicbox;
}
GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
gtk_cell_layout_pack_start(
GTK_CELL_LAYOUT(combobox), renderer,
TRUE);
gtk_cell_layout_set_attributes(
GTK_CELL_LAYOUT(combobox),
renderer,
"text",
0,
NULL);
gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox),
0);
if(f) {
UiEventData *event = ui_malloc(obj->ctx,
sizeof(UiEventData));
event->obj = obj;
event->userdata = udata;
event->callback = f;
event->value =
0;
event->customdata =
NULL;
g_signal_connect(
combobox,
"changed",
G_CALLBACK(ui_combobox_change_event),
event);
}
return combobox;
}
void ui_combobox_change_event(GtkComboBox *widget, UiEventData *e) {
UiEvent event;
event.obj = e->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata =
NULL;
event.intval = gtk_combo_box_get_active(widget);
e->callback(&event, e->userdata);
}
void ui_combobox_modelupdate(UiList *list,
int i) {
UiListView *view = list->obj;
GtkListStore *store = create_list_store(view->var->value, view->model);
gtk_combo_box_set_model(
GTK_COMBO_BOX(view->widget),
GTK_TREE_MODEL(store));
g_object_unref(store);
}
UiListSelection ui_combobox_getselection(UiList *list) {
UiListView *combobox = list->obj;
UiListSelection ret;
ret.rows = malloc(
sizeof(
int*));
ret.count =
1;
ret.rows[
0] = gtk_combo_box_get_active(
GTK_COMBO_BOX(combobox->widget));
return ret;
}
void ui_combobox_setselection(UiList *list, UiListSelection selection) {
UiListView *combobox = list->obj;
if(selection.count >
0) {
gtk_combo_box_set_active(
GTK_COMBO_BOX(combobox->widget), selection.rows[
0]);
}
}
void ui_listview_activate_event(
GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *column,
UiTreeEventData *event)
{
UiListSelection selection = ui_listview_selection(
gtk_tree_view_get_selection(treeview),
event);
UiEvent e;
e.obj = event->obj;
e.window = event->obj->window;
e.document = event->obj->ctx->document;
e.eventdata = &selection;
e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
event->activate(&e, event->activatedata);
if(selection.count >
0) {
free(selection.rows);
}
}
void ui_listview_selection_event(
GtkTreeSelection *treeselection,
UiTreeEventData *event)
{
UiListSelection selection = ui_listview_selection(treeselection, event);
UiEvent e;
e.obj = event->obj;
e.window = event->obj->window;
e.document = event->obj->ctx->document;
e.eventdata = &selection;
e.intval = selection.count >
0 ? selection.rows[
0] : -
1;
event->selection(&e, event->selectiondata);
if(selection.count >
0) {
free(selection.rows);
}
}
UiListSelection ui_listview_selection(
GtkTreeSelection *selection,
UiTreeEventData *event)
{
GList *rows = gtk_tree_selection_get_selected_rows(selection,
NULL);
UiListSelection ls;
ls.count = g_list_length(rows);
ls.rows = calloc(ls.count,
sizeof(
int));
GList *r = rows;
int i =
0;
while(r) {
GtkTreePath *path = r->data;
ls.rows[i] = ui_tree_path_list_index(path);
r = r->next;
i++;
}
return ls;
}
int ui_tree_path_list_index(GtkTreePath *path) {
int depth = gtk_tree_path_get_depth(path);
if(depth ==
0) {
fprintf(stderr,
"UiError: treeview selection: depth == 0\n");
return -
1;
}
int *indices = gtk_tree_path_get_indices(path);
return indices[depth -
1];
}
#endif
#if GTK_MAJOR_VERSION >=
4
static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source,
double x,
double y,
void *data) {
UiListView *listview = data;
UiDnD *dnd = ui_create_dnd();
GdkContentProvider *provider =
NULL;
if(listview->ondragstart) {
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = dnd;
event.intval =
0;
listview->ondragstart(&event, listview->ondragstartdata);
}
size_t numproviders = cxListSize(dnd->providers);
if(numproviders >
0) {
GdkContentProvider **providers = (GdkContentProvider**)cxListAt(dnd->providers,
0);
provider = gdk_content_provider_new_union(providers, numproviders);
}
ui_dnd_free(dnd);
return provider;
}
static void ui_listview_drag_begin(GtkDragSource *self, GdkDrag *drag, gpointer userdata) {
}
static void ui_listview_drag_end(GtkDragSource *self, GdkDrag *drag, gboolean delete_data, gpointer user_data) {
UiListView *listview = user_data;
if(listview->ondragcomplete) {
UiDnD dnd;
dnd.target =
NULL;
dnd.value =
NULL;
dnd.providers =
NULL;
dnd.selected_action = gdk_drag_get_selected_action(drag);
dnd.delete = delete_data;
dnd.accept =
FALSE;
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval =
0;
listview->ondragcomplete(&event, listview->ondragcompletedata);
}
}
static gboolean ui_listview_drop(
GtkDropTarget *target,
const GValue* value,
gdouble x,
gdouble y,
gpointer user_data)
{
UiListView *listview = user_data;
UiDnD dnd;
dnd.providers =
NULL;
dnd.target = target;
dnd.value = value;
dnd.selected_action =
0;
dnd.delete =
FALSE;
dnd.accept =
FALSE;
if(listview->ondrop) {
dnd.accept =
TRUE;
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval =
0;
listview->ondrop(&event, listview->ondropdata);
}
return dnd.accept;
}
void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
GtkDragSource *dragsource = gtk_drag_source_new();
gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(dragsource));
g_signal_connect (dragsource,
"prepare",
G_CALLBACK (ui_listview_dnd_prepare), listview);
g_signal_connect(
dragsource,
"drag-begin",
G_CALLBACK(ui_listview_drag_begin),
listview);
g_signal_connect(
dragsource,
"drag-end",
G_CALLBACK(ui_listview_drag_end),
listview);
}
void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
GtkDropTarget *target = gtk_drop_target_new(
G_TYPE_INVALID,
GDK_ACTION_COPY);
gtk_widget_add_controller(listview->widget,
GTK_EVENT_CONTROLLER(target));
GType default_types[
2] = {
GDK_TYPE_FILE_LIST,
G_TYPE_STRING };
gtk_drop_target_set_gtypes(target, default_types,
2);
g_signal_connect(target,
"drop",
G_CALLBACK(ui_listview_drop), listview);
}
#else
static GtkTargetEntry targetentries[] =
{
{
"STRING",
0,
0 },
{
"text/plain",
0,
1 },
{
"text/uri-list",
0,
2 },
};
static void ui_listview_drag_getdata(
GtkWidget* self,
GdkDragContext* context,
GtkSelectionData* data,
guint info,
guint time,
gpointer user_data)
{
UiListView *listview = user_data;
UiDnD dnd;
dnd.context = context;
dnd.data = data;
dnd.selected_action =
0;
dnd.delete =
FALSE;
dnd.accept =
FALSE;
if(listview->ondragstart) {
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval =
0;
listview->ondragstart(&event, listview->ondragstartdata);
}
}
static void ui_listview_drag_end(
GtkWidget *widget,
GdkDragContext *context,
guint time,
gpointer user_data)
{
UiListView *listview = user_data;
UiDnD dnd;
dnd.context = context;
dnd.data =
NULL;
dnd.selected_action = gdk_drag_context_get_selected_action(context);
dnd.delete = dnd.selected_action ==
UI_DND_ACTION_MOVE ?
TRUE :
FALSE;
dnd.accept =
FALSE;
if(listview->ondragcomplete) {
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval =
0;
listview->ondragcomplete(&event, listview->ondragcompletedata);
}
}
void ui_listview_add_dnd(UiListView *listview, UiListArgs *args) {
gtk_tree_view_enable_model_drag_source(
GTK_TREE_VIEW(listview->widget),
GDK_BUTTON1_MASK,
targetentries,
2,
GDK_ACTION_COPY);
g_signal_connect(listview->widget,
"drag-data-get",
G_CALLBACK(ui_listview_drag_getdata), listview);
g_signal_connect(listview->widget,
"drag-end",
G_CALLBACK(ui_listview_drag_end), listview);
}
static void ui_listview_drag_data_received(
GtkWidget *self,
GdkDragContext *context,
gint x,
gint y,
GtkSelectionData *data,
guint info,
guint time,
gpointer user_data)
{
UiListView *listview = user_data;
UiDnD dnd;
dnd.context = context;
dnd.data = data;
dnd.selected_action =
0;
dnd.delete =
FALSE;
dnd.accept =
FALSE;
if(listview->ondrop) {
dnd.accept =
TRUE;
UiEvent event;
event.obj = listview->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = &dnd;
event.intval =
0;
listview->ondrop(&event, listview->ondropdata);
}
}
void ui_listview_enable_drop(UiListView *listview, UiListArgs *args) {
gtk_tree_view_enable_model_drag_dest(
GTK_TREE_VIEW(listview->widget),
targetentries,
3,
GDK_ACTION_COPY);
if(listview->ondrop) {
g_signal_connect(listview->widget,
"drag_data_received",
G_CALLBACK(ui_listview_drag_data_received), listview);
}
}
#endif
GtkWidget* ui_get_tree_widget(
UIWIDGET widget) {
return SCROLLEDWINDOW_GET_CHILD(widget);
}
static char** targets2array(
char *target0, va_list ap,
int *nelm) {
int al =
16;
char **targets = calloc(
16,
sizeof(
char*));
targets[
0] = target0;
int i =
1;
char *target;
while((target = va_arg(ap,
char*)) !=
NULL) {
if(i >= al) {
al *=
2;
targets = realloc(targets, al*
sizeof(
char*));
}
targets[i] = target;
i++;
}
*nelm = i;
return targets;
}
void ui_table_dragsource(
UIWIDGET tablewidget,
int actions,
char *target0, ...) {
va_list ap;
va_start(ap, target0);
int nelm;
char **targets = targets2array(target0, ap, &nelm);
va_end(ap);
free(targets);
}
void ui_listview_destroy(GtkWidget *w, UiListView *v) {
ui_destroy_boundvar(v->obj->ctx, v->var);
#if GTK_CHECK_VERSION(
4,
10,
0)
free(v->columns);
#endif
free(v->selection.rows);
free(v);
}
void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
ui_destroy_boundvar(v->obj->ctx, v->var);
free(v);
}
static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
cxListFree(v->sublists);
free(v);
}
static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
free(sublist->header);
ui_destroy_boundvar(obj->ctx, sublist->var);
cxListFree(sublist->widgets);
}
static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
UiListBox *listbox = g_object_get_data(
G_OBJECT(row),
"ui_listbox");
if(!listbox) {
return;
}
UiListBoxSubList *sublist = g_object_get_data(
G_OBJECT(row),
"ui_listbox_sublist");
if(!sublist) {
return;
}
if(sublist->separator) {
GtkWidget *separator = gtk_separator_new(
GTK_ORIENTATION_HORIZONTAL);
gtk_list_box_row_set_header(row, separator);
}
else if(sublist->header) {
GtkWidget *header = gtk_label_new(sublist->header);
gtk_widget_set_halign(header,
GTK_ALIGN_START);
if(row == listbox->first_row) {
WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header-first");
}
else {
WIDGET_ADD_CSS_CLASS(header,
"ui-listbox-header");
}
gtk_list_box_row_set_header(row, header);
}
}
#ifdef UI_GTK3
typedef struct _UiSidebarListBoxClass {
GtkListBoxClass parent_class;
} UiSidebarListBoxClass;
typedef struct _UiSidebarListBox {
GtkListBox parent_instance;
} UiSidebarListBox;
G_DEFINE_TYPE(UiSidebarListBox, ui_sidebar_list_box,
GTK_TYPE_LIST_BOX)
static void ui_sidebar_list_box_class_init(UiSidebarListBoxClass *klass) {
GtkWidgetClass *widget_class =
GTK_WIDGET_CLASS(klass);
gtk_widget_class_set_css_name (widget_class,
"placessidebar");
}
static void ui_sidebar_list_box_init(UiSidebarListBox *self) {
}
#endif
UIEXPORT UIWIDGET ui_sourcelist_create(UiObject *obj, UiSourceListArgs args) {
UiObject* current = uic_current_obj(obj);
#ifdef UI_GTK3
GtkWidget *listbox = g_object_new(ui_sidebar_list_box_get_type(),
NULL);
#else
GtkWidget *listbox = gtk_list_box_new();
#endif
if(!args.style_class) {
#if GTK_MAJOR_VERSION >=
4
WIDGET_ADD_CSS_CLASS(listbox,
"navigation-sidebar");
#else
WIDGET_ADD_CSS_CLASS(listbox,
"sidebar");
#endif
}
gtk_list_box_set_header_func(
GTK_LIST_BOX(listbox), listbox_create_header,
NULL,
NULL);
GtkWidget *scroll_area =
SCROLLEDWINDOW_NEW();
SCROLLEDWINDOW_SET_CHILD(scroll_area, listbox);
ui_set_name_and_style(listbox, args.name, args.style_class);
ui_set_widget_groups(obj->ctx, listbox, args.groups);
UI_APPLY_LAYOUT1(current, args);
current->container->add(current->container, scroll_area,
TRUE);
UiListBox *uilistbox = malloc(
sizeof(UiListBox));
uilistbox->obj = obj;
uilistbox->listbox =
GTK_LIST_BOX(listbox);
uilistbox->getvalue = args.getvalue;
uilistbox->onactivate = args.onactivate;
uilistbox->onactivatedata = args.onactivatedata;
uilistbox->onbuttonclick = args.onbuttonclick;
uilistbox->onbuttonclickdata = args.onbuttonclickdata;
uilistbox->sublists = cxArrayListCreateSimple(
sizeof(UiListBoxSubList),
4);
uilistbox->sublists->collection.advanced_destructor = (cx_destructor_func2)sublist_destroy;
uilistbox->sublists->collection.destructor_data = obj;
uilistbox->first_row =
NULL;
if(args.numsublists ==
0 && args.sublists) {
args.numsublists =
INT_MAX;
}
for(
int i=
0;i<args.numsublists;i++) {
UiSubList sublist = args.sublists[i];
if(!sublist.varname && !sublist.value) {
break;
}
UiListBoxSubList uisublist;
uisublist.var = uic_widget_var(
obj->ctx,
current->ctx,
sublist.value,
sublist.varname,
UI_VAR_LIST);
uisublist.numitems =
0;
uisublist.header = sublist.header ? strdup(sublist.header) :
NULL;
uisublist.separator = sublist.separator;
uisublist.widgets = cxLinkedListCreateSimple(
CX_STORE_POINTERS);
uisublist.listbox = uilistbox;
uisublist.userdata = sublist.userdata;
uisublist.index = i;
cxListAdd(uilistbox->sublists, &uisublist);
UiListBoxSubList *sublist_ptr = cxListAt(uilistbox->sublists, cxListSize(uilistbox->sublists)-
1);
UiList *list = uisublist.var->value;
if(list) {
list->obj = sublist_ptr;
list->update = ui_listbox_list_update;
}
}
ui_listbox_update(uilistbox,
0, cxListSize(uilistbox->sublists));
g_object_set_data(
G_OBJECT(scroll_area),
"ui_listbox", uilistbox);
g_object_set_data(
G_OBJECT(listbox),
"ui_listbox", uilistbox);
g_signal_connect(
listbox,
"destroy",
G_CALLBACK(ui_destroy_sourcelist),
uilistbox);
if(args.onactivate) {
g_signal_connect(
listbox,
"row-activated",
G_CALLBACK(ui_listbox_row_activate),
NULL);
}
return scroll_area;
}
void ui_listbox_update(UiListBox *listbox,
int from,
int to) {
CxIterator i = cxListIterator(listbox->sublists);
size_t pos =
0;
cx_foreach(UiListBoxSubList *, sublist, i) {
if(i.index < from) {
pos += sublist->numitems;
continue;
}
if(i.index > to) {
break;
}
ui_listbox_update_sublist(listbox, sublist, pos);
pos += sublist->numitems;
}
}
static GtkWidget* create_listbox_row(UiListBox *listbox, UiListBoxSubList *sublist, UiSubListItem *item,
int index) {
GtkWidget *hbox = gtk_box_new(
GTK_ORIENTATION_HORIZONTAL,
10);
if(item->icon) {
GtkWidget *icon =
ICON_IMAGE(item->icon);
BOX_ADD(hbox, icon);
}
GtkWidget *label = gtk_label_new(item->label);
gtk_widget_set_halign(label,
GTK_ALIGN_START);
BOX_ADD_EXPAND(hbox, label);
GtkWidget *row = gtk_list_box_row_new();
LISTBOX_ROW_SET_CHILD(row, hbox);
UiEventDataExt *event = malloc(
sizeof(UiEventDataExt));
memset(event,
0,
sizeof(UiEventDataExt));
event->obj = listbox->obj;
event->customdata0 = sublist;
event->customdata1 = sublist->var;
event->customdata2 = item->eventdata;
event->callback = listbox->onactivate;
event->userdata = listbox->onactivatedata;
event->callback2 = listbox->onbuttonclick;
event->userdata2 = listbox->onbuttonclickdata;
event->value0 = index;
g_signal_connect(
row,
"destroy",
G_CALLBACK(ui_destroy_userdata),
event);
g_object_set_data(
G_OBJECT(row),
"ui-listbox-row-eventdata", event);
return row;
}
void ui_listbox_update_sublist(UiListBox *listbox, UiListBoxSubList *sublist,
size_t listbox_insert_index) {
CxIterator r = cxListIterator(sublist->widgets);
cx_foreach(GtkWidget*, widget, r) {
LISTBOX_REMOVE(listbox->listbox, widget);
}
cxListClear(sublist->widgets);
sublist->numitems =
0;
UiList *list = sublist->var->value;
if(!list) {
return;
}
size_t index =
0;
void *elm = list->first(list);
while(elm) {
UiSubListItem item = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
listbox->getvalue(sublist->userdata, elm, index, &item);
GtkWidget *row = create_listbox_row(listbox, sublist, &item, (
int)index);
if(index ==
0) {
g_object_set_data(
G_OBJECT(row),
"ui_listbox", listbox);
g_object_set_data(
G_OBJECT(row),
"ui_listbox_sublist", sublist);
if(listbox_insert_index ==
0) {
listbox->first_row =
GTK_LIST_BOX_ROW(row);
}
}
intptr_t rowindex = listbox_insert_index + index;
g_object_set_data(
G_OBJECT(row),
"ui_listbox_row_index", (gpointer)rowindex);
gtk_list_box_insert(listbox->listbox, row, listbox_insert_index + index);
cxListAdd(sublist->widgets, row);
free(item.label);
free(item.icon);
free(item.button_label);
free(item.button_icon);
free(item.badge);
elm = list->next(list);
index++;
}
sublist->numitems = cxListSize(sublist->widgets);
}
void ui_listbox_list_update(UiList *list,
int i) {
UiListBoxSubList *sublist = list->obj;
}
void ui_listbox_row_activate(GtkListBox *self, GtkListBoxRow *row, gpointer user_data) {
UiEventDataExt *data = g_object_get_data(
G_OBJECT(row),
"ui-listbox-row-eventdata");
if(!data) {
return;
}
UiListBoxSubList *sublist = data->customdata0;
UiEvent event;
event.obj = data->obj;
event.window = event.obj->window;
event.document = event.obj->ctx->document;
event.eventdata = data->customdata2;
event.intval = data->value0;
if(data->callback) {
data->callback(&event, data->userdata);
}
}