1 | // =============================================================== // |
---|
2 | // // |
---|
3 | // File : AW_button.cxx // |
---|
4 | // Purpose : // |
---|
5 | // // |
---|
6 | // Institute of Microbiology (Technical University Munich) // |
---|
7 | // http://www.arb-home.de/ // |
---|
8 | // // |
---|
9 | // =============================================================== // |
---|
10 | |
---|
11 | #include "aw_at.hxx" |
---|
12 | #include "aw_select.hxx" |
---|
13 | #include "aw_awar.hxx" |
---|
14 | #include "aw_window_Xm.hxx" |
---|
15 | #include "aw_msg.hxx" |
---|
16 | #include "aw_root.hxx" |
---|
17 | #include "aw_xargs.hxx" |
---|
18 | #include "aw_varupdate.hxx" |
---|
19 | #include "aw_localdef.hxx" |
---|
20 | |
---|
21 | #include <arb_algo.h> |
---|
22 | #include <ad_cb.h> |
---|
23 | |
---|
24 | #include <Xm/Frame.h> |
---|
25 | #include <Xm/ToggleB.h> |
---|
26 | #include <Xm/Label.h> |
---|
27 | #include <Xm/List.h> |
---|
28 | #include <Xm/PushB.h> |
---|
29 | #include <Xm/Text.h> |
---|
30 | #include <Xm/TextF.h> |
---|
31 | #include <Xm/ScrolledW.h> |
---|
32 | #include <Xm/Scale.h> |
---|
33 | |
---|
34 | void AW_variable_update_callback(Widget /*wgt*/, XtPointer variable_update_struct, XtPointer call_data) { |
---|
35 | VarUpdateInfo *vui = (VarUpdateInfo *) variable_update_struct; |
---|
36 | aw_assert(vui); |
---|
37 | |
---|
38 | vui->change_from_widget(call_data); |
---|
39 | } |
---|
40 | |
---|
41 | struct TrackedAwarChange { |
---|
42 | AW_awar *awar; |
---|
43 | bool changed; |
---|
44 | |
---|
45 | TrackedAwarChange(AW_awar *awar_) : awar(awar_), changed(false) {} |
---|
46 | }; |
---|
47 | |
---|
48 | static void track_awar_change_cb(GBDATA *IF_ASSERTION_USED(gbd), TrackedAwarChange *tac, GB_CB_TYPE IF_ASSERTION_USED(cb_type)) { |
---|
49 | aw_assert(cb_type == GB_CB_CHANGED); |
---|
50 | aw_assert(tac->awar->gb_var == gbd); |
---|
51 | tac->changed = true; |
---|
52 | } |
---|
53 | |
---|
54 | #define SCALER_MIN_VALUE 0 |
---|
55 | #define SCALER_MAX_VALUE 1000 |
---|
56 | |
---|
57 | static int calculate_scaler_value(AW_awar *awar, AW_ScalerType scalerType) { |
---|
58 | float modAwarRel = AW_ScalerTransformer(scalerType).awar2scaler(awar); |
---|
59 | int scalerValue = SCALER_MIN_VALUE + modAwarRel * (SCALER_MAX_VALUE-SCALER_MIN_VALUE); |
---|
60 | |
---|
61 | return scalerValue; |
---|
62 | } |
---|
63 | |
---|
64 | static void write_scalervalue_to_awar(int scalerVal, AW_awar *awar, AW_ScalerType scalerType) { |
---|
65 | float scaleRel = scalerVal/double(SCALER_MAX_VALUE-SCALER_MIN_VALUE); |
---|
66 | aw_assert(scaleRel>=0.0 && scaleRel<=1.0); |
---|
67 | |
---|
68 | float aval = AW_ScalerTransformer(scalerType).scaler2awar(scaleRel, awar); |
---|
69 | |
---|
70 | switch (awar->variable_type) { |
---|
71 | case AW_FLOAT: |
---|
72 | awar->write_float(aval); |
---|
73 | break; |
---|
74 | |
---|
75 | case AW_INT: |
---|
76 | awar->write_int(aval); |
---|
77 | break; |
---|
78 | |
---|
79 | default: |
---|
80 | GBK_terminatef("Unsupported awar type %i in write_scalervalue_to_awar", int(awar->variable_type)); |
---|
81 | break; |
---|
82 | } |
---|
83 | } |
---|
84 | |
---|
85 | void VarUpdateInfo::change_from_widget(XtPointer call_data) { |
---|
86 | AW_cb::useraction_init(); |
---|
87 | |
---|
88 | GB_ERROR error = NULp; |
---|
89 | AW_root *root = awar->root; |
---|
90 | |
---|
91 | if (root->value_changed) { |
---|
92 | root->changer_of_variable = widget; |
---|
93 | } |
---|
94 | |
---|
95 | TrackedAwarChange tac(awar); |
---|
96 | if (root->is_tracking()) { |
---|
97 | // add a callback which writes macro-code (BEFORE any other callback happens; last added, first calledback) |
---|
98 | GB_transaction ta(awar->gb_var); |
---|
99 | GB_add_callback(awar->gb_var, GB_CB_CHANGED, makeDatabaseCallback(track_awar_change_cb, &tac)); |
---|
100 | } |
---|
101 | |
---|
102 | bool run_cb = true; |
---|
103 | switch (widget_type) { |
---|
104 | case AW_WIDGET_INPUT_FIELD: |
---|
105 | case AW_WIDGET_TEXT_FIELD: |
---|
106 | if (!root->value_changed) { |
---|
107 | run_cb = false; |
---|
108 | } |
---|
109 | else { |
---|
110 | char *new_text = XmTextGetString((widget)); |
---|
111 | error = awar->write_as_string(new_text); |
---|
112 | XtFree(new_text); |
---|
113 | } |
---|
114 | break; |
---|
115 | |
---|
116 | case AW_WIDGET_TOGGLE: |
---|
117 | root->changer_of_variable = NULp; |
---|
118 | error = awar->toggle_toggle(); |
---|
119 | break; |
---|
120 | |
---|
121 | case AW_WIDGET_TOGGLE_FIELD: |
---|
122 | if (XmToggleButtonGetState(widget) == False) break; // no toggle is selected (?) |
---|
123 | // fall-through |
---|
124 | case AW_WIDGET_CHOICE_MENU: |
---|
125 | error = value.write_to(awar); |
---|
126 | break; |
---|
127 | |
---|
128 | case AW_WIDGET_SELECTION_LIST: { |
---|
129 | AW_selection_list_entry *entry = ts.sellist->list_table; |
---|
130 | { |
---|
131 | int incr = reinterpret_cast<XmListCallbackStruct*>(call_data)->item_position; // [1..N] |
---|
132 | while (entry && --incr>0) entry = entry->next; |
---|
133 | } |
---|
134 | if (!entry) { |
---|
135 | entry = ts.sellist->default_select; // use default selection |
---|
136 | if (!entry) GBK_terminate("no default specified for selection list"); // or die |
---|
137 | } |
---|
138 | entry->get_value().write_to(awar); |
---|
139 | break; |
---|
140 | } |
---|
141 | |
---|
142 | case AW_WIDGET_LABEL_FIELD: |
---|
143 | break; |
---|
144 | |
---|
145 | case AW_WIDGET_SCALER: { |
---|
146 | XmScaleCallbackStruct *xms = (XmScaleCallbackStruct*)call_data; |
---|
147 | write_scalervalue_to_awar(xms->value, awar, ts.scalerType); |
---|
148 | break; |
---|
149 | } |
---|
150 | default: |
---|
151 | GBK_terminatef("Unknown widget type %i in AW_variable_update_callback", widget_type); |
---|
152 | break; |
---|
153 | } |
---|
154 | |
---|
155 | if (root->is_tracking()) { |
---|
156 | { |
---|
157 | GB_transaction ta(awar->gb_var); |
---|
158 | GB_remove_callback(awar->gb_var, GB_CB_CHANGED, makeDatabaseCallback(track_awar_change_cb, &tac)); |
---|
159 | } |
---|
160 | if (tac.changed) { |
---|
161 | root->track_awar_change(awar); |
---|
162 | } |
---|
163 | } |
---|
164 | |
---|
165 | if (error) { |
---|
166 | root->changer_of_variable = NULp; |
---|
167 | awar->update(); |
---|
168 | aw_message(error); |
---|
169 | } |
---|
170 | else { |
---|
171 | if (cbs && run_cb) cbs->run_callbacks(); // @@@ generally unwanted callback (see #559) |
---|
172 | root->value_changed = false; |
---|
173 | |
---|
174 | aw_message_if(GB_incur_error()); // show error exported by awar-change-callback |
---|
175 | } |
---|
176 | |
---|
177 | AW_cb::useraction_done(aw_parent); |
---|
178 | } |
---|
179 | |
---|
180 | |
---|
181 | static void AW_value_changed_callback(Widget /*wgt*/, XtPointer rooti, XtPointer /*call_data*/) { |
---|
182 | AW_root *root = (AW_root *)rooti; |
---|
183 | root->value_changed = true; |
---|
184 | } |
---|
185 | |
---|
186 | void aw_attach_widget(Widget w, AW_at *_at, int default_width) { |
---|
187 | short height = 0; |
---|
188 | short width = 0; |
---|
189 | if (!_at->to_position_exists) { |
---|
190 | XtVaGetValues(w, XmNheight, &height, XmNwidth, &width, NULp); |
---|
191 | if (default_width >0) width = default_width; |
---|
192 | |
---|
193 | switch (_at->correct_for_at_center) { |
---|
194 | case 0: // left justified |
---|
195 | _at->to_position_x = _at->x_for_next_button + width; |
---|
196 | break; |
---|
197 | case 1: // centered |
---|
198 | _at->to_position_x = _at->x_for_next_button + width/2; |
---|
199 | _at->x_for_next_button -= width/2; |
---|
200 | break; |
---|
201 | case 2: // right justified |
---|
202 | _at->to_position_x = _at->x_for_next_button; |
---|
203 | _at->x_for_next_button -= width; |
---|
204 | break; |
---|
205 | } |
---|
206 | _at->to_position_y = _at->y_for_next_button + height; |
---|
207 | _at->attach_x = _at->attach_lx; |
---|
208 | _at->attach_y = _at->attach_ly; |
---|
209 | } |
---|
210 | |
---|
211 | #define MIN_RIGHT_OFFSET 10 |
---|
212 | #define MIN_BOTTOM_OFFSET 10 |
---|
213 | |
---|
214 | aw_xargs args(4*2); |
---|
215 | |
---|
216 | if (_at->attach_x) { |
---|
217 | int right_offset = _at->max_x_size - _at->to_position_x; |
---|
218 | if (right_offset<MIN_RIGHT_OFFSET) { |
---|
219 | right_offset = MIN_RIGHT_OFFSET; |
---|
220 | _at->max_x_size = _at->to_position_x+right_offset; |
---|
221 | } |
---|
222 | |
---|
223 | args.add(XmNrightAttachment, XmATTACH_FORM); |
---|
224 | args.add(XmNrightOffset, right_offset); |
---|
225 | } |
---|
226 | else { |
---|
227 | args.add(XmNrightAttachment, XmATTACH_OPPOSITE_FORM); |
---|
228 | args.add(XmNrightOffset, -_at->to_position_x); |
---|
229 | } |
---|
230 | |
---|
231 | if (_at->attach_lx) { |
---|
232 | args.add(XmNleftAttachment, XmATTACH_NONE); |
---|
233 | args.add(XmNwidth, _at->to_position_x - _at->x_for_next_button); |
---|
234 | } |
---|
235 | else { |
---|
236 | args.add(XmNleftAttachment, XmATTACH_FORM); |
---|
237 | args.add(XmNleftOffset, _at->x_for_next_button); |
---|
238 | } |
---|
239 | |
---|
240 | if (_at->attach_y) { |
---|
241 | int bottom_offset = _at->max_y_size - _at->to_position_y; |
---|
242 | if (bottom_offset<MIN_BOTTOM_OFFSET) { |
---|
243 | bottom_offset = MIN_BOTTOM_OFFSET; |
---|
244 | _at->max_y_size = _at->to_position_y+bottom_offset; |
---|
245 | } |
---|
246 | |
---|
247 | args.add(XmNbottomAttachment, XmATTACH_FORM); |
---|
248 | args.add(XmNbottomOffset, bottom_offset); |
---|
249 | } |
---|
250 | else { |
---|
251 | args.add(XmNbottomAttachment, XmATTACH_OPPOSITE_FORM); |
---|
252 | args.add(XmNbottomOffset, - _at->to_position_y); |
---|
253 | } |
---|
254 | if (_at->attach_ly) { |
---|
255 | args.add(XmNtopAttachment, XmATTACH_NONE); |
---|
256 | args.add(XmNheight, _at->to_position_y - _at->y_for_next_button); |
---|
257 | } |
---|
258 | else { |
---|
259 | args.add(XmNtopAttachment, XmATTACH_FORM); |
---|
260 | args.add(XmNtopOffset, _at->y_for_next_button); |
---|
261 | } |
---|
262 | |
---|
263 | args.assign_to_widget(w); |
---|
264 | } |
---|
265 | |
---|
266 | const char *AW_get_pixmapPath(const char *pixmapName) { |
---|
267 | const char *pixmapsDir = "motifHack/pixmaps"; // see ../lib/motifHack/README |
---|
268 | |
---|
269 | return GB_concat_path_in_ARBLIB(pixmapsDir, pixmapName); |
---|
270 | } |
---|
271 | |
---|
272 | static char *pixmapPath(const char *pixmapName) { |
---|
273 | return nulldup(AW_get_pixmapPath(pixmapName)); |
---|
274 | } |
---|
275 | |
---|
276 | |
---|
277 | #define MAX_LINE_LENGTH 200 |
---|
278 | __ATTR__USERESULT static GB_ERROR detect_bitmap_size(const char *pixmapname, int *width, int *height) { |
---|
279 | GB_ERROR err = NULp; |
---|
280 | |
---|
281 | *width = 0; |
---|
282 | *height = 0; |
---|
283 | |
---|
284 | char *path = pixmapPath(pixmapname); |
---|
285 | FILE *in = fopen(path, "rt"); |
---|
286 | if (in) { |
---|
287 | const char *subdir = strrchr(pixmapname, '/'); |
---|
288 | char *name = strdup(subdir ? subdir+1 : pixmapname); |
---|
289 | { |
---|
290 | char *dot = strrchr(name, '.'); |
---|
291 | if (dot) dot[0] = 0; |
---|
292 | else err = "'.' expected"; |
---|
293 | } |
---|
294 | int namelen = strlen(name); |
---|
295 | char buffer[MAX_LINE_LENGTH]; |
---|
296 | bool done = false; |
---|
297 | |
---|
298 | while (!done && !err) { |
---|
299 | if (!fgets(buffer, MAX_LINE_LENGTH, in)) { |
---|
300 | err = GB_IO_error("reading", pixmapname); |
---|
301 | } |
---|
302 | else if (strchr(buffer, 0)[-1] != '\n') { |
---|
303 | err = GBS_global_string("Line too long ('%s')", buffer); // increase MAX_LINE_LENGTH above |
---|
304 | } |
---|
305 | else if (strncmp(buffer, "#define", 7) != 0) { |
---|
306 | done = true; |
---|
307 | } |
---|
308 | else { |
---|
309 | char *name_pos = strstr(buffer+7, name); |
---|
310 | if (name_pos) { |
---|
311 | char *behind = name_pos+namelen; |
---|
312 | if (strncmp(behind, "_width ", 7) == 0) *width = atoi(behind+7); |
---|
313 | else if (strncmp(behind, "_height ", 8) == 0) *height = atoi(behind+8); |
---|
314 | } |
---|
315 | } |
---|
316 | } |
---|
317 | |
---|
318 | if (done && ((*width == 0) || (*height == 0))) { |
---|
319 | if (strstr(buffer, "XPM")) { |
---|
320 | if (!fgets(buffer, MAX_LINE_LENGTH, in) || !fgets(buffer, MAX_LINE_LENGTH, in)) { |
---|
321 | err = GB_IO_error("reading", pixmapname); |
---|
322 | } |
---|
323 | else { |
---|
324 | char *temp = strtok(buffer+1, " "); |
---|
325 | *width = atoi(temp); |
---|
326 | temp = strtok(NULp, " "); |
---|
327 | *height = atoi(temp); |
---|
328 | } |
---|
329 | } |
---|
330 | else { |
---|
331 | err = "can't detect size"; |
---|
332 | } |
---|
333 | } |
---|
334 | |
---|
335 | free(name); |
---|
336 | fclose(in); |
---|
337 | } |
---|
338 | else { |
---|
339 | err = "no such file"; |
---|
340 | } |
---|
341 | |
---|
342 | if (err) { |
---|
343 | err = GBS_global_string("%s: %s", pixmapname, err); |
---|
344 | } |
---|
345 | |
---|
346 | #if defined(DUMP_BUTTON_CREATION) |
---|
347 | printf("Bitmap '%s' has size %i/%i\n", pixmapname, *width, *height); |
---|
348 | #endif // DUMP_BUTTON_CREATION |
---|
349 | |
---|
350 | free(path); |
---|
351 | return err; |
---|
352 | } |
---|
353 | #undef MAX_LINE_LENGTH |
---|
354 | |
---|
355 | inline void calculate_textsize(const char *str, int *width, int *height) { |
---|
356 | int textwidth = 0; |
---|
357 | int textheight = 1; |
---|
358 | int linewidth = 0; |
---|
359 | |
---|
360 | for (int p = 0; str[p]; ++p) { |
---|
361 | if (str[p] == '\n') { |
---|
362 | if (linewidth>textwidth) textwidth = linewidth; |
---|
363 | |
---|
364 | linewidth = 0; |
---|
365 | textheight++; |
---|
366 | } |
---|
367 | else { |
---|
368 | linewidth++; |
---|
369 | } |
---|
370 | } |
---|
371 | |
---|
372 | if (linewidth>textwidth) textwidth = linewidth; |
---|
373 | |
---|
374 | *width = textwidth; |
---|
375 | *height = textheight; |
---|
376 | } |
---|
377 | |
---|
378 | void AW_window::calculate_label_size(int *width, int *height, bool in_pixel) { |
---|
379 | // in_pixel == true -> calculate size in pixels |
---|
380 | // in_pixel == false -> calculate size in characters |
---|
381 | |
---|
382 | const char *label_ = _at->label_for_inputfield; |
---|
383 | if (label_) { |
---|
384 | aw_assert(label_[0]); // empty string is not allowed as label! |
---|
385 | |
---|
386 | calculate_textsize(label_, width, height); |
---|
387 | if (_at->length_of_label_for_inputfield) { |
---|
388 | *width = _at->length_of_label_for_inputfield; |
---|
389 | } |
---|
390 | if (in_pixel) { |
---|
391 | *width = calculate_string_width(*width); |
---|
392 | *height = calculate_string_height(*height, 0); |
---|
393 | } |
---|
394 | } |
---|
395 | else { |
---|
396 | *width = 0; |
---|
397 | *height = 0; |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | Widget AW_window::get_last_widget() const { |
---|
402 | return p_global->get_last_widget(); |
---|
403 | } |
---|
404 | |
---|
405 | void aw_detect_text_size(const char *text, size_t& width, size_t& height) { |
---|
406 | size_t text_width = strcspn(text, "\n"); |
---|
407 | |
---|
408 | if (text[text_width]) { |
---|
409 | aw_assert(text[text_width] == '\n'); |
---|
410 | |
---|
411 | aw_detect_text_size(text+text_width+1, width, height); |
---|
412 | if (text_width>width) width = text_width; |
---|
413 | height++; |
---|
414 | } |
---|
415 | else { // EOS |
---|
416 | width = text_width; |
---|
417 | height = 1; |
---|
418 | } |
---|
419 | } |
---|
420 | |
---|
421 | void AW_window::create_autosize_button(const char *macro_name, AW_label buttonlabel, const char *mnemonic, unsigned xtraSpace) { |
---|
422 | aw_assert(!AW_IS_IMAGEREF(buttonlabel)); // use create_button for graphical buttons! |
---|
423 | aw_assert(!_at->to_position_exists); // wont work if to-position exists |
---|
424 | |
---|
425 | AW_awar *is_awar = get_root()->label_is_awar(buttonlabel); |
---|
426 | size_t width, height; |
---|
427 | if (is_awar) { |
---|
428 | char *content = is_awar->read_as_string(); |
---|
429 | aw_assert(content[0]); /* you need to fill the awar before calling create_autosize_button, |
---|
430 | * otherwise size cannot be detected */ |
---|
431 | aw_detect_text_size(content, width, height); |
---|
432 | } |
---|
433 | else { |
---|
434 | aw_detect_text_size(buttonlabel, width, height); |
---|
435 | } |
---|
436 | |
---|
437 | int len = width+(xtraSpace*2); |
---|
438 | short length_of_buttons = _at->length_of_buttons; |
---|
439 | short height_of_buttons = _at->height_of_buttons; |
---|
440 | |
---|
441 | _at->length_of_buttons = len+1; |
---|
442 | _at->height_of_buttons = height; |
---|
443 | create_button(macro_name, buttonlabel, mnemonic); |
---|
444 | _at->length_of_buttons = length_of_buttons; |
---|
445 | _at->height_of_buttons = height_of_buttons; |
---|
446 | } |
---|
447 | |
---|
448 | void AW_window::create_button(const char *macro_name, AW_label buttonlabel, const char */*mnemonic*/, const char *color) { |
---|
449 | // Create a button or text display. |
---|
450 | // |
---|
451 | // If a callback is bound via at->callback(), a button is created. |
---|
452 | // Otherwise a text display is created. |
---|
453 | // |
---|
454 | // if buttonlabel starts with '#' the rest of buttonlabel is used as name of pixmap file used for button |
---|
455 | // if buttonlabel contains a '/' it's interpreted as AWAR name and the button displays the content of the awar |
---|
456 | // otherwise buttonlabel is interpreted as button label (may contain '\n'). |
---|
457 | // |
---|
458 | // Note 1: Button width 0 does not work together with labels! |
---|
459 | |
---|
460 | // Note 2: "color" may be specified for the button background (see TuneOrSetBackground for details) |
---|
461 | |
---|
462 | TuneOrSetBackground(_at->attach_any ? INFO_FORM : INFO_WIDGET, // set background for buttons / text displays |
---|
463 | color, |
---|
464 | _callback ? TUNE_BUTTON : 0); |
---|
465 | |
---|
466 | #if defined(DUMP_BUTTON_CREATION) |
---|
467 | printf("------------------------------ Button '%s'\n", buttonlabel); |
---|
468 | printf("x_for_next_button=%i y_for_next_button=%i\n", _at->x_for_next_button, _at->y_for_next_button); |
---|
469 | #endif // DUMP_BUTTON_CREATION |
---|
470 | |
---|
471 | if (_callback && ((long)_callback != 1)) { |
---|
472 | if (macro_name) { |
---|
473 | _callback->id = GBS_global_string_copy("%s/%s", this->window_defaults_name, macro_name); |
---|
474 | get_root()->define_remote_command(_callback); |
---|
475 | } |
---|
476 | else { |
---|
477 | _callback->id = NULp; |
---|
478 | } |
---|
479 | } |
---|
480 | #if defined(DEVEL_RALF) && 1 |
---|
481 | else { |
---|
482 | aw_assert(!macro_name); // please pass NULp for buttons w/o callback |
---|
483 | } |
---|
484 | #endif |
---|
485 | |
---|
486 | #define SPACE_BEHIND_LABEL 10 |
---|
487 | |
---|
488 | #define BUTTON_TEXT_X_PADDING 4 |
---|
489 | #define BUTTON_TEXT_Y_PADDING 10 |
---|
490 | |
---|
491 | #define BUTTON_GRAPHIC_PADDING 12 |
---|
492 | #define FLAT_GRAPHIC_PADDING 4 // for buttons w/o callback |
---|
493 | |
---|
494 | bool is_graphical_button = AW_IS_IMAGEREF(buttonlabel); |
---|
495 | |
---|
496 | #if defined(ASSERTION_USED) |
---|
497 | AW_awar *is_awar = is_graphical_button ? NULp : get_root()->label_is_awar(buttonlabel); |
---|
498 | #endif // ASSERTION_USED |
---|
499 | |
---|
500 | int width_of_button = -1, height_of_button = -1; |
---|
501 | |
---|
502 | int width_of_label, height_of_label; |
---|
503 | calculate_label_size(&width_of_label, &height_of_label, true); |
---|
504 | int width_of_label_and_spacer = _at->label_for_inputfield ? width_of_label+SPACE_BEHIND_LABEL : 0; |
---|
505 | |
---|
506 | bool let_motif_choose_size = false; |
---|
507 | |
---|
508 | if (_at->to_position_exists) { // size has explicitly been specified in xfig -> calculate |
---|
509 | width_of_button = _at->to_position_x - _at->x_for_next_button - width_of_label_and_spacer; |
---|
510 | height_of_button = _at->to_position_y - _at->y_for_next_button; |
---|
511 | } |
---|
512 | else if (_at->length_of_buttons) { // button width specified from client code |
---|
513 | width_of_button = BUTTON_TEXT_X_PADDING + calculate_string_width(_at->length_of_buttons+1); |
---|
514 | |
---|
515 | if (!is_graphical_button) { |
---|
516 | if (_at->height_of_buttons) { // button height specified from client code |
---|
517 | height_of_button = BUTTON_TEXT_Y_PADDING + calculate_string_height(_at->height_of_buttons, 0); |
---|
518 | } |
---|
519 | else { |
---|
520 | int textwidth, textheight; |
---|
521 | calculate_textsize(buttonlabel, &textwidth, &textheight); |
---|
522 | height_of_button = BUTTON_TEXT_Y_PADDING + calculate_string_height(textheight, 0); |
---|
523 | } |
---|
524 | } |
---|
525 | else { |
---|
526 | height_of_button = BUTTON_TEXT_Y_PADDING + calculate_string_height(1, 0); |
---|
527 | } |
---|
528 | } |
---|
529 | else { // no button_length() specified |
---|
530 | aw_assert(!is_awar); // please specify button_length() for AWAR button! |
---|
531 | |
---|
532 | if (is_graphical_button) { |
---|
533 | int width, height; |
---|
534 | GB_ERROR err = detect_bitmap_size(buttonlabel+1, &width, &height); |
---|
535 | |
---|
536 | if (!err) { |
---|
537 | int gpadding = _callback ? BUTTON_GRAPHIC_PADDING : FLAT_GRAPHIC_PADDING; |
---|
538 | |
---|
539 | width_of_button = width+gpadding; |
---|
540 | height_of_button = height+gpadding; |
---|
541 | } |
---|
542 | else { |
---|
543 | err = GBS_global_string("button gfx error: %s", err); |
---|
544 | aw_message(err); |
---|
545 | let_motif_choose_size = true; |
---|
546 | } |
---|
547 | } |
---|
548 | else { |
---|
549 | int textwidth, textheight; |
---|
550 | calculate_textsize(buttonlabel, &textwidth, &textheight); |
---|
551 | |
---|
552 | width_of_button = BUTTON_TEXT_X_PADDING + calculate_string_width(textwidth+1); |
---|
553 | height_of_button = BUTTON_TEXT_Y_PADDING + calculate_string_height(textheight, 0); |
---|
554 | } |
---|
555 | } |
---|
556 | |
---|
557 | if (!let_motif_choose_size) { |
---|
558 | if (height_of_button<height_of_label) height_of_button = height_of_label; |
---|
559 | aw_assert(width_of_button && height_of_button); |
---|
560 | } |
---|
561 | |
---|
562 | int x_label = _at->x_for_next_button; |
---|
563 | int y_label = _at->y_for_next_button; |
---|
564 | int x_button = x_label + width_of_label_and_spacer; |
---|
565 | int y_button = y_label; |
---|
566 | |
---|
567 | int org_correct_for_at_center = _at->correct_for_at_center; // store original justification |
---|
568 | int org_y_for_next_button = _at->y_for_next_button; // store original y pos (modified while creating label) |
---|
569 | |
---|
570 | if (!let_motif_choose_size) { // don't correct position of button w/o known size |
---|
571 | // calculate justification manually |
---|
572 | |
---|
573 | int width_of_button_and_highlight = width_of_button + (_at->highlight ? 2*(_at->shadow_thickness+1)+1 : 0); |
---|
574 | int width_of_label_and_button = width_of_label_and_spacer+width_of_button_and_highlight; |
---|
575 | |
---|
576 | if (_at->correct_for_at_center) { // not if left justified |
---|
577 | int shiftback = width_of_label_and_button; // shiftback for right justification |
---|
578 | if (_at->correct_for_at_center == 1) { // center justification |
---|
579 | shiftback /= 2; |
---|
580 | } |
---|
581 | x_label -= shiftback; |
---|
582 | x_button -= shiftback; |
---|
583 | } |
---|
584 | |
---|
585 | // we already did the justification by calculating all positions manually, so.. |
---|
586 | _at->correct_for_at_center = 0; // ..from now on act like "left justified"! |
---|
587 | } |
---|
588 | |
---|
589 | // correct label Y position |
---|
590 | if (_callback) { // only if button is a real 3D-button |
---|
591 | y_label += (height_of_button-height_of_label)/2; |
---|
592 | } |
---|
593 | |
---|
594 | Widget parent_widget = (_at->attach_any) ? INFO_FORM : INFO_WIDGET; |
---|
595 | Widget label_widget = NULp; |
---|
596 | |
---|
597 | if (_at->label_for_inputfield) { |
---|
598 | _at->x_for_next_button = x_label; |
---|
599 | _at->y_for_next_button = y_label; |
---|
600 | |
---|
601 | Label clientlabel(_at->label_for_inputfield, this); |
---|
602 | label_widget = XtVaCreateManagedWidget("label", |
---|
603 | xmLabelWidgetClass, |
---|
604 | parent_widget, |
---|
605 | XmNwidth, (int)(width_of_label + 2), |
---|
606 | RES_LABEL_CONVERT(clientlabel), |
---|
607 | XmNrecomputeSize, false, |
---|
608 | XmNalignment, XmALIGNMENT_BEGINNING, |
---|
609 | XmNfontList, p_global->fontlist, |
---|
610 | XmNx, (int)(x_label), |
---|
611 | XmNy, (int)(y_label), |
---|
612 | NULp); |
---|
613 | |
---|
614 | if (_at->attach_any) aw_attach_widget(label_widget, _at); |
---|
615 | AW_label_in_awar_list(this, label_widget, _at->label_for_inputfield); |
---|
616 | } |
---|
617 | |
---|
618 | _at->x_for_next_button = x_button; |
---|
619 | _at->y_for_next_button = y_button; |
---|
620 | |
---|
621 | Widget fatherwidget = parent_widget; // used as father for button below |
---|
622 | if (_at->highlight) { |
---|
623 | if (_at->attach_any) { |
---|
624 | #if defined(DEBUG) |
---|
625 | printf("Attaching highlighted buttons does not work - " |
---|
626 | "highlight ignored for button '%s'!\n", buttonlabel); |
---|
627 | #endif // DEBUG |
---|
628 | _at->highlight = false; |
---|
629 | } |
---|
630 | else { |
---|
631 | int shadow_offset = _at->shadow_thickness; |
---|
632 | int x_shadow = x_button - shadow_offset; |
---|
633 | int y_shadow = y_button - shadow_offset; |
---|
634 | |
---|
635 | fatherwidget = XtVaCreateManagedWidget("draw_area", |
---|
636 | xmFrameWidgetClass, |
---|
637 | INFO_WIDGET, |
---|
638 | XmNx, (int)(x_shadow), |
---|
639 | XmNy, (int)(y_shadow), |
---|
640 | XmNshadowType, XmSHADOW_IN, |
---|
641 | XmNshadowThickness, _at->shadow_thickness, |
---|
642 | NULp); |
---|
643 | } |
---|
644 | } |
---|
645 | |
---|
646 | Widget button = NULp; |
---|
647 | |
---|
648 | { |
---|
649 | aw_xargs args(9); |
---|
650 | args.add(XmNx, x_button); |
---|
651 | args.add(XmNy, y_button); |
---|
652 | |
---|
653 | args.add(XmNfontList, (XtArgVal)p_global->fontlist); |
---|
654 | args.add(XmNbackground, _at->background_color); |
---|
655 | |
---|
656 | if (!let_motif_choose_size) { |
---|
657 | args.add(XmNwidth, width_of_button); |
---|
658 | args.add(XmNheight, height_of_button); |
---|
659 | } |
---|
660 | |
---|
661 | Label buttonLabel(buttonlabel, this); |
---|
662 | if (_callback) { |
---|
663 | args.add(XmNshadowThickness, _at->shadow_thickness); |
---|
664 | args.add(XmNalignment, XmALIGNMENT_CENTER); |
---|
665 | |
---|
666 | button = XtVaCreateManagedWidget("button", xmPushButtonWidgetClass, fatherwidget, RES_LABEL_CONVERT(buttonLabel), NULp); |
---|
667 | } |
---|
668 | else { // button w/o callback; (flat, not clickable) |
---|
669 | button = XtVaCreateManagedWidget("label", xmLabelWidgetClass, parent_widget, RES_LABEL_CONVERT(buttonLabel), NULp); |
---|
670 | args.add(XmNalignment, (org_correct_for_at_center == 1) ? XmALIGNMENT_CENTER : XmALIGNMENT_BEGINNING); |
---|
671 | } |
---|
672 | |
---|
673 | if (!_at->attach_any || !_callback) args.add(XmNrecomputeSize, false); |
---|
674 | args.assign_to_widget(button); |
---|
675 | if (_at->attach_any) aw_attach_widget(button, _at); |
---|
676 | |
---|
677 | if (_callback) { |
---|
678 | root->make_sensitive(button, _at->widget_mask); |
---|
679 | } |
---|
680 | else { |
---|
681 | aw_assert(_at->correct_for_at_center == 0); |
---|
682 | AW_JUSTIFY_LABEL(button, _at->correct_for_at_center); // @@@ strange, again sets XmNalignment (already done above). maybe some relict. does nothing if (_at->correct_for_at_center == 0) |
---|
683 | } |
---|
684 | |
---|
685 | AW_label_in_awar_list(this, button, buttonlabel); |
---|
686 | } |
---|
687 | |
---|
688 | short height = 0; |
---|
689 | short width = 0; |
---|
690 | |
---|
691 | if (_at->to_position_exists) { |
---|
692 | // size has explicitly been specified in xfig -> calculate |
---|
693 | height = _at->to_position_y - _at->y_for_next_button; |
---|
694 | width = _at->to_position_x - _at->x_for_next_button; |
---|
695 | } |
---|
696 | |
---|
697 | { |
---|
698 | Widget toRecenter = NULp; |
---|
699 | int recenterSize = 0; |
---|
700 | |
---|
701 | if (!height || !width) { |
---|
702 | // ask motif for real button size |
---|
703 | Widget ButOrHigh = _at->highlight ? fatherwidget : button; |
---|
704 | XtVaGetValues(ButOrHigh, XmNheight, &height, XmNwidth, &width, NULp); |
---|
705 | |
---|
706 | if (let_motif_choose_size) { |
---|
707 | if (_at->correct_for_at_center) { |
---|
708 | toRecenter = ButOrHigh; |
---|
709 | recenterSize = width; |
---|
710 | } |
---|
711 | width = 0; // ignore the used size (because it may use more than the window size) |
---|
712 | } |
---|
713 | } |
---|
714 | |
---|
715 | if (toRecenter) { |
---|
716 | int shiftback = 0; |
---|
717 | switch (_at->correct_for_at_center) { |
---|
718 | case 1: shiftback = recenterSize/2; break; |
---|
719 | case 2: shiftback = recenterSize; break; |
---|
720 | } |
---|
721 | if (shiftback) { |
---|
722 | XtVaSetValues(toRecenter, XmNx, x_button-shiftback, NULp); |
---|
723 | } |
---|
724 | } |
---|
725 | } |
---|
726 | |
---|
727 | _at->correct_for_at_center = org_correct_for_at_center; // restore original justification |
---|
728 | _at->y_for_next_button = org_y_for_next_button; |
---|
729 | |
---|
730 | p_w->toggle_field = button; |
---|
731 | this->_set_activate_callback((void *)button); |
---|
732 | this->unset_at_commands(); |
---|
733 | this->increment_at_commands(width+SPACE_BEHIND_BUTTON, height); |
---|
734 | } |
---|
735 | |
---|
736 | void AW_window::dump_at_position(const char *tmp_label) const { |
---|
737 | printf("%s at x = %i / y = %i\n", tmp_label, _at->x_for_next_button, _at->y_for_next_button); |
---|
738 | } |
---|
739 | |
---|
740 | void AW_window::update_label(Widget widget, const char *var_value) { |
---|
741 | if (get_root()->changer_of_variable != widget) { |
---|
742 | XtVaSetValues(widget, RES_CONVERT(XmNlabelString, var_value), NULp); |
---|
743 | } |
---|
744 | else { |
---|
745 | get_root()->changer_of_variable = NULp; |
---|
746 | } |
---|
747 | } |
---|
748 | |
---|
749 | // ---------------------- |
---|
750 | // on/off toggle |
---|
751 | |
---|
752 | struct aw_toggle_data { |
---|
753 | bool isTextToggle; |
---|
754 | char *bitmapOrText[2]; |
---|
755 | int buttonWidth; // wanted width in characters |
---|
756 | }; |
---|
757 | |
---|
758 | void AW_window::update_toggle(Widget widget, const char *var, AW_CL cd_toggle_data) { |
---|
759 | aw_toggle_data *tdata = (aw_toggle_data*)cd_toggle_data; |
---|
760 | const char *text = tdata->bitmapOrText[(var[0] == '0' || var[0] == 'n') ? 0 : 1]; |
---|
761 | |
---|
762 | if (tdata->isTextToggle) { |
---|
763 | XtVaSetValues(widget, RES_CONVERT(XmNlabelString, text), NULp); |
---|
764 | } |
---|
765 | else { |
---|
766 | char *path = pixmapPath(text+1); |
---|
767 | XtVaSetValues(widget, RES_CONVERT(XmNlabelPixmap, path), NULp); |
---|
768 | free(path); |
---|
769 | } |
---|
770 | } |
---|
771 | |
---|
772 | void AW_window::create_toggle(const char *var_name, aw_toggle_data *tdata) { |
---|
773 | AW_cb *cbs = _callback; |
---|
774 | _callback = (AW_cb *)1; |
---|
775 | |
---|
776 | { |
---|
777 | int old_length_of_buttons = _at->length_of_buttons; |
---|
778 | |
---|
779 | if (tdata->buttonWidth == 0) { |
---|
780 | if (tdata->isTextToggle) { |
---|
781 | int l1 = strlen(tdata->bitmapOrText[0]); |
---|
782 | int l2 = strlen(tdata->bitmapOrText[1]); |
---|
783 | |
---|
784 | _at->length_of_buttons = l1>l2 ? l1 : l2; // use longer text for button size |
---|
785 | } |
---|
786 | else { |
---|
787 | _at->length_of_buttons = 0; |
---|
788 | } |
---|
789 | } |
---|
790 | else { |
---|
791 | _at->length_of_buttons = tdata->buttonWidth; |
---|
792 | } |
---|
793 | |
---|
794 | create_button(NULp, tdata->bitmapOrText[0]); |
---|
795 | |
---|
796 | _at->length_of_buttons = old_length_of_buttons; |
---|
797 | } |
---|
798 | |
---|
799 | AW_awar *vs = this->get_root()->awar(var_name); |
---|
800 | { |
---|
801 | char *var_value = vs->read_as_string(); |
---|
802 | |
---|
803 | this->update_toggle(p_w->toggle_field, var_value, (AW_CL)tdata); |
---|
804 | free(var_value); |
---|
805 | } |
---|
806 | |
---|
807 | VarUpdateInfo *vui; |
---|
808 | vui = new VarUpdateInfo(this, p_w->toggle_field, AW_WIDGET_TOGGLE, vs, cbs); |
---|
809 | |
---|
810 | XtAddCallback(p_w->toggle_field, XmNactivateCallback, |
---|
811 | (XtCallbackProc) AW_variable_update_callback, |
---|
812 | (XtPointer) vui); |
---|
813 | |
---|
814 | vs->tie_widget((AW_CL)tdata, p_w->toggle_field, AW_WIDGET_TOGGLE, this); |
---|
815 | } |
---|
816 | |
---|
817 | |
---|
818 | void AW_window::create_toggle(const char *var_name, const char *no, const char *yes, int buttonWidth) { |
---|
819 | aw_toggle_data *tdata = new aw_toggle_data; |
---|
820 | tdata->isTextToggle = false; |
---|
821 | |
---|
822 | aw_assert(AW_IS_IMAGEREF(no)); |
---|
823 | aw_assert(AW_IS_IMAGEREF(yes)); |
---|
824 | |
---|
825 | tdata->bitmapOrText[0] = strdup(no); |
---|
826 | tdata->bitmapOrText[1] = strdup(yes); |
---|
827 | |
---|
828 | tdata->buttonWidth = buttonWidth; |
---|
829 | |
---|
830 | create_toggle(var_name, tdata); |
---|
831 | } |
---|
832 | |
---|
833 | void AW_window::create_text_toggle(const char *var_name, const char *noText, const char *yesText, int buttonWidth) { |
---|
834 | aw_toggle_data *tdata = new aw_toggle_data; |
---|
835 | tdata->isTextToggle = true; |
---|
836 | tdata->bitmapOrText[0] = strdup(noText); |
---|
837 | tdata->bitmapOrText[1] = strdup(yesText); |
---|
838 | tdata->buttonWidth = buttonWidth; |
---|
839 | |
---|
840 | create_toggle(var_name, tdata); |
---|
841 | } |
---|
842 | |
---|
843 | |
---|
844 | void AW_window::create_toggle(const char *var_name) { |
---|
845 | create_toggle(var_name, "#no.xpm", "#yes.xpm"); |
---|
846 | } |
---|
847 | |
---|
848 | void AW_window::create_inverse_toggle(const char *var_name) { |
---|
849 | // like create_toggle, but displays inverse value |
---|
850 | // (i.e. it's checked if value is zero, and unchecked otherwise) |
---|
851 | create_toggle(var_name, "#yes.xpm", "#no.xpm"); |
---|
852 | } |
---|
853 | |
---|
854 | // --------------------- |
---|
855 | // input fields |
---|
856 | |
---|
857 | void AW_window::create_input_field(const char *var_name, int columns) { |
---|
858 | Widget textField = NULp; |
---|
859 | Widget label_widget = NULp; |
---|
860 | AW_cb *cbs; |
---|
861 | VarUpdateInfo *vui; |
---|
862 | char *str; |
---|
863 | int xoff_for_label = 0; |
---|
864 | |
---|
865 | if (!columns) columns = _at->length_of_buttons; |
---|
866 | |
---|
867 | AW_awar *vs = root->awar(var_name); |
---|
868 | str = root->awar(var_name)->read_as_string(); |
---|
869 | |
---|
870 | int width_of_input_label, height_of_input_label; |
---|
871 | calculate_label_size(&width_of_input_label, &height_of_input_label, true); |
---|
872 | #if defined(DUMP_BUTTON_CREATION) |
---|
873 | printf("width_of_input_label=%i height_of_input_label=%i label='%s'\n", width_of_input_label, height_of_input_label, _at->label_for_inputfield); |
---|
874 | #endif // DUMP_BUTTON_CREATION |
---|
875 | |
---|
876 | int width_of_input = this->calculate_string_width(columns+1) + 9; |
---|
877 | // calculate width for 1 additional character (input field is not completely used) |
---|
878 | // + 4 pixel for shadow + 4 unknown missing pixels + 1 add. pixel needed for visible text area |
---|
879 | |
---|
880 | Widget parentWidget = _at->attach_any ? INFO_FORM : INFO_WIDGET; |
---|
881 | |
---|
882 | if (_at->label_for_inputfield) { |
---|
883 | Label clientlabel(_at->label_for_inputfield, this); |
---|
884 | label_widget = XtVaCreateManagedWidget("label", |
---|
885 | xmLabelWidgetClass, |
---|
886 | parentWidget, |
---|
887 | XmNwidth, (int)(width_of_input_label + 2), |
---|
888 | XmNhighlightThickness, 0, |
---|
889 | RES_LABEL_CONVERT(clientlabel), |
---|
890 | XmNrecomputeSize, false, |
---|
891 | XmNalignment, XmALIGNMENT_BEGINNING, |
---|
892 | XmNfontList, p_global->fontlist, |
---|
893 | (_at->attach_any) ? NULp : XmNx, (int)_at->x_for_next_button, |
---|
894 | XmNy, (int)(_at->y_for_next_button) + 2*root->y_correction_for_input_labels - height_of_input_label/2, |
---|
895 | NULp); |
---|
896 | if (_at->attach_any) aw_attach_widget(label_widget, _at); |
---|
897 | xoff_for_label = width_of_input_label + 10; |
---|
898 | } |
---|
899 | |
---|
900 | |
---|
901 | int width_of_last_widget = xoff_for_label + width_of_input + 2; |
---|
902 | |
---|
903 | if (_at->to_position_exists) { |
---|
904 | width_of_input = _at->to_position_x - _at->x_for_next_button - xoff_for_label + 2; |
---|
905 | width_of_last_widget = _at->to_position_x - _at->x_for_next_button; |
---|
906 | } |
---|
907 | |
---|
908 | { |
---|
909 | TuneBackground(parentWidget, TUNE_INPUT); |
---|
910 | textField = XtVaCreateManagedWidget("textField", |
---|
911 | xmTextFieldWidgetClass, |
---|
912 | parentWidget, |
---|
913 | XmNwidth, (int)width_of_input, |
---|
914 | XmNrows, 1, |
---|
915 | XmNvalue, str, |
---|
916 | XmNfontList, p_global->fontlist, |
---|
917 | XmNbackground, _at->background_color, |
---|
918 | (_at->attach_any) ? NULp : XmNx, (int)(_at->x_for_next_button + xoff_for_label), |
---|
919 | XmNy, (int)(_at->y_for_next_button + 5) - 8, |
---|
920 | NULp); |
---|
921 | if (_at->attach_any) { |
---|
922 | _at->x_for_next_button += xoff_for_label; |
---|
923 | aw_attach_widget(textField, _at); |
---|
924 | _at->x_for_next_button -= xoff_for_label; |
---|
925 | } |
---|
926 | } |
---|
927 | |
---|
928 | free(str); |
---|
929 | |
---|
930 | // user-own callback |
---|
931 | cbs = _callback; |
---|
932 | |
---|
933 | // callback for enter |
---|
934 | vui = new VarUpdateInfo(this, textField, AW_WIDGET_INPUT_FIELD, vs, cbs); |
---|
935 | |
---|
936 | XtAddCallback(textField, XmNactivateCallback, |
---|
937 | (XtCallbackProc) AW_variable_update_callback, |
---|
938 | (XtPointer) vui); |
---|
939 | if (_d_callback) { |
---|
940 | XtAddCallback(textField, XmNactivateCallback, |
---|
941 | (XtCallbackProc) AW_server_callback, |
---|
942 | (XtPointer) _d_callback); |
---|
943 | _d_callback->id = GBS_global_string_copy("INPUT:%s", var_name); |
---|
944 | get_root()->define_remote_command(_d_callback); |
---|
945 | } |
---|
946 | |
---|
947 | // callback for losing focus |
---|
948 | XtAddCallback(textField, XmNlosingFocusCallback, |
---|
949 | (XtCallbackProc) AW_variable_update_callback, |
---|
950 | (XtPointer) vui); |
---|
951 | // callback for value changed |
---|
952 | XtAddCallback(textField, XmNvalueChangedCallback, |
---|
953 | (XtCallbackProc) AW_value_changed_callback, |
---|
954 | (XtPointer) root); |
---|
955 | |
---|
956 | vs->tie_widget(0, textField, AW_WIDGET_INPUT_FIELD, this); |
---|
957 | root->make_sensitive(textField, _at->widget_mask); |
---|
958 | |
---|
959 | short height; |
---|
960 | XtVaGetValues(textField, XmNheight, &height, NULp); |
---|
961 | int height_of_last_widget = height; |
---|
962 | |
---|
963 | if (_at->correct_for_at_center == 1) { // middle centered |
---|
964 | XtVaSetValues(textField, XmNx, ((int)(_at->x_for_next_button + xoff_for_label) - (int)(width_of_last_widget/2) + 1), NULp); |
---|
965 | if (label_widget) { |
---|
966 | XtVaSetValues(label_widget, XmNx, ((int)(_at->x_for_next_button) - (int)(width_of_last_widget/2) + 1), NULp); |
---|
967 | } |
---|
968 | width_of_last_widget = width_of_last_widget / 2; |
---|
969 | } |
---|
970 | if (_at->correct_for_at_center == 2) { // right centered |
---|
971 | XtVaSetValues(textField, XmNx, (int)(_at->x_for_next_button + xoff_for_label - width_of_last_widget + 3), NULp); |
---|
972 | if (label_widget) { |
---|
973 | XtVaSetValues(label_widget, XmNx, (int)(_at->x_for_next_button - width_of_last_widget + 3), NULp); |
---|
974 | } |
---|
975 | width_of_last_widget = 0; |
---|
976 | } |
---|
977 | width_of_last_widget -= 2; |
---|
978 | |
---|
979 | this->unset_at_commands(); |
---|
980 | this->increment_at_commands(width_of_last_widget, height_of_last_widget); |
---|
981 | |
---|
982 | } |
---|
983 | |
---|
984 | void AW_window::update_input_field(Widget widget, const char *var_value) { |
---|
985 | XtVaSetValues(widget, XmNvalue, var_value, NULp); |
---|
986 | } |
---|
987 | |
---|
988 | void AW_window::create_text_field(const char *var_name, int columns, int rows) { |
---|
989 | Widget scrolledWindowText; |
---|
990 | Widget scrolledText; |
---|
991 | Widget label_widget = NULp; |
---|
992 | AW_cb *cbs; |
---|
993 | VarUpdateInfo *vui; |
---|
994 | char *str = NULp; |
---|
995 | short width_of_last_widget = 0; |
---|
996 | short height_of_last_widget = 0; |
---|
997 | int width_of_text = 0; |
---|
998 | int height_of_text = 0; |
---|
999 | int xoff_for_label = 0; |
---|
1000 | |
---|
1001 | AW_awar *vs = root->awar(var_name); |
---|
1002 | str = root->awar(var_name)->read_string(); |
---|
1003 | |
---|
1004 | int width_of_text_label, height_of_text_label; |
---|
1005 | calculate_label_size(&width_of_text_label, &height_of_text_label, true); |
---|
1006 | // @@@ FIXME: use height_of_text_label for propper Y-adjusting of label |
---|
1007 | |
---|
1008 | // width_of_text_label = this->calculate_string_width( calculate_label_length() ); |
---|
1009 | width_of_text = this->calculate_string_width(columns) + 18; |
---|
1010 | height_of_text = this->calculate_string_height(rows, rows*4) + 9; |
---|
1011 | |
---|
1012 | |
---|
1013 | if (_at->label_for_inputfield) { |
---|
1014 | Label clientlabel(_at->label_for_inputfield, this); |
---|
1015 | label_widget = XtVaCreateManagedWidget("label", |
---|
1016 | xmLabelWidgetClass, |
---|
1017 | INFO_WIDGET, |
---|
1018 | XmNx, (int)_at->x_for_next_button, |
---|
1019 | XmNy, (int)(_at->y_for_next_button) + this->get_root()->y_correction_for_input_labels + 5 - 6, |
---|
1020 | XmNwidth, (int)(width_of_text_label + 2), |
---|
1021 | RES_LABEL_CONVERT(clientlabel), |
---|
1022 | XmNrecomputeSize, false, |
---|
1023 | XmNalignment, XmALIGNMENT_BEGINNING, |
---|
1024 | XmNfontList, p_global->fontlist, |
---|
1025 | NULp); |
---|
1026 | |
---|
1027 | xoff_for_label = width_of_text_label + 10; |
---|
1028 | |
---|
1029 | } |
---|
1030 | |
---|
1031 | { |
---|
1032 | aw_xargs args(6); |
---|
1033 | args.add(XmNscrollingPolicy, XmAPPLICATION_DEFINED); |
---|
1034 | args.add(XmNvisualPolicy, XmVARIABLE); |
---|
1035 | args.add(XmNscrollBarDisplayPolicy, XmSTATIC); |
---|
1036 | args.add(XmNfontList, (XtArgVal)p_global->fontlist); |
---|
1037 | |
---|
1038 | if (_at->to_position_exists) { |
---|
1039 | scrolledWindowText = XtVaCreateManagedWidget("scrolledWindowList1", xmScrolledWindowWidgetClass, INFO_FORM, NULp); |
---|
1040 | args.assign_to_widget(scrolledWindowText); |
---|
1041 | |
---|
1042 | aw_attach_widget(scrolledWindowText, _at); |
---|
1043 | width_of_text = _at->to_position_x - _at->x_for_next_button - xoff_for_label - 18; |
---|
1044 | if (_at->y_for_next_button < _at->to_position_y - 18) { |
---|
1045 | height_of_text = _at->to_position_y - _at->y_for_next_button - 18; |
---|
1046 | } |
---|
1047 | } |
---|
1048 | else { |
---|
1049 | scrolledWindowText = XtVaCreateManagedWidget("scrolledWindowText", xmScrolledWindowWidgetClass, INFO_WIDGET, NULp); |
---|
1050 | args.add(XmNx, 10); |
---|
1051 | args.add(XmNy, _at->y_for_next_button); |
---|
1052 | args.assign_to_widget(scrolledWindowText); |
---|
1053 | } |
---|
1054 | } |
---|
1055 | |
---|
1056 | TuneBackground(scrolledWindowText, TUNE_INPUT); |
---|
1057 | scrolledText = XtVaCreateManagedWidget("scrolledText1", |
---|
1058 | xmTextWidgetClass, |
---|
1059 | scrolledWindowText, |
---|
1060 | XmNeditMode, XmMULTI_LINE_EDIT, |
---|
1061 | XmNvalue, str, |
---|
1062 | XmNscrollLeftSide, false, |
---|
1063 | XmNwidth, (int)width_of_text, |
---|
1064 | XmNheight, (int)height_of_text, |
---|
1065 | XmNfontList, p_global->fontlist, |
---|
1066 | XmNbackground, _at->background_color, |
---|
1067 | NULp); |
---|
1068 | free(str); |
---|
1069 | |
---|
1070 | if (!_at->to_position_exists) { |
---|
1071 | XtVaGetValues(scrolledWindowText, XmNheight, &height_of_last_widget, |
---|
1072 | XmNwidth, &width_of_last_widget, NULp); |
---|
1073 | |
---|
1074 | width_of_last_widget += (short)xoff_for_label; |
---|
1075 | |
---|
1076 | switch (_at->correct_for_at_center) { |
---|
1077 | case 0: // left centered |
---|
1078 | XtVaSetValues(scrolledWindowText, XmNx, (int)(_at->x_for_next_button + xoff_for_label), NULp); |
---|
1079 | break; |
---|
1080 | |
---|
1081 | case 1: // middle centered |
---|
1082 | XtVaSetValues(scrolledWindowText, XmNx, (int)(_at->x_for_next_button + xoff_for_label - (width_of_last_widget/2)), NULp); |
---|
1083 | if (_at->label_for_inputfield) { |
---|
1084 | XtVaSetValues(label_widget, XmNx, (int)(_at->x_for_next_button - (width_of_last_widget/2)), NULp); |
---|
1085 | } |
---|
1086 | width_of_last_widget = width_of_last_widget / 2; |
---|
1087 | break; |
---|
1088 | |
---|
1089 | case 2: // right centered |
---|
1090 | XtVaSetValues(scrolledWindowText, XmNx, (int)(_at->x_for_next_button + xoff_for_label - width_of_last_widget), NULp); |
---|
1091 | if (_at->label_for_inputfield) { |
---|
1092 | XtVaSetValues(label_widget, XmNx, (int)(_at->x_for_next_button - width_of_last_widget), NULp); |
---|
1093 | } |
---|
1094 | width_of_last_widget = 0; |
---|
1095 | break; |
---|
1096 | } |
---|
1097 | } |
---|
1098 | |
---|
1099 | // user-own callback |
---|
1100 | cbs = _callback; |
---|
1101 | |
---|
1102 | // callback for enter |
---|
1103 | vui = new VarUpdateInfo(this, scrolledText, AW_WIDGET_TEXT_FIELD, vs, cbs); |
---|
1104 | XtAddCallback(scrolledText, XmNactivateCallback, (XtCallbackProc) AW_variable_update_callback, (XtPointer) vui); |
---|
1105 | // callback for losing focus |
---|
1106 | XtAddCallback(scrolledText, XmNlosingFocusCallback, (XtCallbackProc) AW_variable_update_callback, (XtPointer) vui); |
---|
1107 | // callback for value changed |
---|
1108 | XtAddCallback(scrolledText, XmNvalueChangedCallback, (XtCallbackProc) AW_value_changed_callback, (XtPointer) root); |
---|
1109 | |
---|
1110 | vs->tie_widget(0, scrolledText, AW_WIDGET_TEXT_FIELD, this); |
---|
1111 | root->make_sensitive(scrolledText, _at->widget_mask); |
---|
1112 | |
---|
1113 | this->unset_at_commands(); |
---|
1114 | this->increment_at_commands(width_of_last_widget, height_of_last_widget); |
---|
1115 | } |
---|
1116 | |
---|
1117 | |
---|
1118 | void AW_window::update_text_field(Widget widget, const char *var_value) { |
---|
1119 | XtVaSetValues(widget, XmNvalue, var_value, NULp); |
---|
1120 | } |
---|
1121 | |
---|
1122 | static void scalerChanged_cb(Widget scale, XtPointer variable_update_struct, XtPointer call_data) { |
---|
1123 | XmScaleCallbackStruct *cbs = (XmScaleCallbackStruct*)call_data; |
---|
1124 | VarUpdateInfo *vui = (VarUpdateInfo*)variable_update_struct; |
---|
1125 | |
---|
1126 | bool do_update = true; |
---|
1127 | if (cbs->reason == XmCR_DRAG) { // still dragging? |
---|
1128 | double mean_callback_time = vui->get_awar()->mean_callback_time(); |
---|
1129 | |
---|
1130 | const double MAX_DRAGGED_CALLBACK_TIME = 1.0; |
---|
1131 | if (mean_callback_time>MAX_DRAGGED_CALLBACK_TIME) { |
---|
1132 | do_update = false; // do not update while dragging, if update callback needs more than 1 second |
---|
1133 | // Note that a longer update will happen once! |
---|
1134 | } |
---|
1135 | } |
---|
1136 | |
---|
1137 | if (do_update) { |
---|
1138 | AW_root::SINGLETON->value_changed = true; |
---|
1139 | AW_variable_update_callback(scale, variable_update_struct, call_data); |
---|
1140 | } |
---|
1141 | } |
---|
1142 | |
---|
1143 | void AW_window::create_input_field_with_scaler(const char *awar_name, int textcolumns, int scaler_length, AW_ScalerType scalerType) { |
---|
1144 | create_input_field(awar_name, textcolumns); |
---|
1145 | |
---|
1146 | Widget parentWidget = _at->attach_any ? INFO_FORM : INFO_WIDGET; |
---|
1147 | |
---|
1148 | AW_awar *vs = root->awar(awar_name); |
---|
1149 | // Note: scaler also "works" if no min/max is defined for awar, but scaling is a bit weird |
---|
1150 | int scalerValue = calculate_scaler_value(vs, scalerType); |
---|
1151 | |
---|
1152 | Widget scale = XtVaCreateManagedWidget("scale", |
---|
1153 | xmScaleWidgetClass, |
---|
1154 | parentWidget, |
---|
1155 | XmNx, _at->x_for_next_button, |
---|
1156 | XmNy, _at->y_for_next_button + 4, |
---|
1157 | XmNorientation, XmHORIZONTAL, |
---|
1158 | XmNscaleWidth, scaler_length, |
---|
1159 | XmNshowValue, False, |
---|
1160 | XmNminimum, SCALER_MIN_VALUE, |
---|
1161 | XmNmaximum, SCALER_MAX_VALUE, |
---|
1162 | XmNvalue, scalerValue, |
---|
1163 | NULp); |
---|
1164 | |
---|
1165 | short width_of_last_widget = 0; |
---|
1166 | short height_of_last_widget = 0; |
---|
1167 | |
---|
1168 | XtVaGetValues(scale, |
---|
1169 | XmNheight, &height_of_last_widget, |
---|
1170 | XmNwidth, &width_of_last_widget, |
---|
1171 | NULp); |
---|
1172 | |
---|
1173 | AW_cb *cbs = _callback; |
---|
1174 | VarUpdateInfo *vui = new VarUpdateInfo(this, scale, AW_WIDGET_SCALER, vs, cbs); |
---|
1175 | vui->set_scalerType(scalerType); |
---|
1176 | |
---|
1177 | XtAddCallback(scale, XmNvalueChangedCallback, scalerChanged_cb, (XtPointer)vui); |
---|
1178 | XtAddCallback(scale, XmNdragCallback, scalerChanged_cb, (XtPointer)vui); |
---|
1179 | |
---|
1180 | vs->tie_widget((AW_CL)scalerType, scale, AW_WIDGET_SCALER, this); |
---|
1181 | root->make_sensitive(scale, _at->widget_mask); |
---|
1182 | |
---|
1183 | this->unset_at_commands(); |
---|
1184 | this->increment_at_commands(width_of_last_widget, height_of_last_widget); |
---|
1185 | } |
---|
1186 | |
---|
1187 | void AW_window::update_scaler(Widget widget, AW_awar *awar, AW_ScalerType scalerType) { |
---|
1188 | int scalerVal = calculate_scaler_value(awar, scalerType); |
---|
1189 | XtVaSetValues(widget, XmNvalue, scalerVal, NULp); |
---|
1190 | } |
---|
1191 | |
---|
1192 | // ----------------------- |
---|
1193 | // selection list |
---|
1194 | |
---|
1195 | |
---|
1196 | static void scroll_sellist(Widget scrolledList, bool upwards) { |
---|
1197 | int oldPos, visible, items; |
---|
1198 | XtVaGetValues(scrolledList, |
---|
1199 | XmNtopItemPosition, &oldPos, |
---|
1200 | XmNvisibleItemCount, &visible, |
---|
1201 | XmNitemCount, &items, |
---|
1202 | NULp); |
---|
1203 | |
---|
1204 | int amount = visible/5; |
---|
1205 | if (amount<1) amount = 1; |
---|
1206 | if (upwards) amount = -amount; |
---|
1207 | |
---|
1208 | int newPos = force_in_range(1, oldPos + amount, items-visible+2); |
---|
1209 | if (newPos != oldPos) XmListSetPos(scrolledList, newPos); |
---|
1210 | } |
---|
1211 | |
---|
1212 | static void scroll_sellist_up(Widget scrolledList, XEvent*, String*, Cardinal*) { scroll_sellist(scrolledList, true); } |
---|
1213 | static void scroll_sellist_dn(Widget scrolledList, XEvent*, String*, Cardinal*) { scroll_sellist(scrolledList, false); } |
---|
1214 | |
---|
1215 | AW_selection_list* AW_window::create_selection_list(const char *var_name, int columns, int rows) { |
---|
1216 | Widget scrolledWindowList; // @@@ fix locals |
---|
1217 | Widget scrolledList; |
---|
1218 | VarUpdateInfo *vui; |
---|
1219 | AW_cb *cbs; |
---|
1220 | |
---|
1221 | int width_of_list; |
---|
1222 | int height_of_list; |
---|
1223 | int width_of_last_widget = 0; |
---|
1224 | int height_of_last_widget = 0; |
---|
1225 | |
---|
1226 | aw_assert(!_at->label_for_inputfield); // labels have no effect for selection lists |
---|
1227 | |
---|
1228 | AW_awar *vs = NULp; |
---|
1229 | |
---|
1230 | aw_assert(var_name); // @@@ case where var_name is NULp is relict from multi-selection-list (not used; removed) |
---|
1231 | |
---|
1232 | if (var_name) vs = root->awar(var_name); |
---|
1233 | |
---|
1234 | width_of_list = this->calculate_string_width(columns) + 9; |
---|
1235 | height_of_list = this->calculate_string_height(rows, 4*rows) + 9; |
---|
1236 | |
---|
1237 | { |
---|
1238 | aw_xargs args(7); |
---|
1239 | args.add(XmNvisualPolicy, XmVARIABLE); |
---|
1240 | args.add(XmNscrollBarDisplayPolicy, XmSTATIC); |
---|
1241 | args.add(XmNshadowThickness, 0); |
---|
1242 | args.add(XmNfontList, (XtArgVal)p_global->fontlist); |
---|
1243 | |
---|
1244 | if (_at->to_position_exists) { |
---|
1245 | width_of_list = _at->to_position_x - _at->x_for_next_button - 18; |
---|
1246 | if (_at->y_for_next_button < _at->to_position_y - 18) { |
---|
1247 | height_of_list = _at->to_position_y - _at->y_for_next_button - 18; |
---|
1248 | } |
---|
1249 | scrolledWindowList = XtVaCreateManagedWidget("scrolledWindowList1", xmScrolledWindowWidgetClass, INFO_FORM, NULp); |
---|
1250 | |
---|
1251 | args.assign_to_widget(scrolledWindowList); |
---|
1252 | aw_attach_widget(scrolledWindowList, _at); |
---|
1253 | |
---|
1254 | width_of_last_widget = _at->to_position_x - _at->x_for_next_button; |
---|
1255 | height_of_last_widget = _at->to_position_y - _at->y_for_next_button; |
---|
1256 | } |
---|
1257 | else { |
---|
1258 | scrolledWindowList = XtVaCreateManagedWidget("scrolledWindowList1", xmScrolledWindowWidgetClass, INFO_WIDGET, NULp); |
---|
1259 | |
---|
1260 | args.add(XmNscrollingPolicy, XmAPPLICATION_DEFINED); |
---|
1261 | args.add(XmNx, 10); |
---|
1262 | args.add(XmNy, _at->y_for_next_button); |
---|
1263 | args.assign_to_widget(scrolledWindowList); |
---|
1264 | } |
---|
1265 | } |
---|
1266 | |
---|
1267 | { |
---|
1268 | int select_type = XmMULTIPLE_SELECT; |
---|
1269 | if (vs) select_type = XmBROWSE_SELECT; |
---|
1270 | |
---|
1271 | TuneBackground(scrolledWindowList, TUNE_INPUT); |
---|
1272 | scrolledList = XtVaCreateManagedWidget("scrolledList1", |
---|
1273 | xmListWidgetClass, |
---|
1274 | scrolledWindowList, |
---|
1275 | XmNwidth, (int)width_of_list, |
---|
1276 | XmNheight, (int) height_of_list, |
---|
1277 | XmNscrollBarDisplayPolicy, XmSTATIC, |
---|
1278 | XmNselectionPolicy, select_type, |
---|
1279 | XmNlistSizePolicy, XmCONSTANT, |
---|
1280 | XmNfontList, p_global->fontlist, |
---|
1281 | XmNbackground, _at->background_color, |
---|
1282 | NULp); |
---|
1283 | |
---|
1284 | static bool actionsAdded = false; |
---|
1285 | if (!actionsAdded) { |
---|
1286 | struct _XtActionsRec actions[2] = { |
---|
1287 | {(char*)"scroll_sellist_up", scroll_sellist_up}, |
---|
1288 | {(char*)"scroll_sellist_dn", scroll_sellist_dn} |
---|
1289 | }; |
---|
1290 | |
---|
1291 | XtAppAddActions(p_global->context, actions, 2); |
---|
1292 | } |
---|
1293 | |
---|
1294 | XtTranslations translations = XtParseTranslationTable( |
---|
1295 | "<Btn4Down>:scroll_sellist_up()\n" |
---|
1296 | "<Btn5Down>:scroll_sellist_dn()\n" |
---|
1297 | ); |
---|
1298 | XtAugmentTranslations(scrolledList, translations); |
---|
1299 | } |
---|
1300 | |
---|
1301 | if (!_at->to_position_exists) { |
---|
1302 | short height; |
---|
1303 | XtVaGetValues(scrolledList, XmNheight, &height, NULp); |
---|
1304 | height_of_last_widget = height + 20; |
---|
1305 | width_of_last_widget = width_of_list + 20; |
---|
1306 | |
---|
1307 | switch (_at->correct_for_at_center) { |
---|
1308 | case 3: break; |
---|
1309 | case 0: // left aligned |
---|
1310 | XtVaSetValues(scrolledWindowList, XmNx, (int)(_at->x_for_next_button), NULp); |
---|
1311 | break; |
---|
1312 | |
---|
1313 | case 1: // center aligned |
---|
1314 | XtVaSetValues(scrolledWindowList, XmNx, (int)(_at->x_for_next_button - (width_of_last_widget/2)), NULp); |
---|
1315 | width_of_last_widget = width_of_last_widget / 2; |
---|
1316 | break; |
---|
1317 | |
---|
1318 | case 2: // right aligned |
---|
1319 | XtVaSetValues(scrolledWindowList, XmNx, (int)(_at->x_for_next_button - width_of_list - 18), NULp); |
---|
1320 | width_of_last_widget = 0; |
---|
1321 | break; |
---|
1322 | } |
---|
1323 | |
---|
1324 | } |
---|
1325 | |
---|
1326 | { |
---|
1327 | int type = GB_STRING; |
---|
1328 | if (vs) type = vs->variable_type; |
---|
1329 | |
---|
1330 | if (p_global->selection_list) { |
---|
1331 | p_global->last_selection_list->next = new AW_selection_list(var_name, type, scrolledList); |
---|
1332 | p_global->last_selection_list = p_global->last_selection_list->next; |
---|
1333 | } |
---|
1334 | else { |
---|
1335 | p_global->last_selection_list = p_global->selection_list = new AW_selection_list(var_name, type, scrolledList); |
---|
1336 | } |
---|
1337 | } |
---|
1338 | |
---|
1339 | |
---|
1340 | // user-own callback |
---|
1341 | cbs = _callback; // @@@ generally unwanted (see #559) |
---|
1342 | |
---|
1343 | // callback for enter |
---|
1344 | if (vs) { |
---|
1345 | vui = new VarUpdateInfo(this, scrolledList, AW_WIDGET_SELECTION_LIST, vs, cbs); |
---|
1346 | vui->set_sellist(p_global->last_selection_list); |
---|
1347 | |
---|
1348 | XtAddCallback(scrolledList, XmNbrowseSelectionCallback, |
---|
1349 | (XtCallbackProc) AW_variable_update_callback, |
---|
1350 | (XtPointer) vui); |
---|
1351 | |
---|
1352 | if (_d_callback) { |
---|
1353 | XtAddCallback(scrolledList, XmNdefaultActionCallback, |
---|
1354 | (XtCallbackProc) AW_server_callback, |
---|
1355 | (XtPointer) _d_callback); |
---|
1356 | } |
---|
1357 | vs->tie_widget((AW_CL)p_global->last_selection_list, scrolledList, AW_WIDGET_SELECTION_LIST, this); |
---|
1358 | root->make_sensitive(scrolledList, _at->widget_mask); |
---|
1359 | } |
---|
1360 | |
---|
1361 | this->unset_at_commands(); |
---|
1362 | this->increment_at_commands(width_of_last_widget, height_of_last_widget); |
---|
1363 | return p_global->last_selection_list; |
---|
1364 | } |
---|
1365 | |
---|