ui/motif/button.c

Thu, 12 Dec 2024 20:01:43 +0100

author
Olaf Wintermann <olaf.wintermann@gmail.com>
date
Thu, 12 Dec 2024 20:01:43 +0100
changeset 100
d2bd73d28ff1
parent 0
2483f517c562
permissions
-rw-r--r--

update toolkit

/*
 * 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;
        }
    }
}

mercurial