Wed, 04 Dec 2024 18:31:22 +0100
add first code of the new Motif implementation, implement buttons
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2024 Olaf Wintermann. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdio.h> #include <stdlib.h> #include "button.h" #include "container.h" #include "../common/context.h" #include <cx/mempool.h> #include <cx/linked_list.h> #include <cx/array_list.h> #include <cx/compare.h> #include <Xm/XmAll.h> UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) { Arg xargs[16]; int n = 0; UiContainerPrivate *ctn = ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); Widget parent = ctn->prepare(ctn, xargs, &n); XmString label = NULL; if(args.label) { label = XmStringCreateLocalized((char*)args.label); XtSetArg(xargs[n], XmNlabelString, label); n++; } char *name = args.name ? (char*)args.name : "button"; Widget button = XmCreatePushButton(parent, name, xargs, n); XtManageChild(button); ctn->add(ctn, button); ui_set_widget_groups(obj->ctx, button, args.groups); if(args.onclick) { UiEventData *eventdata = malloc(sizeof(UiEventData)); eventdata->callback = args.onclick; eventdata->userdata = args.onclickdata; eventdata->obj = obj; eventdata->value = 0; XtAddCallback( button, XmNactivateCallback, (XtCallbackProc)ui_push_button_callback, eventdata); XtAddCallback( button, XmNdestroyCallback, (XtCallbackProc)ui_destroy_eventdata, eventdata); } XmStringFree(label); return button; } void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) { UiEvent e; e.obj = event->obj; e.window = event->obj->window; e.document = event->obj->ctx->document; e.intval = event->value; event->callback(&e, event->userdata); } UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) { Arg xargs[16]; int n = 0; UiContainerPrivate *ctn = ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); Widget parent = ctn->prepare(ctn, xargs, &n); XtSetArg(xargs[n], XmNfillOnSelect, True); n++; XtSetArg(xargs[n], XmNindicatorOn, False); n++; XmString label = NULL; if(args.label) { label = XmStringCreateLocalized((char*)args.label); XtSetArg(xargs[n], XmNlabelString, label); n++; } char *name = args.name ? (char*)args.name : "togglebutton"; Widget button = XmCreateToggleButton(parent, name, xargs, n); XtManageChild(button); ctn->add(ctn, button); ui_set_widget_groups(obj->ctx, button, args.groups); ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); XmStringFree(label); return button; } UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) { Arg xargs[16]; int n = 0; UiContainerPrivate *ctn = ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); Widget parent = ctn->prepare(ctn, xargs, &n); XmString label = NULL; if(args.label) { label = XmStringCreateLocalized((char*)args.label); XtSetArg(xargs[n], XmNlabelString, label); n++; } char *name = args.name ? (char*)args.name : "button"; Widget button = XmCreateToggleButton(parent, name, xargs, n); XtManageChild(button); ctn->add(ctn, button); ui_set_widget_groups(obj->ctx, button, args.groups); ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group); XmStringFree(label); return button; } UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) { return ui_checkbox_create(obj, args); } static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { if(event->value > 0) { // button in configured to enable/disable states if(tb->set) { ui_set_group(event->obj->ctx, event->value); } else { ui_unset_group(event->obj->ctx, event->value); } } UiEvent e; e.obj = event->obj; e.window = e.obj->window; e.document = e.obj->ctx->document; e.eventdata = NULL; e.intval = XmToggleButtonGetState(w); if(event->callback) { event->callback(&e, event->userdata); } if(event->var && event->var->value) { UiInteger *v = event->var->value; v->value = e.intval; ui_notify_evt(v->observers, &e); } } void ui_bind_togglebutton( UiObject *obj, Widget widget, const char *varname, UiInteger *value, ui_callback onchange, void *onchangedata, int enable_state) { UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER); if(var) { value = (UiInteger*)var->value; value->obj = widget; value->get = ui_togglebutton_get; value->set = ui_togglebutton_set; if(value->value) { XmToggleButtonSetState(widget, True, False); } } UiVarEventData *event = malloc(sizeof(UiVarEventData)); event->obj = obj; event->callback = onchange; event->userdata = onchangedata; event->var = var; event->observers = NULL; event->value = enable_state; XtAddCallback( widget, XmNvalueChangedCallback, (XtCallbackProc)togglebutton_changed, event); XtAddCallback( widget, XmNdestroyCallback, (XtCallbackProc)ui_destroy_eventdata, event); } int64_t ui_togglebutton_get(UiInteger *i) { Widget togglebutton = i->obj; Boolean state = XmToggleButtonGetState(togglebutton); i->value = state; return state; } void ui_togglebutton_set(UiInteger *i, int64_t value) { Widget togglebutton = i->obj; i->value = value; XmToggleButtonSetState(togglebutton, (Boolean)value, False); } static void destroy_list(Widget w, CxList *list, XtPointer d) { cxListDestroy(list); } static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) { if(event->value > 0) { // button in configured to enable/disable states if(tb->set) { ui_set_group(event->obj->ctx, event->value); } else { ui_unset_group(event->obj->ctx, event->value); } } if(!tb->set) { return; // only handle set-events } UiInteger *value = NULL; int64_t v = 0; if(event->var) { value = event->var->value; // find widget index and update all radiobuttons // the UiInteger value must always be up-to-date CxList *list = value->obj; CxIterator i = cxListIterator(list); cx_foreach(Widget, button, i) { Boolean state = False; if(button == w) { value->value = i.index+1; // update value state = True; } XmToggleButtonSetState(button, state, False); } v = value->value; } UiEvent e; e.obj = event->obj; e.window = e.obj->window; e.document = e.obj->ctx->document; e.eventdata = value; e.intval = v; if(event->callback) { event->callback(&e, event->userdata); } if(value) { ui_notify_evt(value->observers, &e); } } UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) { Arg xargs[16]; int n = 0; UiContainerPrivate *ctn = ui_obj_container(obj); UI_APPLY_LAYOUT(ctn->layout, args); Widget parent = ctn->prepare(ctn, xargs, &n); XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++; XmString label = NULL; if(args.label) { label = XmStringCreateLocalized((char*)args.label); XtSetArg(xargs[n], XmNlabelString, label); n++; } char *name = args.name ? (char*)args.name : "button"; Widget button = XmCreateToggleButton(parent, name, xargs, n); XtManageChild(button); ctn->add(ctn, button); ui_set_widget_groups(obj->ctx, button, args.groups); UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER); if(var) { UiInteger *value = var->value; CxList *rb = value->obj; if(!rb) { // first button in the radiobutton group // create a list for all buttons and use the list as value obj rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4); value->obj = rb; value->get = ui_radiobutton_get; value->set = ui_radiobutton_set; // the first radio button is also responsible for cleanup XtAddCallback( button, XmNdestroyCallback, (XtCallbackProc)destroy_list, rb); } cxListAdd(rb, button); // set the radiobutton state, if the value is already set if(cxListSize(rb) == value->value) { XmToggleButtonSetState(button, True, False); } } // the radio button needs to handle change events to update all // other buttons in the radio button group UiVarEventData *event = malloc(sizeof(UiVarEventData)); event->obj = obj; event->callback = args.onchange; event->userdata = args.onchangedata; event->observers = NULL; event->var = var; event->value = args.enable_group; XtAddCallback( button, XmNvalueChangedCallback, (XtCallbackProc)radiobutton_changed, event); XtAddCallback( button, XmNdestroyCallback, (XtCallbackProc)ui_destroy_eventdata, event); XmStringFree(label); return button; } int64_t ui_radiobutton_get(UiInteger *i) { // the UiInteger should be updated automatically by change events return i->value; } void ui_radiobutton_set(UiInteger *i, int64_t value) { CxList *list = i->obj; if(i->value > 0) { Widget current = cxListAt(list, i->value-1); if(current) { XmToggleButtonSetState(current, False, False); } } if(value > 0 && value <= cxListSize(list)) { Widget button = cxListAt(list, value-1); if(button) { XmToggleButtonSetState(button, True, False); i->value = value; } } }