1 /* Ui.cpp
2 *
3 * Copyright (C) 1992-2021 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "../kar/longchar.h"
20 #include "machine.h"
21 #include "GuiP.h"
22 #include "Collection.h"
23 #include "UiP.h"
24 #include "Editor.h"
25 #include "Graphics.h" // colours
26 #include "NUM2.h" // get elements of ranges
27
28 #include "enums_getText.h"
29 #include "Ui_enums.h"
30 #include "enums_getValue.h"
31 #include "Ui_enums.h"
32
33 kUi_realMatrixFormat theRealMatrixFormat;
34 kUi_stringArrayFormat theStringArrayFormat;
35
Ui_prefs()36 void Ui_prefs () {
37 Preferences_addEnum (U"Ui.realMatrixFormat", & theRealMatrixFormat, kUi_realMatrixFormat, (int) kUi_realMatrixFormat::DEFAULT);
38 Preferences_addEnum (U"Ui.stringArrayFormat", & theStringArrayFormat, kUi_stringArrayFormat, (int) kUi_stringArrayFormat::DEFAULT);
39 }
40
formatNumericMatrix(constMAT cells,kUi_realMatrixFormat format)41 static conststring32 formatNumericMatrix (constMAT cells, kUi_realMatrixFormat format) {
42 static MelderString buffer;
43 MelderString_empty (& buffer);
44 switch (format) {
45 case kUi_realMatrixFormat::ONE_ROW_PER_LINE_: {
46 for (integer irow = 1; irow <= cells.nrow; irow ++) {
47 for (integer icol = 1; icol <= cells.ncol; icol ++) {
48 MelderString_append (& buffer, cells [irow] [icol]);
49 if (icol < cells.ncol)
50 MelderString_appendCharacter (& buffer, U' ');
51 }
52 if (irow < cells.nrow)
53 MelderString_appendCharacter (& buffer, U'\n');
54 }
55 } break; case kUi_realMatrixFormat::FORMULA_: {
56 if (NUMisEmpty (cells)) {
57 MelderString_append (& buffer, U"zero## (0, 0)");
58 } else {
59 MelderString_append (& buffer, U"{ ");
60 for (integer irow = 1; irow <= cells.nrow; irow ++) {
61 MelderString_append (& buffer, U"{ ");
62 for (integer icol = 1; icol <= cells.ncol; icol ++) {
63 MelderString_append (& buffer, cells [irow] [icol]);
64 if (icol < cells.ncol)
65 MelderString_append (& buffer, U", ");
66 }
67 MelderString_append (& buffer, U" }");
68 if (irow < cells.nrow)
69 MelderString_append (& buffer, U", ");
70 }
71 MelderString_append (& buffer, U" }");
72 }
73 } break; case kUi_realMatrixFormat::UNDEFINED: {
74 Melder_fatal (U"Unknown numeric matrix format.");
75 }
76 }
77 return buffer.string;
78 }
79
MelderString_appendQuoted(MelderString * buffer,conststring32 string)80 static void MelderString_appendQuoted (MelderString *buffer, conststring32 string) {
81 MelderString_appendCharacter (buffer, U'\"');
82 for (const char32 *p = & string [0]; *p != U'\0'; p ++)
83 if (*p == U'\"')
84 MelderString_append (buffer, U"\"\"");
85 else
86 MelderString_appendCharacter (buffer, *p);
87 MelderString_appendCharacter (buffer, U'\"');
88 }
89
formatStringArray(constSTRVEC strings,kUi_stringArrayFormat format)90 static conststring32 formatStringArray (constSTRVEC strings, kUi_stringArrayFormat format) {
91 static MelderString buffer;
92 MelderString_empty (& buffer);
93 switch (format) {
94 case kUi_stringArrayFormat::WHITESPACE_SEPARATED_: {
95 for (integer i = 1; i <= strings.size; i ++) {
96 if (str32chr (strings [i], U'\"') || Melder_findHorizontalOrVerticalSpace (strings [i]))
97 MelderString_appendQuoted (& buffer, strings [i]);
98 else
99 MelderString_append (& buffer, strings [i]);
100 if (i < strings.size)
101 MelderString_appendCharacter (& buffer, U' ');
102 }
103 } break; case kUi_stringArrayFormat::COMMA_SEPARATED_: {
104 for (integer i = 1; i <= strings.size; i ++) {
105 if (str32chr (strings [i], U'\"') || str32chr (strings [i], U','))
106 MelderString_appendQuoted (& buffer, strings [i]);
107 else
108 MelderString_append (& buffer, strings [i]);
109 if (i < strings.size)
110 MelderString_appendCharacter (& buffer, U',');
111 }
112 } break; case kUi_stringArrayFormat::SEMICOLON_SEPARATED_: {
113 for (integer i = 1; i <= strings.size; i ++) {
114 if (str32chr (strings [i], U'\"') || str32chr (strings [i], U';'))
115 MelderString_appendQuoted (& buffer, strings [i]);
116 else
117 MelderString_append (& buffer, strings [i]);
118 if (i < strings.size)
119 MelderString_appendCharacter (& buffer, U';');
120 }
121 } break; case kUi_stringArrayFormat::PIPE_SEPARATED_: {
122 for (integer i = 1; i <= strings.size; i ++) {
123 if (str32chr (strings [i], U'\"') || str32chr (strings [i], U'|'))
124 MelderString_appendQuoted (& buffer, strings [i]);
125 else
126 MelderString_append (& buffer, strings [i]);
127 if (i < strings.size)
128 MelderString_appendCharacter (& buffer, U'|');
129 }
130 } break; case kUi_stringArrayFormat::ONE_PER_LINE_: {
131 for (integer i = 1; i <= strings.size; i ++) {
132 MelderString_append (& buffer, strings [i]);
133 if (i < strings.size)
134 MelderString_appendCharacter (& buffer, U'\n');
135 }
136 } break; case kUi_stringArrayFormat::FORMULA_: {
137 if (NUMisEmpty (strings)) {
138 MelderString_append (& buffer, U"empty$# (0)");
139 } else {
140 MelderString_append (& buffer, U"{ ");
141 for (integer i = 1; i <= strings.size; i ++) {
142 MelderString_appendQuoted (& buffer, strings [i]);
143 if (i < strings.size)
144 MelderString_append (& buffer, U", ");
145 }
146 MelderString_append (& buffer, U" }");
147 }
148 } break; case kUi_stringArrayFormat::UNDEFINED: {
149 Melder_fatal (U"Unknown string array format.");
150 }
151 }
152 return buffer.string;
153 }
154
155 /***** class UiField: the things that have values in an UiForm dialog *****/
156
157 Thing_implement (UiField, Thing, 0);
158
v_destroy()159 void structUiField :: v_destroy () noexcept {
160 our UiField_Parent :: v_destroy ();
161 }
162
UiField_create(_kUiField_type type,conststring32 nameOrNull)163 static autoUiField UiField_create (_kUiField_type type, conststring32 nameOrNull) {
164 autoUiField me = Thing_new (UiField);
165 my type = type;
166 my formLabel = Melder_dup (nameOrNull);
167 if (nameOrNull) {
168 char32 shortName [1+100], *p;
169 str32ncpy (shortName, nameOrNull, 100);
170 shortName [100] = U'\0';
171 /*
172 Strip parentheses and colon off parameter name.
173 */
174 if (!! (p = (char32 *) str32chr (shortName, U'('))) {
175 *p = U'\0';
176 if (p - shortName > 0 && p [-1] == U' ')
177 p [-1] = U'\0';
178 }
179 p = shortName;
180 if (*p != U'\0' && p [str32len (p) - 1] == U':')
181 p [str32len (p) - 1] = U'\0';
182 Thing_setName (me.get(), shortName);
183 }
184 return me;
185 }
186
187 /***** class UiOption: radio buttons and menu options *****/
188
189 Thing_implement (UiOption, Thing, 0);
190
UiOption_create(conststring32 label)191 static autoUiOption UiOption_create (conststring32 label) {
192 autoUiOption me = Thing_new (UiOption);
193 Thing_setName (me.get(), label);
194 return me;
195 }
196
UiRadio_addButton(UiField me,conststring32 label)197 UiOption UiRadio_addButton (UiField me, conststring32 label) {
198 if (! me)
199 return nullptr;
200 Melder_assert (my type == _kUiField_type::RADIO_ || my type == _kUiField_type::OPTIONMENU_);
201 autoUiOption thee = UiOption_create (label);
202 return my options. addItem_move (thee.move());
203 }
204
UiOptionMenu_addButton(UiField me,conststring32 label)205 UiOption UiOptionMenu_addButton (UiField me, conststring32 label) {
206 if (! me)
207 return nullptr;
208 Melder_assert (my type == _kUiField_type::RADIO_ || my type == _kUiField_type::OPTIONMENU_);
209 autoUiOption thee = UiOption_create (label);
210 return my options. addItem_move (thee.move());
211 }
212
213 /***** Things to do with UiField objects. *****/
214
UiField_setDefault(UiField me)215 static void UiField_setDefault (UiField me) {
216 switch (my type) {
217 case _kUiField_type::LABEL_:
218 {
219 // do nothing
220 }
221 break;
222 case _kUiField_type::REAL_:
223 case _kUiField_type::REAL_OR_UNDEFINED_:
224 case _kUiField_type::POSITIVE_:
225 case _kUiField_type::INTEGER_:
226 case _kUiField_type::NATURAL_:
227 case _kUiField_type::WORD_:
228 case _kUiField_type::SENTENCE_:
229 case _kUiField_type::COLOUR_:
230 case _kUiField_type::CHANNEL_:
231 case _kUiField_type::TEXT_:
232 case _kUiField_type::FORMULA_:
233 case _kUiField_type::INFILE_:
234 case _kUiField_type::OUTFILE_:
235 case _kUiField_type::FOLDER_:
236 {
237 GuiText_setString (my text, my stringDefaultValue.get());
238 }
239 break;
240 case _kUiField_type::REALVECTOR_:
241 case _kUiField_type::POSITIVEVECTOR_:
242 {
243 GuiOptionMenu_setValue (my optionMenu, (int) my realVectorDefaultFormat);
244 GuiText_setString (my text, my stringDefaultValue.get());
245 }
246 break;
247 case _kUiField_type::INTEGERVECTOR_:
248 case _kUiField_type::NATURALVECTOR_:
249 {
250 GuiOptionMenu_setValue (my optionMenu, (int) my integerVectorDefaultFormat);
251 GuiText_setString (my text, my stringDefaultValue.get());
252 }
253 break;
254 case _kUiField_type::REALMATRIX_:
255 {
256 theRealMatrixFormat = (kUi_realMatrixFormat) GuiOptionMenu_getValue (my optionMenu);
257 GuiText_setString (my text, formatNumericMatrix (my numericMatrixDefaultValue.get(), theRealMatrixFormat));
258 }
259 break;
260 case _kUiField_type::STRINGARRAY_:
261 {
262 theStringArrayFormat = (kUi_stringArrayFormat) GuiOptionMenu_getValue (my optionMenu);
263 GuiText_setString (my text, formatStringArray (my stringArrayDefaultValue.get(), theStringArrayFormat));
264 }
265 break;
266 case _kUiField_type::BOOLEAN_:
267 {
268 GuiCheckButton_setValue (my checkButton, my integerDefaultValue);
269 }
270 break;
271 case _kUiField_type::RADIO_:
272 {
273 for (int i = 1; i <= my options.size; i ++) {
274 if (i == my integerDefaultValue) {
275 UiOption b = my options.at [i];
276 GuiRadioButton_set (b -> radioButton);
277 }
278 }
279 }
280 break;
281 case _kUiField_type::OPTIONMENU_:
282 {
283 GuiOptionMenu_setValue (my optionMenu, my integerDefaultValue);
284 }
285 break;
286 case _kUiField_type::LIST_:
287 {
288 GuiList_selectItem (my list, my integerDefaultValue);
289 }
290 }
291 }
292
UiField_widgetToValue(UiField me)293 static void UiField_widgetToValue (UiField me) {
294 switch (my type)
295 {
296 case _kUiField_type::LABEL_:
297 {
298 // do nothing
299 }
300 break;
301 case _kUiField_type::REAL_:
302 case _kUiField_type::REAL_OR_UNDEFINED_:
303 case _kUiField_type::POSITIVE_:
304 {
305 autostring32 text = GuiText_getString (my text); // the text as typed by the user
306 Interpreter_numericExpression (nullptr, text.get(), & my realValue);
307 if (isundef (my realValue) && my type != _kUiField_type::REAL_OR_UNDEFINED_)
308 Melder_throw (U"“", my name.get(), U"” has the value \"undefined\".");
309 if (my type == _kUiField_type::POSITIVE_ && my realValue <= 0.0)
310 Melder_throw (U"“", my name.get(), U"” should be greater than 0.0.");
311 if (my realVariable)
312 *my realVariable = my realValue;
313 }
314 break;
315 case _kUiField_type::INTEGER_:
316 case _kUiField_type::NATURAL_:
317 case _kUiField_type::CHANNEL_:
318 {
319 autostring32 text = GuiText_getString (my text);
320 if (my type == _kUiField_type::CHANNEL_ && (str32equ (text.get(), U"Left") || str32equ (text.get(), U"Mono"))) {
321 my integerValue = 1;
322 } else if (my type == _kUiField_type::CHANNEL_ && (str32equ (text.get(), U"Right") || str32equ (text.get(), U"Stereo"))) {
323 my integerValue = 2;
324 } else {
325 double realValue;
326 Interpreter_numericExpression (nullptr, text.get(), & realValue);
327 my integerValue = Melder_iround (realValue);
328 Melder_require (my integerValue == realValue,
329 U"“", my name.get(), U"” should be a whole number.");
330 }
331 if (my type == _kUiField_type::NATURAL_) {
332 Melder_require (my integerValue >= 1,
333 U"“", my name.get(), U"” should be a positive whole number.");
334 }
335 if (my type == _kUiField_type::CHANNEL_) {
336 Melder_require (my integerValue >= 0,
337 U"“", my name.get(), U"” should be a positive whole number or zero.");
338 }
339 if (my integerVariable)
340 *my integerVariable = my integerValue;
341 }
342 break;
343 case _kUiField_type::WORD_:
344 {
345 my stringValue = GuiText_getString (my text);
346 Melder_require (*Melder_findEndOfInk (my stringValue.get()) == U'\0',
347 U"“", my name.get(), U"” should be a single ink-word and cannot contain a space.");
348 if (my stringVariable)
349 *my stringVariable = my stringValue.get();
350 }
351 break;
352 case _kUiField_type::SENTENCE_:
353 case _kUiField_type::TEXT_:
354 case _kUiField_type::FORMULA_:
355 case _kUiField_type::INFILE_:
356 case _kUiField_type::OUTFILE_:
357 case _kUiField_type::FOLDER_:
358 {
359 my stringValue = GuiText_getString (my text);
360 if (my stringVariable)
361 *my stringVariable = my stringValue.get();
362 }
363 break;
364 case _kUiField_type::REALVECTOR_:
365 case _kUiField_type::POSITIVEVECTOR_:
366 {
367 autostring32 stringValue = GuiText_getString (my text);
368 kUi_realVectorFormat format = (kUi_realVectorFormat) GuiOptionMenu_getValue (my optionMenu);
369 switch (format) {
370 case kUi_realVectorFormat::WHITESPACE_SEPARATED_: {
371 my realVectorValue = splitByWhitespace_VEC (stringValue.get());
372 } break; case kUi_realVectorFormat::FORMULA_: {
373 VEC result;
374 bool ownedByInterpreter;
375 Interpreter_numericVectorExpression (nullptr, stringValue.get(), & result, & ownedByInterpreter);
376 if (ownedByInterpreter) {
377 my realVectorValue. adoptFromAmbiguousOwner (result);
378 } else {
379 my realVectorValue = copy_VEC (result);
380 }
381 } break; case kUi_realVectorFormat::UNDEFINED: {
382 Melder_fatal (U"Unknown real vector format.");
383 }
384 }
385 if (my type == _kUiField_type::POSITIVEVECTOR_)
386 for (integer i = 1; i <= my realVectorValue.size; i ++)
387 if (my realVectorValue [i] <= 0.0)
388 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my realVectorValue [i], U" but should be greater than 0.0.");
389 if (my realVectorVariable)
390 *my realVectorVariable = my realVectorValue.get();
391 }
392 break;
393 case _kUiField_type::INTEGERVECTOR_:
394 case _kUiField_type::NATURALVECTOR_:
395 {
396 autostring32 stringValue = GuiText_getString (my text);
397 kUi_integerVectorFormat format = (kUi_integerVectorFormat) GuiOptionMenu_getValue (my optionMenu);
398 switch (format) {
399 case kUi_integerVectorFormat::WHITESPACE_SEPARATED_: {
400 my integerVectorValue = iround_INTVEC (splitByWhitespace_VEC (stringValue.get()).get());
401 } break; case kUi_integerVectorFormat::RANGES_: {
402 my integerVectorValue = splitByWhitespaceWithRanges_INTVEC (stringValue.get());
403 } break; case kUi_integerVectorFormat::FORMULA_: {
404 VEC result;
405 bool ownedByInterpreter;
406 Interpreter_numericVectorExpression (nullptr, stringValue.get(), & result, & ownedByInterpreter);
407 my integerVectorValue = raw_INTVEC (result.size);
408 for (integer i = 1; i <= result.size; i ++) {
409 my integerVectorValue [i] = Melder_iround (result [i]);
410 Melder_require (my integerVectorValue [i] == result [i],
411 U"Element ", i, U" of vector “", my name.get(), U"” is ", result [i], U" but should be a whole number.");
412 }
413 } break; case kUi_integerVectorFormat::UNDEFINED: {
414 Melder_fatal (U"Unknown integer vector format.");
415 }
416 }
417 if (my type == _kUiField_type::NATURALVECTOR_)
418 for (integer i = 1; i <= my integerVectorValue.size; i ++)
419 if (my integerVectorValue [i] <= 0)
420 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my integerVectorValue [i], U" but should be greater than 0.");
421 if (my integerVectorVariable)
422 *my integerVectorVariable = my integerVectorValue.get();
423 }
424 break;
425 case _kUiField_type::REALMATRIX_:
426 {
427 autostring32 stringValue = GuiText_getString (my text);
428 MAT result;
429 bool ownedByInterpreter;
430 Interpreter_numericMatrixExpression (nullptr, stringValue.get(), & result, & ownedByInterpreter);
431 if (ownedByInterpreter) {
432 my numericMatrixValue. adoptFromAmbiguousOwner (result);
433 } else {
434 my numericMatrixValue = copy_MAT (result);
435 }
436 if (my numericMatrixVariable)
437 *my numericMatrixVariable = my numericMatrixValue.get();
438 }
439 break;
440 case _kUiField_type::STRINGARRAY_:
441 {
442 autostring32 stringValue = GuiText_getString (my text);
443 theStringArrayFormat = (kUi_stringArrayFormat) GuiOptionMenu_getValue (my optionMenu);
444 switch (theStringArrayFormat) {
445 case kUi_stringArrayFormat::WHITESPACE_SEPARATED_: {
446 my stringArrayValue = splitByWhitespace_STRVEC (stringValue.get());
447 } break; case kUi_stringArrayFormat::COMMA_SEPARATED_: {
448 my stringArrayValue = splitBy_STRVEC (stringValue.get(), U",");
449 } break; case kUi_stringArrayFormat::SEMICOLON_SEPARATED_: {
450 my stringArrayValue = splitBy_STRVEC (stringValue.get(), U";");
451 } break; case kUi_stringArrayFormat::PIPE_SEPARATED_: {
452 my stringArrayValue = splitBy_STRVEC (stringValue.get(), U"|");
453 } break; case kUi_stringArrayFormat::ONE_PER_LINE_: {
454 my stringArrayValue = splitBy_STRVEC (stringValue.get(), U"\n");
455 } break; case kUi_stringArrayFormat::FORMULA_: {
456 if (stringValue [0] == U'\0') {
457 my stringArrayValue = autoSTRVEC(); // interpret the empty string as zero elements, as for all other formats
458 } else {
459 STRVEC result;
460 bool ownedByInterpreter;
461 Interpreter_stringArrayExpression (nullptr, stringValue.get(), & result, & ownedByInterpreter);
462 if (ownedByInterpreter) {
463 my stringArrayValue. adoptFromAmbiguousOwner (result);
464 } else {
465 my stringArrayValue = copy_STRVEC (result);
466 }
467 }
468 } break; case kUi_stringArrayFormat::UNDEFINED: {
469 Melder_fatal (U"Unknown string array format.");
470 }
471 }
472 if (my stringArrayVariable)
473 *my stringArrayVariable = my stringArrayValue.get();
474 }
475 break;
476 case _kUiField_type::BOOLEAN_:
477 {
478 my integerValue = GuiCheckButton_getValue (my checkButton);
479 if (my boolVariable)
480 *my boolVariable = my integerValue;
481 }
482 break;
483 case _kUiField_type::RADIO_:
484 {
485 my integerValue = 0;
486 for (int i = 1; i <= my options.size; i ++) {
487 UiOption b = my options.at [i];
488 if (GuiRadioButton_getValue (b -> radioButton))
489 my integerValue = i;
490 }
491 if (my integerValue == 0)
492 Melder_throw (U"No option chosen for “", my name.get(), U"”.");
493 if (my intVariable)
494 *my intVariable = int (my integerValue) - my subtract;
495 if (my stringVariable)
496 *my stringVariable = my options.at [my integerValue] -> name.get();
497 }
498 break;
499 case _kUiField_type::OPTIONMENU_:
500 {
501 my integerValue = GuiOptionMenu_getValue (my optionMenu);
502 if (my integerValue == 0)
503 Melder_throw (U"No option chosen for “", my name.get(), U"”.");
504 if (my intVariable)
505 *my intVariable = int (my integerValue) - my subtract;
506 if (my stringVariable)
507 *my stringVariable = my options.at [my integerValue] -> name.get();
508 }
509 break;
510 case _kUiField_type::LIST_:
511 {
512 autoINTVEC selected = GuiList_getSelectedPositions (my list);
513 if (selected.size == 0) {
514 Melder_warning (U"No items selected.");
515 my integerValue = 1;
516 } else {
517 if (selected.size > 1)
518 Melder_warning (U"More than one item selected.");
519 my integerValue = selected [1];
520 }
521 if (my integerVariable)
522 *my integerVariable = my integerValue;
523 if (my stringVariable)
524 *my stringVariable = (char32 *) my strings [my integerValue];
525 }
526 break;
527 case _kUiField_type::COLOUR_:
528 {
529 autostring32 string = GuiText_getString (my text);
530 MelderColour colour = MelderColour_fromColourNameOrRGBString (string.get());
531 if (colour.valid()) {
532 my colourValue = colour;
533 } else {
534 double greyValue;
535 Interpreter_numericExpression (nullptr, string.get(), & greyValue);
536 my colourValue = MelderColour (greyValue);
537 }
538 if (my colourVariable)
539 *my colourVariable = my colourValue;
540 }
541 }
542 }
543
544 /***** History mechanism. *****/
545
546 static MelderString theHistory;
UiHistory_write(conststring32 string)547 void UiHistory_write (conststring32 string) {
548 MelderString_append (& theHistory, string);
549 }
UiHistory_write_expandQuotes(conststring32 string)550 void UiHistory_write_expandQuotes (conststring32 string) {
551 if (! string)
552 return;
553 for (const char32 *p = & string [0]; *p != U'\0'; p ++) {
554 if (*p == U'\"')
555 MelderString_append (& theHistory, U"\"\"");
556 else
557 MelderString_appendCharacter (& theHistory, *p);
558 }
559 }
UiHistory_write_colonize(conststring32 string)560 void UiHistory_write_colonize (conststring32 string) {
561 if (! string)
562 return;
563 for (const char32 *p = & string [0]; *p != U'\0'; p ++) {
564 if (*p == U'.' && p [1] == U'.' && p [2] == U'.') {
565 MelderString_append (& theHistory, U":");
566 p += 2;
567 } else {
568 MelderString_appendCharacter (& theHistory, *p);
569 }
570 }
571 }
UiHistory_get()572 char32 *UiHistory_get () {
573 return theHistory.string;
574 }
UiHistory_clear()575 void UiHistory_clear () {
576 MelderString_empty (& theHistory);
577 }
578
579 /***** class UiForm: dialog windows *****/
580
581 Thing_implement (UiForm, Thing, 0);
582
583 bool (*theAllowExecutionHookHint) (void *closure) = nullptr;
584 void *theAllowExecutionClosureHint = nullptr;
585
Ui_setAllowExecutionHook(bool (* allowExecutionHook)(void * closure),void * allowExecutionClosure)586 void Ui_setAllowExecutionHook (bool (*allowExecutionHook) (void *closure), void *allowExecutionClosure) {
587 theAllowExecutionHookHint = allowExecutionHook;
588 theAllowExecutionClosureHint = allowExecutionClosure;
589 }
590
v_destroy()591 void structUiForm :: v_destroy () noexcept {
592 if (our d_dialogForm) {
593 trace (U"form <<", our d_dialogForm -> name.get(), U">>, invoking-button title <<", our invokingButtonTitle.get(), U">>");
594 GuiObject_destroy (our d_dialogForm -> d_widget); // BUG: make sure this destroys the shell
595 }
596 our UiForm_Parent :: v_destroy ();
597 }
598
gui_button_cb_revert(UiForm me,GuiButtonEvent)599 static void gui_button_cb_revert (UiForm me, GuiButtonEvent /* event */) {
600 for (int ifield = 1; ifield <= my numberOfFields; ifield ++)
601 UiField_setDefault (my field [ifield].get());
602 }
603
gui_dialog_cb_close(UiForm me)604 static void gui_dialog_cb_close (UiForm me) {
605 if (my cancelCallback)
606 my cancelCallback (me, my buttonClosure);
607 GuiThing_hide (my d_dialogForm);
608 if (my destroyWhenUnmanaged)
609 forget (me);
610 }
gui_button_cb_cancel(UiForm me,GuiButtonEvent)611 static void gui_button_cb_cancel (UiForm me, GuiButtonEvent /* event */) {
612 if (my cancelCallback)
613 my cancelCallback (me, my buttonClosure);
614 GuiThing_hide (my d_dialogForm);
615 if (my destroyWhenUnmanaged)
616 forget (me);
617 }
618
UiForm_okOrApply(UiForm me,GuiButton button,int hide)619 static void UiForm_okOrApply (UiForm me, GuiButton button, int hide) {
620 if (my allowExecutionHook && ! my allowExecutionHook (my allowExecutionClosure)) {
621 Melder_flushError (U"Cannot execute command window “", my name.get(), U"”.");
622 return;
623 }
624 try {
625 for (int ifield = 1; ifield <= my numberOfFields; ifield ++)
626 UiField_widgetToValue (my field [ifield].get());
627 } catch (MelderError) {
628 Melder_flushError (U"Please correct command window “", my name.get(), U"” or cancel.");
629 return;
630 }
631 if (my okButton) GuiThing_setSensitive (my okButton, false);
632 if (my applyButton) GuiThing_setSensitive (my applyButton, false);
633 if (my cancelButton) GuiThing_setSensitive (my cancelButton, false);
634 if (my revertButton) GuiThing_setSensitive (my revertButton, false);
635 if (my helpButton) GuiThing_setSensitive (my helpButton, false);
636 for (int i = 1; i <= my numberOfContinueButtons; i ++)
637 if (my continueButtons [i])
638 GuiThing_setSensitive (my continueButtons [i], false);
639
640 #if defined (_WIN32)
641 GdiFlush ();
642 #endif
643 if (my isPauseForm) {
644 for (int i = 1; i <= my numberOfContinueButtons; i ++)
645 if (button == my continueButtons [i])
646 my clickedContinueButton = i;
647 }
648 /*
649 Keep the gate for error handling.
650 */
651 try {
652 my okCallback (me, 0, nullptr, nullptr, nullptr, nullptr, false, my buttonClosure);
653 /*
654 Write everything to history. Before destruction!
655 */
656 if (! my isPauseForm) {
657 UiHistory_write (U"\n");
658 UiHistory_write_colonize (my invokingButtonTitle.get());
659 int size = my numberOfFields;
660 while (size >= 1 && my field [size] -> type == _kUiField_type::LABEL_)
661 size --; // ignore trailing fields without a value
662 int next = 0;
663 for (int ifield = 1; ifield <= size; ifield ++) {
664 UiField field = my field [ifield].get();
665 switch (field -> type)
666 {
667 case _kUiField_type::LABEL_:
668 {
669 // do nothing
670 }
671 break;
672 case _kUiField_type::REAL_:
673 case _kUiField_type::REAL_OR_UNDEFINED_:
674 case _kUiField_type::POSITIVE_:
675 {
676 UiHistory_write (next -- ? U", " : U" ");
677 UiHistory_write (Melder_double (field -> realValue));
678 }
679 break;
680 case _kUiField_type::INTEGER_:
681 case _kUiField_type::NATURAL_:
682 case _kUiField_type::CHANNEL_:
683 {
684 UiHistory_write (next -- ? U", " : U" ");
685 UiHistory_write (Melder_integer (field -> integerValue));
686 }
687 break;
688 case _kUiField_type::WORD_:
689 case _kUiField_type::SENTENCE_:
690 case _kUiField_type::TEXT_:
691 case _kUiField_type::FORMULA_:
692 case _kUiField_type::INFILE_:
693 case _kUiField_type::OUTFILE_:
694 case _kUiField_type::FOLDER_:
695 {
696 UiHistory_write (next -- ? U", \"" : U" \"");
697 UiHistory_write_expandQuotes (field -> stringValue.get());
698 UiHistory_write (U"\"");
699 }
700 break;
701 case _kUiField_type:: REALVECTOR_:
702 case _kUiField_type:: POSITIVEVECTOR_:
703 {
704 if (NUMisEmpty (field -> realVectorValue.get())) {
705 UiHistory_write (next -- ? U", zero# (0)" : U" zero# (0)");
706 } else {
707 UiHistory_write (next -- ? U", { " : U" { ");
708 for (integer i = 1; i <= field -> realVectorValue.size; i ++) {
709 UiHistory_write (Melder_double (field -> realVectorValue [i]));
710 UiHistory_write (i == field -> realVectorValue.size ? U" }" : U", ");
711 }
712 }
713 } break;
714 case _kUiField_type:: INTEGERVECTOR_:
715 case _kUiField_type:: NATURALVECTOR_:
716 {
717 if (NUMisEmpty (field -> integerVectorValue.get())) {
718 UiHistory_write (next -- ? U", zero# (0)" : U" zero# (0)");
719 } else {
720 UiHistory_write (next -- ? U", { " : U" { ");
721 for (integer i = 1; i <= field -> integerVectorValue.size; i ++) {
722 UiHistory_write (Melder_integer (field -> integerVectorValue [i]));
723 UiHistory_write (i == field -> integerVectorValue.size ? U" }" : U", ");
724 }
725 }
726 } break;
727 case _kUiField_type:: REALMATRIX_:
728 {
729 if (NUMisEmpty (field -> numericMatrixValue.get())) {
730 UiHistory_write (next -- ? U", zero## (0, 0)" : U" zero## (0, 0)");
731 } else {
732 UiHistory_write (next -- ? U", { " : U" { ");
733 for (integer irow = 1; irow <= field -> numericMatrixValue.nrow; irow ++) {
734 UiHistory_write (U"{ ");
735 for (integer icol = 1; icol <= field -> numericMatrixValue.ncol; icol ++) {
736 UiHistory_write (Melder_double (field -> numericMatrixValue [irow] [icol]));
737 UiHistory_write (icol == field -> numericMatrixValue.ncol ? U" }" : U", ");
738 }
739 UiHistory_write (irow == field -> numericMatrixValue.nrow ? U" }" : U", ");
740 }
741 }
742 } break;
743 case _kUiField_type:: STRINGARRAY_:
744 {
745 if (NUMisEmpty (field -> stringArrayValue.get())) {
746 UiHistory_write_expandQuotes (next -- ? U", empty$# (0)" : U" empty$# (0)");
747 } else {
748 UiHistory_write (next -- ? U", { " : U" { ");
749 for (integer i = 1; i <= field -> stringArrayValue.size; i ++) {
750 UiHistory_write (U"\"");
751 UiHistory_write_expandQuotes (field -> stringArrayValue [i].get());
752 UiHistory_write (U"\"");
753 UiHistory_write (i == field -> stringArrayValue.size ? U" }" : U", ");
754 }
755 }
756 } break;
757 case _kUiField_type::BOOLEAN_:
758 {
759 UiHistory_write (field -> integerValue ? (next -- ? U", \"yes\"" : U" \"yes\"") : (next -- ? U", \"no\"" : U" \"no\""));
760 }
761 break;
762 case _kUiField_type::RADIO_:
763 case _kUiField_type::OPTIONMENU_:
764 {
765 UiOption b = field -> options.at [field -> integerValue];
766 UiHistory_write (next -- ? U", \"" : U" \"");
767 UiHistory_write_expandQuotes (b -> name.get());
768 UiHistory_write (U"\"");
769 }
770 break;
771 case _kUiField_type::LIST_:
772 {
773 UiHistory_write (next -- ? U", \"" : U" \"");
774 UiHistory_write_expandQuotes (field -> strings [field -> integerValue]);
775 UiHistory_write (U"\"");
776 }
777 break;
778 case _kUiField_type::COLOUR_:
779 {
780 conststring32 colourString = MelderColour_namePrettyOrNull (field -> colourValue);
781 if (colourString) {
782 UiHistory_write (next -- ? U", \"" : U" \"");
783 UiHistory_write (colourString);
784 UiHistory_write (U"\"");
785 } else if (field -> colourValue. isGrey()) {
786 UiHistory_write (next -- ? U", " : U" ");
787 UiHistory_write (Melder_double (field -> colourValue. red));
788 } else {
789 colourString = MelderColour_nameRGB (field -> colourValue);
790 UiHistory_write (next -- ? U", " : U" ");
791 UiHistory_write (colourString);
792 }
793 }
794 }
795 }
796 }
797 if (hide) {
798 GuiThing_hide (my d_dialogForm);
799 if (my destroyWhenUnmanaged) {
800 forget (me);
801 return;
802 }
803 }
804 } catch (MelderError) {
805 /*
806 If a solution has already been suggested, or the "error" was actually a conscious user action, do not add anything more.
807 */
808 if (! str32str (Melder_getError (), U"Please ") && ! str32str (Melder_getError (), U"You could ") &&
809 ! str32str (Melder_getError (), U"You interrupted ") && ! str32str (Melder_getError (), U"Interrupted!"))
810 {
811 /*
812 Otherwise, show a generic message.
813 */
814 if (str32str (Melder_getError (), U"Selection changed!")) {
815 Melder_appendError (U"Please change the selection in the object list, or click Cancel in the command window “",
816 my name.get(), U"”.");
817 } else {
818 Melder_appendError (U"Please change something in the command window “",
819 my name.get(), U"”, or click Cancel in that window.");
820 }
821 }
822 Melder_flushError ();
823 }
824 if (my okButton) GuiThing_setSensitive (my okButton, true);
825 if (my applyButton) GuiThing_setSensitive (my applyButton, true);
826 if (my cancelButton) GuiThing_setSensitive (my cancelButton, true);
827 if (my revertButton) GuiThing_setSensitive (my revertButton, true);
828 if (my helpButton) GuiThing_setSensitive (my helpButton, true);
829 for (int i = 1; i <= my numberOfContinueButtons; i ++)
830 if (my continueButtons [i])
831 GuiThing_setSensitive (my continueButtons [i], true);
832 }
833
gui_button_cb_ok(UiForm me,GuiButtonEvent event)834 static void gui_button_cb_ok (UiForm me, GuiButtonEvent event) {
835 UiForm_okOrApply (me, event -> button, true);
836 }
837
gui_dialog_cb_default(UiForm me)838 static void gui_dialog_cb_default (UiForm me) {
839 UiForm_okOrApply (me,
840 my defaultContinueButton >= 1 && my defaultContinueButton <= my numberOfContinueButtons ? my continueButtons [my defaultContinueButton] : nullptr,
841 true
842 );
843 }
844
gui_button_cb_apply(UiForm me,GuiButtonEvent event)845 static void gui_button_cb_apply (UiForm me, GuiButtonEvent event) {
846 UiForm_okOrApply (me, event -> button, false);
847 }
848
gui_button_cb_help(UiForm me,GuiButtonEvent)849 static void gui_button_cb_help (UiForm me, GuiButtonEvent /* event */) {
850 Melder_help (my helpTitle.get());
851 }
852
gui_button_cb_browseInfile(UiField me,GuiButtonEvent)853 static void gui_button_cb_browseInfile (UiField me, GuiButtonEvent /* event */) {
854 autoStringSet chosenFilePath = GuiFileSelect_getInfileNames (nullptr, U"Open file", false);
855 if (chosenFilePath->size != 0)
856 GuiText_setString (my text, chosenFilePath->at [1] -> string.get());
857 }
gui_button_cb_browseOutfile(UiField me,GuiButtonEvent)858 static void gui_button_cb_browseOutfile (UiField me, GuiButtonEvent /* event */) {
859 autostring32 chosenFilePath = GuiFileSelect_getOutfileName (nullptr, U"Save file", U"");
860 if (chosenFilePath)
861 GuiText_setString (my text, chosenFilePath.get());
862 }
gui_button_cb_browseFolder(UiField me,GuiButtonEvent)863 static void gui_button_cb_browseFolder (UiField me, GuiButtonEvent /* event */) {
864 autostring32 chosenFolderPath = GuiFileSelect_getFolderName (nullptr, U"Choose folder");
865 if (chosenFolderPath)
866 GuiText_setString (my text, chosenFolderPath.get());
867 }
868
UiForm_create(GuiWindow parent,conststring32 title,UiCallback okCallback,void * buttonClosure,conststring32 invokingButtonTitle,conststring32 helpTitle)869 autoUiForm UiForm_create (GuiWindow parent, conststring32 title,
870 UiCallback okCallback, void *buttonClosure,
871 conststring32 invokingButtonTitle, conststring32 helpTitle)
872 {
873 autoUiForm me = Thing_new (UiForm);
874 my d_dialogParent = parent;
875 Thing_setName (me.get(), title);
876 my okCallback = okCallback;
877 my buttonClosure = buttonClosure;
878 my invokingButtonTitle = Melder_dup (invokingButtonTitle);
879 my helpTitle = Melder_dup (helpTitle);
880 return me;
881 }
882
UiForm_setPauseForm(UiForm me,int numberOfContinueButtons,int defaultContinueButton,int cancelContinueButton,conststring32 continue1,conststring32 continue2,conststring32 continue3,conststring32 continue4,conststring32 continue5,conststring32 continue6,conststring32 continue7,conststring32 continue8,conststring32 continue9,conststring32 continue10,void (* cancelCallback)(UiForm dia,void * closure))883 void UiForm_setPauseForm (UiForm me,
884 int numberOfContinueButtons, int defaultContinueButton, int cancelContinueButton,
885 conststring32 continue1, conststring32 continue2, conststring32 continue3,
886 conststring32 continue4, conststring32 continue5, conststring32 continue6,
887 conststring32 continue7, conststring32 continue8, conststring32 continue9,
888 conststring32 continue10,
889 void (*cancelCallback) (UiForm dia, void *closure))
890 {
891 my isPauseForm = true;
892 my numberOfContinueButtons = numberOfContinueButtons;
893 my defaultContinueButton = defaultContinueButton;
894 my cancelContinueButton = cancelContinueButton;
895 my continueTexts [1] = continue1;
896 my continueTexts [2] = continue2;
897 my continueTexts [3] = continue3;
898 my continueTexts [4] = continue4;
899 my continueTexts [5] = continue5;
900 my continueTexts [6] = continue6;
901 my continueTexts [7] = continue7;
902 my continueTexts [8] = continue8;
903 my continueTexts [9] = continue9;
904 my continueTexts [10] = continue10;
905 my cancelCallback = cancelCallback;
906 }
907
commonOkCallback(UiForm,integer,Stackel,conststring32,Interpreter interpreter,conststring32,bool,void * closure)908 static void commonOkCallback (UiForm /* dia */, integer /* narg */, Stackel /* args */, conststring32 /* sendingString */,
909 Interpreter interpreter, conststring32 /* invokingButtonTitle */, bool /* modified */, void *closure)
910 {
911 EditorCommand cmd = (EditorCommand) closure;
912 cmd -> commandCallback (cmd -> d_editor, cmd, cmd -> d_uiform.get(), 0, nullptr, nullptr, interpreter);
913 }
914
UiForm_createE(EditorCommand cmd,conststring32 title,conststring32 invokingButtonTitle,conststring32 helpTitle)915 autoUiForm UiForm_createE (EditorCommand cmd, conststring32 title, conststring32 invokingButtonTitle, conststring32 helpTitle) {
916 Editor editor = cmd -> d_editor;
917 autoUiForm dia (UiForm_create (editor -> windowForm, title, commonOkCallback, cmd, invokingButtonTitle, helpTitle));
918 dia -> command = cmd;
919 return dia;
920 }
921
UiForm_addField(UiForm me,_kUiField_type type,conststring32 label)922 static UiField UiForm_addField (UiForm me, _kUiField_type type, conststring32 label) {
923 if (my numberOfFields == MAXIMUM_NUMBER_OF_FIELDS)
924 Melder_throw (U"Cannot have more than ", MAXIMUM_NUMBER_OF_FIELDS, U"fields in a form.");
925 my field [++ my numberOfFields] = UiField_create (type, label);
926 return my field [my numberOfFields].get();
927 }
928
UiForm_addReal(UiForm me,double * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)929 UiField UiForm_addReal (UiForm me, double *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
930 UiField thee = UiForm_addField (me, _kUiField_type::REAL_, label);
931 thy stringDefaultValue = Melder_dup (defaultValue);
932 thy realVariable = variable;
933 thy variableName = variableName;
934 return thee;
935 }
936
UiForm_addRealOrUndefined(UiForm me,double * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)937 UiField UiForm_addRealOrUndefined (UiForm me, double *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
938 UiField thee = UiForm_addField (me, _kUiField_type::REAL_OR_UNDEFINED_, label);
939 thy stringDefaultValue = Melder_dup (defaultValue);
940 thy realVariable = variable;
941 thy variableName = variableName;
942 return thee;
943 }
944
UiForm_addPositive(UiForm me,double * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)945 UiField UiForm_addPositive (UiForm me, double *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
946 UiField thee = UiForm_addField (me, _kUiField_type::POSITIVE_, label);
947 thy stringDefaultValue = Melder_dup (defaultValue);
948 thy realVariable = variable;
949 thy variableName = variableName;
950 return thee;
951 }
952
UiForm_addInteger(UiForm me,integer * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)953 UiField UiForm_addInteger (UiForm me, integer *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
954 UiField thee = UiForm_addField (me, _kUiField_type::INTEGER_, label);
955 thy stringDefaultValue = Melder_dup (defaultValue);
956 thy integerVariable = variable;
957 thy variableName = variableName;
958 return thee;
959 }
960
UiForm_addNatural(UiForm me,integer * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)961 UiField UiForm_addNatural (UiForm me, integer *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
962 UiField thee = UiForm_addField (me, _kUiField_type::NATURAL_, label);
963 thy stringDefaultValue = Melder_dup (defaultValue);
964 thy integerVariable = variable;
965 thy variableName = variableName;
966 return thee;
967 }
968
UiForm_addWord(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)969 UiField UiForm_addWord (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
970 UiField thee = UiForm_addField (me, _kUiField_type::WORD_, label);
971 thy stringDefaultValue = Melder_dup (defaultValue);
972 thy stringVariable = variable;
973 thy variableName = variableName;
974 return thee;
975 }
976
UiForm_addSentence(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)977 UiField UiForm_addSentence (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
978 UiField thee = UiForm_addField (me, _kUiField_type::SENTENCE_, label);
979 thy stringDefaultValue = Melder_dup (defaultValue);
980 thy stringVariable = variable;
981 thy variableName = variableName;
982 return thee;
983 }
984
UiForm_addLabel(UiForm me,conststring32 * variable,conststring32 label)985 UiField UiForm_addLabel (UiForm me, conststring32 *variable, conststring32 label) {
986 UiField thee = UiForm_addField (me, _kUiField_type::LABEL_, U""); // this field gets no name; so that the user can give it any title
987 thy stringVariable = variable;
988 thy stringValue = Melder_dup (label);
989 return thee;
990 }
991
UiForm_addBoolean(UiForm me,bool * variable,conststring32 variableName,conststring32 label,bool defaultValue)992 UiField UiForm_addBoolean (UiForm me, bool *variable, conststring32 variableName, conststring32 label, bool defaultValue) {
993 UiField thee = UiForm_addField (me, _kUiField_type::BOOLEAN_, label);
994 thy integerDefaultValue = defaultValue;
995 thy boolVariable = variable;
996 thy variableName = variableName;
997 return thee;
998 }
999
UiForm_addText(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue,integer numberOfLines)1000 UiField UiForm_addText (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue, integer numberOfLines) {
1001 UiField thee = UiForm_addField (me, _kUiField_type::TEXT_, label);
1002 thy stringDefaultValue = Melder_dup (defaultValue);
1003 thy stringVariable = variable;
1004 thy variableName = variableName;
1005 thy numberOfLines = Melder_clipped (1_integer, numberOfLines, 33_integer);
1006 return thee;
1007 }
1008
UiForm_addFormula(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1009 UiField UiForm_addFormula (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1010 UiField thee = UiForm_addField (me, _kUiField_type::FORMULA_, label);
1011 thy stringDefaultValue = Melder_dup (defaultValue);
1012 thy stringVariable = variable;
1013 thy variableName = variableName;
1014 thy numberOfLines = 5;
1015 return thee;
1016 }
1017
UiForm_addInfile(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1018 UiField UiForm_addInfile (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1019 UiField thee = UiForm_addField (me, _kUiField_type::INFILE_, label);
1020 thy stringDefaultValue = Melder_dup (defaultValue);
1021 thy stringVariable = variable;
1022 thy variableName = variableName;
1023 thy numberOfLines = 3;
1024 return thee;
1025 }
1026
UiForm_addOutfile(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1027 UiField UiForm_addOutfile (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1028 UiField thee = UiForm_addField (me, _kUiField_type::OUTFILE_, label);
1029 thy stringDefaultValue = Melder_dup (defaultValue);
1030 thy stringVariable = variable;
1031 thy variableName = variableName;
1032 thy numberOfLines = 3;
1033 return thee;
1034 }
1035
UiForm_addFolder(UiForm me,conststring32 * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1036 UiField UiForm_addFolder (UiForm me, conststring32 *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1037 UiField thee = UiForm_addField (me, _kUiField_type::FOLDER_, label);
1038 thy stringDefaultValue = Melder_dup (defaultValue);
1039 thy stringVariable = variable;
1040 thy variableName = variableName;
1041 thy numberOfLines = 3;
1042 return thee;
1043 }
1044
UiForm_addRealVector(UiForm me,constVEC * variable,conststring32 variableName,conststring32 label,kUi_realVectorFormat defaultFormat,conststring32 defaultValue)1045 UiField UiForm_addRealVector (UiForm me, constVEC *variable, conststring32 variableName, conststring32 label, kUi_realVectorFormat defaultFormat, conststring32 defaultValue) {
1046 UiField thee = UiForm_addField (me, _kUiField_type::REALVECTOR_, label);
1047 thy realVectorDefaultFormat = defaultFormat;
1048 thy stringDefaultValue = Melder_dup (defaultValue);
1049 thy realVectorVariable = variable;
1050 thy variableName = variableName;
1051 thy numberOfLines = 7;
1052 return thee;
1053 }
1054
UiForm_addPositiveVector(UiForm me,constVEC * variable,conststring32 variableName,conststring32 label,kUi_realVectorFormat defaultFormat,conststring32 defaultValue)1055 UiField UiForm_addPositiveVector (UiForm me, constVEC *variable, conststring32 variableName, conststring32 label, kUi_realVectorFormat defaultFormat, conststring32 defaultValue) {
1056 UiField thee = UiForm_addField (me, _kUiField_type::POSITIVEVECTOR_, label);
1057 thy realVectorDefaultFormat = defaultFormat;
1058 thy stringDefaultValue = Melder_dup (defaultValue);
1059 thy realVectorVariable = variable;
1060 thy variableName = variableName;
1061 thy numberOfLines = 7;
1062 return thee;
1063 }
1064
UiForm_addIntegerVector(UiForm me,constINTVEC * variable,conststring32 variableName,conststring32 label,kUi_integerVectorFormat defaultFormat,conststring32 defaultValue)1065 UiField UiForm_addIntegerVector (UiForm me, constINTVEC *variable, conststring32 variableName, conststring32 label, kUi_integerVectorFormat defaultFormat, conststring32 defaultValue) {
1066 UiField thee = UiForm_addField (me, _kUiField_type::INTEGERVECTOR_, label);
1067 thy integerVectorDefaultFormat = defaultFormat;
1068 thy stringDefaultValue = Melder_dup (defaultValue);
1069 thy integerVectorVariable = variable;
1070 thy variableName = variableName;
1071 thy numberOfLines = 7;
1072 return thee;
1073 }
1074
UiForm_addNaturalVector(UiForm me,constINTVEC * variable,conststring32 variableName,conststring32 label,kUi_integerVectorFormat defaultFormat,conststring32 defaultValue)1075 UiField UiForm_addNaturalVector (UiForm me, constINTVEC *variable, conststring32 variableName, conststring32 label, kUi_integerVectorFormat defaultFormat, conststring32 defaultValue) {
1076 UiField thee = UiForm_addField (me, _kUiField_type::NATURALVECTOR_, label);
1077 thy integerVectorDefaultFormat = defaultFormat;
1078 thy stringDefaultValue = Melder_dup (defaultValue);
1079 thy integerVectorVariable = variable;
1080 thy variableName = variableName;
1081 thy numberOfLines = 7;
1082 return thee;
1083 }
1084
UiForm_addRealMatrix(UiForm me,constMAT * variable,conststring32 variableName,conststring32 label,constMATVU defaultValue)1085 UiField UiForm_addRealMatrix (UiForm me, constMAT *variable, conststring32 variableName, conststring32 label, constMATVU defaultValue) {
1086 UiField thee = UiForm_addField (me, _kUiField_type::REALMATRIX_, label);
1087 thy numericMatrixDefaultValue = copy_MAT (defaultValue);
1088 thy numericMatrixVariable = variable;
1089 thy variableName = variableName;
1090 thy numberOfLines = 10;
1091 return thee;
1092 }
1093
UiForm_addStringArray(UiForm me,constSTRVEC * variable,conststring32 variableName,conststring32 label,constSTRVEC defaultValue,integer numberOfLines)1094 UiField UiForm_addStringArray (UiForm me, constSTRVEC *variable, conststring32 variableName, conststring32 label, constSTRVEC defaultValue, integer numberOfLines) {
1095 UiField thee = UiForm_addField (me, _kUiField_type::STRINGARRAY_, label);
1096 thy stringArrayDefaultValue = copy_STRVEC (defaultValue);
1097 thy stringArrayFormat = theStringArrayFormat;
1098 thy stringArrayVariable = variable;
1099 thy variableName = variableName;
1100 thy numberOfLines = numberOfLines;
1101 return thee;
1102 }
1103
UiForm_addRadio(UiForm me,int * intVariable,conststring32 * stringVariable,conststring32 variableName,conststring32 label,int defaultValue,int base)1104 UiField UiForm_addRadio (UiForm me, int *intVariable, conststring32 *stringVariable, conststring32 variableName, conststring32 label, int defaultValue, int base) {
1105 UiField thee = UiForm_addField (me, _kUiField_type::RADIO_, label);
1106 thy integerDefaultValue = defaultValue;
1107 thy intVariable = intVariable;
1108 thy stringVariable = stringVariable;
1109 thy variableName = variableName;
1110 thy subtract = ( base == 1 ? 0 : 1 );
1111 return thee;
1112 }
1113
UiForm_addOptionMenu(UiForm me,int * intVariable,conststring32 * stringVariable,conststring32 variableName,conststring32 label,int defaultValue,int base)1114 UiField UiForm_addOptionMenu (UiForm me, int *intVariable, conststring32 *stringVariable, conststring32 variableName, conststring32 label, int defaultValue, int base) {
1115 UiField thee = UiForm_addField (me, _kUiField_type::OPTIONMENU_, label);
1116 thy integerDefaultValue = defaultValue;
1117 thy intVariable = intVariable;
1118 thy stringVariable = stringVariable;
1119 thy variableName = variableName;
1120 thy subtract = ( base == 1 ? 0 : 1 );
1121 return thee;
1122 }
1123
UiForm_addList(UiForm me,integer * integerVariable,conststring32 * stringVariable,conststring32 variableName,conststring32 label,constSTRVEC strings,integer defaultValue)1124 UiField UiForm_addList (UiForm me, integer *integerVariable, conststring32 *stringVariable, conststring32 variableName, conststring32 label, constSTRVEC strings, integer defaultValue) {
1125 UiField thee = UiForm_addField (me, _kUiField_type::LIST_, label);
1126 thy strings = strings;
1127 thy integerDefaultValue = defaultValue;
1128 thy integerVariable = integerVariable;
1129 thy stringVariable = stringVariable;
1130 thy variableName = variableName;
1131 return thee;
1132 }
1133
UiForm_addColour(UiForm me,MelderColour * colourVariable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1134 UiField UiForm_addColour (UiForm me, MelderColour *colourVariable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1135 UiField thee = UiForm_addField (me, _kUiField_type::COLOUR_, label);
1136 thy stringDefaultValue = Melder_dup (defaultValue);
1137 thy colourVariable = colourVariable;
1138 thy variableName = variableName;
1139 return thee;
1140 }
1141
UiForm_addChannel(UiForm me,integer * variable,conststring32 variableName,conststring32 label,conststring32 defaultValue)1142 UiField UiForm_addChannel (UiForm me, integer *variable, conststring32 variableName, conststring32 label, conststring32 defaultValue) {
1143 UiField thee = UiForm_addField (me, _kUiField_type::CHANNEL_, label);
1144 thy stringDefaultValue = Melder_dup (defaultValue);
1145 thy integerVariable = variable;
1146 thy variableName = variableName;
1147 return thee;
1148 }
1149
1150 #define DIALOG_X 150
1151 #define DIALOG_Y 70
1152 #define HELP_BUTTON_WIDTH 60
1153 #define STANDARDS_BUTTON_WIDTH 100
1154 #define REVERT_BUTTON_WIDTH 60
1155 #define STOP_BUTTON_WIDTH 50
1156 #define HELP_BUTTON_X 20
1157 #define LIST_HEIGHT 192
1158
1159 static MelderString theFinishBuffer;
1160
appendColon()1161 static void appendColon () {
1162 integer length = theFinishBuffer.length;
1163 if (length < 1)
1164 return;
1165 char32 lastCharacter = theFinishBuffer.string [length - 1];
1166 if (lastCharacter == U':' || lastCharacter == U'?' || lastCharacter == U'.')
1167 return;
1168 MelderString_appendCharacter (& theFinishBuffer, U':');
1169 }
1170
multiLineTextHeight(integer numberOfLines)1171 static int multiLineTextHeight (integer numberOfLines) {
1172 if (numberOfLines <= 1)
1173 return Gui_TEXTFIELD_HEIGHT;
1174 #if defined (macintosh)
1175 return numberOfLines * (Gui_TEXTFIELD_HEIGHT - 9) + 21; // 15 is the minimum, but 21 gives some feedback about what is outside (2020-10-19)
1176 #elif defined (_WIN32)
1177 return numberOfLines * (Gui_TEXTFIELD_HEIGHT - 4) + 21; // 21 is the mininum (2020-10-19)
1178 #elif defined (linux)
1179 return numberOfLines * (Gui_TEXTFIELD_HEIGHT - 8) + 15; // 13 is the minimum, but if there is no horizontal scrollbar, there is a line more (2020-10-19)
1180 #else
1181 return numberOfLines * Gui_TEXTFIELD_HEIGHT;
1182 #endif
1183 }
1184
UiForm_finish(UiForm me)1185 void UiForm_finish (UiForm me) {
1186 if (! my d_dialogParent && ! my isPauseForm)
1187 return;
1188
1189 int size = my numberOfFields;
1190 int dialogHeight = 0;
1191 const int textFieldHeight = Gui_TEXTFIELD_HEIGHT;
1192 const int headerLabelHeight = textFieldHeight
1193 #ifdef _WIN32
1194 - 6;
1195 #else
1196 - 10;
1197 #endif
1198 int dialogWidth = 520, dialogCentre = dialogWidth / 2, fieldX = dialogCentre + Gui_LABEL_SPACING / 2;
1199 int labelWidth = fieldX - Gui_LABEL_SPACING - Gui_LEFT_DIALOG_SPACING, fieldWidth = labelWidth, halfFieldWidth = fieldWidth / 2 - 6;
1200
1201 GuiForm form;
1202 bool okButtonIsDefault = true;
1203
1204 /*
1205 Compute height. Cannot leave this to the default geometry management system.
1206 */
1207 for (integer ifield = 1; ifield <= my numberOfFields; ifield ++ ) {
1208 UiField thee = my field [ifield].get(), previous = my field [ifield - 1].get();
1209 dialogHeight +=
1210 ifield == 1 ?
1211 Gui_TOP_DIALOG_SPACING
1212 : thy type == _kUiField_type::RADIO_ || previous -> type == _kUiField_type::RADIO_ ?
1213 Gui_VERTICAL_DIALOG_SPACING_DIFFERENT
1214 : thy type >= _kUiField_type::LABELLED_TEXT_MIN_ && thy type <= _kUiField_type::LABELLED_TEXT_MAX_ && str32nequ (thy name.get(), U"right ", 6) &&
1215 previous -> type >= _kUiField_type::LABELLED_TEXT_MIN_ && previous -> type <= _kUiField_type::LABELLED_TEXT_MAX_ &&
1216 str32nequ (previous -> name.get(), U"left ", 5) ?
1217 - textFieldHeight
1218 :
1219 Gui_VERTICAL_DIALOG_SPACING_SAME;
1220 const bool thouHastVerticallyAddedLabel =
1221 thy type == _kUiField_type::TEXT_ || thy type == _kUiField_type::FORMULA_ ||
1222 thy type == _kUiField_type::INFILE_ || thy type == _kUiField_type::OUTFILE_ || thy type == _kUiField_type::FOLDER_ ||
1223 thy type == _kUiField_type::REALVECTOR_ || thy type == _kUiField_type::POSITIVEVECTOR_ ||
1224 thy type == _kUiField_type::INTEGERVECTOR_ || thy type == _kUiField_type::NATURALVECTOR_ ||
1225 thy type == _kUiField_type::REALMATRIX_ ||
1226 thy type == _kUiField_type::STRINGARRAY_
1227 ;
1228 if (thouHastVerticallyAddedLabel)
1229 dialogHeight += (headerLabelHeight + Gui_VERTICAL_DIALOG_SPACING_SAME) * !! thy formLabel;
1230 thy y = dialogHeight;
1231 dialogHeight +=
1232 thy type == _kUiField_type::BOOLEAN_ ?
1233 Gui_CHECKBUTTON_HEIGHT
1234 : thy type == _kUiField_type::RADIO_ ?
1235 thy options.size * Gui_RADIOBUTTON_HEIGHT + (thy options.size - 1) * Gui_RADIOBUTTON_SPACING
1236 : thy type == _kUiField_type::OPTIONMENU_ ?
1237 Gui_OPTIONMENU_HEIGHT
1238 : thy type == _kUiField_type::LIST_ ?
1239 LIST_HEIGHT
1240 : thy type == _kUiField_type::LABEL_ && thy stringValue [0] != U'\0' && thy stringValue [str32len (thy stringValue.get()) - 1] != U'.' && ifield != my numberOfFields ?
1241 headerLabelHeight
1242 : thouHastVerticallyAddedLabel ?
1243 multiLineTextHeight (thy numberOfLines)
1244 :
1245 textFieldHeight;
1246 }
1247 dialogHeight += 2 * Gui_BOTTOM_DIALOG_SPACING + Gui_PUSHBUTTON_HEIGHT;
1248 my d_dialogForm = GuiDialog_create (my d_dialogParent, DIALOG_X, DIALOG_Y, dialogWidth, dialogHeight, my name.get(), gui_dialog_cb_close, me, 0);
1249 GuiDialog_setDefaultCallback (my d_dialogForm, gui_dialog_cb_default, me);
1250
1251 form = my d_dialogForm;
1252
1253 for (integer ifield = 1; ifield <= size; ifield ++) {
1254 UiField thee = my field [ifield].get();
1255 switch (thy type)
1256 {
1257 case _kUiField_type::REAL_:
1258 case _kUiField_type::REAL_OR_UNDEFINED_:
1259 case _kUiField_type::POSITIVE_:
1260 case _kUiField_type::INTEGER_:
1261 case _kUiField_type::NATURAL_:
1262 case _kUiField_type::WORD_:
1263 case _kUiField_type::SENTENCE_:
1264 case _kUiField_type::COLOUR_:
1265 case _kUiField_type::CHANNEL_:
1266 {
1267 int ylabel = thy y;
1268 #if defined (macintosh)
1269 ylabel += 3;
1270 #endif
1271 if (str32nequ (thy name.get(), U"left ", 5)) {
1272 MelderString_copy (& theFinishBuffer, thy formLabel.get() + 5);
1273 appendColon ();
1274 thy label = GuiLabel_createShown (form, 0, Gui_LEFT_DIALOG_SPACING + labelWidth, ylabel, ylabel + textFieldHeight,
1275 theFinishBuffer.string, GuiLabel_RIGHT);
1276 thy text = GuiText_createShown (form, fieldX, fieldX + halfFieldWidth, thy y, thy y + Gui_TEXTFIELD_HEIGHT, 0);
1277 } else if (str32nequ (thy name.get(), U"right ", 6)) {
1278 thy text = GuiText_createShown (form, fieldX + halfFieldWidth + 12, fieldX + fieldWidth,
1279 thy y, thy y + Gui_TEXTFIELD_HEIGHT, 0);
1280 } else {
1281 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1282 appendColon ();
1283 thy label = GuiLabel_createShown (form, 0, Gui_LEFT_DIALOG_SPACING + labelWidth,
1284 ylabel, ylabel + textFieldHeight,
1285 theFinishBuffer.string, GuiLabel_RIGHT);
1286 thy text = GuiText_createShown (form, fieldX, fieldX + fieldWidth, // or once the dialog is a Form: - Gui_RIGHT_DIALOG_SPACING,
1287 thy y, thy y + Gui_TEXTFIELD_HEIGHT, 0);
1288 }
1289 }
1290 break;
1291 case _kUiField_type::TEXT_:
1292 {
1293 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1294 appendColon ();
1295 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1296 thy label = GuiLabel_createShown (form,
1297 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1298 ylabel, ylabel + textFieldHeight,
1299 theFinishBuffer.string, 0
1300 );
1301 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1302 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_INKWRAP | GuiText_SCROLLED);
1303 }
1304 break;
1305 case _kUiField_type::REALVECTOR_:
1306 case _kUiField_type::POSITIVEVECTOR_:
1307 {
1308 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1309 appendColon ();
1310 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1311 thy label = GuiLabel_createShown (form,
1312 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1313 ylabel, ylabel + textFieldHeight,
1314 theFinishBuffer.string, 0
1315 );
1316 thy optionMenu = GuiOptionMenu_createShown (form,
1317 dialogWidth - Gui_LEFT_DIALOG_SPACING - 200, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1318 thy y - Gui_OPTIONMENU_HEIGHT, thy y, 0);
1319 for (int format = (int) kUi_realVectorFormat::MIN; format <= (int) kUi_realVectorFormat::MAX; format ++)
1320 GuiOptionMenu_addOption (thy optionMenu, kUi_realVectorFormat_getText ((kUi_realVectorFormat) format));
1321 //GuiOptionMenu_setValue (thy optionMenu, (int) thy realVectorDefaultFormat); // SUPERFLUOUS
1322 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1323 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_INKWRAP | GuiText_SCROLLED);
1324 }
1325 break;
1326 case _kUiField_type::INTEGERVECTOR_:
1327 case _kUiField_type::NATURALVECTOR_:
1328 {
1329 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1330 appendColon ();
1331 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1332 thy label = GuiLabel_createShown (form,
1333 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1334 ylabel, ylabel + textFieldHeight,
1335 theFinishBuffer.string, 0
1336 );
1337 thy optionMenu = GuiOptionMenu_createShown (form,
1338 dialogWidth - Gui_LEFT_DIALOG_SPACING - 200, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1339 thy y - Gui_OPTIONMENU_HEIGHT, thy y, 0);
1340 for (int format = (int) kUi_integerVectorFormat::MIN; format <= (int) kUi_integerVectorFormat::MAX; format ++)
1341 GuiOptionMenu_addOption (thy optionMenu, kUi_integerVectorFormat_getText ((kUi_integerVectorFormat) format));
1342 //GuiOptionMenu_setValue (thy optionMenu, (int) thy integerVectorDefaultFormat); // SUPERFLUOUS
1343 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1344 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_INKWRAP | GuiText_SCROLLED);
1345 }
1346 break;
1347 case _kUiField_type::REALMATRIX_:
1348 {
1349 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1350 appendColon ();
1351 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1352 thy label = GuiLabel_createShown (form,
1353 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1354 ylabel, ylabel + textFieldHeight,
1355 theFinishBuffer.string, 0
1356 );
1357 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1358 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_SCROLLED);
1359 thy optionMenu = GuiOptionMenu_createShown (form,
1360 dialogWidth - Gui_LEFT_DIALOG_SPACING - 200, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1361 thy y - Gui_OPTIONMENU_HEIGHT, thy y, 0);
1362 for (int format = (int) kUi_realMatrixFormat::MIN; format <= (int) kUi_realMatrixFormat::MAX; format ++)
1363 GuiOptionMenu_addOption (thy optionMenu, kUi_realMatrixFormat_getText ((kUi_realMatrixFormat) format));
1364 GuiOptionMenu_setValue (thy optionMenu, (int) theRealMatrixFormat);
1365 okButtonIsDefault = false; // because otherwise, the Enter key would be ambiguous
1366 }
1367 break;
1368 case _kUiField_type::STRINGARRAY_:
1369 {
1370 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1371 appendColon ();
1372 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1373 thy label = GuiLabel_createShown (form,
1374 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1375 ylabel, ylabel + textFieldHeight,
1376 theFinishBuffer.string, 0
1377 );
1378 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1379 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_INKWRAP | GuiText_SCROLLED);
1380 thy optionMenu = GuiOptionMenu_createShown (form,
1381 dialogWidth - Gui_LEFT_DIALOG_SPACING - 200, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1382 thy y - Gui_OPTIONMENU_HEIGHT, thy y, 0);
1383 for (int format = (int) kUi_stringArrayFormat::MIN; format <= (int) kUi_stringArrayFormat::MAX; format ++)
1384 GuiOptionMenu_addOption (thy optionMenu, kUi_stringArrayFormat_getText ((kUi_stringArrayFormat) format));
1385 GuiOptionMenu_setValue (thy optionMenu, (int) theStringArrayFormat);
1386 }
1387 break;
1388 case _kUiField_type::FORMULA_:
1389 {
1390 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1391 appendColon ();
1392 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1393 thy label = GuiLabel_createShown (form,
1394 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1395 ylabel, ylabel + textFieldHeight,
1396 theFinishBuffer.string, 0
1397 );
1398 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1399 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_INKWRAP | GuiText_SCROLLED);
1400 }
1401 break;
1402 case _kUiField_type::INFILE_:
1403 {
1404 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1405 appendColon ();
1406 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1407 thy label = GuiLabel_createShown (form,
1408 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1409 ylabel, ylabel + textFieldHeight,
1410 theFinishBuffer.string, 0
1411 );
1412 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1413 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_CHARWRAP | GuiText_SCROLLED);
1414 thy pushButton = GuiButton_createShown (form,
1415 dialogWidth - Gui_LEFT_DIALOG_SPACING - 100, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1416 thy y - 3 - Gui_PUSHBUTTON_HEIGHT, thy y - 3, U"Browse...", gui_button_cb_browseInfile, thee, 0
1417 );
1418 }
1419 break;
1420 case _kUiField_type::OUTFILE_:
1421 {
1422 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1423 appendColon ();
1424 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1425 thy label = GuiLabel_createShown (form,
1426 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1427 ylabel, ylabel + textFieldHeight,
1428 theFinishBuffer.string, 0
1429 );
1430 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1431 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_CHARWRAP | GuiText_SCROLLED);
1432 thy pushButton = GuiButton_createShown (form,
1433 dialogWidth - Gui_LEFT_DIALOG_SPACING - 100, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1434 thy y - 3 - Gui_PUSHBUTTON_HEIGHT, thy y - 3, U"Browse...", gui_button_cb_browseOutfile, thee, 0
1435 );
1436 }
1437 break;
1438 case _kUiField_type::FOLDER_:
1439 {
1440 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1441 appendColon ();
1442 const int ylabel = thy y + 5 - headerLabelHeight - Gui_VERTICAL_DIALOG_SPACING_SAME;
1443 thy label = GuiLabel_createShown (form,
1444 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1445 ylabel, ylabel + textFieldHeight,
1446 theFinishBuffer.string, 0
1447 );
1448 thy text = GuiText_createShown (form, Gui_LEFT_DIALOG_SPACING, dialogWidth - Gui_RIGHT_DIALOG_SPACING,
1449 thy y, thy y + multiLineTextHeight (thy numberOfLines), GuiText_CHARWRAP | GuiText_SCROLLED);
1450 thy pushButton = GuiButton_createShown (form,
1451 dialogWidth - Gui_LEFT_DIALOG_SPACING - 100, dialogWidth - Gui_LEFT_DIALOG_SPACING,
1452 thy y - 3 - Gui_PUSHBUTTON_HEIGHT, thy y - 3, U"Browse...", gui_button_cb_browseFolder, thee, 0
1453 );
1454 }
1455 break;
1456 case _kUiField_type::LABEL_:
1457 {
1458 MelderString_copy (& theFinishBuffer, thy stringValue.get());
1459 thy label = GuiLabel_createShown (form,
1460 Gui_LEFT_DIALOG_SPACING, dialogWidth /* allow to extend into the margin */,
1461 thy y + 5, thy y + 5 + textFieldHeight,
1462 theFinishBuffer.string, 0
1463 );
1464 }
1465 break;
1466 case _kUiField_type::RADIO_:
1467 {
1468 int ylabel = thy y;
1469 #if defined (macintosh)
1470 ylabel += 1;
1471 #endif
1472 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1473 appendColon ();
1474 thy label = GuiLabel_createShown (form, Gui_LEFT_DIALOG_SPACING, Gui_LEFT_DIALOG_SPACING + labelWidth, ylabel, ylabel + Gui_RADIOBUTTON_HEIGHT,
1475 theFinishBuffer.string, GuiLabel_RIGHT);
1476 GuiRadioGroup_begin ();
1477 for (integer ibutton = 1; ibutton <= thy options.size; ibutton ++) {
1478 UiOption button = thy options.at [ibutton];
1479 MelderString_copy (& theFinishBuffer, button -> name.get());
1480 button -> radioButton = GuiRadioButton_createShown (form,
1481 fieldX, dialogWidth /* allow to extend into the margin */,
1482 thy y + (ibutton - 1) * (Gui_RADIOBUTTON_HEIGHT + Gui_RADIOBUTTON_SPACING),
1483 thy y + (ibutton - 1) * (Gui_RADIOBUTTON_HEIGHT + Gui_RADIOBUTTON_SPACING) + Gui_RADIOBUTTON_HEIGHT,
1484 theFinishBuffer.string, nullptr, nullptr, 0);
1485 }
1486 GuiRadioGroup_end ();
1487 }
1488 break;
1489 case _kUiField_type::OPTIONMENU_:
1490 {
1491 int ylabel = thy y;
1492 #if defined (macintosh)
1493 ylabel += 2;
1494 #endif
1495 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1496 appendColon ();
1497 thy label = GuiLabel_createShown (form, Gui_LEFT_DIALOG_SPACING, Gui_LEFT_DIALOG_SPACING + labelWidth, ylabel, ylabel + Gui_OPTIONMENU_HEIGHT,
1498 theFinishBuffer.string, GuiLabel_RIGHT);
1499 thy optionMenu = GuiOptionMenu_createShown (form, fieldX, fieldX + fieldWidth, thy y, thy y + Gui_OPTIONMENU_HEIGHT, 0);
1500 for (integer ibutton = 1; ibutton <= thy options.size; ibutton ++) {
1501 UiOption button = thy options.at [ibutton];
1502 MelderString_copy (& theFinishBuffer, button -> name.get());
1503 GuiOptionMenu_addOption (thy optionMenu, theFinishBuffer.string);
1504 }
1505 }
1506 break;
1507 case _kUiField_type::BOOLEAN_:
1508 {
1509 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1510 /*field -> label = GuiLabel_createShown (form, x, x + labelWidth, thy y, thy y + Gui_CHECKBUTTON_HEIGHT,
1511 theFinishBuffer.string, GuiLabel_RIGHT); */
1512 thy checkButton = GuiCheckButton_createShown (form,
1513 fieldX, dialogWidth /* allow to extend into the margin */, thy y, thy y + Gui_CHECKBUTTON_HEIGHT,
1514 theFinishBuffer.string, nullptr, nullptr, 0);
1515 }
1516 break;
1517 case _kUiField_type::LIST_:
1518 {
1519 int listWidth = my numberOfFields == 1 ? dialogWidth - fieldX : fieldWidth;
1520 MelderString_copy (& theFinishBuffer, thy formLabel.get());
1521 appendColon ();
1522 thy label = GuiLabel_createShown (form, Gui_LEFT_DIALOG_SPACING, Gui_LEFT_DIALOG_SPACING + labelWidth, thy y + 1, thy y + 21,
1523 theFinishBuffer.string, GuiLabel_RIGHT);
1524 thy list = GuiList_create (form, fieldX, fieldX + listWidth, thy y, thy y + LIST_HEIGHT, false, theFinishBuffer.string);
1525 for (integer i = 1; i <= thy strings.size; i ++)
1526 GuiList_insertItem (thy list, thy strings [i], 0);
1527 GuiThing_show (thy list);
1528 }
1529 break;
1530 }
1531 }
1532 for (integer ifield = 1; ifield <= my numberOfFields; ifield ++)
1533 UiField_setDefault (my field [ifield].get());
1534 /*separator = XmCreateSeparatorGadget (column, "separator", nullptr, 0);*/
1535 const int y = dialogHeight - Gui_BOTTOM_DIALOG_SPACING - Gui_PUSHBUTTON_HEIGHT;
1536 if (my helpTitle) {
1537 my helpButton = GuiButton_createShown (form, HELP_BUTTON_X, HELP_BUTTON_X + HELP_BUTTON_WIDTH, y, y + Gui_PUSHBUTTON_HEIGHT,
1538 U"Help", gui_button_cb_help, me, 0);
1539 }
1540 bool commentsOnly = true;
1541 for (integer ifield = 1; ifield <= my numberOfFields; ifield ++) {
1542 if (my field [ifield] -> type != _kUiField_type::LABEL_) {
1543 commentsOnly = false;
1544 break;
1545 }
1546 }
1547 if (! commentsOnly) {
1548 if (my isPauseForm) {
1549 my revertButton = GuiButton_createShown (form,
1550 HELP_BUTTON_X, HELP_BUTTON_X + REVERT_BUTTON_WIDTH,
1551 y, y + Gui_PUSHBUTTON_HEIGHT, U"Undo", gui_button_cb_revert, me, 0);
1552 } else {
1553 my revertButton = GuiButton_createShown (form,
1554 HELP_BUTTON_X + HELP_BUTTON_WIDTH + Gui_HORIZONTAL_DIALOG_SPACING,
1555 HELP_BUTTON_X + HELP_BUTTON_WIDTH + Gui_HORIZONTAL_DIALOG_SPACING + STANDARDS_BUTTON_WIDTH,
1556 y, y + Gui_PUSHBUTTON_HEIGHT, U"Standards", gui_button_cb_revert, me, 0);
1557 }
1558 }
1559 if (my isPauseForm) {
1560 int x = HELP_BUTTON_X + REVERT_BUTTON_WIDTH + Gui_HORIZONTAL_DIALOG_SPACING;
1561 if (my cancelContinueButton == 0) {
1562 my cancelButton = GuiButton_createShown (form, x, x + STOP_BUTTON_WIDTH, y, y + Gui_PUSHBUTTON_HEIGHT,
1563 U"Stop", gui_button_cb_cancel, me, GuiButton_CANCEL);
1564 x += STOP_BUTTON_WIDTH + 7;
1565 } else {
1566 x += 30;
1567 }
1568 int room = dialogWidth - Gui_RIGHT_DIALOG_SPACING - x;
1569 int roomPerContinueButton = room / my numberOfContinueButtons;
1570 int horizontalSpacing = (
1571 my numberOfContinueButtons > 7 ?
1572 Gui_HORIZONTAL_DIALOG_SPACING - 2 * (my numberOfContinueButtons - 7)
1573 :
1574 Gui_HORIZONTAL_DIALOG_SPACING
1575 );
1576 int continueButtonWidth = roomPerContinueButton - horizontalSpacing;
1577 for (int i = 1; i <= my numberOfContinueButtons; i ++) {
1578 x = dialogWidth - Gui_RIGHT_DIALOG_SPACING - roomPerContinueButton * (my numberOfContinueButtons - i + 1) + horizontalSpacing;
1579 my continueButtons [i] = GuiButton_createShown (form, x, x + continueButtonWidth, y, y + Gui_PUSHBUTTON_HEIGHT,
1580 my continueTexts [i], gui_button_cb_ok, me,
1581 i == my defaultContinueButton && okButtonIsDefault ? GuiButton_DEFAULT : i == my cancelContinueButton ? GuiButton_CANCEL : 0
1582 );
1583 }
1584 } else {
1585 int x = dialogWidth - Gui_RIGHT_DIALOG_SPACING - Gui_OK_BUTTON_WIDTH - 2 * Gui_HORIZONTAL_DIALOG_SPACING
1586 - Gui_APPLY_BUTTON_WIDTH - Gui_CANCEL_BUTTON_WIDTH;
1587 my cancelButton = GuiButton_createShown (form, x, x + Gui_CANCEL_BUTTON_WIDTH, y, y + Gui_PUSHBUTTON_HEIGHT,
1588 U"Cancel", gui_button_cb_cancel, me, GuiButton_CANCEL);
1589 x = dialogWidth - Gui_RIGHT_DIALOG_SPACING - Gui_OK_BUTTON_WIDTH - Gui_HORIZONTAL_DIALOG_SPACING - Gui_APPLY_BUTTON_WIDTH;
1590 if (my numberOfFields > 1 || my field [1] -> type != _kUiField_type::LABEL_) {
1591 my applyButton = GuiButton_createShown (form, x, x + Gui_APPLY_BUTTON_WIDTH, y, y + Gui_PUSHBUTTON_HEIGHT,
1592 U"Apply", gui_button_cb_apply, me, 0);
1593 }
1594 x = dialogWidth - Gui_RIGHT_DIALOG_SPACING - Gui_OK_BUTTON_WIDTH;
1595 my okButton = GuiButton_createShown (form, x, x + Gui_OK_BUTTON_WIDTH, y, y + Gui_PUSHBUTTON_HEIGHT,
1596 my isPauseForm ? U"Continue" : U"OK", gui_button_cb_ok, me, okButtonIsDefault ? GuiButton_DEFAULT : 0);
1597 }
1598 /*GuiObject_show (separator);*/
1599 }
1600
UiForm_destroyWhenUnmanaged(UiForm me)1601 void UiForm_destroyWhenUnmanaged (UiForm me) {
1602 my destroyWhenUnmanaged = true;
1603 }
1604
UiForm_do(UiForm me,bool modified)1605 void UiForm_do (UiForm me, bool modified) {
1606 my allowExecutionHook = theAllowExecutionHookHint;
1607 my allowExecutionClosure = theAllowExecutionClosureHint;
1608 Melder_assert (my d_dialogForm);
1609 GuiThing_show (my d_dialogForm);
1610 if (modified)
1611 UiForm_okOrApply (me, nullptr, true);
1612 }
1613
UiField_api_header_C(UiField me,UiField next,bool isLastNonLabelField)1614 static void UiField_api_header_C (UiField me, UiField next, bool isLastNonLabelField) {
1615 if (my type == _kUiField_type::LABEL_) {
1616 bool weAreFollowedByAWideField =
1617 next && (next -> type == _kUiField_type::TEXT_ || next -> type == _kUiField_type::FORMULA_ ||
1618 next -> type == _kUiField_type::INFILE_ || next -> type == _kUiField_type::OUTFILE_ ||
1619 next -> type == _kUiField_type::FOLDER_ ||
1620 next -> type == _kUiField_type::REALMATRIX_ ||
1621 next -> type == _kUiField_type::STRINGARRAY_);
1622 bool weLabelTheFollowingField =
1623 weAreFollowedByAWideField &&
1624 Melder_stringMatchesCriterion (my stringValue.get(), kMelder_string::ENDS_WITH, U":", true);
1625 bool weAreAComment = ! weLabelTheFollowingField;
1626 if (weAreAComment)
1627 MelderInfo_writeLine (U"\t/* ", my stringValue.get(), U" */");
1628 return;
1629 }
1630
1631 /*
1632 Write the type of the field.
1633 */
1634 bool isText = false, isBoolean = false, isEnum = false, isPositive = false;
1635 switch (my type)
1636 {
1637 case _kUiField_type::REAL_:
1638 case _kUiField_type::REAL_OR_UNDEFINED_:
1639 case _kUiField_type::POSITIVE_:
1640 {
1641 MelderInfo_write (U"\tdouble ");
1642 isPositive = ( my type == _kUiField_type::POSITIVE_);
1643 }
1644 break;
1645 case _kUiField_type::INTEGER_:
1646 case _kUiField_type::NATURAL_:
1647 case _kUiField_type::CHANNEL_:
1648 {
1649 MelderInfo_write (U"\tint64_t ");
1650 isPositive = ( my type == _kUiField_type::NATURAL_);
1651 }
1652 break;
1653 case _kUiField_type::WORD_:
1654 case _kUiField_type::SENTENCE_:
1655 case _kUiField_type::TEXT_:
1656 case _kUiField_type::FORMULA_:
1657 case _kUiField_type::INFILE_:
1658 case _kUiField_type::OUTFILE_:
1659 case _kUiField_type::FOLDER_:
1660 case _kUiField_type::COLOUR_:
1661 case _kUiField_type::LIST_:
1662 {
1663 MelderInfo_write (U"\tconst char *");
1664 isText = true;
1665 }
1666 break;
1667 case _kUiField_type::RADIO_:
1668 case _kUiField_type::OPTIONMENU_:
1669 {
1670 MelderInfo_write (U"\tconst char *");
1671 isText = true;
1672 isEnum = true;
1673 }
1674 break;
1675 case _kUiField_type::BOOLEAN_:
1676 {
1677 MelderInfo_write (U"\tint32_t ");
1678 isBoolean = true;
1679 }
1680 break;
1681 default:
1682 {
1683 }
1684 }
1685
1686 /*
1687 Write the title of the field.
1688 */
1689 char32 cName [100], *q = & cName [0];
1690 Melder_assert (my formLabel);
1691 const char32 *p = & my formLabel [0];
1692 *q ++ = Melder_toLowerCase (*p ++);
1693 bool up = false;
1694 for (; *p != U'\0'; p ++) {
1695 if (*p == U'(') {
1696 break;
1697 } else if (*p == U'\'') {
1698 continue;
1699 } else if (*p == U' ' || *p == U'-') {
1700 if (p [1] == U'(') { p ++; break; }
1701 up = true;
1702 } else if (*p == U'*') {
1703 *q ++ = U'S';
1704 *q ++ = U't';
1705 *q ++ = U'a';
1706 *q ++ = U'r';
1707 } else if (up) {
1708 *q ++ = Melder_toUpperCase (*p);
1709 up = false;
1710 } else {
1711 *q ++ = *p;
1712 }
1713 }
1714 *q = U'\0';
1715 if (! my variableName)
1716 Melder_warning (U"Missing variable name for field label: ", my formLabel.get());
1717 MelderInfo_write (my variableName ? my variableName : cName);
1718 if (! isLastNonLabelField) MelderInfo_write (U",");
1719
1720 /*
1721 Get the units.
1722 */
1723 char32 units [100];
1724 q = & units [0];
1725 if (*p == U'(') {
1726 for (p ++; *p != U'\0'; p ++) {
1727 if (*p == U')') {
1728 break;
1729 } else {
1730 *q ++ = *p;
1731 }
1732 }
1733 }
1734 *q = U'\0';
1735 bool unitsAreAvailable = ( units [0] != U'\0' );
1736 bool unitsContainRange = str32str (units, U"-");
1737
1738 /*
1739 Get the example.
1740 */
1741 conststring32 example = my stringDefaultValue.get(); // BUG dangle
1742 bool exampleIsAvailable = ( example && example [0] != U'\0' );
1743
1744 if (exampleIsAvailable) {
1745 /*
1746 Split up the default string.
1747 */
1748 char32 defaultValue [100], defaultComment [100];
1749 str32cpy (defaultValue, my stringDefaultValue.get());
1750 str32cpy (defaultComment, U"");
1751 if (unitsAreAvailable) {
1752 char32 *parenthesis = str32chr (defaultValue, U'(');
1753 if (parenthesis && parenthesis - defaultValue > 1) {
1754 parenthesis [-1] = U'\0';
1755 str32cpy (defaultComment, parenthesis);
1756 }
1757 }
1758
1759 MelderInfo_write (U" // ");
1760 if (isPositive)
1761 MelderInfo_write (U"positive, ");
1762 if (unitsContainRange)
1763 MelderInfo_write (units, U", ");
1764 MelderInfo_write (U"e.g. ");
1765 if (isText)
1766 MelderInfo_write (U"\"");
1767 MelderInfo_write (defaultValue);
1768 if (isText)
1769 MelderInfo_write (U"\"");
1770 if (unitsAreAvailable && ! unitsContainRange) {
1771 MelderInfo_write (U" ", units);
1772 }
1773 if (defaultComment [0]) {
1774 MelderInfo_write (U" ", defaultComment);
1775 }
1776 } else if (isBoolean) {
1777 MelderInfo_write (U" // boolean, e.g. ");
1778 MelderInfo_write (my integerDefaultValue, my integerDefaultValue ? U" (true)" : U" (false)");
1779 } else if (isEnum) {
1780 MelderInfo_write (U" // e.g. \"");
1781 MelderInfo_write (my options.at [my integerDefaultValue] -> name.get());
1782 MelderInfo_write (U"\"; other choice", ( my options.size > 2 ? U"s" : U"" ), U":");
1783 bool firstWritten = false;
1784 for (int i = 1; i <= my options.size; i ++) {
1785 if (i == my integerDefaultValue)
1786 continue;
1787 if (firstWritten) MelderInfo_write (U",");
1788 MelderInfo_write (U" \"", my options.at [i] -> name.get(), U"\"");
1789 firstWritten = true;
1790 }
1791 }
1792 MelderInfo_writeLine (U"");
1793 }
1794
UiForm_info(UiForm me,integer narg)1795 void UiForm_info (UiForm me, integer narg) {
1796 if (narg == -1) {
1797 /*
1798 The C interface.
1799 */
1800 int lastNonLabelFieldNumber = 0;
1801 for (int ifield = my numberOfFields; ifield > 0; ifield --) {
1802 if (my field [ifield] -> type != _kUiField_type::LABEL_) {
1803 lastNonLabelFieldNumber = ifield;
1804 break;
1805 }
1806 }
1807 for (int ifield = 1; ifield <= my numberOfFields; ifield ++)
1808 UiField_api_header_C (my field [ifield].get(), ifield == my numberOfFields ? nullptr : my field [ifield + 1].get(), ifield == lastNonLabelFieldNumber);
1809 }
1810 }
1811
UiField_argToValue(UiField me,Stackel arg,Interpreter)1812 static void UiField_argToValue (UiField me, Stackel arg, Interpreter /* interpreter */) {
1813 switch (my type)
1814 {
1815 case _kUiField_type::REAL_:
1816 case _kUiField_type::REAL_OR_UNDEFINED_:
1817 case _kUiField_type::POSITIVE_:
1818 {
1819 if (arg -> which != Stackel_NUMBER)
1820 Melder_throw (U"Argument \"", my name.get(), U"\" should be a number, not ", arg -> whichText(), U".");
1821 my realValue = arg -> number;
1822 if (isundef (my realValue) && my type != _kUiField_type::REAL_OR_UNDEFINED_)
1823 Melder_throw (U"Argument \"", my name.get(), U"\" has the value \"undefined\".");
1824 if (my type == _kUiField_type::POSITIVE_ && my realValue <= 0.0)
1825 Melder_throw (U"Argument \"", my name.get(), U"\" must be greater than 0.");
1826 if (my realVariable)
1827 *my realVariable = my realValue;
1828 }
1829 break;
1830 case _kUiField_type::INTEGER_:
1831 case _kUiField_type::NATURAL_:
1832 case _kUiField_type::CHANNEL_:
1833 {
1834 if (arg -> which == Stackel_STRING) {
1835 if (my type == _kUiField_type::CHANNEL_) {
1836 if (str32equ (arg -> getString(), U"All") || str32equ (arg -> getString(), U"Average")) {
1837 my integerValue = 0;
1838 } else if (str32equ (arg -> getString(), U"Left") || str32equ (arg -> getString(), U"Mono")) {
1839 my integerValue = 1;
1840 } else if (str32equ (arg -> getString(), U"Right") || str32equ (arg -> getString(), U"Stereo")) {
1841 my integerValue = 2;
1842 } else {
1843 Melder_throw (U"Channel argument \"", my name.get(),
1844 U"\" can only be a number or one of the strings \"All\", \"Average\", \"Left\", \"Right\", \"Mono\" or \"Stereo\".");
1845 }
1846 } else {
1847 Melder_throw (U"Argument \"", my name.get(), U"\" should be a number, not ", arg -> whichText(), U".");
1848 }
1849 } else if (arg -> which == Stackel_NUMBER) {
1850 double realValue = arg -> number;
1851 my integerValue = Melder_iround (realValue);
1852 Melder_require (my integerValue == realValue,
1853 U"Argument \"", my name.get(), U"\" should be a whole number.");
1854 if (my type == _kUiField_type::NATURAL_ && my integerValue < 1)
1855 Melder_throw (U"Argument \"", my name.get(), U"\" should be a positive whole number.");
1856 } else {
1857 Melder_throw (U"Argument \"", my name.get(), U"\" should be a number, not ", arg -> whichText(), U".");
1858 }
1859 if (my integerVariable)
1860 *my integerVariable = my integerValue;
1861 }
1862 break;
1863 case _kUiField_type::WORD_:
1864 case _kUiField_type::SENTENCE_:
1865 case _kUiField_type::TEXT_:
1866 case _kUiField_type::FORMULA_:
1867 case _kUiField_type::INFILE_:
1868 case _kUiField_type::OUTFILE_:
1869 case _kUiField_type::FOLDER_:
1870 {
1871 if (arg -> which != Stackel_STRING)
1872 Melder_throw (U"Argument \"", my name.get(), U"\" should be a string, not ", arg -> whichText(), U".");
1873 my stringValue = Melder_dup (arg -> getString());
1874 if (my stringVariable)
1875 *my stringVariable = my stringValue.get(); // BUG dangle
1876 }
1877 break;
1878 case _kUiField_type::REALVECTOR_:
1879 case _kUiField_type::POSITIVEVECTOR_:
1880 {
1881 if (arg -> which != Stackel_NUMERIC_VECTOR && arg -> which != Stackel_STRING)
1882 Melder_throw (U"Argument \"", my name.get(), U"\" should be a numeric vector, not ", arg -> whichText(), U".");
1883 if (arg -> which == Stackel_STRING) {
1884 my realVectorValue = splitByWhitespace_VEC (arg -> getString());
1885 } else if (arg -> owned) {
1886 my realVectorValue. adoptFromAmbiguousOwner (arg -> numericVector);
1887 arg -> owned = false;
1888 } else {
1889 my realVectorValue = copy_VEC (arg -> numericVector);
1890 }
1891 if (my type == _kUiField_type::POSITIVEVECTOR_)
1892 for (integer i = 1; i <= my realVectorValue.size; i ++)
1893 if (my realVectorValue [i] <= 0.0)
1894 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my realVectorValue [i], U" but should be greater than 0.0.");
1895 if (my realVectorVariable)
1896 *my realVectorVariable = my realVectorValue.get();
1897 }
1898 break;
1899 case _kUiField_type::INTEGERVECTOR_:
1900 case _kUiField_type::NATURALVECTOR_:
1901 {
1902 if (arg -> which != Stackel_NUMERIC_VECTOR && arg -> which != Stackel_STRING)
1903 Melder_throw (U"Argument \"", my name.get(), U"\" should be a numeric vector, not ", arg -> whichText(), U".");
1904 if (arg -> which == Stackel_STRING) {
1905 my integerVectorValue = splitByWhitespaceWithRanges_INTVEC (arg -> getString());
1906 } else {
1907 my integerVectorValue = raw_INTVEC (arg -> numericVector.size);
1908 for (integer i = 1; i <= arg -> numericVector.size; i ++) {
1909 my integerVectorValue [i] = Melder_iround (arg -> numericVector [i]);
1910 Melder_require (my integerVectorValue [i] == arg -> numericVector [i],
1911 U"Element ", i, U" of vector “", my name.get(), U"” is ", arg -> numericVector [i], U" but should be a whole number.");
1912 }
1913 }
1914 if (my type == _kUiField_type::NATURALVECTOR_)
1915 for (integer i = 1; i <= my integerVectorValue.size; i ++)
1916 if (my integerVectorValue [i] <= 0)
1917 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my integerVectorValue [i], U" but should be greater than 0.");
1918 if (my integerVectorVariable)
1919 *my integerVectorVariable = my integerVectorValue.get();
1920 }
1921 break;
1922 case _kUiField_type::REALMATRIX_:
1923 {
1924 if (arg -> which != Stackel_NUMERIC_MATRIX)
1925 Melder_throw (U"Argument \"", my name.get(), U"\" should be a numeric matrix, not ", arg -> whichText(), U".");
1926 if (arg -> owned) {
1927 my numericMatrixValue. adoptFromAmbiguousOwner (arg -> numericMatrix);
1928 arg -> owned = false;
1929 } else {
1930 my numericMatrixValue = copy_MAT (arg -> numericMatrix);
1931 }
1932 if (my numericMatrixVariable)
1933 *my numericMatrixVariable = my numericMatrixValue.get();
1934 }
1935 break;
1936 case _kUiField_type::STRINGARRAY_:
1937 {
1938 if (arg -> which != Stackel_STRING_ARRAY && arg -> which != Stackel_STRING)
1939 Melder_throw (U"Argument \"", my name.get(), U"\" should be a string array, not ", arg -> whichText(), U".");
1940 if (arg -> which == Stackel_STRING) {
1941 my stringArrayValue = splitByWhitespace_STRVEC (arg -> getString());
1942 } else if (arg -> owned) {
1943 my stringArrayValue. adoptFromAmbiguousOwner (arg -> stringArray);
1944 arg -> owned = false;
1945 } else {
1946 my stringArrayValue = copy_STRVEC (arg -> stringArray);
1947 }
1948 if (my stringArrayVariable)
1949 *my stringArrayVariable = my stringArrayValue.get();
1950 }
1951 break;
1952 case _kUiField_type::BOOLEAN_:
1953 {
1954 if (arg -> which == Stackel_STRING) {
1955 if (str32equ (arg -> getString(), U"no") || str32equ (arg -> getString(), U"off")) {
1956 my integerValue = 0;
1957 } else if (str32equ (arg -> getString(), U"yes") || str32equ (arg -> getString(), U"on")) {
1958 my integerValue = 1;
1959 } else {
1960 Melder_throw (U"Boolean argument \"", my name.get(),
1961 U"\" can only be a number or one of the strings \"yes\" or \"no\".");
1962 }
1963 } else if (arg -> which == Stackel_NUMBER) {
1964 my integerValue = ( arg -> number == 0.0 ? 0.0 : 1.0 );
1965 } else {
1966 Melder_throw (U"Boolean argument \"", my name.get(), U"\" should be a number (0 or 1), not ", arg -> whichText(), U".");
1967 }
1968 if (my boolVariable)
1969 *my boolVariable = my integerValue;
1970 }
1971 break;
1972 case _kUiField_type::RADIO_:
1973 case _kUiField_type::OPTIONMENU_:
1974 {
1975 if (arg -> which != Stackel_STRING)
1976 Melder_throw (U"Option argument \"", my name.get(), U"\" should be a string, not ", arg -> whichText(), U".");
1977 my integerValue = 0;
1978 for (int i = 1; i <= my options.size; i ++) {
1979 UiOption b = my options.at [i];
1980 if (str32equ (arg -> getString(), b -> name.get()))
1981 my integerValue = i;
1982 }
1983 if (my integerValue == 0) {
1984 /*
1985 Retry with different case.
1986 */
1987 for (int i = 1; i <= my options.size; i ++) {
1988 UiOption b = my options.at [i];
1989 if (Melder_equ_firstCharacterCaseInsensitive (arg -> getString(), b -> name.get()))
1990 my integerValue = i;
1991 }
1992 }
1993 if (my integerValue == 0) {
1994 if (my intVariable)
1995 Melder_throw (U"Option argument \"", my name.get(), U"\" cannot have the value \"", arg -> getString(), U"\".");
1996 if (my stringVariable) {
1997 *my stringVariable = arg -> getString();
1998 return;
1999 }
2000 }
2001 if (my intVariable)
2002 *my intVariable = int (my integerValue) - my subtract;
2003 if (my stringVariable)
2004 *my stringVariable = my options.at [my integerValue] -> name.get();
2005 }
2006 break;
2007 case _kUiField_type::LIST_:
2008 {
2009 if (arg -> which != Stackel_STRING)
2010 Melder_throw (U"List argument \"", my name.get(), U"\" should be a string, not ", arg -> whichText(), U".");
2011 integer i = 1;
2012 for (; i <= my strings.size; i ++)
2013 if (str32equ (arg -> getString(), my strings [i]))
2014 break;
2015 if (i > my strings.size)
2016 Melder_throw (U"List argument \"", my name.get(), U"\" cannot have the value \"", arg -> getString(), U"\".");
2017 my integerValue = i;
2018 if (my integerVariable)
2019 *my integerVariable = my integerValue;
2020 if (my stringVariable)
2021 *my stringVariable = (char32 *) my strings [my integerValue];
2022 }
2023 break;
2024 case _kUiField_type::COLOUR_:
2025 {
2026 if (arg -> which == Stackel_NUMBER) {
2027 if (arg -> number < 0.0 || arg -> number > 1.0)
2028 Melder_throw (U"Grey colour argument \"", my name.get(), U"\" has to lie between 0.0 and 1.0.");
2029 my colourValue. red = my colourValue. green = my colourValue. blue = arg -> number;
2030 } else if (arg -> which == Stackel_STRING) {
2031 autostring32 string2 = Melder_dup (arg -> getString());
2032 MelderColour colour = MelderColour_fromColourNameOrRGBString (string2.get());
2033 if (colour.valid())
2034 my colourValue = colour;
2035 else
2036 Melder_throw (U"Cannot compute a colour from \"", string2.get(), U"\".");
2037 } else if (arg -> which == Stackel_NUMERIC_VECTOR) {
2038 my colourValue = MelderColour (arg -> numericVector);
2039 } else
2040 Melder_throw (U"Colour argument \"", my name.get(), U"\" should be a string or number or numeric vector, not ", arg -> whichText(), U".");
2041 if (my colourVariable)
2042 *my colourVariable = my colourValue;
2043 }
2044 break;
2045 default:
2046 {
2047 Melder_throw (U"Unknown field type ", (int) my type, U".");
2048 }
2049 }
2050 }
2051
UiForm_call(UiForm me,integer narg,Stackel args,Interpreter interpreter)2052 void UiForm_call (UiForm me, integer narg, Stackel args, Interpreter interpreter) {
2053 integer size = my numberOfFields, iarg = 0;
2054 //while (size >= 1 && my field [size] -> type == _kUiField_type::LABEL_)
2055 // size --; // ignore trailing fields without a value
2056 for (integer i = 1; i <= size; i ++) {
2057 if (my field [i] -> type == _kUiField_type::LABEL_)
2058 continue; // ignore non-trailing fields without a value
2059 iarg ++;
2060 if (iarg > narg)
2061 Melder_throw (U"Command requires more than the given ", narg, U" arguments: argument \"", my field [i] -> name.get(), U"\" not given.");
2062 UiField_argToValue (my field [i].get(), & args [iarg], interpreter);
2063 }
2064 if (iarg < narg)
2065 Melder_throw (U"Command requires only ", iarg, U" arguments, not the ", narg, U" given.");
2066 my okCallback (me, 0, nullptr, nullptr, interpreter, nullptr, false, my buttonClosure);
2067 }
2068
2069 /*
2070 DEPRECATED_2014 (i.e. remove in 2036)
2071 */
UiField_stringToValue(UiField me,conststring32 string,Interpreter interpreter)2072 static void UiField_stringToValue (UiField me, conststring32 string, Interpreter interpreter) {
2073 /*
2074 This belongs to the deprecated dots-based syntax described below at `UiForm_parseString`.
2075 This is included for backward compatibility (until 2036),
2076 but does not support newer expression types such as numeric vectors and matrices.
2077 */
2078 switch (my type)
2079 {
2080 case _kUiField_type::REAL_:
2081 case _kUiField_type::REAL_OR_UNDEFINED_:
2082 case _kUiField_type::POSITIVE_:
2083 {
2084 if (str32spn (string, U" \t") == str32len (string))
2085 Melder_throw (U"Argument “", my name.get(), U"” empty.");
2086 Interpreter_numericExpression (interpreter, string, & my realValue);
2087 if (isundef (my realValue) && my type != _kUiField_type::REAL_OR_UNDEFINED_)
2088 Melder_throw (U"\"", my name.get(), U"\" has the value \"undefined\".");
2089 if (my type == _kUiField_type::POSITIVE_ && my realValue <= 0.0)
2090 Melder_throw (U"\"", my name.get(), U"\" must be greater than 0.");
2091 if (my realVariable)
2092 *my realVariable = my realValue;
2093 }
2094 break;
2095 case _kUiField_type::INTEGER_:
2096 case _kUiField_type::NATURAL_:
2097 case _kUiField_type::CHANNEL_: {
2098 if (str32spn (string, U" \t") == str32len (string))
2099 Melder_throw (U"Argument “", my name.get(), U"” empty.");
2100 if (my type == _kUiField_type::CHANNEL_ && (str32equ (string, U"All") || str32equ (string, U"Average"))) {
2101 my integerValue = 0;
2102 } else if (my type == _kUiField_type::CHANNEL_ && (str32equ (string, U"Left") || str32equ (string, U"Mono"))) {
2103 my integerValue = 1;
2104 } else if (my type == _kUiField_type::CHANNEL_ && (str32equ (string, U"Right") || str32equ (string, U"Stereo"))) {
2105 my integerValue = 2;
2106 } else {
2107 double realValue;
2108 Interpreter_numericExpression (interpreter, string, & realValue);
2109 my integerValue = Melder_iround (realValue);
2110 }
2111 if (my type == _kUiField_type::NATURAL_ && my integerValue < 1)
2112 Melder_throw (U"\"", my name.get(), U"\" should be a positive whole number.");
2113 if (my integerVariable)
2114 *my integerVariable = my integerValue;
2115 }
2116 break;
2117 case _kUiField_type::WORD_:
2118 case _kUiField_type::SENTENCE_:
2119 case _kUiField_type::TEXT_:
2120 case _kUiField_type::FORMULA_:
2121 case _kUiField_type::INFILE_:
2122 case _kUiField_type::OUTFILE_:
2123 case _kUiField_type::FOLDER_:
2124 {
2125 my stringValue = Melder_dup (string);
2126 if (my stringVariable)
2127 *my stringVariable = my stringValue.get(); // BUG dangle
2128 }
2129 break;
2130 case _kUiField_type::REALVECTOR_:
2131 case _kUiField_type::POSITIVEVECTOR_:
2132 {
2133 my realVectorValue = splitByWhitespace_VEC (string);
2134 if (my type == _kUiField_type::POSITIVEVECTOR_)
2135 for (integer i = 1; i <= my realVectorValue.size; i ++)
2136 if (my realVectorValue [i] <= 0.0)
2137 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my realVectorValue [i], U" but should be greater than 0.0.");
2138 if (my realVectorVariable)
2139 *my realVectorVariable = my realVectorValue.get();
2140 }
2141 break;
2142 case _kUiField_type::INTEGERVECTOR_:
2143 case _kUiField_type::NATURALVECTOR_:
2144 {
2145 my integerVectorValue = splitByWhitespaceWithRanges_INTVEC (string);
2146 if (my type == _kUiField_type::NATURALVECTOR_)
2147 for (integer i = 1; i <= my integerVectorValue.size; i ++)
2148 if (my integerVectorValue [i] <= 0)
2149 Melder_throw (U"Element ", i, U" of vector “", my name.get(), U"” is ", my integerVectorValue [i], U" but should be greater than 0.");
2150 if (my integerVectorVariable)
2151 *my integerVectorVariable = my integerVectorValue.get();
2152 }
2153 break;
2154 case _kUiField_type::STRINGARRAY_:
2155 my stringArrayValue = splitByWhitespace_STRVEC (string);
2156 if (my stringArrayVariable)
2157 *my stringArrayVariable = my stringArrayValue.get();
2158 break;
2159 case _kUiField_type::BOOLEAN_:
2160 {
2161 if (! string [0])
2162 Melder_throw (U"Empty argument for toggle button.");
2163 my integerValue = string [0] == U'1' || string [0] == U'y' || string [0] == U'Y' ||
2164 string [0] == U't' || string [0] == U'T';
2165 if (my boolVariable)
2166 *my boolVariable = my integerValue;
2167 }
2168 break;
2169 case _kUiField_type::RADIO_:
2170 case _kUiField_type::OPTIONMENU_:
2171 {
2172 my integerValue = 0;
2173 for (int i = 1; i <= my options.size; i ++) {
2174 UiOption b = my options.at [i];
2175 if (str32equ (string, b -> name.get()))
2176 my integerValue = i;
2177 }
2178 if (my integerValue == 0) {
2179 /*
2180 Retry with different case.
2181 */
2182 for (int i = 1; i <= my options.size; i ++) {
2183 UiOption b = my options.at [i];
2184 if (Melder_equ_firstCharacterCaseInsensitive (string, b -> name.get()))
2185 my integerValue = i;
2186 }
2187 }
2188 if (my integerValue == 0) {
2189 Melder_throw (U"Field \"", my name.get(), U"\" must not have the value \"", string, U"\".");
2190 }
2191 if (my intVariable)
2192 *my intVariable = int (my integerValue) - my subtract;
2193 if (my stringVariable)
2194 *my stringVariable = my options.at [my integerValue] -> name.get();
2195 }
2196 break;
2197 case _kUiField_type::LIST_:
2198 {
2199 integer i = 1;
2200 for (; i <= my strings.size; i ++)
2201 if (str32equ (string, my strings [i]))
2202 break;
2203 if (i > my strings.size)
2204 Melder_throw (U"Field \"", my name.get(), U"\" must not have the value \"", string, U"\".");
2205 my integerValue = i;
2206 if (my integerVariable)
2207 *my integerVariable = my integerValue;
2208 if (my stringVariable)
2209 *my stringVariable = (char32 *) my strings [my integerValue];
2210 }
2211 break;
2212 case _kUiField_type::COLOUR_:
2213 {
2214 autostring32 string2 = Melder_dup (string);
2215 MelderColour colour = MelderColour_fromColourNameOrRGBString (string2.get());
2216 if (colour.valid()) {
2217 my colourValue = colour;
2218 } else {
2219 try {
2220 double greyValue;
2221 Interpreter_numericExpression (interpreter, string2.get(), & greyValue);
2222 my colourValue = MelderColour (greyValue);
2223 } catch (MelderError) {
2224 Melder_clearError ();
2225 Melder_throw (U"Cannot compute a colour from \"", string2.get(), U"\".");
2226 }
2227 }
2228 if (my colourVariable)
2229 *my colourVariable = my colourValue;
2230 }
2231 break;
2232 default:
2233 {
2234 Melder_throw (U"Unknown field type ", (int) my type, U".");
2235 }
2236 }
2237 }
2238
2239 /*
2240 DEPRECATED_2014 (i.e. remove in 2036)
2241 */
UiForm_parseString(UiForm me,conststring32 arguments,Interpreter interpreter)2242 void UiForm_parseString (UiForm me, conststring32 arguments, Interpreter interpreter) {
2243 /*
2244 This implements the dots-based scripting style
2245 Create Sound from formula... sineWithNoise 1 0 1 44100 0.5 * sin (2*pi*377*x)
2246 This was deprecated with the advent of the colon-based scripting style
2247 Create Sound from formula: "sineWithNoise", 1, 0, 1, 44100, "0.5 * sin (2*pi*377*x)"
2248 or
2249 Create Sound from formula: "sineWithNoise", 1, 0, 1, 44100, ~ 0.5 * sin (2*pi*377*x)
2250 in 2014, i.e. 22 years after Praat started.
2251 If we want to conservatively support old scripts, we will have
2252 to continue to support the dots-based scripting style until 2036.
2253 */
2254 int size = my numberOfFields;
2255 while (size >= 1 && my field [size] -> type == _kUiField_type::LABEL_)
2256 size --; // ignore trailing fields without a value
2257 for (int i = 1; i < size; i ++) {
2258 static char32 stringValue [3000];
2259 int ichar = 0;
2260 if (my field [i] -> type == _kUiField_type::LABEL_)
2261 continue; // ignore non-trailing fields without a value
2262 Melder_skipHorizontalOrVerticalSpace (& arguments); // go to next argument
2263 /*
2264 The argument is everything up to the next space, or, if that starts with a double quote,
2265 everything between this quote and the matching double quote;
2266 in this case, the argument can represent a double quote by a sequence of two double quotes.
2267 Example: the string
2268 "I said ""hello"""
2269 will be passed to the dialog as a single argument containing the text
2270 I said "hello"
2271 */
2272 if (*arguments == U'\"') {
2273 arguments ++; // do not include leading double quote
2274 for (;;) {
2275 if (*arguments == U'\0')
2276 Melder_throw (U"Missing matching quote.");
2277 if (*arguments == U'\"' && * ++ arguments != U'\"')
2278 break; // remember second quote
2279 stringValue [ichar ++] = *arguments ++;
2280 }
2281 } else {
2282 while (*arguments != U' ' && *arguments != U'\t' && *arguments != U'\0')
2283 stringValue [ichar ++] = *arguments ++;
2284 }
2285 stringValue [ichar] = U'\0'; // trailing null character
2286 try {
2287 UiField_stringToValue (my field [i].get(), stringValue, interpreter);
2288 } catch (MelderError) {
2289 Melder_throw (U"Don't understand contents of field \"", my field [i] -> name.get(), U"\".");
2290 }
2291 }
2292 /*
2293 The last item is handled separately, because it consists of the rest of the line.
2294 Leading spaces are skipped, but trailing spaces are included.
2295 */
2296 if (size > 0) {
2297 Melder_skipHorizontalOrVerticalSpace (& arguments); // rid leading spaces
2298 try {
2299 UiField_stringToValue (my field [size].get(), arguments, interpreter);
2300 } catch (MelderError) {
2301 Melder_throw (U"Don't understand contents of field \"", my field [size] -> name.get(), U"\".");
2302 }
2303 }
2304 my okCallback (me, 0, nullptr, nullptr, interpreter, nullptr, false, my buttonClosure);
2305 }
2306
UiForm_parseStringE(EditorCommand cmd,integer narg,Stackel args,conststring32 arguments,Interpreter interpreter)2307 void UiForm_parseStringE (EditorCommand cmd, integer narg, Stackel args, conststring32 arguments, Interpreter interpreter) {
2308 if (args)
2309 UiForm_call (cmd -> d_uiform.get(), narg, args, interpreter);
2310 else
2311 UiForm_parseString (cmd -> d_uiform.get(), arguments, interpreter);
2312 }
2313
fatalField(UiForm dia)2314 static void fatalField (UiForm dia) {
2315 Melder_fatal (U"Wrong field in command window \"", dia -> name.get(), U"\".");
2316 }
2317
UiForm_setReal(UiForm me,double * p_variable,double value)2318 void UiForm_setReal (UiForm me, double *p_variable, double value) {
2319 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2320 UiField field = my field [ifield].get();
2321 if (field -> realVariable == p_variable) {
2322 switch (field -> type)
2323 {
2324 case _kUiField_type::REAL_:
2325 case _kUiField_type::REAL_OR_UNDEFINED_:
2326 case _kUiField_type::POSITIVE_:
2327 {
2328 if (value == Melder_atof (field -> stringDefaultValue.get())) {
2329 GuiText_setString (field -> text, field -> stringDefaultValue.get());
2330 } else {
2331 char32 s [40];
2332 str32cpy (s, Melder_double (value));
2333 /*
2334 If the default is overtly real, the shown value should be as well.
2335 */
2336 if ((str32chr (field -> stringDefaultValue.get(), U'.') || str32chr (field -> stringDefaultValue.get(), U'e')) &&
2337 ! (str32chr (s, U'.') || str32chr (s, U'e')))
2338 {
2339 str32cpy (s + str32len (s), U".0");
2340 }
2341 GuiText_setString (field -> text, s);
2342 }
2343 }
2344 break;
2345 default:
2346 {
2347 fatalField (me);
2348 }
2349 }
2350 return;
2351 }
2352 }
2353 Melder_fatal (U"Real field not found in command window \"", my name.get(), U"\".");
2354 }
2355
UiForm_setRealAsString(UiForm me,double * p_variable,conststring32 stringValue)2356 void UiForm_setRealAsString (UiForm me, double *p_variable, conststring32 stringValue) {
2357 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2358 UiField field = my field [ifield].get();
2359 if (field -> realVariable == p_variable) {
2360 switch (field -> type)
2361 {
2362 case _kUiField_type::REAL_:
2363 case _kUiField_type::REAL_OR_UNDEFINED_:
2364 case _kUiField_type::POSITIVE_:
2365 {
2366 GuiText_setString (field -> text, stringValue);
2367 }
2368 break;
2369 default:
2370 {
2371 fatalField (me);
2372 }
2373 }
2374 return;
2375 }
2376 }
2377 Melder_fatal (U"Real field not found in command window \"", my name.get(), U"\".");
2378 }
2379
UiForm_setInteger(UiForm me,integer * p_variable,integer value)2380 void UiForm_setInteger (UiForm me, integer *p_variable, integer value) {
2381 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2382 UiField field = my field [ifield].get();
2383 if (field -> integerVariable == p_variable) {
2384 switch (field -> type)
2385 {
2386 case _kUiField_type::INTEGER_:
2387 case _kUiField_type::NATURAL_:
2388 case _kUiField_type::CHANNEL_:
2389 {
2390 if (value == Melder_atoi (field -> stringDefaultValue.get())) {
2391 GuiText_setString (field -> text, field -> stringDefaultValue.get());
2392 } else {
2393 GuiText_setString (field -> text, Melder_integer (value));
2394 }
2395 }
2396 break;
2397 case _kUiField_type::LIST_:
2398 {
2399 if (value < 1 || value > field -> strings.size)
2400 value = 1; // guard against incorrect prefs file
2401 GuiList_selectItem (field -> list, value);
2402 }
2403 break;
2404 default:
2405 {
2406 fatalField (me);
2407 }
2408 }
2409 return;
2410 }
2411 }
2412 Melder_fatal (U"Integer field not found in command window \"", my name.get(), U"\".");
2413 }
2414
UiForm_setIntegerAsString(UiForm me,integer * p_variable,conststring32 stringValue)2415 void UiForm_setIntegerAsString (UiForm me, integer *p_variable, conststring32 stringValue /* cattable */) {
2416 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2417 UiField field = my field [ifield].get();
2418 if (field -> integerVariable == p_variable) {
2419 switch (field -> type)
2420 {
2421 case _kUiField_type::INTEGER_:
2422 case _kUiField_type::NATURAL_:
2423 case _kUiField_type::CHANNEL_:
2424 {
2425 GuiText_setString (field -> text, stringValue);
2426 }
2427 break;
2428 case _kUiField_type::LIST_:
2429 {
2430 integer i = 1;
2431 for (; i <= field -> strings.size; i ++)
2432 if (str32equ (stringValue, field -> strings [i])) break;
2433 if (i > field -> strings.size)
2434 i = 1; // guard against incorrect prefs file
2435 GuiList_selectItem (field -> list, i);
2436 }
2437 break;
2438 default:
2439 {
2440 fatalField (me);
2441 }
2442 }
2443 return;
2444 }
2445 }
2446 Melder_fatal (U"Integer field not found in command window \"", my name.get(), U"\".");
2447 }
2448
UiForm_setBoolean(UiForm me,bool * p_variable,bool value)2449 void UiForm_setBoolean (UiForm me, bool *p_variable, bool value) {
2450 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2451 UiField field = my field [ifield].get();
2452 if (field -> boolVariable == p_variable) {
2453 switch (field -> type)
2454 {
2455 case _kUiField_type::BOOLEAN_:
2456 {
2457 GuiCheckButton_setValue (field -> checkButton, value);
2458 }
2459 break;
2460 default:
2461 {
2462 fatalField (me);
2463 }
2464 }
2465 return;
2466 }
2467 }
2468 Melder_fatal (U"Boolean field not found in command window \"", my name.get(), U"\".");
2469 }
2470
UiForm_setOption(UiForm me,int * p_variable,int value)2471 void UiForm_setOption (UiForm me, int *p_variable, int value) {
2472 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2473 UiField field = my field [ifield].get();
2474 if (field -> intVariable == p_variable) {
2475 switch (field -> type)
2476 {
2477 case _kUiField_type::RADIO_:
2478 {
2479 if (value < 1 || value > field -> options.size)
2480 value = 1; // guard against incorrect prefs file
2481 UiOption option = field -> options.at [value];
2482 GuiRadioButton_set (option -> radioButton);
2483 }
2484 break;
2485 case _kUiField_type::OPTIONMENU_:
2486 {
2487 if (value < 1 || value > field -> options.size)
2488 value = 1; // guard against incorrect prefs file
2489 GuiOptionMenu_setValue (field -> optionMenu, value);
2490 }
2491 break;
2492 default:
2493 {
2494 fatalField (me);
2495 }
2496 }
2497 return;
2498 }
2499 }
2500 Melder_fatal (U"Option field not found in command window \"", my name.get(), U"\".");
2501 }
2502
UiForm_setOptionAsString(UiForm me,int * p_variable,conststring32 stringValue)2503 void UiForm_setOptionAsString (UiForm me, int *p_variable, conststring32 stringValue) {
2504 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2505 UiField field = my field [ifield].get();
2506 if (field -> intVariable == p_variable) {
2507 switch (field -> type)
2508 {
2509 case _kUiField_type::RADIO_:
2510 {
2511 for (int i = 1; i <= field -> options.size; i ++) {
2512 UiOption b = field -> options.at [i];
2513 if (str32equ (stringValue, b -> name.get())) {
2514 GuiRadioButton_set (b -> radioButton);
2515 }
2516 }
2517 /* If not found: do nothing (guard against incorrect prefs file). */
2518 }
2519 break;
2520 case _kUiField_type::OPTIONMENU_:
2521 {
2522 int optionValue = 0;
2523 for (int i = 1; i <= field -> options.size; i ++) {
2524 UiOption b = field -> options.at [i];
2525 if (str32equ (stringValue, b -> name.get())) {
2526 optionValue = i;
2527 break;
2528 }
2529 }
2530 GuiOptionMenu_setValue (field -> optionMenu, optionValue);
2531 /* If not found: do nothing (guard against incorrect prefs file). */
2532 }
2533 break;
2534 default:
2535 {
2536 fatalField (me);
2537 }
2538 }
2539 return;
2540 }
2541 }
2542 Melder_fatal (U"Option field not found in command window \"", my name.get(), U"\".");
2543 }
2544
UiForm_setString(UiForm me,conststring32 * p_variable,conststring32 value)2545 void UiForm_setString (UiForm me, conststring32 *p_variable, conststring32 value /* cattable */) {
2546 if (! value) value = U""; // accept null strings
2547 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2548 UiField field = my field [ifield].get();
2549 if (field -> stringVariable == p_variable) {
2550 switch (field -> type)
2551 {
2552 case _kUiField_type::WORD_:
2553 case _kUiField_type::SENTENCE_:
2554 case _kUiField_type::COLOUR_:
2555 case _kUiField_type::TEXT_:
2556 case _kUiField_type::FORMULA_:
2557 case _kUiField_type::INFILE_:
2558 case _kUiField_type::OUTFILE_:
2559 case _kUiField_type::FOLDER_:
2560 {
2561 GuiText_setString (field -> text, value);
2562 }
2563 break;
2564 case _kUiField_type::LABEL_:
2565 {
2566 GuiLabel_setText (field -> label, value);
2567 }
2568 break;
2569 default:
2570 {
2571 fatalField (me);
2572 }
2573 }
2574 return;
2575 }
2576 }
2577 Melder_fatal (U"Text field not found in command window \"", my name.get(), U"\".");
2578 }
2579
UiForm_setColourAsGreyValue(UiForm me,MelderColour * p_variable,double greyValue)2580 void UiForm_setColourAsGreyValue (UiForm me, MelderColour *p_variable, double greyValue) {
2581 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2582 UiField field = my field [ifield].get();
2583 if (field -> colourVariable == p_variable) {
2584 switch (field -> type)
2585 {
2586 case _kUiField_type::COLOUR_:
2587 {
2588 GuiText_setString (field -> text, Melder_double (greyValue));
2589 }
2590 break;
2591 default:
2592 {
2593 fatalField (me);
2594 }
2595 }
2596 return;
2597 }
2598 }
2599 Melder_fatal (U"Colour field not found in command window \"", my name.get(), U"\".");
2600 }
2601
findField(UiForm me,conststring32 fieldName)2602 static UiField findField (UiForm me, conststring32 fieldName) {
2603 for (int ifield = 1; ifield <= my numberOfFields; ifield ++)
2604 if (str32equ (fieldName, my field [ifield] -> name.get()))
2605 return my field [ifield].get();
2606 return nullptr;
2607 }
2608
findField_check(UiForm me,conststring32 fieldName)2609 static UiField findField_check (UiForm me, conststring32 fieldName) {
2610 UiField result = findField (me, fieldName);
2611 if (! result)
2612 Melder_throw (U"Cannot find field \"", fieldName, U"\" in form.\n"
2613 U"The script may have changed while the form was open.\n"
2614 U"Please click Cancel in the form and try again.");
2615 return result;
2616 }
2617
UiForm_getReal_check(UiForm me,conststring32 fieldName)2618 double UiForm_getReal_check (UiForm me, conststring32 fieldName) {
2619 UiField field = findField_check (me, fieldName);
2620 switch (field -> type)
2621 {
2622 case _kUiField_type::REAL_:
2623 case _kUiField_type::REAL_OR_UNDEFINED_:
2624 case _kUiField_type::POSITIVE_:
2625 {
2626 return field -> realValue;
2627 }
2628 break;
2629 default:
2630 {
2631 Melder_throw (U"Cannot find a real value in field \"", fieldName, U"\" in the form.\n"
2632 U"The script may have changed while the form was open.\n"
2633 U"Please click Cancel in the form and try again.");
2634 }
2635 }
2636 return 0.0;
2637 }
2638
UiForm_getInteger(UiForm me,conststring32 fieldName)2639 integer UiForm_getInteger (UiForm me, conststring32 fieldName) {
2640 UiField field = findField (me, fieldName);
2641 if (! field)
2642 Melder_fatal (U"(UiForm_getInteger:) No field \"", fieldName, U"\" in command window \"", my name.get(), U"\".");
2643 switch (field -> type)
2644 {
2645 case _kUiField_type::INTEGER_:
2646 case _kUiField_type::NATURAL_:
2647 case _kUiField_type::CHANNEL_:
2648 case _kUiField_type::BOOLEAN_:
2649 case _kUiField_type::RADIO_:
2650 case _kUiField_type::OPTIONMENU_:
2651 case _kUiField_type::LIST_:
2652 {
2653 return field -> integerValue;
2654 }
2655 break;
2656 default:
2657 {
2658 fatalField (me);
2659 }
2660 }
2661 return 0;
2662 }
2663
UiForm_getInteger_check(UiForm me,conststring32 fieldName)2664 integer UiForm_getInteger_check (UiForm me, conststring32 fieldName) {
2665 UiField field = findField_check (me, fieldName);
2666 switch (field -> type)
2667 {
2668 case _kUiField_type::INTEGER_:
2669 case _kUiField_type::NATURAL_:
2670 case _kUiField_type::CHANNEL_:
2671 case _kUiField_type::BOOLEAN_:
2672 case _kUiField_type::RADIO_:
2673 case _kUiField_type::OPTIONMENU_:
2674 case _kUiField_type::LIST_:
2675 {
2676 return field -> integerValue;
2677 }
2678 break;
2679 default:
2680 {
2681 Melder_throw (U"Cannot find an integer value in field \"", fieldName, U"\" in the form.\n"
2682 U"The script may have changed while the form was open.\n"
2683 U"Please click Cancel in the form and try again.");
2684 }
2685 }
2686 return 0;
2687 }
2688
UiForm_getString(UiForm me,conststring32 fieldName)2689 char32 * UiForm_getString (UiForm me, conststring32 fieldName) {
2690 UiField field = findField (me, fieldName);
2691 if (! field)
2692 Melder_fatal (U"(UiForm_getString:) No field \"", fieldName, U"\" in command window \"", my name.get(), U"\".");
2693 switch (field -> type)
2694 {
2695 case _kUiField_type::WORD_:
2696 case _kUiField_type::SENTENCE_:
2697 case _kUiField_type::TEXT_:
2698 case _kUiField_type::FORMULA_:
2699 case _kUiField_type::INFILE_:
2700 case _kUiField_type::OUTFILE_:
2701 case _kUiField_type::FOLDER_:
2702 {
2703 return field -> stringValue.get(); // BUG dangle
2704 }
2705 break;
2706 case _kUiField_type::RADIO_:
2707 case _kUiField_type::OPTIONMENU_:
2708 {
2709 UiOption b = field -> options.at [field -> integerValue];
2710 return b -> name.get();
2711 }
2712 break;
2713 case _kUiField_type::LIST_:
2714 {
2715 return (char32 *) field -> strings [field -> integerValue];
2716 }
2717 break;
2718 default:
2719 {
2720 fatalField (me);
2721 }
2722 }
2723 return nullptr;
2724 }
2725
UiForm_getString_check(UiForm me,conststring32 fieldName)2726 char32 * UiForm_getString_check (UiForm me, conststring32 fieldName) {
2727 UiField field = findField_check (me, fieldName);
2728 switch (field -> type)
2729 {
2730 case _kUiField_type::WORD_:
2731 case _kUiField_type::SENTENCE_:
2732 case _kUiField_type::TEXT_:
2733 case _kUiField_type::INFILE_:
2734 case _kUiField_type::OUTFILE_:
2735 case _kUiField_type::FOLDER_:
2736 {
2737 return field -> stringValue.get();
2738 }
2739 break;
2740 case _kUiField_type::RADIO_:
2741 case _kUiField_type::OPTIONMENU_:
2742 {
2743 UiOption b = field -> options.at [field -> integerValue];
2744 return b -> name.get();
2745 }
2746 break;
2747 case _kUiField_type::LIST_:
2748 {
2749 return (char32 *) field -> strings [field -> integerValue];
2750 }
2751 break;
2752 default:
2753 {
2754 Melder_throw (U"Cannot find a string in field \"", fieldName, U"\" in the form.\n"
2755 U"The script may have changed while the form was open.\n"
2756 U"Please click Cancel in the form and try again.");
2757 }
2758 }
2759 return nullptr;
2760 }
2761
UiForm_getRealVector(UiForm me,conststring32 fieldName)2762 VEC UiForm_getRealVector (UiForm me, conststring32 fieldName) {
2763 UiField field = findField (me, fieldName);
2764 if (! field)
2765 Melder_fatal (U"(UiForm_getRealVector:) No field \"", fieldName, U"\" in command window \"", my name.get(), U"\".");
2766 switch (field -> type)
2767 {
2768 case _kUiField_type::REALVECTOR_:
2769 case _kUiField_type::POSITIVEVECTOR_:
2770 {
2771 return field -> realVectorValue.get();
2772 }
2773 break;
2774 default:
2775 {
2776 fatalField (me);
2777 }
2778 }
2779 return VEC();
2780 }
2781
UiForm_getIntegerVector(UiForm me,conststring32 fieldName)2782 INTVEC UiForm_getIntegerVector (UiForm me, conststring32 fieldName) {
2783 UiField field = findField (me, fieldName);
2784 if (! field)
2785 Melder_fatal (U"(UiForm_getIntegerVector:) No field \"", fieldName, U"\" in command window \"", my name.get(), U"\".");
2786 switch (field -> type)
2787 {
2788 case _kUiField_type::INTEGERVECTOR_:
2789 case _kUiField_type::NATURALVECTOR_:
2790 {
2791 return field -> integerVectorValue.get();
2792 }
2793 break;
2794 default:
2795 {
2796 fatalField (me);
2797 }
2798 }
2799 return INTVEC();
2800 }
2801
UiForm_getColour_check(UiForm me,conststring32 fieldName)2802 MelderColour UiForm_getColour_check (UiForm me, conststring32 fieldName) {
2803 UiField field = findField_check (me, fieldName);
2804 switch (field -> type)
2805 {
2806 case _kUiField_type::COLOUR_: {
2807 return field -> colourValue;
2808 }
2809 break;
2810 default:
2811 {
2812 Melder_throw (U"Cannot find a colour value in field \"", fieldName, U"\" in the form.\n"
2813 U"The script may have changed while the form was open.\n"
2814 U"Please click Cancel in the form and try again.");
2815 }
2816 }
2817 return Melder_BLACK;
2818 }
2819
UiForm_Interpreter_addVariables(UiForm me,Interpreter interpreter)2820 void UiForm_Interpreter_addVariables (UiForm me, Interpreter interpreter) {
2821 static MelderString lowerCaseFieldName;
2822 for (int ifield = 1; ifield <= my numberOfFields; ifield ++) {
2823 UiField field = my field [ifield].get();
2824 MelderString_copy (& lowerCaseFieldName, field -> name.get());
2825 /*
2826 Change e.g. "Number of people" to "number_of_people".
2827 */
2828 lowerCaseFieldName.string [0] = Melder_toLowerCase (lowerCaseFieldName.string [0]);
2829 for (char32 *p = & lowerCaseFieldName.string [0]; *p != U'\0'; p ++) {
2830 if (*p == U' ')
2831 *p = U'_';
2832 }
2833 switch (field -> type)
2834 {
2835 case _kUiField_type::INTEGER_:
2836 case _kUiField_type::NATURAL_:
2837 case _kUiField_type::CHANNEL_:
2838 case _kUiField_type::BOOLEAN_:
2839 {
2840 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2841 var -> numericValue = field -> integerValue;
2842 }
2843 break;
2844 case _kUiField_type::REAL_:
2845 case _kUiField_type::REAL_OR_UNDEFINED_:
2846 case _kUiField_type::POSITIVE_:
2847 {
2848 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2849 var -> numericValue = field -> realValue;
2850 }
2851 break;
2852 case _kUiField_type::RADIO_:
2853 case _kUiField_type::OPTIONMENU_:
2854 {
2855 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2856 var -> numericValue = field -> integerValue;
2857 MelderString_appendCharacter (& lowerCaseFieldName, U'$');
2858 var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2859 UiOption b = field -> options.at [field -> integerValue];
2860 var -> stringValue = Melder_dup (b -> name.get());
2861 }
2862 break;
2863 case _kUiField_type::LIST_:
2864 {
2865 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2866 var -> numericValue = field -> integerValue;
2867 MelderString_appendCharacter (& lowerCaseFieldName, U'$');
2868 var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2869 var -> stringValue = Melder_dup (field -> strings [field -> integerValue]);
2870 }
2871 break;
2872 case _kUiField_type::WORD_:
2873 case _kUiField_type::SENTENCE_:
2874 case _kUiField_type::TEXT_:
2875 case _kUiField_type::FORMULA_:
2876 case _kUiField_type::INFILE_:
2877 case _kUiField_type::OUTFILE_:
2878 case _kUiField_type::FOLDER_:
2879 {
2880 MelderString_appendCharacter (& lowerCaseFieldName, U'$');
2881 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2882 var -> stringValue = Melder_dup (field -> stringValue.get());
2883 }
2884 break;
2885 case _kUiField_type::REALVECTOR_:
2886 case _kUiField_type::POSITIVEVECTOR_:
2887 {
2888 MelderString_appendCharacter (& lowerCaseFieldName, U'#');
2889 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2890 var -> numericVectorValue = copy_VEC (field -> realVectorValue.get()); // TODO: can we move this instead of copying it?
2891 }
2892 break;
2893 case _kUiField_type::INTEGERVECTOR_:
2894 case _kUiField_type::NATURALVECTOR_:
2895 {
2896 MelderString_appendCharacter (& lowerCaseFieldName, U'#');
2897 InterpreterVariable var = Interpreter_lookUpVariable (interpreter, lowerCaseFieldName.string);
2898 var -> numericVectorValue = cast_VEC (field -> integerVectorValue.get());
2899 }
2900 break;
2901 case _kUiField_type::COLOUR_:
2902 {
2903 // to be implemented
2904 }
2905 break;
2906 default:
2907 {
2908 }
2909 }
2910 }
2911 }
2912
UiForm_getClickedContinueButton(UiForm me)2913 int UiForm_getClickedContinueButton (UiForm me) {
2914 return my clickedContinueButton;
2915 }
2916
2917 /* End of file Ui.cpp */
2918