ui/motif/button.c

branch
newapi
changeset 406
0ebf9d7b23e8
parent 176
bc63cb601f6d
child 419
7d15cad351fc
equal deleted inserted replaced
405:a7f18dda6baf 406:0ebf9d7b23e8
1 /* 1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 * 3 *
4 * Copyright 2014 Olaf Wintermann. All rights reserved. 4 * Copyright 2024 Olaf Wintermann. All rights reserved.
5 * 5 *
6 * Redistribution and use in source and binary forms, with or without 6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met: 7 * modification, are permitted provided that the following conditions are met:
8 * 8 *
9 * 1. Redistributions of source code must retain the above copyright 9 * 1. Redistributions of source code must retain the above copyright
36 36
37 #include <cx/linked_list.h> 37 #include <cx/linked_list.h>
38 #include <cx/array_list.h> 38 #include <cx/array_list.h>
39 #include <cx/compare.h> 39 #include <cx/compare.h>
40 40
41 41 #include <Xm/XmAll.h>
42 UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) { 42
43 UiContainer *ct = uic_get_current_container(obj); 43
44 XmString str = XmStringCreateLocalized(label); 44 UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) {
45 45 Arg xargs[16];
46 int n = 0; 46 int n = 0;
47 Arg args[16]; 47
48 48 UiContainerPrivate *ctn = ui_obj_container(obj);
49 XtSetArg(args[n], XmNlabelString, str); 49 UI_APPLY_LAYOUT(ctn->layout, args);
50 n++; 50
51 51 Widget parent = ctn->prepare(ctn, xargs, &n);
52 Widget parent = ct->prepare(ct, args, &n, FALSE); 52
53 Widget button = XmCreatePushButton(parent, "button", args, n); 53 XmString label = NULL;
54 ct->add(ct, button); 54 if(args.label) {
55 55 label = XmStringCreateLocalized((char*)args.label);
56 if(f) { 56 XtSetArg(xargs[n], XmNlabelString, label); n++;
57 UiEventData *event = cxMalloc( 57 }
58 obj->ctx->allocator, 58
59 sizeof(UiEventData)); 59 char *name = args.name ? (char*)args.name : "button";
60 event->obj = obj; 60 Widget button = XmCreatePushButton(parent, name, xargs, n);
61 event->userdata = data; 61 XtManageChild(button);
62 event->callback = f; 62 ctn->add(ctn, button);
63 event->value = 0; 63
64 ui_set_widget_groups(obj->ctx, button, args.groups);
65
66 if(args.onclick) {
67 UiEventData *eventdata = malloc(sizeof(UiEventData));
68 eventdata->callback = args.onclick;
69 eventdata->userdata = args.onclickdata;
70 eventdata->obj = obj;
71 eventdata->value = 0;
64 XtAddCallback( 72 XtAddCallback(
65 button, 73 button,
66 XmNactivateCallback, 74 XmNactivateCallback,
67 (XtCallbackProc)ui_push_button_callback, 75 (XtCallbackProc)ui_push_button_callback,
68 event); 76 eventdata);
69 } 77 XtAddCallback(
70 78 button,
71 XtManageChild(button); 79 XmNdestroyCallback,
72 80 (XtCallbackProc)ui_destroy_eventdata,
81 eventdata);
82 }
83
84
85 XmStringFree(label);
73 return button; 86 return button;
74 }
75
76 // wrapper
77 int64_t ui_toggle_button_get(UiInteger *i) {
78 int state = 0;
79 XtVaGetValues(i->obj, XmNset, &state, NULL);
80 i->value = state;
81 return state;
82 }
83
84 void ui_toggle_button_set(UiInteger *i, int64_t value) {
85 Arg arg;
86 XtSetArg(arg, XmNset, value);
87 XtSetValues(i->obj, &arg, 1);
88 i->value = value;
89 }
90
91 void ui_toggle_button_callback(
92 Widget widget,
93 UiEventData *event,
94 XmToggleButtonCallbackStruct *tb)
95 {
96 UiEvent e;
97 e.obj = event->obj;
98 e.window = event->obj->window;
99 // TODO: e.document
100 e.intval = tb->set;
101 event->callback(&e, event->userdata);
102 } 87 }
103 88
104 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) { 89 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) {
105 UiEvent e; 90 UiEvent e;
106 e.obj = event->obj; 91 e.obj = event->obj;
108 e.document = event->obj->ctx->document; 93 e.document = event->obj->ctx->document;
109 e.intval = event->value; 94 e.intval = event->value;
110 event->callback(&e, event->userdata); 95 event->callback(&e, event->userdata);
111 } 96 }
112 97
113 98 UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) {
114 static void radio_callback( 99 Arg xargs[16];
100 int n = 0;
101
102 UiContainerPrivate *ctn = ui_obj_container(obj);
103 UI_APPLY_LAYOUT(ctn->layout, args);
104
105 Widget parent = ctn->prepare(ctn, xargs, &n);
106 XtSetArg(xargs[n], XmNfillOnSelect, True); n++;
107 XtSetArg(xargs[n], XmNindicatorOn, False); n++;
108
109 XmString label = NULL;
110 if(args.label) {
111 label = XmStringCreateLocalized((char*)args.label);
112 XtSetArg(xargs[n], XmNlabelString, label); n++;
113 }
114
115 char *name = args.name ? (char*)args.name : "togglebutton";
116 Widget button = XmCreateToggleButton(parent, name, xargs, n);
117 XtManageChild(button);
118 ctn->add(ctn, button);
119
120 ui_set_widget_groups(obj->ctx, button, args.groups);
121
122 ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group);
123
124 XmStringFree(label);
125 return button;
126 }
127
128 UIWIDGET ui_checkbox_create(UiObject* obj, UiToggleArgs args) {
129 Arg xargs[16];
130 int n = 0;
131
132 UiContainerPrivate *ctn = ui_obj_container(obj);
133 UI_APPLY_LAYOUT(ctn->layout, args);
134
135 Widget parent = ctn->prepare(ctn, xargs, &n);
136
137 XmString label = NULL;
138 if(args.label) {
139 label = XmStringCreateLocalized((char*)args.label);
140 XtSetArg(xargs[n], XmNlabelString, label); n++;
141 }
142
143 char *name = args.name ? (char*)args.name : "button";
144 Widget button = XmCreateToggleButton(parent, name, xargs, n);
145 XtManageChild(button);
146 ctn->add(ctn, button);
147
148 ui_set_widget_groups(obj->ctx, button, args.groups);
149
150 ui_bind_togglebutton(obj, button, args.varname, args.value, args.onchange, args.onchangedata, args.enable_group);
151
152 XmStringFree(label);
153 return button;
154 }
155
156 UIWIDGET ui_switch_create(UiObject* obj, UiToggleArgs args) {
157 return ui_checkbox_create(obj, args);
158 }
159
160 static void togglebutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
161 if(event->value > 0) {
162 // button in configured to enable/disable states
163 if(tb->set) {
164 ui_set_group(event->obj->ctx, event->value);
165 } else {
166 ui_unset_group(event->obj->ctx, event->value);
167 }
168 }
169
170 UiEvent e;
171 e.obj = event->obj;
172 e.window = e.obj->window;
173 e.document = e.obj->ctx->document;
174 e.eventdata = NULL;
175 e.intval = XmToggleButtonGetState(w);
176
177 if(event->callback) {
178 event->callback(&e, event->userdata);
179 }
180
181 if(event->var && event->var->value) {
182 UiInteger *v = event->var->value;
183 v->value = e.intval;
184 ui_notify_evt(v->observers, &e);
185 }
186 }
187
188 void ui_bind_togglebutton(
189 UiObject *obj,
115 Widget widget, 190 Widget widget,
116 RadioEventData *event, 191 const char *varname,
117 XmToggleButtonCallbackStruct *tb) 192 UiInteger *value,
193 ui_callback onchange,
194 void *onchangedata,
195 int enable_state)
118 { 196 {
119 if(tb->set) { 197 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
120 RadioButtonGroup *group = event->group; 198 if(var) {
121 if(group->current) { 199 value = (UiInteger*)var->value;
122 Arg arg; 200 value->obj = widget;
123 XtSetArg(arg, XmNset, FALSE); 201 value->get = ui_togglebutton_get;
124 XtSetValues(group->current, &arg, 1); 202 value->set = ui_togglebutton_set;
125 } 203
126 group->current = widget; 204 if(value->value) {
127 } 205 XmToggleButtonSetState(widget, True, False);
128 } 206 }
129 207 }
130 UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) { 208
131 UiContainer *ct = uic_get_current_container(obj); 209 UiVarEventData *event = malloc(sizeof(UiVarEventData));
132 XmString str = XmStringCreateLocalized(label); 210 event->obj = obj;
133 211 event->callback = onchange;
212 event->userdata = onchangedata;
213 event->var = var;
214 event->observers = NULL;
215 event->value = enable_state;
216 XtAddCallback(
217 widget,
218 XmNvalueChangedCallback,
219 (XtCallbackProc)togglebutton_changed,
220 event);
221 XtAddCallback(
222 widget,
223 XmNdestroyCallback,
224 (XtCallbackProc)ui_destroy_eventdata,
225 event);
226 }
227
228 int64_t ui_togglebutton_get(UiInteger *i) {
229 Widget togglebutton = i->obj;
230 Boolean state = XmToggleButtonGetState(togglebutton);
231 i->value = state;
232 return state;
233 }
234
235 void ui_togglebutton_set(UiInteger *i, int64_t value) {
236 Widget togglebutton = i->obj;
237 i->value = value;
238 XmToggleButtonSetState(togglebutton, (Boolean)value, False);
239 }
240
241 static void destroy_list(Widget w, CxList *list, XtPointer d) {
242 cxListDestroy(list);
243 }
244
245 static void radiobutton_changed(Widget w, UiVarEventData *event, XmToggleButtonCallbackStruct *tb) {
246 if(event->value > 0) {
247 // button in configured to enable/disable states
248 if(tb->set) {
249 ui_set_group(event->obj->ctx, event->value);
250 } else {
251 ui_unset_group(event->obj->ctx, event->value);
252 }
253 }
254
255 if(!tb->set) {
256 return; // only handle set-events
257 }
258
259 UiInteger *value = NULL;
260 int64_t v = 0;
261 if(event->var) {
262 value = event->var->value;
263 // find widget index and update all radiobuttons
264 // the UiInteger value must always be up-to-date
265 CxList *list = value->obj;
266 CxIterator i = cxListIterator(list);
267 cx_foreach(Widget, button, i) {
268 Boolean state = False;
269 if(button == w) {
270 value->value = i.index+1; // update value
271 state = True;
272 }
273 XmToggleButtonSetState(button, state, False);
274 }
275 v = value->value;
276 }
277
278 UiEvent e;
279 e.obj = event->obj;
280 e.window = e.obj->window;
281 e.document = e.obj->ctx->document;
282 e.eventdata = value;
283 e.intval = v;
284
285 if(event->callback) {
286 event->callback(&e, event->userdata);
287 }
288
289 if(value) {
290 ui_notify_evt(value->observers, &e);
291 }
292 }
293
294 UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) {
295 Arg xargs[16];
134 int n = 0; 296 int n = 0;
135 Arg args[16]; 297
136 298 UiContainerPrivate *ctn = ui_obj_container(obj);
137 XtSetArg(args[n], XmNlabelString, str); 299 UI_APPLY_LAYOUT(ctn->layout, args);
138 n++; 300
139 XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); 301 Widget parent = ctn->prepare(ctn, xargs, &n);
140 n++; 302 XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
141 303 XmString label = NULL;
142 Widget parent = ct->prepare(ct, args, &n, FALSE); 304 if(args.label) {
143 Widget button = XmCreateToggleButton(parent, "radiobutton", args, n); 305 label = XmStringCreateLocalized((char*)args.label);
144 ct->add(ct, button); 306 XtSetArg(xargs[n], XmNlabelString, label); n++;
145 307 }
146 if(rgroup) { 308
147 RadioButtonGroup *group; 309 char *name = args.name ? (char*)args.name : "button";
148 if(rgroup->obj) { 310 Widget button = XmCreateToggleButton(parent, name, xargs, n);
149 group = rgroup->obj; 311 XtManageChild(button);
150 if(!group->buttons) { 312 ctn->add(ctn, button);
151 group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8); 313
152 } 314 ui_set_widget_groups(obj->ctx, button, args.groups);
153 cxListAdd(group->buttons, button); 315
154 group->ref++; 316 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER);
155 } else { 317 if(var) {
156 group = malloc(sizeof(RadioButtonGroup)); 318 UiInteger *value = var->value;
157 group->buttons = cxArrayListCreate(cxDefaultAllocator, cx_cmp_uintptr, CX_STORE_POINTERS, 8); 319 CxList *rb = value->obj;
158 cxListAdd(group->buttons, button); 320 if(!rb) {
159 group->current = button; 321 // first button in the radiobutton group
160 // this is the first button in the radiobutton group 322 // create a list for all buttons and use the list as value obj
161 // so we should enable it 323 rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
162 Arg arg; 324 value->obj = rb;
163 XtSetArg(arg, XmNset, TRUE); 325 value->get = ui_radiobutton_get;
164 XtSetValues(button, &arg, 1); 326 value->set = ui_radiobutton_set;
165 rgroup->obj = group;
166 327
167 group->current = button; 328 // the first radio button is also responsible for cleanup
168 } 329 XtAddCallback(
330 button,
331 XmNdestroyCallback,
332 (XtCallbackProc)destroy_list,
333 rb);
334 }
335 cxListAdd(rb, button);
169 336
170 RadioEventData *event = malloc(sizeof(RadioEventData)); 337 // set the radiobutton state, if the value is already set
171 event->obj = obj; 338 if(cxListSize(rb) == value->value) {
172 event->callback = NULL; 339 XmToggleButtonSetState(button, True, False);
173 event->userdata = NULL; 340 }
174 event->group = group; 341 }
175 XtAddCallback( 342
343 // the radio button needs to handle change events to update all
344 // other buttons in the radio button group
345 UiVarEventData *event = malloc(sizeof(UiVarEventData));
346 event->obj = obj;
347 event->callback = args.onchange;
348 event->userdata = args.onchangedata;
349 event->observers = NULL;
350 event->var = var;
351 event->value = args.enable_group;
352 XtAddCallback(
176 button, 353 button,
177 XmNvalueChangedCallback, 354 XmNvalueChangedCallback,
178 (XtCallbackProc)radio_callback, 355 (XtCallbackProc)radiobutton_changed,
179 event); 356 event);
180 357 XtAddCallback(
181 rgroup->get = ui_radiobutton_get; 358 button,
182 rgroup->set = ui_radiobutton_set; 359 XmNdestroyCallback,
183 } 360 (XtCallbackProc)ui_destroy_eventdata,
184 361 event);
185 XtManageChild(button); 362
363 XmStringFree(label);
186 return button; 364 return button;
187 } 365
188 366
189 int64_t ui_radiobutton_get(UiInteger *value) { 367 }
190 RadioButtonGroup *group = value->obj; 368
191 369 int64_t ui_radiobutton_get(UiInteger *i) {
192 int i = cxListFind(group->buttons, group->current); 370 // the UiInteger should be updated automatically by change events
193 if (i >= 0) { 371 return i->value;
194 value->value = i; 372 }
195 return i; 373
196 } else { 374 void ui_radiobutton_set(UiInteger *i, int64_t value) {
197 return 0; 375 CxList *list = i->obj;
198 } 376 if(i->value > 0) {
199 } 377 Widget current = cxListAt(list, i->value-1);
200 378 if(current) {
201 void ui_radiobutton_set(UiInteger *value, int64_t i) { 379 XmToggleButtonSetState(current, False, False);
202 RadioButtonGroup *group = value->obj; 380 }
203 Arg arg; 381 }
204 382 if(value > 0 && value <= cxListSize(list)) {
205 XtSetArg(arg, XmNset, FALSE); 383 Widget button = cxListAt(list, value-1);
206 XtSetValues(group->current, &arg, 1); 384 if(button) {
207 385 XmToggleButtonSetState(button, True, False);
208 Widget button = cxListAt(group->buttons, i); 386 i->value = value;
209 if(button) { 387 }
210 XtSetArg(arg, XmNset, TRUE); 388 }
211 XtSetValues(button, &arg, 1); 389 }
212 group->current = button;
213 }
214 }

mercurial