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