ui/gtk/list.c

changeset 101
7b3a3130be44
parent 100
d2bd73d28ff1
equal deleted inserted replaced
100:d2bd73d28ff1 101:7b3a3130be44
45 45
46 46
47 void* ui_strmodel_getvalue(void *elm, int column) { 47 void* ui_strmodel_getvalue(void *elm, int column) {
48 return column == 0 ? elm : NULL; 48 return column == 0 ? elm : NULL;
49 } 49 }
50
51 /*
52 static GtkTargetEntry targetentries[] =
53 {
54 { "STRING", 0, 0 },
55 { "text/plain", 0, 1 },
56 { "text/uri-list", 0, 2 },
57 };
58 */
59
60 #if GTK_CHECK_VERSION(4, 10, 0)
61
62
63 /* BEGIN GObject wrapper for generic pointers */
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 /* END GObject wrapper for generic pointers */
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 // to simplify things and share code with ui_table_create, we also
208 // use a UiModel for the listview
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 // init listview
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 // bind listview to list
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 // event handling
254 if(args.onactivate) {
255 // columnview and listview can use the same callback function, because
256 // the first parameter (which is technically a different pointer type)
257 // is ignored
258 g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview);
259 }
260
261 // add widget to parent
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); // GTK_POLICY_ALWAYS
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 // ct->current should point to view, not scroll_area, to make it possible
273 // to add a context menu
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 // to simplify things and share code with ui_tableview_create, we also
283 // use a UiModel for the listview
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 // init listview
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 // bind listview to list
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 // event handling
329 if(args.onactivate) {
330 g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), listview);
331 }
332
333 // add widget to parent
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 //g_list_store_append(ls, v1);
344
345 // create obj to store all relevant data we need for handling events
346 // and list updates
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 // init tableview
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 // create columns from UiModel
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 // icon+text has 2 data columns
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 // bind listview to list
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 // event handling
406 if(args.onactivate) {
407 g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview);
408 }
409
410 // add widget to parent
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); // GTK_POLICY_ALWAYS
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 // ct->current should point to view, not scroll_area, to make it possible
422 // to add a context menu
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
50 571
51 static GtkListStore* create_list_store(UiList *list, UiModel *model) { 572 static GtkListStore* create_list_store(UiList *list, UiModel *model) {
52 int columns = model->columns; 573 int columns = model->columns;
53 GType types[2*columns]; 574 GType types[2*columns];
54 int c = 0; 575 int c = 0;
197 UiListView *listview = malloc(sizeof(UiListView)); 718 UiListView *listview = malloc(sizeof(UiListView));
198 listview->obj = obj; 719 listview->obj = obj;
199 listview->widget = view; 720 listview->widget = view;
200 listview->var = var; 721 listview->var = var;
201 listview->model = model; 722 listview->model = model;
723 listview->selection.count = 0;
724 listview->selection.rows = NULL;
202 g_signal_connect( 725 g_signal_connect(
203 view, 726 view,
204 "destroy", 727 "destroy",
205 G_CALLBACK(ui_listview_destroy), 728 G_CALLBACK(ui_listview_destroy),
206 listview); 729 listview);
261 // to add a context menu 784 // to add a context menu
262 current->container->current = view; 785 current->container->current = view;
263 786
264 return scroll_area; 787 return scroll_area;
265 } 788 }
266
267 /*
268 static void drag_begin(GtkWidget *widget, GdkDragContext *context, gpointer udata) {
269 printf("drag begin\n");
270
271 }
272
273 static void drag_end(
274 GtkWidget *widget,
275 GdkDragContext *context,
276 guint time,
277 gpointer udata)
278 {
279 printf("drag end\n");
280
281 }
282 */
283
284 /*
285 static GtkTargetEntry targetentries[] =
286 {
287 { "STRING", 0, 0 },
288 { "text/plain", 0, 1 },
289 { "text/uri-list", 0, 2 },
290 };
291 */
292 789
293 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { 790 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
294 UiObject* current = uic_current_obj(obj); 791 UiObject* current = uic_current_obj(obj);
295 792
296 // create treeview 793 // create treeview
374 tableview->ondragstartdata = args.ondragstartdata; 871 tableview->ondragstartdata = args.ondragstartdata;
375 tableview->ondragcomplete = args.ondragcomplete; 872 tableview->ondragcomplete = args.ondragcomplete;
376 tableview->ondragcompletedata = args.ondragcompletedata; 873 tableview->ondragcompletedata = args.ondragcompletedata;
377 tableview->ondrop = args.ondrop; 874 tableview->ondrop = args.ondrop;
378 tableview->ondropdata = args.ondropsdata; 875 tableview->ondropdata = args.ondropsdata;
876 tableview->selection.count = 0;
877 tableview->selection.rows = NULL;
379 g_signal_connect( 878 g_signal_connect(
380 view, 879 view,
381 "destroy", 880 "destroy",
382 G_CALLBACK(ui_listview_destroy), 881 G_CALLBACK(ui_listview_destroy),
383 tableview); 882 tableview);
450 // to add a context menu 949 // to add a context menu
451 current->container->current = view; 950 current->container->current = view;
452 951
453 return scroll_area; 952 return scroll_area;
454 } 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 //g_object_unref(path);
978 }
979
980
981
982 /* --------------------------- ComboBox --------------------------- */
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 // bind var
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 // add callback
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
455 1173
456 #if GTK_MAJOR_VERSION >= 4 1174 #if GTK_MAJOR_VERSION >= 4
457 1175
458 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) { 1176 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) {
459 //printf("drag prepare\n"); 1177 //printf("drag prepare\n");
760 nelm, 1478 nelm,
761 GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK); 1479 GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);
762 free(t); 1480 free(t);
763 } 1481 }
764 */ 1482 */
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 //g_object_unref(path);
787 }
788 1483
789 void ui_listview_destroy(GtkWidget *w, UiListView *v) { 1484 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
790 //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); 1485 //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL);
791 ui_destroy_boundvar(v->obj->ctx, v->var); 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);
792 free(v); 1491 free(v);
793 } 1492 }
794 1493
795 void ui_combobox_destroy(GtkWidget *w, UiListView *v) { 1494 void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
796 ui_destroy_boundvar(v->obj->ctx, v->var); 1495 ui_destroy_boundvar(v->obj->ctx, v->var);
797 free(v); 1496 free(v);
798 } 1497 }
799 1498
800 1499
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 /* --------------------------- ComboBox --------------------------- */
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 // bind var
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 // add callback
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 /* ------------------------------ Source List ------------------------------ */ 1500 /* ------------------------------ Source List ------------------------------ */
989 1501
990 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) { 1502 static void ui_destroy_sourcelist(GtkWidget *w, UiListBox *v) {
991 cxListDestroy(v->sublists); 1503 cxListFree(v->sublists);
992 free(v); 1504 free(v);
993 } 1505 }
994 1506
995 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) { 1507 static void sublist_destroy(UiObject *obj, UiListBoxSubList *sublist) {
996 free(sublist->header); 1508 free(sublist->header);
997 ui_destroy_boundvar(obj->ctx, sublist->var); 1509 ui_destroy_boundvar(obj->ctx, sublist->var);
998 cxListDestroy(sublist->widgets); 1510 cxListFree(sublist->widgets);
999 } 1511 }
1000 1512
1001 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) { 1513 static void listbox_create_header(GtkListBoxRow* row, GtkListBoxRow* before, gpointer user_data) {
1002 // first rows in sublists have the ui_listbox property 1514 // first rows in sublists have the ui_listbox property
1003 UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox"); 1515 UiListBox *listbox = g_object_get_data(G_OBJECT(row), "ui_listbox");

mercurial