ui/gtk/list.c

changeset 436
222205801430
parent 413
b8e41d42f400
child 437
f02a62de0328
equal deleted inserted replaced
435:883a569cc9a3 436:222205801430
288 { "text/plain", 0, 1 }, 288 { "text/plain", 0, 1 },
289 { "text/uri-list", 0, 2 }, 289 { "text/uri-list", 0, 2 },
290 }; 290 };
291 */ 291 */
292 292
293 #if GTK_CHECK_VERSION(4, 10, 0)
294
295
296 /* BEGIN GObject wrapper for generic pointers */
297
298 typedef struct _ObjWrapper {
299 GObject parent_instance;
300 void *data;
301 } ObjWrapper;
302
303 typedef struct _ObjWrapperClass {
304 GObjectClass parent_class;
305 } ObjWrapperClass;
306
307 G_DEFINE_TYPE(ObjWrapper, obj_wrapper, G_TYPE_OBJECT)
308
309 static void obj_wrapper_class_init(ObjWrapperClass *klass) {
310
311 }
312
313 static void obj_wrapper_init(ObjWrapper *self) {
314 self->data = NULL;
315 }
316
317 ObjWrapper* obj_wrapper_new(void* data) {
318 ObjWrapper *obj = g_object_new(obj_wrapper_get_type(), NULL);
319 obj->data = data;
320 return obj;
321 }
322
323 /* END GObject wrapper for generic pointers */
324
325 static void column_factory_setup(GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
326 UiColData *col = userdata;
327 UiModel *model = col->listview->model;
328 UiModelType type = model->types[col->model_column];
329 if(type == UI_ICON_TEXT || type == UI_ICON_TEXT_FREE) {
330 GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6);
331 GtkWidget *image = gtk_image_new();
332 GtkWidget *label = gtk_label_new(NULL);
333 BOX_ADD(hbox, image);
334 BOX_ADD(hbox, label);
335 gtk_list_item_set_child(item, hbox);
336 g_object_set_data(G_OBJECT(hbox), "image", image);
337 g_object_set_data(G_OBJECT(hbox), "label", label);
338 } else if(type == UI_ICON) {
339 GtkWidget *image = gtk_image_new();
340 gtk_list_item_set_child(item, image);
341 } else {
342 GtkWidget *label = gtk_label_new(NULL);
343 gtk_list_item_set_child(item, label);
344 }
345 }
346
347 static void column_factory_bind( GtkListItemFactory *factory, GtkListItem *item, gpointer userdata) {
348 UiColData *col = userdata;
349
350 ObjWrapper *obj = gtk_list_item_get_item(item);
351 UiModel *model = col->listview->model;
352 UiModelType type = model->types[col->model_column];
353
354 void *data = model->getvalue(obj->data, col->data_column);
355 GtkWidget *child = gtk_list_item_get_child(item);
356
357 bool freevalue = TRUE;
358 switch(type) {
359 case UI_STRING: {
360 freevalue = FALSE;
361 }
362 case UI_STRING_FREE: {
363 gtk_label_set_label(GTK_LABEL(child), data);
364 if(freevalue) {
365 free(data);
366 }
367 break;
368 }
369 case UI_INTEGER: {
370 intptr_t intvalue = (intptr_t)data;
371 char buf[32];
372 snprintf(buf, 32, "%d", (int)intvalue);
373 gtk_label_set_label(GTK_LABEL(child), buf);
374 break;
375 }
376 case UI_ICON: {
377 UiIcon *icon = data;
378 if(icon) {
379 gtk_image_set_from_paintable(GTK_IMAGE(child), GDK_PAINTABLE(icon->info));
380 }
381 break;
382 }
383 case UI_ICON_TEXT: {
384 freevalue = FALSE;
385 }
386 case UI_ICON_TEXT_FREE: {
387 void *data2 = model->getvalue(obj->data, col->data_column+1);
388 GtkWidget *image = g_object_get_data(G_OBJECT(child), "image");
389 GtkWidget *label = g_object_get_data(G_OBJECT(child), "label");
390 if(data && image) {
391 UiIcon *icon = data;
392 gtk_image_set_from_paintable(GTK_IMAGE(image), GDK_PAINTABLE(icon->info));
393 }
394 if(data2 && label) {
395 gtk_label_set_label(GTK_LABEL(label), data2);
396 }
397 if(freevalue) {
398 free(data2);
399 }
400 break;
401 }
402 }
403 }
404
405 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
406 UiObject* current = uic_current_obj(obj);
407
408 GListStore *ls = g_list_store_new(G_TYPE_OBJECT);
409 //g_list_store_append(ls, v1);
410
411 GtkSelectionModel *selection_model;
412 if(args.multiselection) {
413 selection_model = GTK_SELECTION_MODEL(gtk_multi_selection_new(G_LIST_MODEL(ls)));
414 } else {
415 selection_model = GTK_SELECTION_MODEL(gtk_single_selection_new(G_LIST_MODEL(ls)));
416 }
417
418 GtkWidget *view = gtk_column_view_new(GTK_SELECTION_MODEL(selection_model));
419
420 UiVar* var = uic_widget_var(obj->ctx, current->ctx, args.list, args.varname, UI_VAR_LIST);
421
422 // create obj to store all relevant data we need for handling events
423 // and list updates
424 UiListView *tableview = malloc(sizeof(UiListView));
425 tableview->obj = obj;
426 tableview->widget = view;
427 tableview->var = var;
428 tableview->model = args.model;
429 tableview->liststore = ls;
430 tableview->selectionmodel = selection_model;
431 tableview->onactivate = args.onactivate;
432 tableview->onactivatedata = args.onactivatedata;
433 tableview->onselection = args.onselection;
434 tableview->onselectiondata = args.onselectiondata;
435 tableview->ondragstart = args.ondragstart;
436 tableview->ondragstartdata = args.ondragstartdata;
437 tableview->ondragcomplete = args.ondragcomplete;
438 tableview->ondragcompletedata = args.ondragcompletedata;
439 tableview->ondrop = args.ondrop;
440 tableview->ondropdata = args.ondropsdata;
441 tableview->selection.count = 0;
442 tableview->selection.rows = NULL;
443 g_signal_connect(
444 view,
445 "destroy",
446 G_CALLBACK(ui_listview_destroy),
447 tableview);
448
449
450 // create columns from UiModel
451 UiModel *model = args.model;
452 int columns = model ? model->columns : 0;
453
454 tableview->columns = calloc(columns, sizeof(UiColData));
455
456 int addi = 0;
457 for(int i=0;i<columns;i++) {
458 tableview->columns[i].listview = tableview;
459 tableview->columns[i].model_column = i;
460 tableview->columns[i].data_column = i+addi;
461
462 if(model->types[i] == UI_ICON_TEXT || model->types[i] == UI_ICON_TEXT_FREE) {
463 // icon+text has 2 data columns
464 addi++;
465 }
466
467 GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
468 UiColData *col = &tableview->columns[i];
469 g_signal_connect(factory, "setup", G_CALLBACK(column_factory_setup), col);
470 g_signal_connect(factory, "bind", G_CALLBACK(column_factory_bind), col);
471
472 GtkColumnViewColumn *column = gtk_column_view_column_new(model->titles[i], factory);
473 gtk_column_view_column_set_resizable(column, true);
474 gtk_column_view_append_column(GTK_COLUMN_VIEW(view), column);
475 }
476
477 // bind listview to list
478 if(var && var->value) {
479 UiList *list = var->value;
480
481 list->obj = tableview;
482 list->update = ui_listview_update2;
483 list->getselection = ui_listview_getselection2;
484 list->setselection = ui_listview_setselection2;
485
486 ui_update_liststore(ls, list);
487 }
488
489 // event handling
490 if(args.onactivate) {
491 g_signal_connect(view, "activate", G_CALLBACK(ui_columnview_activate), tableview);
492 }
493 // always handle selection-changed, to keep track of the current selection
494 g_signal_connect(selection_model, "selection-changed", G_CALLBACK(ui_listview_selection_changed), tableview);
495
496 // add widget to parent
497 GtkWidget *scroll_area = SCROLLEDWINDOW_NEW();
498 gtk_scrolled_window_set_policy(
499 GTK_SCROLLED_WINDOW(scroll_area),
500 GTK_POLICY_AUTOMATIC,
501 GTK_POLICY_AUTOMATIC); // GTK_POLICY_ALWAYS
502 SCROLLEDWINDOW_SET_CHILD(scroll_area, view);
503
504 UI_APPLY_LAYOUT1(current, args);
505 current->container->add(current->container, scroll_area, FALSE);
506
507 // ct->current should point to view, not scroll_area, to make it possible
508 // to add a context menu
509 current->container->current = view;
510
511 return scroll_area;
512 }
513
514 static UiListSelection selectionmodel_get_selection(GtkSelectionModel *model) {
515 UiListSelection sel = { 0, NULL };
516 GtkBitset *bitset = gtk_selection_model_get_selection(model);
517 int n = gtk_bitset_get_size(bitset);
518 printf("bitset %d\n", n);
519
520 gtk_bitset_unref(bitset);
521 return sel;
522 }
523
524 static void listview_event(ui_callback cb, void *cbdata, UiListView *view) {
525 UiEvent event;
526 event.obj = view->obj;
527 event.document = event.obj->ctx->document;
528 event.window = event.obj->window;
529 event.intval = view->selection.count;
530 event.eventdata = &view->selection;
531 if(cb) {
532 cb(&event, cbdata);
533 }
534 }
535
536 void ui_columnview_activate(GtkColumnView* self, guint position, gpointer userdata) {
537 UiListView *view = userdata;
538 listview_event(view->onactivate, view->onactivatedata, view);
539 }
540
541 void ui_listview_selection_changed(GtkSelectionModel* self, guint position, guint n_items, gpointer userdata) {
542 UiListView *view = userdata;
543 free(view->selection.rows);
544 view->selection.count = 0;
545 view->selection.rows = NULL;
546
547 CX_ARRAY_DECLARE(int, newselection);
548 cx_array_initialize(newselection, 8);
549
550 size_t nitems = g_list_model_get_n_items(G_LIST_MODEL(view->liststore));
551
552 for(size_t i=0;i<nitems;i++) {
553 if(gtk_selection_model_is_selected(view->selectionmodel, i)) {
554 int s = (int)i;
555 cx_array_simple_add(newselection, s);
556 }
557 }
558
559 if(newselection_size > 0) {
560 view->selection.count = newselection_size;
561 view->selection.rows = newselection;
562 } else {
563 free(newselection);
564 }
565
566 listview_event(view->onselection, view->onselectiondata, view);
567 }
568
569 void ui_update_liststore(GListStore *liststore, UiList *list) {
570 g_list_store_remove_all(liststore);
571 void *elm = list->first(list);
572 while(elm) {
573 ObjWrapper *obj = obj_wrapper_new(elm);
574 g_list_store_append(liststore, obj);
575 elm = list->next(list);
576 }
577 }
578
579 void ui_listview_update2(UiList *list, int i) {
580 UiListView *view = list->obj;
581 ui_update_liststore(view->liststore, view->var->value);
582 }
583
584 UiListSelection ui_listview_getselection2(UiList *list) {
585 UiListView *view = list->obj;
586 UiListSelection selection;
587 selection.count = view->selection.count;
588 selection.rows = calloc(selection.count, sizeof(int));
589 memcpy(selection.rows, view->selection.rows, selection.count*sizeof(int));
590 return selection;
591 }
592
593 void ui_listview_setselection2(UiList *list, UiListSelection selection) {
594 UiListView *view = list->obj;
595 UiListSelection newselection;
596 newselection.count = view->selection.count;
597 if(selection.count > 0) {
598 newselection.rows = calloc(newselection.count, sizeof(int));
599 memcpy(newselection.rows, selection.rows, selection.count*sizeof(int));
600 } else {
601 newselection.rows = NULL;
602 }
603 free(view->selection.rows);
604 view->selection = newselection;
605
606 gtk_selection_model_unselect_all(view->selectionmodel);
607 if(selection.count > 0) {
608 for(int i=0;i<selection.count;i++) {
609 gtk_selection_model_select_item(view->selectionmodel, selection.rows[i], FALSE);
610 }
611 }
612 }
613
614 #else
615
293 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) { 616 UIWIDGET ui_table_create(UiObject *obj, UiListArgs args) {
294 UiObject* current = uic_current_obj(obj); 617 UiObject* current = uic_current_obj(obj);
295 618
296 // create treeview 619 // create treeview
297 GtkWidget *view = gtk_tree_view_new(); 620 GtkWidget *view = gtk_tree_view_new();
374 tableview->ondragstartdata = args.ondragstartdata; 697 tableview->ondragstartdata = args.ondragstartdata;
375 tableview->ondragcomplete = args.ondragcomplete; 698 tableview->ondragcomplete = args.ondragcomplete;
376 tableview->ondragcompletedata = args.ondragcompletedata; 699 tableview->ondragcompletedata = args.ondragcompletedata;
377 tableview->ondrop = args.ondrop; 700 tableview->ondrop = args.ondrop;
378 tableview->ondropdata = args.ondropsdata; 701 tableview->ondropdata = args.ondropsdata;
702 tableview->selection.count = 0;
703 tableview->selection.rows = NULL;
379 g_signal_connect( 704 g_signal_connect(
380 view, 705 view,
381 "destroy", 706 "destroy",
382 G_CALLBACK(ui_listview_destroy), 707 G_CALLBACK(ui_listview_destroy),
383 tableview); 708 tableview);
450 // to add a context menu 775 // to add a context menu
451 current->container->current = view; 776 current->container->current = view;
452 777
453 return scroll_area; 778 return scroll_area;
454 } 779 }
780
781
782 #endif
783
455 784
456 #if GTK_MAJOR_VERSION >= 4 785 #if GTK_MAJOR_VERSION >= 4
457 786
458 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) { 787 static GdkContentProvider *ui_listview_dnd_prepare(GtkDragSource *source, double x, double y, void *data) {
459 //printf("drag prepare\n"); 788 //printf("drag prepare\n");
787 } 1116 }
788 1117
789 void ui_listview_destroy(GtkWidget *w, UiListView *v) { 1118 void ui_listview_destroy(GtkWidget *w, UiListView *v) {
790 //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL); 1119 //gtk_tree_view_set_model(GTK_TREE_VIEW(w), NULL);
791 ui_destroy_boundvar(v->obj->ctx, v->var); 1120 ui_destroy_boundvar(v->obj->ctx, v->var);
1121 #if GTK_CHECK_VERSION(4, 10, 0)
1122 free(v->columns);
1123 #endif
1124 free(v->selection.rows);
792 free(v); 1125 free(v);
793 } 1126 }
794 1127
795 void ui_combobox_destroy(GtkWidget *w, UiListView *v) { 1128 void ui_combobox_destroy(GtkWidget *w, UiListView *v) {
796 ui_destroy_boundvar(v->obj->ctx, v->var); 1129 ui_destroy_boundvar(v->obj->ctx, v->var);

mercurial