1 /* praat_objectMenus.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 "praatP.h"
20 #include "praatM.h"
21 #include "praat_script.h"
22 #include "ScriptEditor.h"
23 #include "ButtonEditor.h"
24 #include "DataEditor.h"
25 #include "site.h"
26 #include "GraphicsP.h"
27 
28 #define EDITOR  theCurrentPraatObjects -> list [IOBJECT]. editors
29 
30 /********** Callbacks of the fixed buttons. **********/
31 
DIRECT(PRAAT_Remove)32 DIRECT (PRAAT_Remove) {
33 	WHERE_DOWN (SELECTED)
34 		praat_removeObject (IOBJECT);
35 	praat_show ();
36 	END_NO_NEW_DATA
37 }
38 
39 FORM (MODIFY_Rename, U"Rename object", U"Rename...") {
40 	TEXTFIELD (newName, U"New name", U"", 3)
41 OK
42 	WHERE (SELECTED) SET_STRING (newName, NAME)
43 DO
44 	if (theCurrentPraatObjects -> totalSelection == 0)
45 		Melder_throw (U"Selection changed!\nNo object selected. Cannot rename.");
46 	if (theCurrentPraatObjects -> totalSelection > 1)
47 		Melder_throw (U"Selection changed!\nCannot rename more than one object at a time.");
48 	WHERE (SELECTED) break;
49 	static MelderString string;
50 	MelderString_copy (& string, newName);
51 	praat_cleanUpName (string.string);
52 	static MelderString fullName;
53 	MelderString_copy (& fullName, Thing_className (OBJECT), U" ", string.string);
54 	if (! str32equ (fullName.string, FULL_NAME)) {
55 		theCurrentPraatObjects -> list [IOBJECT]. name = Melder_dup_f (fullName.string);
56 		autoMelderString listName;
57 		MelderString_append (& listName, ID, U". ", fullName.string);
58 		praat_list_renameAndSelect (IOBJECT, listName.string);
59 		for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
60 			if (EDITOR [ieditor]) Thing_setName (EDITOR [ieditor], fullName.string);
61 		Thing_setName (OBJECT, string.string);
62 	}
63 	END_NO_NEW_DATA
64 }
65 
66 FORM (NEW1_Copy, U"Copy object", U"Copy...") {
67 	TEXTFIELD (newName, U"Name of new object", U"", 3)
68 OK
69 	WHERE (SELECTED) SET_STRING (newName, NAME)
70 DO
71 	if (theCurrentPraatObjects -> totalSelection == 0)
72 		Melder_throw (U"Selection changed!\nNo object selected. Cannot copy.");
73 	if (theCurrentPraatObjects -> totalSelection > 1)
74 		Melder_throw (U"Selection changed!\nCannot copy more than one object at a time.");
75 	if (interpreter)
76 		interpreter -> returnType = kInterpreter_ReturnType::OBJECT_;
77 	WHERE (SELECTED)
78 		praat_new (Data_copy ((Daata) OBJECT), newName);
79 	END_WITH_NEW_DATA
80 }
81 
DIRECT(INFO_Info)82 DIRECT (INFO_Info) {
83 	INFO_NONE
84 		if (theCurrentPraatObjects -> totalSelection == 0)
85 			Melder_throw (U"Selection changed!\nNo object selected. Cannot query.");
86 		if (theCurrentPraatObjects -> totalSelection > 1)
87 			Melder_throw (U"Selection changed!\nCannot query more than one object at a time.");
88 		WHERE (SELECTED) Thing_infoWithIdAndFile (OBJECT, ID, & theCurrentPraatObjects -> list [IOBJECT]. file);
89 	INFO_NONE_END
90 }
91 
DIRECT(PRAAT__Inspect)92 DIRECT (PRAAT__Inspect) {
93 	PRAAT
94 		if (theCurrentPraatObjects -> totalSelection == 0)
95 			Melder_throw (U"Selection changed!\nNo object selected. Cannot inspect.");
96 		if (theCurrentPraatApplication -> batch) {
97 			Melder_throw (U"Cannot inspect data from batch.");
98 		} else {
99 			WHERE (SELECTED) {
100 				autoDataEditor editor = DataEditor_create (ID_AND_FULL_NAME, OBJECT);
101 				praat_installEditor (editor.get(), IOBJECT);
102 				editor.releaseToUser();
103 			}
104 		}
105 	PRAAT_END
106 }
107 
108 /********** The fixed menus. **********/
109 
110 static GuiMenu praatMenu, editMenu, windowMenu, newMenu, readMenu, goodiesMenu, preferencesMenu, technicalMenu, applicationHelpMenu, helpMenu;
111 
praat_objects_resolveMenu(conststring32 menu)112 GuiMenu praat_objects_resolveMenu (conststring32 menu) {
113 	return
114 		str32equ (menu, U"Praat") || str32equ (menu, U"Control") ? praatMenu :
115 		#if cocoa
116 			str32equ (menu, U"Edit") ? editMenu :
117 			str32equ (menu, U"Window") ? windowMenu :
118 		#endif
119 		str32equ (menu, U"New") || str32equ (menu, U"Create") ? newMenu :
120 		str32equ (menu, U"Open") || str32equ (menu, U"Read") ? readMenu :
121 		str32equ (menu, U"Help") ? helpMenu :
122 		str32equ (menu, U"Goodies") ? goodiesMenu :
123 		str32equ (menu, U"Preferences") ? preferencesMenu :
124 		str32equ (menu, U"Technical") ? technicalMenu :
125 		#ifdef macintosh
126 			str32equ (menu, U"ApplicationHelp") ? applicationHelpMenu :
127 		#else
128 			str32equ (menu, U"ApplicationHelp") ? helpMenu :
129 		#endif
130 		newMenu;   // default
131 }
132 
133 /********** Callbacks of the Praat menu. **********/
134 
DIRECT(PRAAT__About)135 DIRECT (PRAAT__About) {
136 	PRAAT
137 		praat_showLogo ();
138 	PRAAT_END
139 }
140 
DIRECT(PRAAT__newScript)141 DIRECT (PRAAT__newScript) {
142 	PRAAT
143 		autoScriptEditor editor = ScriptEditor_createFromText (nullptr, nullptr);
144 		editor.releaseToUser();
145 	PRAAT_END
146 }
147 
DIRECT(PRAAT__openScript)148 DIRECT (PRAAT__openScript) {
149 	PRAAT
150 		autoScriptEditor editor = ScriptEditor_createFromText (nullptr, nullptr);
151 		TextEditor_showOpen (editor.get());
152 		editor.releaseToUser();
153 	PRAAT_END
154 }
155 
156 static ButtonEditor theReferenceToTheOnlyButtonEditor;
157 
cb_ButtonEditor_destruction(Editor)158 static void cb_ButtonEditor_destruction (Editor /* editor */) {
159 	theReferenceToTheOnlyButtonEditor = nullptr;
160 }
161 
DIRECT(PRAAT__editButtons)162 DIRECT (PRAAT__editButtons) {
163 	PRAAT
164 		if (theReferenceToTheOnlyButtonEditor) {
165 			Editor_raise (theReferenceToTheOnlyButtonEditor);
166 		} else {
167 			autoButtonEditor editor = ButtonEditor_create ();
168 			Editor_setDestructionCallback (editor.get(), cb_ButtonEditor_destruction);
169 			theReferenceToTheOnlyButtonEditor = editor.get();
170 			editor.releaseToUser();
171 		}
172 	PRAAT_END
173 }
174 
175 FORM (PRAAT__addMenuCommand, U"Add menu command", U"Add menu command...") {
176 	WORD (window, U"Window", U"Objects")
177 	WORD (menu, U"Menu", U"New")
178 	SENTENCE (command, U"Command", U"Hallo...")
179 	SENTENCE (afterCommand, U"After command", U"")
180 	INTEGER (depth, U"Depth", U"0")
181 	INFILE (script, U"Script file", U"/u/miep/hallo.praat")
182 	OK
183 DO
184 	PRAAT
185 		praat_addMenuCommandScript (window, menu, command, afterCommand, depth, script);
186 	PRAAT_END
187 }
188 
189 FORM (PRAAT__hideMenuCommand, U"Hide menu command", U"Hide menu command...") {
190 	WORD (window, U"Window", U"Objects")
191 	WORD (menu, U"Menu", U"New")
192 	SENTENCE (command, U"Command", U"Hallo...")
193 	OK
194 DO
195 	PRAAT
196 		praat_hideMenuCommand (window, menu, command);
197 	PRAAT_END
198 }
199 
200 FORM (PRAAT__showMenuCommand, U"Show menu command", U"Show menu command...") {
201 	WORD (window, U"Window", U"Objects")
202 	WORD (menu, U"Menu", U"New")
203 	SENTENCE (command, U"Command", U"Hallo...")
204 	OK
205 DO
206 	PRAAT
207 		praat_showMenuCommand (window, menu, command);
208 	PRAAT_END
209 }
210 
211 FORM (PRAAT__addAction, U"Add action command", U"Add action command...") {
212 	WORD (class1, U"Class 1", U"Sound")
213 	INTEGER (number1, U"Number 1", U"0")
214 	WORD (class2, U"Class 2", U"")
215 	INTEGER (number2, U"Number 2", U"0")
216 	WORD (class3, U"Class 3", U"")
217 	INTEGER (number3, U"Number 3", U"0")
218 	SENTENCE (command, U"Command", U"Play reverse")
219 	SENTENCE (afterCommand, U"After command", U"Play")
220 	INTEGER (depth, U"Depth", U"0")
221 	INFILE (script, U"Script file", U"/u/miep/playReverse.praat")
222 	OK
223 DO
224 	PRAAT
225 		praat_addActionScript (class1, number1, class2, number2, class3, number3, command, afterCommand, depth, script);
226 	PRAAT_END
227 }
228 
229 FORM (PRAAT__hideAction, U"Hide action command", U"Hide action command...") {
230 	WORD (class1, U"Class 1", U"Sound")
231 	WORD (class2, U"Class 2", U"")
232 	WORD (class3, U"Class 3", U"")
233 	SENTENCE (command, U"Command", U"Play")
234 	OK
235 DO
236 	PRAAT
237 		praat_hideAction_classNames (class1, class2, class3, command);
238 	PRAAT_END
239 }
240 
241 FORM (PRAAT__showAction, U"Show action command", U"Show action command...") {
242 	WORD (class1, U"Class 1", U"Sound")
243 	WORD (class2, U"Class 2", U"")
244 	WORD (class3, U"Class 3", U"")
245 	SENTENCE (command, U"Command", U"Play")
246 	OK
247 DO
248 	PRAAT
249 		praat_showAction_classNames (class1, class2, class3, command);
250 	PRAAT_END
251 }
252 
253 /********** Callbacks of the Preferences menu. **********/
254 
255 FORM (PREFS__TextInputEncodingSettings, U"Text reading preferences", U"Unicode") {
256 	RADIO_ENUM (kMelder_textInputEncoding, encodingOf8BitTextFiles,
257 			U"Encoding of 8-bit text files", kMelder_textInputEncoding::DEFAULT)
258 OK
259 	SET_ENUM (encodingOf8BitTextFiles, kMelder_textInputEncoding, Melder_getInputEncoding ())
260 DO
261 	PREFS
262 		Melder_setInputEncoding (encodingOf8BitTextFiles);
263 	PREFS_END
264 }
265 
266 FORM (PREFS__TextOutputEncodingSettings, U"Text writing preferences", U"Unicode") {
267 	RADIO_ENUM (kMelder_textOutputEncoding, outputEncoding,
268 			U"Output encoding", kMelder_textOutputEncoding::DEFAULT)
269 OK
270 	SET_ENUM (outputEncoding, kMelder_textOutputEncoding, Melder_getOutputEncoding ())
271 DO
272 	PREFS
273 		Melder_setOutputEncoding (outputEncoding);
274 	PREFS_END
275 }
276 
277 FORM (PREFS__GraphicsCjkFontStyleSettings, U"CJK font style preferences", nullptr) {
278 	OPTIONMENU_ENUM (kGraphics_cjkFontStyle, cjkFontStyle,
279 			U"CJK font style", kGraphics_cjkFontStyle::DEFAULT)
280 OK
281 	SET_ENUM (cjkFontStyle, kGraphics_cjkFontStyle, theGraphicsCjkFontStyle)
282 DO
283 	PREFS
284 		theGraphicsCjkFontStyle = cjkFontStyle;
285 	PREFS_END
286 }
287 
288 /********** Callbacks of the Goodies menu. **********/
289 
290 FORM (INFO_NONE__praat_calculator, U"Calculator", U"Calculator") {
291 	TEXTFIELD (expression, U"Type any numeric formula or string formula", U"5*5", 5)
292 	LABEL (U"Note that you can include many special functions in your formula,")
293 	LABEL (U"including statistical functions and acoustics-auditory conversions.")
294 	LABEL (U"For details, click Help.")
295 	OK
296 DO
297 	INFO_NONE
298 		Formula_Result result;
299 		if (! interpreter) {
300 			autoInterpreter tempInterpreter = Interpreter_create (nullptr, nullptr);
301 			Interpreter_anyExpression (tempInterpreter.get(), expression, & result);
302 		} else {
303 			Interpreter_anyExpression (interpreter, expression, & result);
304 		}
305 		switch (result. expressionType) {
306 			case kFormula_EXPRESSION_TYPE_NUMERIC:
307 				Melder_information (result. numericResult);
308 			break; case kFormula_EXPRESSION_TYPE_STRING:
309 				Melder_information (result. stringResult.get());
310 			break; case kFormula_EXPRESSION_TYPE_NUMERIC_VECTOR:
311 				Melder_information (constVECVU (result. numericVectorResult));
312 			break; case kFormula_EXPRESSION_TYPE_NUMERIC_MATRIX:
313 				Melder_information (constMATVU (result. numericMatrixResult));
314 			break; case kFormula_EXPRESSION_TYPE_STRING_ARRAY:
315 				Melder_information (result. stringArrayResult);
316 		}
317 	INFO_NONE_END
318 }
319 
320 FORM (INFO_reportDifferenceOfTwoProportions, U"Report difference of two proportions", U"Difference of two proportions") {
321 	INTEGER (a_int, U"left Row 1", U"71")
322 	INTEGER (b_int, U"right Row 1", U"39")
323 	INTEGER (c_int, U"left Row 2", U"93")
324 	INTEGER (d_int, U"right Row 2", U"27")
325 	OK
326 DO
327 	INFO_NONE
328 		double a = a_int, b = b_int, c = c_int, d = d_int;
329 		double n = a + b + c + d;
330 		if (a < 0 || b < 0 || c < 0 || d < 0)
331 			Melder_throw (U"The numbers should not be negative.");
332 		if (a + b <= 0 || c + d <= 0)
333 			Melder_throw (U"The row totals should be positive.");
334 		if (a + c <= 0 || b + d <= 0)
335 			Melder_throw (U"The column totals should be positive.");
336 		MelderInfo_open ();
337 		MelderInfo_writeLine (U"Observed row 1 =    ", Melder_iround (a), U"    ", Melder_iround (b));
338 		MelderInfo_writeLine (U"Observed row 2 =    ", Melder_iround (c), U"    ", Melder_iround (d));
339 		double aexp = (a + b) * (a + c) / n;
340 		double bexp = (a + b) * (b + d) / n;
341 		double cexp = (a + c) * (c + d) / n;
342 		double dexp = (b + d) * (c + d) / n;
343 		MelderInfo_writeLine (U"");
344 		MelderInfo_writeLine (U"Expected row 1 =    ", aexp, U"    ", bexp);
345 		MelderInfo_writeLine (U"Expected row 2 =    ", cexp, U"    ", dexp);
346 		/*
347 			Continuity correction:
348 			bring the observed numbers closer to the expected numbers by 0.5 (if possible).
349 		*/
350 		Melder_moveCloserToBy (& a, aexp, 0.5);
351 		Melder_moveCloserToBy (& b, bexp, 0.5);
352 		Melder_moveCloserToBy (& c, cexp, 0.5);
353 		Melder_moveCloserToBy (& d, dexp, 0.5);
354 		MelderInfo_writeLine (U"");
355 		MelderInfo_writeLine (U"Corrected observed row 1 =    ", a, U"    ", b);
356 		MelderInfo_writeLine (U"Corrected observed row 2 =    ", c, U"    ", d);
357 
358 		n = a + b + c + d;
359 		double crossDifference = a * d - b * c;
360 		double x2 = n * crossDifference * crossDifference / (a + b) / (c + d) / (a + c) / (b + d);
361 		MelderInfo_writeLine (U"");
362 		MelderInfo_writeLine (U"Chi-square =    ", x2);
363 		MelderInfo_writeLine (U"Two-tailed p =    ", NUMchiSquareQ (x2, 1));
364 		MelderInfo_close ();
365 	INFO_NONE_END
366 }
367 
368 /********** Callbacks of the Technical menu. **********/
369 
370 FORM (PREFS__debug, U"Set debugging options", nullptr) {
371 	LABEL (U"If you switch Tracing on, Praat will write lots of detailed ")
372 	LABEL (U"information about what goes on in Praat")
373 	structMelderDir dir;
374 	Melder_getPrefDir (& dir);
375 	structMelderFile file;
376 	#ifdef UNIX
377 		MelderDir_getFile (& dir, U"tracing", & file);
378 	#else
379 		MelderDir_getFile (& dir, U"Tracing.txt", & file);
380 	#endif
381 	LABEL (Melder_cat (U"to ", Melder_fileToPath (& file), U"."))
382 	BOOLEAN (tracing, U"Tracing", false)
383 	LABEL (U"Setting the following to anything other than zero")
384 	LABEL (U"will alter the behaviour of Praat")
385 	LABEL (U"in unpredictable ways.")
386 	INTEGER (debugOption, U"Debug option", U"0")
387 OK
388 	SET_BOOLEAN (tracing, Melder_isTracing)
389 	SET_INTEGER (debugOption, Melder_debug)
390 DO
391 	PREFS
392 		Melder_setTracing (tracing);
393 		Melder_debug = debugOption;
394 	PREFS_END
395 }
396 
DIRECT(INFO_NONE__listReadableTypesOfObjects)397 DIRECT (INFO_NONE__listReadableTypesOfObjects) {
398 	INFO_NONE
399 		Thing_listReadableClasses ();
400 	INFO_NONE_END
401 }
402 
403 FORM (INFO_praat_library_createC, U"PraatLib: Create C header or file", nullptr) {
404 	BOOLEAN (isInHeader, U"Is in header", true)
405 	BOOLEAN (includeCreateAPI, U"Include \"Create\" API", true)
406 	BOOLEAN (includeReadAPI, U"Include \"Read\" API", true)
407 	BOOLEAN (includeSaveAPI, U"Include \"Save\" API", true)
408 	BOOLEAN (includeQueryAPI, U"Include \"Query\" API", true)
409 	BOOLEAN (includeModifyAPI, U"Include \"Modify\" API", true)
410 	BOOLEAN (includeToAPI, U"Include \"To\" API", true)
411 	BOOLEAN (includeRecordAPI, U"Include \"Record\" API", true)
412 	BOOLEAN (includePlayAPI, U"Include \"Play\" API", true)
413 	BOOLEAN (includeDrawAPI, U"Include \"Draw\" API", true)
414 	BOOLEAN (includeHelpAPI, U"Include \"Help\" API", false)
415 	BOOLEAN (includeWindowAPI, U"Include \"Window\" API", false)
416 	BOOLEAN (includeDemoAPI, U"Include \"Demo\" API", false)
417 	OK
418 DO
419 	praat_library_createC (isInHeader, includeCreateAPI, includeReadAPI, includeSaveAPI,
420 		includeQueryAPI, includeModifyAPI, includeToAPI, includeRecordAPI, includePlayAPI,
421 		includeDrawAPI, includeHelpAPI, includeWindowAPI, includeDemoAPI);
422 	END_NO_NEW_DATA
423 }
424 
DIRECT(INFO_NONE__reportSystemProperties)425 DIRECT (INFO_NONE__reportSystemProperties) {
426 	INFO_NONE
427 		praat_reportSystemProperties ();
428 	INFO_NONE_END
429 }
430 
DIRECT(INFO_NONE__reportGraphicalProperties)431 DIRECT (INFO_NONE__reportGraphicalProperties) {
432 	INFO_NONE
433 		praat_reportGraphicalProperties ();
434 	INFO_NONE_END
435 }
436 
DIRECT(INFO_NONE__reportIntegerProperties)437 DIRECT (INFO_NONE__reportIntegerProperties) {
438 	INFO_NONE
439 		praat_reportIntegerProperties ();
440 	INFO_NONE_END
441 }
442 
DIRECT(INFO_NONE__reportMemoryUse)443 DIRECT (INFO_NONE__reportMemoryUse) {
444 	INFO_NONE
445 		praat_reportMemoryUse ();
446 	INFO_NONE_END
447 }
448 
DIRECT(INFO_NONE__reportTextProperties)449 DIRECT (INFO_NONE__reportTextProperties) {
450 	INFO_NONE
451 		praat_reportTextProperties ();
452 	INFO_NONE_END
453 }
454 
DIRECT(INFO_NONE__reportFontProperties)455 DIRECT (INFO_NONE__reportFontProperties) {
456 	INFO_NONE
457 		praat_reportFontProperties ();
458 	INFO_NONE_END
459 }
460 
461 /********** Callbacks of the Open menu. **********/
462 
463 /*
464 	Note: readFromFile should not call praat_updateSelection(),
465 	because praat_updateSelection() should be called after all files have been read,
466 	not just the current one.
467 */
readFromFile(MelderFile file)468 static void readFromFile (MelderFile file) {
469 	autoDaata object = Data_readFromFile (file);
470 	if (! object)
471 		return;   // this can happen with Picture_readFromPraatPictureFile (file recognized, but no data)
472 	if (Thing_isa (object.get(), classManPages) && ! Melder_batch) {
473 		ManPages manPages = (ManPages) object.get();
474 		ManPage firstPage = manPages -> pages.at [1];
475 		autoManual manual = Manual_create (firstPage -> title.get(), object.releaseToAmbiguousOwner(), true);
476 		if (manPages -> executable)
477 			Melder_warning (U"These manual pages contain links to executable scripts.\n"
478 				"Only navigate these pages if you trust their author!");
479 		manual.releaseToUser();
480 		return;
481 	}
482 	if (Thing_isa (object.get(), classScript) && ! Melder_batch) {
483 		autoScriptEditor editor = ScriptEditor_createFromScript_canBeNull (nullptr, (Script) object.get());
484 		if (! editor) {
485 			(void) 0;   // the script was already open, and the user has been notified of that
486 		} else {
487 			editor.releaseToUser();
488 		}
489 		return;
490 	}
491 	praat_newWithFile (object.move(), file, MelderFile_name (file));
492 }
493 
494 FORM_READ (READMANY_Data_readFromFile, U"Read Object(s) from file", 0, true) {
495 	readFromFile (file);
496 	if (interpreter)
497 		interpreter -> returnType = kInterpreter_ReturnType::OBJECT_;
498 	END_WITH_NEW_DATA   // this calls praat_updateSelection(), after reading a single file; see also cb_openDocument
499 }
500 
501 /********** Callbacks of the Save menu. **********/
502 
503 FORM_SAVE (SAVE_Data_writeToTextFile, U"Save Object(s) as one text file", nullptr, nullptr) {
504 	if (theCurrentPraatObjects -> totalSelection == 1) {
505 		LOOP {
506 			iam_LOOP (Daata);
507 			Data_writeToTextFile (me, file);
508 		}
509 	} else {
510 		autoCollection set = praat_getSelectedObjects ();
511 		Data_writeToTextFile (set.get(), file);
512 	}
513 	END_NO_NEW_DATA
514 }
515 
516 FORM_SAVE (SAVE_Data_writeToShortTextFile, U"Save Object(s) as one short text file", nullptr, nullptr) {
517 	if (theCurrentPraatObjects -> totalSelection == 1) {
518 		LOOP {
519 			iam_LOOP (Daata);
520 			Data_writeToShortTextFile (me, file);
521 		}
522 	} else {
523 		autoCollection set = praat_getSelectedObjects ();
524 		Data_writeToShortTextFile (set.get(), file);
525 	}
526 	END_NO_NEW_DATA
527 }
528 
529 FORM_SAVE (SAVE_Data_writeToBinaryFile, U"Save Object(s) as one binary file", nullptr, nullptr) {
530 	if (theCurrentPraatObjects -> totalSelection == 1) {
531 		LOOP {
532 			iam_LOOP (Daata);
533 			Data_writeToBinaryFile (me, file);
534 		}
535 	} else {
536 		autoCollection set = praat_getSelectedObjects ();
537 		Data_writeToBinaryFile (set.get(), file);
538 	}
539 	END_NO_NEW_DATA
540 }
541 
542 FORM (PRAAT_ManPages_saveToHtmlFolder, U"Save all pages as HTML files", nullptr) {
543 	FOLDER (folder, U"Folder", U"")
544 OK
545 	LOOP {
546 		iam_LOOP (ManPages);
547 		SET_STRING (folder, Melder_dirToPath (& my rootDirectory))
548 	}
549 DO
550 	LOOP {
551 		iam_LOOP (ManPages);
552 		ManPages_writeAllToHtmlDir (me, folder);
553 	}
554 	END_NO_NEW_DATA
555 }
556 
DIRECT(WINDOW_ManPages_view)557 DIRECT (WINDOW_ManPages_view) {
558 	LOOP {
559 		iam_LOOP (ManPages);
560 		ManPage firstPage = my pages.at [1];
561 		autoManual manual = Manual_create (firstPage -> title.get(), me, false);
562 		if (my executable)
563 			Melder_warning (U"These manual pages contain links to executable scripts.\n"
564 				"Only navigate these pages if you trust their author!");
565 		praat_installEditor (manual.get(), IOBJECT);
566 		manual.releaseToUser();
567 	}
568 	END_NO_NEW_DATA
569 }
570 
571 /********** Callbacks of the Help menu. **********/
572 
573 FORM (PRAAT__SearchManual, U"Search manual", U"Manual") {
574 	TEXTFIELD (query, U"Search for strings (separate with spaces)", U"", 3)
575 	OK
576 DO
577 	PRAAT
578 		if (theCurrentPraatApplication -> batch)
579 			Melder_throw (U"Cannot view a manual from batch.");
580 		autoManual manual = Manual_create (U"Intro", theCurrentPraatApplication -> manPages, false);
581 		Manual_search (manual.get(), query);
582 		manual.releaseToUser();
583 	PRAAT_END
584 }
585 
586 FORM (PRAAT__GoToManualPage, U"Go to manual page", nullptr) {
587 	static constSTRVEC pages;
588 	pages = ManPages_getTitles (theCurrentPraatApplication -> manPages);
589 	LIST (goToPageNumber, U"Page", pages, 1)
590 	OK
591 DO
592 	PRAAT
593 		if (theCurrentPraatApplication -> batch)
594 			Melder_throw (U"Cannot view a manual from batch.");
595 		autoManual manual = Manual_create (U"Intro", theCurrentPraatApplication -> manPages, false);
596 		HyperPage_goToPage_number (manual.get(), goToPageNumber);
597 		manual.releaseToUser();
598 	PRAAT_END
599 }
600 
601 FORM (HELP_SaveManualToHtmlFolder, U"Save all pages as HTML files", nullptr) {
602 	FOLDER (folder, U"Folder", U"")
603 OK
604 	structMelderDir currentDirectory { };
605 	Melder_getDefaultDir (& currentDirectory);
606 	SET_STRING (folder, Melder_dirToPath (& currentDirectory))
607 DO
608 	ManPages_writeAllToHtmlDir (theCurrentPraatApplication -> manPages, folder);
609 	END_NO_NEW_DATA
610 }
611 
612 /********** Menu descriptions. **********/
613 
praat_show()614 void praat_show () {
615 	/*
616 		(De)sensitivize the fixed buttons as appropriate for the current selection.
617 	*/
618 	praat_sensitivizeFixedButtonCommand (U"Remove", theCurrentPraatObjects -> totalSelection != 0);
619 	praat_sensitivizeFixedButtonCommand (U"Rename...", theCurrentPraatObjects -> totalSelection == 1);
620 	praat_sensitivizeFixedButtonCommand (U"Copy...", theCurrentPraatObjects -> totalSelection == 1);
621 	praat_sensitivizeFixedButtonCommand (U"Info", theCurrentPraatObjects -> totalSelection == 1);
622 	praat_sensitivizeFixedButtonCommand (U"Inspect", theCurrentPraatObjects -> totalSelection != 0);
623 	praat_actions_show ();
624 	if (theCurrentPraatApplication == & theForegroundPraatApplication && theReferenceToTheOnlyButtonEditor)
625 		Editor_dataChanged (theReferenceToTheOnlyButtonEditor);
626 }
627 
628 /********** Menu descriptions. **********/
629 
praat_addFixedButtons(GuiWindow window)630 void praat_addFixedButtons (GuiWindow window) {
631 	praat_addFixedButtonCommand (window, U"Rename...", MODIFY_Rename, 8, 70);
632 	praat_addFixedButtonCommand (window, U"Copy...", NEW1_Copy, 98, 70);
633 	praat_addFixedButtonCommand (window, U"Inspect",
634 			PRAAT__Inspect, 8, 40);
635 	praat_addFixedButtonCommand (window, U"Info", INFO_Info, 98, 40);
636 	praat_addFixedButtonCommand (window, U"Remove", PRAAT_Remove, 8, 10);
637 }
638 
searchProc()639 static void searchProc () {
640 	PRAAT__SearchManual (nullptr, 0, nullptr, nullptr, nullptr, nullptr, false, nullptr);
641 }
642 
643 static MelderString itemTitle_about;
644 
scriptRecognizer(integer nread,const char * header,MelderFile file)645 static autoDaata scriptRecognizer (integer nread, const char *header, MelderFile file) {
646 	conststring32 name = MelderFile_name (file);
647 	if (nread < 2)
648 		return autoDaata ();
649 	if ((header [0] == '#' && header [1] == '!')
650 		|| Melder_stringMatchesCriterion (name, kMelder_string::ENDS_WITH, U".praat", false)
651 	    || Melder_stringMatchesCriterion (name, kMelder_string::ENDS_WITH, U".html", false)
652 	) {
653 		return Script_createFromFile (file);
654 	}
655 	return autoDaata ();
656 }
657 
cb_openDocument(MelderFile file)658 static void cb_openDocument (MelderFile file) {
659 	try {
660 		readFromFile (file);   // read a single file without calling praat_updateSelection()
661 	} catch (MelderError) {
662 		Melder_flushError ();
663 	}
664 }
cb_finishedOpeningDocuments()665 static void cb_finishedOpeningDocuments () {
666 	try {
667 		praat_updateSelection ();   // this finally calls praat_updateSelection(), after each separate file has been read
668 	} catch (MelderError) {
669 		Melder_flushError ();
670 	}
671 }
672 
673 #if cocoa
DIRECT(PRAAT__cut)674 DIRECT (PRAAT__cut) {
675 	PRAAT
676 		[[[NSApp keyWindow] firstResponder] cut: nil];
677 	PRAAT_END
678 }
DIRECT(PRAAT__copy)679 DIRECT (PRAAT__copy) {
680 	PRAAT
681 		[[[NSApp keyWindow] firstResponder] copy: nil];
682 	PRAAT_END
683 }
DIRECT(PRAAT__paste)684 DIRECT (PRAAT__paste) {
685 	PRAAT
686 		[[[NSApp keyWindow] firstResponder] pasteAsPlainText: nil];
687 	PRAAT_END
688 }
DIRECT(PRAAT__minimize)689 DIRECT (PRAAT__minimize) {
690 	PRAAT
691 		[[NSApp keyWindow] performMiniaturize: nil];
692 	PRAAT_END
693 }
DIRECT(PRAAT__zoom)694 DIRECT (PRAAT__zoom) {
695 	PRAAT
696 		[[NSApp keyWindow] performZoom: nil];
697 	PRAAT_END
698 }
DIRECT(PRAAT__close)699 DIRECT (PRAAT__close) {
700 	PRAAT
701 		[[NSApp keyWindow] performClose: nil];
702 	PRAAT_END
703 }
704 #endif
705 
praat_addMenus(GuiWindow window)706 void praat_addMenus (GuiWindow window) {
707 	Melder_setSearchProc (searchProc);
708 
709 	Data_recognizeFileType (scriptRecognizer);
710 
711 	/*
712 		Create the menu titles in the bar.
713 	*/
714 	if (! theCurrentPraatApplication -> batch) {
715 		#ifdef macintosh
716 			praatMenu = GuiMenu_createInWindow (nullptr, U"\024", 0);
717 			#if cocoa
718 				editMenu = GuiMenu_createInWindow (nullptr, U"Edit", 0);
719 				windowMenu = GuiMenu_createInWindow (nullptr, U"Window", 0);
720 			#endif
721 		#else
722 			praatMenu = GuiMenu_createInWindow (window, U"Praat", 0);
723 		#endif
724 		newMenu = GuiMenu_createInWindow (window, U"New", 0);
725 		readMenu = GuiMenu_createInWindow (window, U"Open", 0);
726 		praat_actions_createWriteMenu (window);
727 		#ifdef macintosh
728 			applicationHelpMenu = GuiMenu_createInWindow (nullptr, U"Help", 0);
729 		#endif
730 		helpMenu = GuiMenu_createInWindow (window, U"Help", 0);
731 	}
732 
733 	MelderString_append (& itemTitle_about, U"About ", praatP.title.get());
734 	praat_addMenuCommand (U"Objects", U"Praat", itemTitle_about.string, nullptr, praat_UNHIDABLE,
735 			PRAAT__About);
736 	#ifdef macintosh
737 		#if cocoa
738 			/*
739 				HACK: give the following commands weird names,
740 				because otherwise they may be called from a script.
741 				(we add three alt-spaces)
742 			*/
743 			praat_addMenuCommand (U"Objects", U"Edit", U"Cut   ", nullptr, praat_UNHIDABLE | 'X' | praat_NO_API,
744 					PRAAT__cut);
745 			praat_addMenuCommand (U"Objects", U"Edit", U"Copy   ", nullptr, praat_UNHIDABLE | 'C' | praat_NO_API,
746 					PRAAT__copy);
747 			praat_addMenuCommand (U"Objects", U"Edit", U"Paste   ", nullptr, praat_UNHIDABLE | 'V' | praat_NO_API,
748 					PRAAT__paste);
749 			praat_addMenuCommand (U"Objects", U"Window", U"Minimize   ", nullptr, praat_UNHIDABLE | praat_NO_API,
750 					PRAAT__minimize);
751 			praat_addMenuCommand (U"Objects", U"Window", U"Zoom   ", nullptr, praat_UNHIDABLE | praat_NO_API,
752 					PRAAT__zoom);
753 			praat_addMenuCommand (U"Objects", U"Window", U"Close   ", nullptr, 'W' | praat_NO_API,
754 					PRAAT__close);
755 		#endif
756 	#endif
757 	praat_addMenuCommand (U"Objects", U"Praat", U"-- script --", nullptr, 0, nullptr);
758 	praat_addMenuCommand (U"Objects", U"Praat", U"New Praat script", nullptr, praat_NO_API,
759 			PRAAT__newScript);
760 	praat_addMenuCommand (U"Objects", U"Praat", U"Open Praat script...", nullptr, praat_NO_API,
761 			PRAAT__openScript);
762 	praat_addMenuCommand (U"Objects", U"Praat", U"-- buttons --", nullptr, 0, nullptr);
763 	praat_addMenuCommand (U"Objects", U"Praat", U"Add menu command...", nullptr, praat_HIDDEN | praat_NO_API,
764 			PRAAT__addMenuCommand);
765 	praat_addMenuCommand (U"Objects", U"Praat", U"Hide menu command...", nullptr, praat_HIDDEN | praat_NO_API,
766 			PRAAT__hideMenuCommand);
767 	praat_addMenuCommand (U"Objects", U"Praat", U"Show menu command...", nullptr, praat_HIDDEN | praat_NO_API,
768 			PRAAT__showMenuCommand);
769 	praat_addMenuCommand (U"Objects", U"Praat", U"Add action command...", nullptr, praat_HIDDEN | praat_NO_API,
770 			PRAAT__addAction);
771 	praat_addMenuCommand (U"Objects", U"Praat", U"Hide action command...", nullptr, praat_HIDDEN | praat_NO_API,
772 			PRAAT__hideAction);
773 	praat_addMenuCommand (U"Objects", U"Praat", U"Show action command...", nullptr, praat_HIDDEN | praat_NO_API,
774 			PRAAT__showAction);
775 
776 	GuiMenuItem menuItem = praat_addMenuCommand (U"Objects", U"Praat", U"Goodies", nullptr, praat_UNHIDABLE, nullptr);
777 	goodiesMenu = menuItem ? menuItem -> d_menu : nullptr;
778 	praat_addMenuCommand (U"Objects", U"Goodies", U"Calculator...", nullptr, 'U',
779 			INFO_NONE__praat_calculator);
780 	praat_addMenuCommand (U"Objects", U"Goodies", U"Report difference of two proportions...", nullptr, 0, INFO_reportDifferenceOfTwoProportions);
781 
782 	menuItem = praat_addMenuCommand (U"Objects", U"Praat", U"Preferences", nullptr, praat_UNHIDABLE, nullptr);
783 	preferencesMenu = menuItem ? menuItem -> d_menu : nullptr;
784 	praat_addMenuCommand (U"Objects", U"Preferences", U"Buttons...", nullptr, praat_UNHIDABLE,
785 			PRAAT__editButtons);
786 	praat_addMenuCommand (U"Objects", U"Preferences", U"-- encoding prefs --", nullptr, 0, nullptr);
787 	praat_addMenuCommand (U"Objects", U"Preferences", U"Text reading preferences...", nullptr, 0,
788 			PREFS__TextInputEncodingSettings);
789 	praat_addMenuCommand (U"Objects", U"Preferences", U"Text writing preferences...", nullptr, 0,
790 			PREFS__TextOutputEncodingSettings);
791 	praat_addMenuCommand (U"Objects", U"Preferences", U"CJK font style preferences...", nullptr, 0,
792 			PREFS__GraphicsCjkFontStyleSettings);
793 
794 	menuItem = praat_addMenuCommand (U"Objects", U"Praat", U"Technical", nullptr, praat_UNHIDABLE, nullptr);
795 	technicalMenu = menuItem ? menuItem -> d_menu : nullptr;
796 	praat_addMenuCommand (U"Objects", U"Technical", U"Report memory use", nullptr, 0,
797 			INFO_NONE__reportMemoryUse);
798 	praat_addMenuCommand (U"Objects", U"Technical", U"Report integer properties", nullptr, 0,
799 			INFO_NONE__reportIntegerProperties);
800 	praat_addMenuCommand (U"Objects", U"Technical", U"Report system properties", nullptr, 0,
801 			INFO_NONE__reportSystemProperties);
802 	praat_addMenuCommand (U"Objects", U"Technical", U"Report graphical properties", nullptr, 0,
803 			INFO_NONE__reportGraphicalProperties);
804 	praat_addMenuCommand (U"Objects", U"Technical", U"Report text properties", nullptr, 0,
805 			INFO_NONE__reportTextProperties);
806 	praat_addMenuCommand (U"Objects", U"Technical", U"Report font properties", nullptr, 0,
807 			INFO_NONE__reportFontProperties);
808 	praat_addMenuCommand (U"Objects", U"Technical", U"Debug...", nullptr, 0,
809 			PREFS__debug);
810 	praat_addMenuCommand (U"Objects", U"Technical", U"-- api --", nullptr, 0, nullptr);
811 	praat_addMenuCommand (U"Objects", U"Technical", U"List readable types of objects", nullptr, 0,
812 			INFO_NONE__listReadableTypesOfObjects);
813 	praat_addMenuCommand (U"Objects", U"Technical", U"Create C interface...", nullptr, 0, INFO_praat_library_createC);
814 
815 	praat_addMenuCommand (U"Objects", U"Open", U"Read from file...", nullptr, praat_ATTRACTIVE | 'O', READMANY_Data_readFromFile);
816 
817 	praat_addAction1 (classDaata, 0, U"Save as text file...", nullptr, 0, SAVE_Data_writeToTextFile);
818 	praat_addAction1 (classDaata, 0,   U"Write to text file...", nullptr, praat_DEPRECATED_2011, SAVE_Data_writeToTextFile);
819 	praat_addAction1 (classDaata, 0, U"Save as short text file...", nullptr, 0, SAVE_Data_writeToShortTextFile);
820 	praat_addAction1 (classDaata, 0,   U"Write to short text file...", nullptr, praat_DEPRECATED_2011, SAVE_Data_writeToShortTextFile);
821 	praat_addAction1 (classDaata, 0, U"Save as binary file...", nullptr, 0, SAVE_Data_writeToBinaryFile);
822 	praat_addAction1 (classDaata, 0,   U"Write to binary file...", nullptr, praat_DEPRECATED_2011, SAVE_Data_writeToBinaryFile);
823 
824 	praat_addAction1 (classManPages, 1, U"Save to HTML folder...", nullptr, 0, PRAAT_ManPages_saveToHtmlFolder);
825 	praat_addAction1 (classManPages, 1, U"Save to HTML directory...", nullptr, praat_DEPRECATED_2020, PRAAT_ManPages_saveToHtmlFolder);
826 	praat_addAction1 (classManPages, 1, U"View", nullptr, 0, WINDOW_ManPages_view);
827 }
828 
praat_addMenus2()829 void praat_addMenus2 () {
830 	praat_addMenuCommand (U"Objects", U"ApplicationHelp", U"-- manual --", nullptr, 0, nullptr);
831 	praat_addMenuCommand (U"Objects", U"ApplicationHelp", U"Go to manual page...", nullptr, 0,
832 			PRAAT__GoToManualPage);
833 	praat_addMenuCommand (U"Objects", U"ApplicationHelp", U"Save manual to HTML folder...", nullptr, praat_HIDDEN, HELP_SaveManualToHtmlFolder);
834 	praat_addMenuCommand (U"Objects", U"ApplicationHelp", Melder_cat (U"Search ", praatP.title.get(), U" manual..."), nullptr, 'M' | praat_NO_API,
835 			PRAAT__SearchManual);
836 	praat_addMenuCommand (U"Objects", U"ApplicationHelp", itemTitle_about.string, nullptr, praat_UNHIDABLE,
837 			PRAAT__About);
838 
839 	#if defined (macintosh) || defined (_WIN32)
840 		Gui_setOpenDocumentCallback (cb_openDocument, cb_finishedOpeningDocuments);
841 	#endif
842 }
843 
844 /* End of file praat_objectMenus.cpp */
845