| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2025 Olaf Wintermann. All rights reserved. |
| |
5 * |
| |
6 * Redistribution and use in source and binary forms, with or without |
| |
7 * modification, are permitted provided that the following conditions are met: |
| |
8 * |
| |
9 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 */ |
| |
28 |
| |
29 #include "entry.h" |
| |
30 |
| |
31 |
| |
32 UIWIDGET ui_spinbox_create(UiObject *obj, UiSpinBoxArgs *args) { |
| |
33 Arg xargs[16]; |
| |
34 int n = 0; |
| |
35 |
| |
36 double min = args->min; |
| |
37 double max = args->max != 0 ? args->max : 1000; |
| |
38 |
| |
39 UiVar *var = NULL; |
| |
40 UiVarType vartype = 0; |
| |
41 if(args->varname) { |
| |
42 var = uic_get_var(obj->ctx, args->varname); |
| |
43 if(var) { |
| |
44 vartype = var->type; |
| |
45 } else { |
| |
46 var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, args->varname, UI_VAR_RANGE); |
| |
47 vartype = UI_VAR_RANGE; |
| |
48 } |
| |
49 } |
| |
50 |
| |
51 if(!var) { |
| |
52 if(args->intvalue) { |
| |
53 var = uic_widget_var(obj->ctx, obj->ctx, args->intvalue, NULL, UI_VAR_INTEGER); |
| |
54 vartype = UI_VAR_INTEGER; |
| |
55 } else if(args->doublevalue) { |
| |
56 var = uic_widget_var(obj->ctx, obj->ctx, args->doublevalue, NULL, UI_VAR_DOUBLE); |
| |
57 vartype = UI_VAR_DOUBLE; |
| |
58 } else if(args->rangevalue) { |
| |
59 var = uic_widget_var(obj->ctx, obj->ctx, args->rangevalue, NULL, UI_VAR_RANGE); |
| |
60 vartype = UI_VAR_RANGE; |
| |
61 } |
| |
62 } |
| |
63 |
| |
64 if(vartype == UI_VAR_RANGE) { |
| |
65 UiRange *r = var->value; |
| |
66 min = r->min; |
| |
67 max = r->max; |
| |
68 } |
| |
69 if(args->step == 0) { |
| |
70 args->step = 1; |
| |
71 } |
| |
72 |
| |
73 UiContainerPrivate *ctn = ui_obj_container(obj); |
| |
74 UiLayout layout = UI_ARGS2LAYOUT(args); |
| |
75 |
| |
76 |
| |
77 XtSetArg(xargs[n], XmNminimumValue, 0); n++; |
| |
78 XtSetArg(xargs[n], XmNmaximumValue, 100); n++; |
| |
79 XtSetArg(xargs[n], XmNincrementValue, 1); n++; |
| |
80 XtSetArg(xargs[n], XmNspinBoxChildType, XmNUMERIC); n++; |
| |
81 |
| |
82 Widget parent = ui_container_prepare(ctn, &layout, xargs, &n); |
| |
83 |
| |
84 char *name = args->name ? (char*)args->name : "button"; |
| |
85 Widget spinbox = XmCreateSimpleSpinBox(parent, name, xargs, n); |
| |
86 XtManageChild(spinbox); |
| |
87 ui_container_add(ctn, spinbox); |
| |
88 |
| |
89 ui_set_widget_groups(obj->ctx, spinbox, args->states); |
| |
90 |
| |
91 WidgetList children; |
| |
92 Cardinal num_children; |
| |
93 unsigned char type; |
| |
94 |
| |
95 Widget textfield = NULL; |
| |
96 XtVaGetValues( |
| |
97 spinbox, |
| |
98 XmNchildren, &children, |
| |
99 XmNnumChildren, &num_children, |
| |
100 NULL); |
| |
101 |
| |
102 for(int i = 0;i<num_children;i++) { |
| |
103 XtVaGetValues(children[i], XmNspinBoxChildType, &type, NULL); |
| |
104 Widget w = children[i]; |
| |
105 if(type == XmNUMERIC) { |
| |
106 textfield = children[i]; |
| |
107 } |
| |
108 } |
| |
109 |
| |
110 UiSpinBox *data = malloc(sizeof(UiSpinBox)); |
| |
111 data->obj = obj; |
| |
112 data->textfield = textfield; |
| |
113 data->var = var; |
| |
114 data->vartype = vartype; |
| |
115 data->obs = NULL; |
| |
116 data->onchange = args->onchange; |
| |
117 data->onchangedata = args->onchangedata; |
| |
118 data->value = 0; |
| |
119 data->min = min; |
| |
120 data->max = max; |
| |
121 data->increment = args->step; |
| |
122 data->digits = args->digits; |
| |
123 |
| |
124 UiObserver **obs = NULL; |
| |
125 if(var) { |
| |
126 double value = 0; |
| |
127 switch(vartype) { |
| |
128 default: break; |
| |
129 case UI_VAR_INTEGER: { |
| |
130 UiInteger *i = var->value; |
| |
131 i->get = ui_spinbutton_getint; |
| |
132 i->set = ui_spinbutton_setint; |
| |
133 i->obj = data; |
| |
134 value = (double)i->value; |
| |
135 obs = &i->observers; |
| |
136 break; |
| |
137 } |
| |
138 case UI_VAR_DOUBLE: { |
| |
139 UiDouble *d = var->value; |
| |
140 d->get = ui_spinbutton_getdouble; |
| |
141 d->set = ui_spinbutton_setdouble; |
| |
142 d->obj = data; |
| |
143 value = d->value; |
| |
144 obs = &d->observers; |
| |
145 break; |
| |
146 } |
| |
147 case UI_VAR_RANGE: { |
| |
148 UiRange *r = var->value; |
| |
149 r->get = ui_spinbutton_getrangeval; |
| |
150 r->set = ui_spinbutton_setrangeval; |
| |
151 r->setrange = ui_spinbutton_setrange; |
| |
152 r->setextent = ui_spinbutton_setextent; |
| |
153 r->obj = data; |
| |
154 value = r->value; |
| |
155 obs = &r->observers; |
| |
156 break; |
| |
157 } |
| |
158 } |
| |
159 ui_spinbox_set_value(data, value); |
| |
160 } |
| |
161 data->obs = obs; |
| |
162 |
| |
163 XtAddCallback( |
| |
164 spinbox, |
| |
165 XmNvalueChangedCallback, |
| |
166 (XtCallbackProc)ui_spinbox_value_changed, |
| |
167 data); |
| |
168 |
| |
169 XtAddCallback( |
| |
170 spinbox, |
| |
171 XmNdestroyCallback, |
| |
172 (XtCallbackProc)ui_destroy_data, |
| |
173 data); |
| |
174 |
| |
175 XmTextFieldSetString(textfield, "0"); |
| |
176 |
| |
177 |
| |
178 return spinbox; |
| |
179 } |
| |
180 |
| |
181 void ui_spinbox_set_value(UiSpinBox *spinbox, double value) { |
| |
182 if(value < spinbox->min) { |
| |
183 value = spinbox->min; |
| |
184 } |
| |
185 if(value > spinbox->max) { |
| |
186 value = spinbox->max; |
| |
187 } |
| |
188 |
| |
189 char buf[32]; |
| |
190 snprintf(buf, 32, "%.*f", spinbox->digits, spinbox->value); |
| |
191 XmTextFieldSetString(spinbox->textfield, buf); |
| |
192 spinbox->value = value; |
| |
193 } |
| |
194 |
| |
195 void ui_spinbox_value_changed(Widget widget, UiSpinBox *spinbox, XmSpinBoxCallbackStruct *cb) { |
| |
196 Boolean update_value = TRUE; |
| |
197 double value = spinbox->value; |
| |
198 switch(cb->reason) { |
| |
199 case XmCR_OK: { |
| |
200 update_value = FALSE; |
| |
201 break; |
| |
202 } |
| |
203 case XmCR_SPIN_NEXT: { |
| |
204 value += spinbox->increment; |
| |
205 break; |
| |
206 } |
| |
207 case XmCR_SPIN_PRIOR: { |
| |
208 value -= spinbox->increment; |
| |
209 break; |
| |
210 } |
| |
211 } |
| |
212 |
| |
213 if(update_value) { |
| |
214 ui_spinbox_set_value(spinbox, value); |
| |
215 |
| |
216 UiEvent event; |
| |
217 event.obj = spinbox->obj; |
| |
218 event.window = event.obj->window; |
| |
219 event.document = event.obj->ctx->document; |
| |
220 event.eventdata = NULL; |
| |
221 event.eventdatatype = 0; |
| |
222 event.intval = (int64_t)value; |
| |
223 event.set = ui_get_setop(); |
| |
224 |
| |
225 if(spinbox->onchange) { |
| |
226 spinbox->onchange(&event, spinbox->onchangedata); |
| |
227 } |
| |
228 |
| |
229 UiObserver *obs = *spinbox->obs; |
| |
230 ui_notify_evt(*spinbox->obs, &event); |
| |
231 } |
| |
232 } |
| |
233 |
| |
234 int64_t ui_spinbutton_getint(UiInteger *i) { |
| |
235 UiSpinBox *spinbox = i->obj; |
| |
236 i->value = (int64_t)spinbox->value; |
| |
237 return i->value; |
| |
238 } |
| |
239 |
| |
240 void ui_spinbutton_setint(UiInteger *i, int64_t val) { |
| |
241 UiSpinBox *spinbox = i->obj; |
| |
242 ui_spinbox_set_value(spinbox, (double)val); |
| |
243 i->value = spinbox->value; |
| |
244 } |
| |
245 |
| |
246 double ui_spinbutton_getdouble(UiDouble *d) { |
| |
247 UiSpinBox *spinbox = d->obj; |
| |
248 d->value = spinbox->value; |
| |
249 return d->value; |
| |
250 } |
| |
251 |
| |
252 void ui_spinbutton_setdouble(UiDouble *d, double val) { |
| |
253 UiSpinBox *spinbox = d->obj; |
| |
254 ui_spinbox_set_value(spinbox, val); |
| |
255 d->value = spinbox->value; |
| |
256 } |
| |
257 |
| |
258 double ui_spinbutton_getrangeval(UiRange *r) { |
| |
259 UiSpinBox *spinbox = r->obj; |
| |
260 r->value = spinbox->value; |
| |
261 return r->value; |
| |
262 } |
| |
263 |
| |
264 void ui_spinbutton_setrangeval(UiRange *r, double val) { |
| |
265 UiSpinBox *spinbox = r->obj; |
| |
266 ui_spinbox_set_value(spinbox, val); |
| |
267 r->value = spinbox->value; |
| |
268 } |
| |
269 void ui_spinbutton_setrange(UiRange *r, double min, double max) { |
| |
270 UiSpinBox *spinbox = r->obj; |
| |
271 spinbox->min = min; |
| |
272 spinbox->max = max; |
| |
273 r->min = min; |
| |
274 r->max = max; |
| |
275 } |
| |
276 |
| |
277 void ui_spinbutton_setextent(UiRange *r, double extent) { |
| |
278 UiSpinBox *spinbox = r->obj; |
| |
279 spinbox->increment = extent; |
| |
280 r->extent = extent; |
| |
281 } |