ui/motif/button.c

changeset 431
bb7da585debc
parent 419
7d15cad351fc
child 433
605bb5dc34f1
equal deleted inserted replaced
169:fe49cff3c571 431:bb7da585debc
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
30 #include <stdlib.h> 30 #include <stdlib.h>
31 31
32 #include "button.h" 32 #include "button.h"
33 #include "container.h" 33 #include "container.h"
34 #include "../common/context.h" 34 #include "../common/context.h"
35 #include <ucx/mempool.h> 35 #include <cx/mempool.h>
36 36
37 37 #include <cx/linked_list.h>
38 UIWIDGET ui_button(UiObject *obj, char *label, ui_callback f, void *data) { 38 #include <cx/array_list.h>
39 UiContainer *ct = uic_get_current_container(obj); 39 #include <cx/compare.h>
40 XmString str = XmStringCreateLocalized(label); 40
41 41 #include <Xm/XmAll.h>
42
43
44 UIWIDGET ui_button_create(UiObject* obj, UiButtonArgs args) {
45 Arg xargs[16];
42 int n = 0; 46 int n = 0;
43 Arg args[16]; 47
44 48 UiContainerPrivate *ctn = ui_obj_container(obj);
45 XtSetArg(args[n], XmNlabelString, str); 49 UI_APPLY_LAYOUT(ctn->layout, args);
46 n++; 50
47 51 Widget parent = ctn->prepare(ctn, xargs, &n);
48 Widget parent = ct->prepare(ct, args, &n, FALSE); 52
49 Widget button = XmCreatePushButton(parent, "button", args, n); 53 XmString label = NULL;
50 ct->add(ct, button); 54 if(args.label) {
51 55 label = XmStringCreateLocalized((char*)args.label);
52 if(f) { 56 XtSetArg(xargs[n], XmNlabelString, label); n++;
53 UiEventData *event = ucx_mempool_malloc( 57 }
54 obj->ctx->mempool, 58
55 sizeof(UiEventData)); 59 char *name = args.name ? (char*)args.name : "button";
56 event->obj = obj; 60 Widget button = XmCreatePushButton(parent, name, xargs, n);
57 event->userdata = data; 61 XtManageChild(button);
58 event->callback = f; 62 ctn->add(ctn, button);
59 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;
60 XtAddCallback( 72 XtAddCallback(
61 button, 73 button,
62 XmNactivateCallback, 74 XmNactivateCallback,
63 (XtCallbackProc)ui_push_button_callback, 75 (XtCallbackProc)ui_push_button_callback,
64 event); 76 eventdata);
65 } 77 XtAddCallback(
66 78 button,
67 XtManageChild(button); 79 XmNdestroyCallback,
68 80 (XtCallbackProc)ui_destroy_eventdata,
81 eventdata);
82 }
83
84
85 XmStringFree(label);
69 return button; 86 return button;
70 }
71
72 // wrapper
73 int64_t ui_toggle_button_get(UiInteger *i) {
74 int state = 0;
75 XtVaGetValues(i->obj, XmNset, &state, NULL);
76 i->value = state;
77 return state;
78 }
79
80 void ui_toggle_button_set(UiInteger *i, int64_t value) {
81 Arg arg;
82 XtSetArg(arg, XmNset, value);
83 XtSetValues(i->obj, &arg, 1);
84 i->value = value;
85 }
86
87 void ui_toggle_button_callback(
88 Widget widget,
89 UiEventData *event,
90 XmToggleButtonCallbackStruct *tb)
91 {
92 UiEvent e;
93 e.obj = event->obj;
94 e.window = event->obj->window;
95 // TODO: e.document
96 e.intval = tb->set;
97 event->callback(&e, event->userdata);
98 } 87 }
99 88
100 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) { 89 void ui_push_button_callback(Widget widget, UiEventData *event, XtPointer d) {
101 UiEvent e; 90 UiEvent e;
102 e.obj = event->obj; 91 e.obj = event->obj;
104 e.document = event->obj->ctx->document; 93 e.document = event->obj->ctx->document;
105 e.intval = event->value; 94 e.intval = event->value;
106 event->callback(&e, event->userdata); 95 event->callback(&e, event->userdata);
107 } 96 }
108 97
109 98 UIWIDGET ui_togglebutton_create(UiObject* obj, UiToggleArgs args) {
110 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,
111 Widget widget, 190 Widget widget,
112 RadioEventData *event, 191 const char *varname,
113 XmToggleButtonCallbackStruct *tb) 192 UiInteger *value,
193 ui_callback onchange,
194 void *onchangedata,
195 int enable_state)
114 { 196 {
115 if(tb->set) { 197 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
116 RadioButtonGroup *group = event->group; 198 if(var) {
117 if(group->current) { 199 value = (UiInteger*)var->value;
118 Arg arg; 200 value->obj = widget;
119 XtSetArg(arg, XmNset, FALSE); 201 value->get = ui_togglebutton_get;
120 XtSetValues(group->current, &arg, 1); 202 value->set = ui_togglebutton_set;
121 } 203
122 group->current = widget; 204 if(value->value) {
123 } 205 XmToggleButtonSetState(widget, True, False);
124 } 206 }
125 207 }
126 UIWIDGET ui_radiobutton(UiObject *obj, char *label, UiInteger *rgroup) { 208
127 UiContainer *ct = uic_get_current_container(obj); 209 UiVarEventData *event = malloc(sizeof(UiVarEventData));
128 XmString str = XmStringCreateLocalized(label); 210 event->obj = obj;
129 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 void ui_bind_radiobutton(UiObject *obj, Widget rbutton, UiInteger *value, const char *varname, ui_callback onchange, void *onchangedata, int enable_group) {
295 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, value, varname, UI_VAR_INTEGER);
296 if(var) {
297 UiInteger *value = var->value;
298 CxList *rb = value->obj;
299 if(!rb) {
300 // first button in the radiobutton group
301 // create a list for all buttons and use the list as value obj
302 rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
303 value->obj = rb;
304 value->get = ui_radiobutton_get;
305 value->set = ui_radiobutton_set;
306
307 // the first radio button is also responsible for cleanup
308 XtAddCallback(
309 rbutton,
310 XmNdestroyCallback,
311 (XtCallbackProc)destroy_list,
312 rb);
313 }
314 cxListAdd(rb, rbutton);
315
316 // set the radiobutton state, if the value is already set
317 if(cxListSize(rb) == value->value) {
318 XmToggleButtonSetState(rbutton, True, False);
319 }
320 }
321
322 // the radio button needs to handle change events to update all
323 // other buttons in the radio button group
324 UiVarEventData *event = malloc(sizeof(UiVarEventData));
325 event->obj = obj;
326 event->callback = onchange;
327 event->userdata = onchangedata;
328 event->observers = NULL;
329 event->var = var;
330 event->value = enable_group;
331 XtAddCallback(
332 rbutton,
333 XmNvalueChangedCallback,
334 (XtCallbackProc)radiobutton_changed,
335 event);
336 XtAddCallback(
337 rbutton,
338 XmNdestroyCallback,
339 (XtCallbackProc)ui_destroy_eventdata,
340 event);
341 }
342
343 UIWIDGET ui_radiobutton_create(UiObject* obj, UiToggleArgs args) {
344 Arg xargs[16];
130 int n = 0; 345 int n = 0;
131 Arg args[16]; 346
132 347 UiContainerPrivate *ctn = ui_obj_container(obj);
133 XtSetArg(args[n], XmNlabelString, str); 348 UI_APPLY_LAYOUT(ctn->layout, args);
134 n++; 349
135 XtSetArg(args[n], XmNindicatorType, XmONE_OF_MANY_ROUND); 350 Widget parent = ctn->prepare(ctn, xargs, &n);
136 n++; 351 XtSetArg(xargs[n], XmNindicatorType, XmONE_OF_MANY_ROUND); n++;
137 352 XmString label = NULL;
138 Widget parent = ct->prepare(ct, args, &n, FALSE); 353 if(args.label) {
139 Widget button = XmCreateToggleButton(parent, "radiobutton", args, n); 354 label = XmStringCreateLocalized((char*)args.label);
140 ct->add(ct, button); 355 XtSetArg(xargs[n], XmNlabelString, label); n++;
141 356 }
142 if(rgroup) { 357
143 RadioButtonGroup *group; 358 char *name = args.name ? (char*)args.name : "button";
144 if(rgroup->obj) { 359 Widget button = XmCreateToggleButton(parent, name, xargs, n);
145 group = rgroup->obj; 360 XtManageChild(button);
146 group->buttons = ucx_list_append(group->buttons, button); 361 ctn->add(ctn, button);
147 group->ref++; 362
148 } else { 363 ui_set_widget_groups(obj->ctx, button, args.groups);
149 group = malloc(sizeof(RadioButtonGroup)); 364
150 group->buttons = ucx_list_append(NULL, button); 365 UiVar* var = uic_widget_var(obj->ctx, obj->ctx, args.value, args.varname, UI_VAR_INTEGER);
151 group->current = button; 366 if(var) {
152 // this is the first button in the radiobutton group 367 UiInteger *value = var->value;
153 // so we should enable it 368 CxList *rb = value->obj;
154 Arg arg; 369 if(!rb) {
155 XtSetArg(arg, XmNset, TRUE); 370 // first button in the radiobutton group
156 XtSetValues(button, &arg, 1); 371 // create a list for all buttons and use the list as value obj
157 rgroup->obj = group; 372 rb = cxArrayListCreateSimple(CX_STORE_POINTERS, 4);
373 value->obj = rb;
374 value->get = ui_radiobutton_get;
375 value->set = ui_radiobutton_set;
158 376
159 group->current = button; 377 // the first radio button is also responsible for cleanup
160 } 378 XtAddCallback(
379 button,
380 XmNdestroyCallback,
381 (XtCallbackProc)destroy_list,
382 rb);
383 }
384 cxListAdd(rb, button);
161 385
162 RadioEventData *event = malloc(sizeof(RadioEventData)); 386 // set the radiobutton state, if the value is already set
163 event->obj = obj; 387 if(cxListSize(rb) == value->value) {
164 event->callback = NULL; 388 XmToggleButtonSetState(button, True, False);
165 event->userdata = NULL; 389 }
166 event->group = group; 390 }
167 XtAddCallback( 391
392 // the radio button needs to handle change events to update all
393 // other buttons in the radio button group
394 UiVarEventData *event = malloc(sizeof(UiVarEventData));
395 event->obj = obj;
396 event->callback = args.onchange;
397 event->userdata = args.onchangedata;
398 event->observers = NULL;
399 event->var = var;
400 event->value = args.enable_group;
401 XtAddCallback(
168 button, 402 button,
169 XmNvalueChangedCallback, 403 XmNvalueChangedCallback,
170 (XtCallbackProc)radio_callback, 404 (XtCallbackProc)radiobutton_changed,
171 event); 405 event);
172 406 XtAddCallback(
173 rgroup->get = ui_radiobutton_get; 407 button,
174 rgroup->set = ui_radiobutton_set; 408 XmNdestroyCallback,
175 } 409 (XtCallbackProc)ui_destroy_eventdata,
176 410 event);
177 XtManageChild(button); 411
412 XmStringFree(label);
178 return button; 413 return button;
179 } 414
180 415
181 int64_t ui_radiobutton_get(UiInteger *value) { 416 }
182 RadioButtonGroup *group = value->obj; 417
183 418 int64_t ui_radiobutton_get(UiInteger *i) {
184 int i = ucx_list_find(group->buttons, group->current, NULL, NULL); 419 // the UiInteger should be updated automatically by change events
185 if (i >= 0) { 420 return i->value;
186 value->value = i; 421 }
187 return i; 422
188 } else { 423 void ui_radiobutton_set(UiInteger *i, int64_t value) {
189 return 0; 424 CxList *list = i->obj;
190 } 425 if(i->value > 0) {
191 } 426 Widget current = cxListAt(list, i->value-1);
192 427 if(current) {
193 void ui_radiobutton_set(UiInteger *value, int64_t i) { 428 XmToggleButtonSetState(current, False, False);
194 RadioButtonGroup *group = value->obj; 429 }
195 Arg arg; 430 }
196 431 if(value > 0 && value <= cxListSize(list)) {
197 XtSetArg(arg, XmNset, FALSE); 432 Widget button = cxListAt(list, value-1);
198 XtSetValues(group->current, &arg, 1); 433 if(button) {
199 434 XmToggleButtonSetState(button, True, False);
200 UcxList *elm = ucx_list_get(group->buttons, i); 435 i->value = value;
201 if(elm) { 436 }
202 Widget button = elm->data; 437 }
203 XtSetArg(arg, XmNset, TRUE); 438 }
204 XtSetValues(button, &arg, 1);
205 group->current = button;
206 }
207 }

mercurial