1 /* praat_menuCommands.cpp
2 *
3 * Copyright (C) 1992-2018,2020,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 "praatP.h"
20 #include "praat_script.h"
21 #include "praat_version.h"
22 #include "GuiP.h"
23
24 static OrderedOf <structPraat_Command> theCommands;
praat_menuCommands_exit_optimizeByLeaking()25 void praat_menuCommands_exit_optimizeByLeaking () { theCommands. _ownItems = false; }
26
praat_menuCommands_init()27 void praat_menuCommands_init () {
28 }
29
praat_sortMenuCommands()30 void praat_sortMenuCommands () {
31 for (integer i = 1; i <= theCommands.size; i ++) {
32 Praat_Command command = theCommands.at [i];
33 command -> sortingTail = i;
34 }
35 std::sort (theCommands.begin(), theCommands.end(),
36 [] (Praat_Command me, Praat_Command thee) {
37 if (my window) {
38 if (! thy window)
39 return false;
40 int compare = str32cmp (my window.get(), thy window.get());
41 if (compare != 0)
42 return compare < 0;
43 } else if (thy window)
44 return true;
45 if (my menu) {
46 if (! thy menu)
47 return false;
48 int compare = str32cmp (my menu.get(), thy menu.get());
49 if (compare != 0)
50 return compare < 0;
51 } else if (thy menu)
52 return true;
53 return my sortingTail < thy sortingTail;
54 }
55 );
56 }
57
lookUpMatchingMenuCommand(conststring32 window,conststring32 menu,conststring32 title)58 static integer lookUpMatchingMenuCommand (conststring32 window, conststring32 menu, conststring32 title) {
59 /*
60 * A menu command is fully specified by its environment (window + menu) and its title.
61 */
62 for (integer i = 1; i <= theCommands.size; i ++) {
63 Praat_Command command = theCommands.at [i];
64 conststring32 tryWindow = command -> window.get();
65 conststring32 tryMenu = command -> menu.get();
66 conststring32 tryTitle = command -> title.get();
67 if ((window == tryWindow || (window && tryWindow && str32equ (window, tryWindow))) &&
68 (menu == tryMenu || (menu && tryMenu && str32equ (menu, tryMenu))) &&
69 (title == tryTitle || (title && tryTitle && str32equ (title, tryTitle)))) return i;
70 }
71 return 0; // not found
72 }
73
do_menu(Praat_Command me,bool isModified)74 static void do_menu (Praat_Command me, bool isModified) {
75 if (my callback == DO_RunTheScriptFromAnyAddedMenuCommand) {
76 UiHistory_write (U"\nrunScript: ");
77 try {
78 DO_RunTheScriptFromAnyAddedMenuCommand (nullptr, 0, nullptr, my script.get(), nullptr, nullptr, false, nullptr);
79 } catch (MelderError) {
80 Melder_flushError (U"Command \"", my title.get(), U"\" not executed.");
81 }
82 praat_updateSelection ();
83 } else {
84 if (my title && ! str32str (my title.get(), U"...")) {
85 UiHistory_write (U"\n");
86 UiHistory_write (my title.get());
87 }
88 try {
89 my callback (nullptr, 0, nullptr, nullptr, nullptr, my title.get(), isModified, nullptr);
90 } catch (MelderError) {
91 Melder_flushError (U"Command \"", my title.get(), U"\" not executed.");
92 }
93 praat_updateSelection ();
94 }
95 }
96
gui_button_cb_menu(Praat_Command me,GuiButtonEvent event)97 static void gui_button_cb_menu (Praat_Command me, GuiButtonEvent event) {
98 const bool isModified = event -> shiftKeyPressed || event -> commandKeyPressed || event -> optionKeyPressed;
99 do_menu (me, isModified);
100 }
101
gui_cb_menu(Praat_Command me,GuiMenuItemEvent event)102 static void gui_cb_menu (Praat_Command me, GuiMenuItemEvent event) {
103 const bool isModified = event -> shiftKeyPressed || event -> commandKeyPressed || event -> optionKeyPressed;
104 do_menu (me, isModified);
105 }
106
windowMenuToWidget(conststring32 window,conststring32 menu)107 static GuiMenu windowMenuToWidget (conststring32 window, conststring32 menu) {
108 return
109 str32equ (window, U"Picture") ? praat_picture_resolveMenu (menu) :
110 str32equ (window, U"Objects") ? praat_objects_resolveMenu (menu) : nullptr;
111 }
112
praat_addMenuCommand_(conststring32 window,conststring32 menu,conststring32 title,conststring32 after,uint32 flags,UiCallback callback,conststring32 nameOfCallback)113 GuiMenuItem praat_addMenuCommand_ (conststring32 window, conststring32 menu, conststring32 title /* cattable */,
114 conststring32 after, uint32 flags, UiCallback callback, conststring32 nameOfCallback)
115 {
116 integer position;
117 int depth = flags, key = 0;
118 bool unhidable = false, hidden = false, noApi = false, forceApi = false;
119 int deprecationYear = 0;
120 uint32 guiFlags = 0;
121 if (flags > 7) {
122 depth = (flags & praat_DEPTH_7) >> 16;
123 unhidable = (flags & praat_UNHIDABLE) != 0;
124 hidden = (flags & praat_HIDDEN) != 0 && ! unhidable;
125 key = flags & 0x000000FF;
126 noApi = (flags & praat_NO_API) != 0;
127 forceApi = (flags & praat_FORCE_API) != 0;
128 deprecationYear = (flags & praat_DEPRECATED) == praat_DEPRECATED ? 2000 + (flags >> 24) : 0;
129 guiFlags = key ? flags & (0x000000FF | GuiMenu_SHIFT | GuiMenu_OPTION | GuiMenu_BUTTON_STATE_MASK) : flags & GuiMenu_BUTTON_STATE_MASK;
130 }
131 if (callback && ! title) {
132 Melder_flushError (U"praat_addMenuCommand: command with callback has no title. Window \"", window, U"\", menu \"", menu, U"\".");
133 return nullptr;
134 }
135
136 /*
137 * Determine the position of the new command.
138 */
139 if (after && after [0] != U'*') { // search for existing command with same selection
140 integer found = lookUpMatchingMenuCommand (window, menu, after);
141 if (found) {
142 position = found + 1; // after 'after'
143 } else {
144 Melder_flushError (U"praat_addMenuCommand: the command \"", title, U"\" cannot be put after \"", after, U"\",\n"
145 U"in the menu \"", menu, U"\" in the window \"", window, U"\"\n"
146 U"because the latter command does not exist.");
147 return nullptr;
148 }
149 } else {
150 position = theCommands.size + 1; // at end
151 }
152
153 /*
154 * Make new command.
155 */
156 autoPraat_Command command = Thing_new (Praat_Command);
157
158 command -> window = Melder_dup_f (window);
159 command -> menu = Melder_dup_f (menu);
160 command -> title = Melder_dup_f (title);
161 trace (U"insert new command \"", title, U"\"");
162 command -> depth = depth;
163 command -> callback = callback; // null for a separator or cascade button
164 command -> nameOfCallback = nameOfCallback;
165 command -> executable = !! callback;
166 command -> script = autostring32();
167 command -> hidden = hidden;
168 command -> unhidable = unhidable;
169 command -> deprecationYear = deprecationYear;
170 command -> noApi = noApi;
171 command -> forceApi = forceApi;
172
173 if (! theCurrentPraatApplication -> batch) {
174 GuiMenu parentMenu = nullptr;
175
176 /* WHERE TO PUT IT?
177 * Determine parent menu widget.
178 * This is not going to fail:
179 * if 'depth' is inappropriate, the alleged subitem will be put in the top menu.
180 */
181 if (depth == 0) {
182 /*
183 * Put the new command in the window's top menu.
184 */
185 parentMenu = windowMenuToWidget (window, menu);
186 } else {
187 /*
188 * Put the new command in a submenu.
189 * The supermenu to put the new command in is the first menu that we find when going up.
190 */
191 for (integer parentPosition = position - 1; parentPosition > 0; parentPosition --) {
192 Praat_Command parentCommand = theCommands.at [parentPosition];
193 if (parentCommand -> depth == depth - 1 && str32equ (parentCommand -> menu.get(), command -> menu.get())) {
194 /*
195 * We found the supermenu.
196 */
197 if (! parentCommand -> callback && parentCommand -> title && parentCommand -> title [0] != U'-') {
198 if (! parentCommand -> button)
199 Melder_fatal (U"No button for ", window, U"/", menu, U"/", title, U".");
200 Thing_cast (GuiMenuItem, parentButton_as_GuiMenuItem, parentCommand -> button);
201 parentMenu = parentButton_as_GuiMenuItem -> d_menu;
202 }
203 break;
204 }
205 }
206 if (! parentMenu)
207 parentMenu = windowMenuToWidget (window, menu); // fallback: put the command in the window's top menu
208 }
209 if (! parentMenu) {
210 trace (U"WARNING: no parent menu for ", window, U"/", menu, U"/", title, U".");
211 return nullptr;
212 }
213
214 /*
215 * WHAT TO PUT THERE?
216 */
217
218 if (! title || title [0] == U'-') {
219 trace (U"insert the command as a separator");
220 command -> button = GuiMenu_addSeparator (parentMenu);
221 Melder_assert (command -> button);
222 } else if (! callback) {
223 trace (U"insert the command as a submenu");
224 command -> button = GuiMenu_createInMenu (parentMenu, title, 0) -> d_menuItem.get();
225 Melder_assert (command -> button);
226 } else {
227 trace (U"insert the command as a normal menu item");
228 command -> button = GuiMenu_addItem (parentMenu, title, guiFlags, gui_cb_menu, command.get());
229 Melder_assert (command -> button);
230 }
231 if (hidden) GuiThing_hide (command -> button);
232 }
233 Thing_cast (GuiMenuItem, button_as_GuiMenuItem, command -> button);
234 theCommands. addItemAtPosition_move (command.move(), position);
235 return button_as_GuiMenuItem;
236 }
237
praat_addMenuCommandScript(conststring32 window,conststring32 menu,conststring32 title,conststring32 after,integer depth,conststring32 script)238 void praat_addMenuCommandScript (conststring32 window, conststring32 menu, conststring32 title,
239 conststring32 after, integer depth, conststring32 script)
240 {
241 try {
242 Melder_assert (window && menu && title && after && script);
243 if (script [0] != U'\0' && title [0] == U'\0')
244 Melder_throw (U"Command with script has no title. Window \"", window, U"\", menu \"", menu, U"\".");
245
246 /*
247 * Determine the position of the new command.
248 */
249 integer position;
250 if (str32len (after) && after [0] != U'*') { // search for existing command with same selection
251 integer found = lookUpMatchingMenuCommand (window, menu, after);
252 if (found) {
253 position = found + 1; // after 'after'
254 } else {
255 /*Melder_throw (U"The menu command \"", title, U"\" cannot be put after \"", after, U"\",\n"
256 U"in the menu \"", menu, "\" in the window \"", window, U"\"\n"
257 U"because the latter command does not exist.");*/
258 position = theCommands.size + 1; // default: at end
259 }
260 } else {
261 position = theCommands.size + 1; // at end
262 }
263
264 /*
265 * Make new command.
266 */
267 autoPraat_Command command = Thing_new (Praat_Command);
268 command -> window = Melder_dup_f (window);
269 command -> menu = Melder_dup_f (menu);
270 command -> title = ( title [0] != U'\0' ? Melder_dup_f (title) : autostring32() ); // allow old-fashioned untitled separators
271 command -> depth = depth;
272 command -> callback = ( script [0] != U'\0' ? DO_RunTheScriptFromAnyAddedMenuCommand : nullptr ); // null for a separator or cascade button
273 command -> executable = ( script [0] != U'\0' );
274 command -> noApi = true;
275 if (script [0] == U'\0') {
276 command -> script = Melder_dup_f (U""); // empty string, which will be needed to signal origin
277 } else {
278 structMelderFile file { };
279 Melder_relativePathToFile (script, & file);
280 command -> script = Melder_dup_f (Melder_fileToPath (& file));
281 }
282 command -> after = ( after [0] != U'\0' ? Melder_dup_f (after) : autostring32() );
283 if (praatP.phase >= praat_READING_BUTTONS) {
284 static integer uniqueID = 0;
285 command -> uniqueID = ++ uniqueID;
286 }
287
288 if (! theCurrentPraatApplication -> batch) {
289 GuiMenu parentMenu = nullptr;
290
291 /* WHERE TO PUT IT?
292 * Determine parent menu widget.
293 * This is not going to fail:
294 * if 'depth' is inappropriate, the alleged subitem will be put in the top menu.
295 */
296 if (depth == 0) {
297 parentMenu = windowMenuToWidget (window, menu); // not a subitem: in the top menu
298 } else {
299 for (integer parentPosition = position - 1; parentPosition > 0; parentPosition --) {
300 Praat_Command parentCommand = theCommands.at [parentPosition];
301 if (parentCommand -> depth == depth - 1) {
302 if (! parentCommand -> callback && parentCommand -> title && parentCommand -> title [0] != U'-') {
303 if (! parentCommand -> button)
304 Melder_fatal (U"No button for ", window, U"/", menu, U"/", title, U".");
305 Melder_assert (parentCommand -> button -> classInfo == classGuiMenuItem);
306 parentMenu = (static_cast <GuiMenuItem> (parentCommand -> button)) -> d_menu;
307 }
308 break;
309 }
310 }
311 if (! parentMenu)
312 parentMenu = windowMenuToWidget (window, menu); // fallback: a subitem without a menu title
313 }
314 if (parentMenu) {
315 /* WHAT TO PUT THERE?
316 */
317 if (title [0] == U'\0' || title [0] == U'-') {
318 command -> button = GuiMenu_addSeparator (parentMenu);
319 } else if (script [0] == '\0') {
320 command -> button = GuiMenu_createInMenu (parentMenu, title, 0) -> d_menuItem.get();
321 } else {
322 command -> button = GuiMenu_addItem (parentMenu, title, 0, gui_cb_menu, command.get());
323 }
324 }
325 }
326 theCommands. addItemAtPosition_move (command.move(), position);
327
328 if (praatP.phase >= praat_HANDLING_EVENTS) praat_sortMenuCommands ();
329 } catch (MelderError) {
330 Melder_throw (U"Script menu command not added.");
331 }
332 }
333
praat_hideMenuCommand(conststring32 window,conststring32 menu,conststring32 title)334 void praat_hideMenuCommand (conststring32 window, conststring32 menu, conststring32 title) {
335 if (theCurrentPraatApplication -> batch || ! window || ! menu || ! title) return;
336 integer found = lookUpMatchingMenuCommand (window, menu, title);
337 if (! found) return;
338 Praat_Command command = theCommands.at [found];
339 if (! command -> hidden && ! command -> unhidable) {
340 command -> hidden = true;
341 if (praatP.phase >= praat_READING_BUTTONS) command -> toggled = ! command -> toggled;
342 if (command -> button) GuiThing_hide (command -> button);
343 }
344 }
345
praat_showMenuCommand(conststring32 window,conststring32 menu,conststring32 title)346 void praat_showMenuCommand (conststring32 window, conststring32 menu, conststring32 title) {
347 if (theCurrentPraatApplication -> batch || ! window || ! menu || ! title) return;
348 integer found = lookUpMatchingMenuCommand (window, menu, title);
349 if (! found) return;
350 Praat_Command command = theCommands.at [found];
351 if (command -> hidden) {
352 command -> hidden = false;
353 if (praatP.phase >= praat_READING_BUTTONS) command -> toggled = ! command -> toggled;
354 if (command -> button) GuiThing_show (command -> button);
355 }
356 }
357
praat_saveAddedMenuCommands(MelderString * buffer)358 void praat_saveAddedMenuCommands (MelderString *buffer) {
359 /*
360 The procedure as it is now, runs in M*N time,
361 where M is the number of added commands and N is the total number of commands.
362 Sorting first instead runs in N log N time and will therefore be faster if M >> log N ≈ 10.
363 */
364 integer maxID = 0;
365 for (integer i = 1; i <= theCommands.size; i ++) {
366 Praat_Command command = theCommands.at [i];
367 if (command -> uniqueID > maxID)
368 maxID = command -> uniqueID;
369 }
370 for (integer id = 1; id <= maxID; id ++) // sorted
371 for (integer i = 1; i <= theCommands.size; i ++) {
372 Praat_Command me = theCommands.at [i];
373 if (my uniqueID == id && ! my hidden && my window && my menu && my title) {
374 MelderString_append (buffer, U"Add menu command... \"", my window.get(), U"\" \"", my menu.get(), U"\" \"", my title.get(), U"\" \"",
375 ( my after ? my after.get() : U"" ), U"\" ", my depth, U" ", ( my script ? my script.get() : U"" ), U"\n");
376 break;
377 }
378 }
379 }
380
praat_saveToggledMenuCommands(MelderString * buffer)381 void praat_saveToggledMenuCommands (MelderString *buffer) {
382 for (integer i = 1; i <= theCommands.size; i ++) {
383 Praat_Command me = theCommands.at [i];
384 if (my toggled && my window && my menu && my title && ! my uniqueID && ! my script)
385 MelderString_append (buffer, my hidden ? U"Hide" : U"Show", U" menu command... \"",
386 my window.get(), U"\" \"", my menu.get(), U"\" ", my title.get(), U"\n");
387 }
388 }
389
390 /***** FIXED BUTTONS *****/
391
praat_addFixedButtonCommand_(GuiForm parent,conststring32 title,UiCallback callback,conststring32 nameOfCallback,int x,int y)392 void praat_addFixedButtonCommand_ (GuiForm parent, conststring32 title, UiCallback callback, conststring32 nameOfCallback, int x, int y) {
393 autoPraat_Command me = Thing_new (Praat_Command);
394 my window = Melder_dup_f (U"Objects");
395 my title = Melder_dup_f (title);
396 my callback = callback;
397 my nameOfCallback = nameOfCallback;
398 my unhidable = true;
399 my noApi = ( str32equ (title, U"Inspect") );
400 if (theCurrentPraatApplication -> batch) {
401 my button = nullptr;
402 } else {
403 GuiThing button = my button = GuiButton_create (parent, x, x + 82, -y - Gui_PUSHBUTTON_HEIGHT, -y,
404 title, gui_button_cb_menu, me.get(), 0);
405 GuiThing_setSensitive (button, false);
406 GuiThing_show (button);
407 }
408 my executable = false;
409 theCommands. addItemAtPosition_move (me.move(), 0);
410 }
411
praat_sensitivizeFixedButtonCommand(conststring32 title,bool sensitive)412 void praat_sensitivizeFixedButtonCommand (conststring32 title, bool sensitive) {
413 Praat_Command commandFound = nullptr;
414 for (integer i = 1; i <= theCommands.size; i ++) {
415 Praat_Command command = theCommands.at [i];
416 if (str32equ (command -> title.get(), title)) {
417 commandFound = command;
418 break;
419 }
420 }
421 if (! commandFound)
422 Melder_fatal (U"Unkown fixed button <<", title, U">>");
423 commandFound -> executable = sensitive;
424 if (! theCurrentPraatApplication -> batch && ! Melder_backgrounding)
425 GuiThing_setSensitive (commandFound -> button, sensitive);
426 }
427
praat_doMenuCommand(conststring32 title,conststring32 arguments,Interpreter interpreter)428 int praat_doMenuCommand (conststring32 title, conststring32 arguments, Interpreter interpreter) {
429 Praat_Command commandFound = nullptr;
430 for (integer i = 1; i <= theCommands.size; i ++) {
431 Praat_Command command = theCommands.at [i];
432 if (command -> executable && str32equ (command -> title.get(), title) &&
433 (str32equ (command -> window.get(), U"Objects") || str32equ (command -> window.get(), U"Picture")))
434 {
435 commandFound = command;
436 break;
437 }
438 }
439 if (! commandFound)
440 return 0;
441 if (commandFound -> callback == DO_RunTheScriptFromAnyAddedMenuCommand) {
442 const conststring32 scriptPath = commandFound -> script.get();
443 const conststring32 preferencesFolderPath = Melder_dirToPath (& Melder_preferencesFolder);
444 const bool scriptIsInPlugin =
445 Melder_stringMatchesCriterion (scriptPath, kMelder_string::STARTS_WITH, preferencesFolderPath, true);
446 Melder_throw (
447 U"From a script you cannot directly call a menu command that calls another script. Use instead: \nrunScript: ",
448 scriptIsInPlugin ? U"preferencesDirectory$ + " : U"",
449 U"\"",
450 scriptIsInPlugin ? scriptPath + str32len (preferencesFolderPath) : scriptPath,
451 U"\"",
452 arguments && arguments [0] ? U", " : U"",
453 arguments && arguments [0] ? arguments : U"",
454 U"\n"
455 );
456 }
457 commandFound -> callback (nullptr, 0, nullptr, arguments, interpreter, title, false, nullptr);
458 return 1;
459 }
460
praat_doMenuCommand(conststring32 title,integer narg,Stackel args,Interpreter interpreter)461 int praat_doMenuCommand (conststring32 title, integer narg, Stackel args, Interpreter interpreter) {
462 Praat_Command commandFound = nullptr;
463 for (integer i = 1; i <= theCommands.size; i ++) {
464 Praat_Command command = theCommands.at [i];
465 if (command -> executable && str32equ (command -> title.get(), title) &&
466 (str32equ (command -> window.get(), U"Objects") || str32equ (command -> window.get(), U"Picture")))
467 {
468 commandFound = command;
469 break;
470 }
471 }
472 if (! commandFound)
473 return 0;
474 if (commandFound -> callback == DO_RunTheScriptFromAnyAddedMenuCommand) {
475 const conststring32 scriptPath = commandFound -> script.get();
476 const conststring32 preferencesFolderPath = Melder_dirToPath (& Melder_preferencesFolder);
477 const bool scriptIsInPlugin =
478 Melder_stringMatchesCriterion (scriptPath, kMelder_string::STARTS_WITH, preferencesFolderPath, true);
479 Melder_throw (
480 U"From a script you cannot directly call a menu command that calls another script. Use instead: \nrunScript: ",
481 scriptIsInPlugin ? U"preferencesDirectory$ + " : U"",
482 U"\"",
483 scriptIsInPlugin ? scriptPath + str32len (preferencesFolderPath) : scriptPath,
484 U"\"",
485 narg > 0 ? U", ..." : U"",
486 U"\n"
487 );
488 }
489 commandFound -> callback (nullptr, narg, args, nullptr, interpreter, title, false, nullptr);
490 return 1;
491 }
492
praat_getNumberOfMenuCommands()493 integer praat_getNumberOfMenuCommands () { return theCommands.size; }
494
praat_getMenuCommand(integer i)495 Praat_Command praat_getMenuCommand (integer i)
496 { return i < 1 || i > theCommands.size ? nullptr : theCommands.at [i]; }
497
praat_addCommandsToEditor(Editor me)498 void praat_addCommandsToEditor (Editor me) {
499 conststring32 windowName = my classInfo -> className;
500 for (integer i = 1; i <= theCommands.size; i ++) {
501 Praat_Command command = theCommands.at [i];
502 if (str32equ (command -> window.get(), windowName)) {
503 Editor_addCommandScript (me, command -> menu.get(), command -> title.get(), 0, command -> script.get());
504 }
505 }
506 }
507
commandIsToBeIncluded(Praat_Command command,bool deprecated,bool includeCreateAPI,bool includeReadAPI,bool includeRecordAPI,bool includePlayAPI,bool includeDrawAPI,bool includeHelpAPI,bool includeWindowAPI)508 static bool commandIsToBeIncluded (Praat_Command command, bool deprecated, bool includeCreateAPI, bool includeReadAPI,
509 bool includeRecordAPI, bool includePlayAPI, bool includeDrawAPI, bool includeHelpAPI, bool includeWindowAPI)
510 {
511 const bool obsolete = ( deprecated && (command -> deprecationYear < PRAAT_YEAR - 10 || command -> deprecationYear < 2017) );
512 const bool hiddenByDefault = ( command -> hidden != command -> toggled );
513 const bool explicitlyHidden = hiddenByDefault && ! deprecated;
514 const bool hidden = explicitlyHidden || ! command -> callback || command -> noApi || obsolete ||
515 (! includeWindowAPI && Melder_nequ (command -> nameOfCallback, U"WINDOW_", 7)) ||
516 (! includeHelpAPI && Melder_nequ (command -> nameOfCallback, U"HELP_", 5)) ||
517 (! includeDrawAPI && Melder_nequ (command -> nameOfCallback, U"GRAPHICS_", 9)) ||
518 (! includePlayAPI && Melder_nequ (command -> nameOfCallback, U"PLAY_", 5)) ||
519 (! includeRecordAPI && Melder_nequ (command -> nameOfCallback, U"RECORD_", 7)) ||
520 (! includeReadAPI && Melder_nequ (command -> nameOfCallback, U"READ_", 5)) ||
521 (! includeReadAPI && Melder_nequ (command -> nameOfCallback, U"READ1_", 6)) ||
522 (! includeCreateAPI && Melder_nequ (command -> nameOfCallback, U"NEW1_", 5));
523 return (command -> forceApi || ! hidden) && command -> callback != DO_RunTheScriptFromAnyAddedMenuCommand;
524 }
525
commandHasFileNameArgument(Praat_Command command)526 static bool commandHasFileNameArgument (Praat_Command command) {
527 const bool hasFileNameArgument =
528 Melder_nequ (command -> nameOfCallback, U"READ1_", 6) ||
529 Melder_nequ (command -> nameOfCallback, U"SAVE_", 5)
530 ;
531 return hasFileNameArgument;
532 }
533
getReturnType(Praat_Command command)534 static conststring32 getReturnType (Praat_Command command) {
535 const conststring32 returnType =
536 Melder_nequ (command -> nameOfCallback, U"NEW1_", 5) ? U"PraatObject" :
537 Melder_nequ (command -> nameOfCallback, U"READ1_", 6) ? U"PraatObject" :
538 Melder_nequ (command -> nameOfCallback, U"REAL_", 5) ? U"double" :
539 Melder_nequ (command -> nameOfCallback, U"INTEGER_", 8) ? U"int64_t" :
540 Melder_nequ (command -> nameOfCallback, U"STRING_", 7) ? U"char *" :
541 Melder_nequ (command -> nameOfCallback, U"REPORT_", 7) ? U"char *" :
542 Melder_nequ (command -> nameOfCallback, U"LIST_", 5) ? U"char *" :
543 Melder_nequ (command -> nameOfCallback, U"INFO_", 5) ? U"char *" :
544 Melder_nequ (command -> nameOfCallback, U"HINT_", 5) ? U"char *" :
545 U"void";
546 return returnType;
547 }
548
praat_menuCommands_writeC(bool isInHeaderFile,bool includeCreateAPI,bool includeReadAPI,bool includeRecordAPI,bool includePlayAPI,bool includeDrawAPI,bool includeHelpAPI,bool includeWindowAPI)549 void praat_menuCommands_writeC (bool isInHeaderFile, bool includeCreateAPI, bool includeReadAPI,
550 bool includeRecordAPI, bool includePlayAPI, bool includeDrawAPI, bool includeHelpAPI, bool includeWindowAPI)
551 {
552 try {
553 integer numberOfApiMenuCommands = 0;
554 for (integer i = 1; i <= theCommands.size; i ++) {
555 Praat_Command command = theCommands.at [i];
556 const bool deprecated = ( command -> deprecationYear > 0 );
557 if (! commandIsToBeIncluded (command, deprecated, includeCreateAPI, includeReadAPI,
558 includeRecordAPI, includePlayAPI, includeDrawAPI, includeHelpAPI, includeWindowAPI)) continue;
559 MelderInfo_writeLine (U"\n/* Menu command \"", command -> title.get(), U"\"",
560 deprecated ? U", deprecated " : U"", deprecated ? Melder_integer (command -> deprecationYear) : U"",
561 U" */");
562 conststring32 returnType = getReturnType (command);
563 MelderInfo_writeLine (returnType, U" Praat", str32chr (command -> nameOfCallback, U'_'), U" (");
564 const bool isDirect = ! str32str (command -> title.get(), U"...");
565 if (isDirect) {
566 } else {
567 command -> callback (nullptr, -1, nullptr, nullptr, nullptr, nullptr, false, nullptr);
568 }
569 if (commandHasFileNameArgument (command))
570 MelderInfo_writeLine (U"\tconst char *fileName");
571 MelderInfo_write (U")");
572 if (isInHeaderFile) {
573 MelderInfo_writeLine (U";");
574 } else {
575 MelderInfo_writeLine (U" {");
576 MelderInfo_writeLine (U"}");
577 }
578 numberOfApiMenuCommands += 1;
579 }
580 } catch (MelderError) {
581 Melder_throw (U"Menu commands not written to C library.");
582 }
583 }
584
585 /* End of file praat_menuCommands.cpp */
586