1 /* praat.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 "melder.h"
20 #include <stdarg.h>
21 #if defined (UNIX) || defined (macintosh)
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <signal.h>
25 #endif
26 #include <locale.h>
27 #if defined (UNIX)
28 #include <unistd.h>
29 #endif
30 #if defined (_WIN32)
31 #include <windows.h>
32 #include <fcntl.h>
33 #include <io.h>
34 #endif
35
36 #include "praatP.h"
37 #include "praatM.h"
38 #include "praat_script.h"
39 #include "praat_version.h"
40 #include "site.h"
41 #include "machine.h"
42 #include "Printer.h"
43 #include "ScriptEditor.h"
44 #include "Strings_.h"
45 #include "../kar/UnicodeData.h"
46 #include "InfoEditor.h"
47
48 Thing_implement (Praat_Command, Thing, 0);
49
50 #define EDITOR theCurrentPraatObjects -> list [IOBJECT]. editors
51
52 #define WINDOW_WIDTH 520
53 #define WINDOW_HEIGHT 700
54
55 /*
56 Six globals, not `inline` (see praat.h 2021-04-15).
57 */
58 structPraatApplication theForegroundPraatApplication;
59 PraatApplication theCurrentPraatApplication = & theForegroundPraatApplication;
60 structPraatObjects theForegroundPraatObjects;
61 PraatObjects theCurrentPraatObjects = & theForegroundPraatObjects;
62 structPraatPicture theForegroundPraatPicture;
63 PraatPicture theCurrentPraatPicture = & theForegroundPraatPicture;
64
65 static char32 programName [64];
66 static structMelderDir homeDir { };
67
68 /*
69 * Melder_preferencesFolder: a folder containing preferences file, buttons file, message files, tracing file, plugins.
70 * Unix: /home/miep/.praat-dir (without slash)
71 * Windows XP/Vista/7/8/10: \\myserver\myshare\Miep\Praat
72 * or: C:\Users\Miep\Praat
73 * MacOS: /Users/Miep/Library/Preferences/Praat Prefs
74 */
75 // inline structMelderDir Melder_preferencesFolder { }; // already declared in Melder_files.h (checked 2021-03-07)
76
77 /*
78 * prefsFile: preferences file.
79 * Unix: /home/miep/.praat-dir/prefs5
80 * Windows XP/Vista/7/8/10: \\myserver\myshare\Miep\Praat\Preferences5.ini
81 * or: C:\Users\Miep\Praat\Preferences5.ini
82 * MacOS: /Users/Miep/Library/Preferences/Praat Prefs/Prefs5
83 */
84 static structMelderFile prefsFile { };
85
86 /*
87 * buttonsFile: buttons file.
88 * Unix: /home/miep/.praat-dir/buttons
89 * Windows XP/Vista/7/8/10: \\myserver\myshare\Miep\Praat\Buttons5.ini
90 * or: C:\Users\Miep\Praat\Buttons5.ini
91 * MacOS: /Users/Miep/Library/Preferences/Praat Prefs/Buttons5
92 */
93 static structMelderFile buttonsFile { };
94
95 #if defined (UNIX)
96 static structMelderFile pidFile { }; // like /home/miep/.praat-dir/pid
97 static structMelderFile messageFile { }; // like /home/miep/.praat-dir/message
98 #elif defined (_WIN32)
99 static structMelderFile messageFile { }; // like C:\Users\Miep\Praat\Message.txt
100 #endif
101
102 /*
103 * tracingFile: tracing file.
104 * Unix: /home/miep/.praat-dir/tracing
105 * Windows XP/Vista/7/8/10: \\myserver\myshare\Miep\Praat\Tracing.txt
106 * or: C:\Users\Miep\Praat\Tracing.txt
107 * MacOS: /Users/Miep/Library/Preferences/Praat Prefs/Tracing.txt
108 */
109 static structMelderFile tracingFile { };
110
111 static GuiList praatList_objects;
112
113 /***** selection *****/
114
praat_idOfSelected(ClassInfo klas,integer inplace)115 integer praat_idOfSelected (ClassInfo klas, integer inplace) {
116 integer place = inplace, IOBJECT;
117 if (place == 0)
118 place = 1;
119 if (place > 0) {
120 WHERE (SELECTED && (! klas || CLASS == klas)) {
121 if (place == 1)
122 return ID;
123 place --;
124 }
125 } else {
126 WHERE_DOWN (SELECTED && (! klas || CLASS == klas)) {
127 if (place == -1)
128 return ID;
129 place ++;
130 }
131 }
132 if (inplace)
133 Melder_throw (U"No ", klas ? klas -> className : U"object", U" #", inplace, U" selected.");
134 else
135 Melder_throw (U"No ", klas ? klas -> className : U"object", U" selected.");
136 return 0;
137 }
138
praat_idsOfAllSelected(ClassInfo klas)139 autoVEC praat_idsOfAllSelected (ClassInfo klas) {
140 autoVEC result = raw_VEC (praat_numberOfSelected (klas));
141 integer selectedObjectNumber = 0, IOBJECT;
142 WHERE (SELECTED && (! klas || CLASS == klas))
143 result [++ selectedObjectNumber] = ID;
144 return result;
145 }
146
praat_nameOfSelected(ClassInfo klas,integer inplace)147 char32 * praat_nameOfSelected (ClassInfo klas, integer inplace) {
148 integer place = inplace, IOBJECT;
149 if (place == 0)
150 place = 1;
151 if (place > 0) {
152 WHERE (SELECTED && (! klas || CLASS == klas)) {
153 if (place == 1)
154 return klas ? NAME : FULL_NAME;
155 place --;
156 }
157 } else {
158 WHERE_DOWN (SELECTED && (! klas || CLASS == klas)) {
159 if (place == -1)
160 return klas ? NAME : FULL_NAME;
161 place ++;
162 }
163 }
164 if (inplace)
165 Melder_throw (U"No ", klas ? klas -> className : U"object", U" #", inplace, U" selected.");
166 else
167 Melder_throw (U"No ", klas ? klas -> className : U"object", U" selected.");
168 return nullptr; // failure, but never reached anyway
169 }
170
praat_numberOfSelected(ClassInfo klas)171 integer praat_numberOfSelected (ClassInfo klas) {
172 if (! klas)
173 return theCurrentPraatObjects -> totalSelection;
174 integer readableClassId = klas -> sequentialUniqueIdOfReadableClass;
175 if (readableClassId == 0)
176 Melder_fatal (U"No sequential unique ID for class ", klas -> className, U".");
177 return theCurrentPraatObjects -> numberOfSelected [readableClassId];
178 }
179
praat_deselect(integer IOBJECT)180 void praat_deselect (integer IOBJECT) {
181 if (! SELECTED)
182 return;
183 SELECTED = false;
184 theCurrentPraatObjects -> totalSelection -= 1;
185 integer readableClassId = theCurrentPraatObjects -> list [IOBJECT]. object -> classInfo -> sequentialUniqueIdOfReadableClass;
186 Melder_assert (readableClassId != 0);
187 theCurrentPraatObjects -> numberOfSelected [readableClassId] -= 1;
188 if (! theCurrentPraatApplication -> batch && ! Melder_backgrounding) {
189 trace (U"deselecting object ", IOBJECT);
190 GuiList_deselectItem (praatList_objects, IOBJECT);
191 trace (U"deselected object ", IOBJECT);
192 }
193 }
194
praat_deselectAll()195 void praat_deselectAll () {
196 integer IOBJECT;
197 WHERE (1)
198 praat_deselect (IOBJECT);
199 }
200
praat_select(integer IOBJECT)201 void praat_select (integer IOBJECT) {
202 if (SELECTED)
203 return;
204 SELECTED = true;
205 theCurrentPraatObjects -> totalSelection += 1;
206 Thing object = theCurrentPraatObjects -> list [IOBJECT]. object;
207 Melder_assert (object);
208 integer readableClassId = object -> classInfo -> sequentialUniqueIdOfReadableClass;
209 if (readableClassId == 0)
210 Melder_fatal (U"No sequential unique ID for class ", object -> classInfo -> className, U".");
211 theCurrentPraatObjects -> numberOfSelected [readableClassId] += 1;
212 if (! theCurrentPraatApplication -> batch && ! Melder_backgrounding)
213 GuiList_selectItem (praatList_objects, IOBJECT);
214 }
215
praat_selectAll()216 void praat_selectAll () {
217 integer IOBJECT;
218 WHERE (true)
219 praat_select (IOBJECT);
220 }
221
praat_list_background()222 void praat_list_background () {
223 integer IOBJECT;
224 WHERE (SELECTED)
225 GuiList_deselectItem (praatList_objects, IOBJECT);
226 }
praat_list_foreground()227 void praat_list_foreground () {
228 integer IOBJECT;
229 WHERE (SELECTED)
230 GuiList_selectItem (praatList_objects, IOBJECT);
231 }
232
praat_getSelectedObjects()233 autoCollection praat_getSelectedObjects () {
234 autoCollection thee = Collection_create ();
235 integer IOBJECT;
236 LOOP {
237 iam_LOOP (Daata);
238 thy addItem_ref (me);
239 }
240 return thee;
241 }
242
praat_name(integer IOBJECT)243 char32 *praat_name (integer IOBJECT) { return str32chr (FULL_NAME, U' ') + 1; }
244
praat_write_do(UiForm dia,conststring32 extension)245 void praat_write_do (UiForm dia, conststring32 extension) {
246 static MelderString defaultFileName;
247 if (extension && str32chr (extension, U'.')) {
248 /*
249 Apparently, the "extension" is a complete file name.
250 This should be used as the default file name.
251 (This case typically occurs when saving a picture.)
252 */
253 MelderString_copy (& defaultFileName, extension);
254 } else {
255 /*
256 Apparently, the "extension" is not a complete file name.
257 We are expected to prepend the "extension" with the name of a selected object.
258 */
259 integer IOBJECT, found = 0;
260 Daata data = nullptr;
261 WHERE (SELECTED) {
262 if (! data)
263 data = (Daata) OBJECT;
264 found += 1;
265 }
266 if (found == 1) {
267 MelderString_copy (& defaultFileName, data -> name.get());
268 if (defaultFileName.length > 200) {
269 defaultFileName.string [200] = U'\0';
270 defaultFileName.length = 200;
271 }
272 MelderString_append (& defaultFileName, U".", extension ? extension : Thing_className (data));
273 } else if (! extension) {
274 MelderString_copy (& defaultFileName, U"praat.Collection");
275 } else {
276 MelderString_copy (& defaultFileName, U"praat.", extension);
277 }
278 }
279 UiOutfile_do (dia, defaultFileName.string);
280 }
281
removeAllReferencesToMoribundEditor(Editor editor)282 static void removeAllReferencesToMoribundEditor (Editor editor) {
283 /*
284 Remove all references to this editor.
285 It may be editing multiple objects.
286 */
287 for (integer iobject = 1; iobject <= theCurrentPraatObjects -> n; iobject ++)
288 for (integer ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
289 if (theCurrentPraatObjects -> list [iobject]. editors [ieditor] == editor)
290 theCurrentPraatObjects -> list [iobject]. editors [ieditor] = nullptr;
291 if (praatP. editor == editor)
292 praatP. editor = nullptr;
293 }
294
295 /**
296 Remove the "object" from the list,
297 killing everything that has to do with the selection.
298 */
praat_remove(integer iobject,bool removeVisibly)299 static void praat_remove (integer iobject, bool removeVisibly) {
300 Melder_assert (iobject >= 1 && iobject <= theCurrentPraatObjects -> n);
301 if (theCurrentPraatObjects -> list [iobject]. isBeingCreated) {
302 theCurrentPraatObjects -> list [iobject]. isBeingCreated = false;
303 theCurrentPraatObjects -> totalBeingCreated --;
304 }
305 trace (U"deselect object ", iobject);
306 if (removeVisibly)
307 praat_deselect (iobject);
308 trace (U"deselected object ", iobject);
309
310 /*
311 To prevent synchronization problems, kill editors before killing the data.
312 */
313 for (integer ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
314 Editor editor = theCurrentPraatObjects -> list [iobject]. editors [ieditor]; // save this one reference
315 if (editor) {
316 trace (U"remove references to editor ", ieditor);
317 removeAllReferencesToMoribundEditor (editor);
318 trace (U"forget editor ", ieditor);
319 if (removeVisibly)
320 forget (editor); // TODO: doesn't this call removeAllReferencesToMoribundEditor() again?
321 trace (U"forgotten editor ", ieditor);
322 }
323 }
324 MelderFile_setToNull (& theCurrentPraatObjects -> list [iobject]. file);
325 trace (U"free name");
326 theCurrentPraatObjects -> list [iobject]. name. reset();
327 trace (U"forget object");
328 forget (theCurrentPraatObjects -> list [iobject]. object); // note: this might save a file-based object to file
329 trace (U"forgotten object");
330 }
331
praat_cleanUpName(char32 * name)332 void praat_cleanUpName (char32 *name) {
333 /*
334 Replaces spaces and special characters by underscores.
335 */
336 for (; *name; name ++)
337 if (str32chr (U" ,.:;\\/()[]{}~`\'<>*&^%#@!?$\"|", *name))
338 *name = U'_';
339 }
340
341 /***** objects + commands *****/
342
praat_new_unpackCollection(autoCollection me,const char32 * myName)343 static void praat_new_unpackCollection (autoCollection me, const char32* myName) {
344 for (integer idata = 1; idata <= my size; idata ++) {
345 autoDaata object;
346 object. adoptFromAmbiguousOwner ((Daata) my at [idata]);
347 my at [idata] = nullptr; // disown; once the elements are autoThings, the move will handle this
348 const conststring32 name = ( object -> name ? object -> name.get() : myName );
349 Melder_assert (name);
350 praat_new (object.move(), name); // recurse
351 }
352 }
353
praat_newWithFile(autoDaata me,MelderFile file,conststring32 myName)354 void praat_newWithFile (autoDaata me, MelderFile file, conststring32 myName) {
355 if (! me)
356 Melder_throw (U"No object was put into the list.");
357
358 if (my classInfo == classCollection) {
359 praat_new_unpackCollection (me.static_cast_move<structCollection>(), myName);
360 return;
361 }
362
363 autoMelderString name, givenName;
364 if (myName && myName [0]) {
365 MelderString_copy (& givenName, myName);
366 /*
367 * Remove extension.
368 */
369 char32 *p = str32rchr (givenName.string, U'.');
370 if (p)
371 *p = U'\0';
372 } else {
373 MelderString_copy (& givenName, my name && my name [0] ? my name.get() : U"untitled");
374 }
375 praat_cleanUpName (givenName.string);
376 MelderString_append (& name, Thing_className (me.get()), U" ", givenName.string);
377
378 if (theCurrentPraatObjects -> n == praat_MAXNUM_OBJECTS) {
379 //forget (me);
380 Melder_throw (U"The Object Window cannot contain more than ", praat_MAXNUM_OBJECTS, U" objects. You could remove some objects.");
381 }
382
383 integer IOBJECT = ++ theCurrentPraatObjects -> n;
384 Melder_assert (FULL_NAME == nullptr);
385 theCurrentPraatObjects -> list [IOBJECT]. name = Melder_dup_f (name.string); // all right to crash if out of memory
386 ++ theCurrentPraatObjects -> uniqueId;
387
388 if (! theCurrentPraatApplication -> batch) // put a new object on the screen, at the bottom of the list
389 GuiList_insertItem (
390 praatList_objects,
391 Melder_cat (theCurrentPraatObjects -> uniqueId, U". ", name.string),
392 theCurrentPraatObjects -> n
393 );
394 CLASS = my classInfo;
395 OBJECT = me.releaseToAmbiguousOwner(); // FIXME: should be move()
396 SELECTED = false;
397 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
398 EDITOR [ieditor] = nullptr;
399 if (file)
400 MelderFile_copy (file, & theCurrentPraatObjects -> list [IOBJECT]. file);
401 else
402 MelderFile_setToNull (& theCurrentPraatObjects -> list [IOBJECT]. file);
403 ID = theCurrentPraatObjects -> uniqueId;
404 theCurrentPraatObjects -> list [IOBJECT]. isBeingCreated = true;
405 Thing_setName (OBJECT, givenName.string);
406 theCurrentPraatObjects -> totalBeingCreated ++;
407 }
408
409 static MelderString thePraatNewName;
praat_new(autoDaata me)410 void praat_new (autoDaata me) {
411 praat_newWithFile (me.move(), nullptr, U"");
412 }
praat_new(autoDaata me,const MelderArg & arg)413 void praat_new (autoDaata me, const MelderArg& arg) {
414 praat_newWithFile (me.move(), nullptr, arg._arg);
415 }
praat_new(autoDaata me,const MelderArg & arg1,const MelderArg & arg2,const MelderArg & arg3,const MelderArg & arg4,const MelderArg & arg5)416 void praat_new (autoDaata me,
417 const MelderArg& arg1, const MelderArg& arg2, const MelderArg& arg3,
418 const MelderArg& arg4, const MelderArg& arg5)
419 {
420 MelderString_copy (& thePraatNewName, arg1._arg, arg2._arg, arg3._arg, arg4._arg, arg5._arg);
421 praat_new (me.move(), thePraatNewName.string);
422 }
423
praat_updateSelection()424 void praat_updateSelection () {
425 if (theCurrentPraatObjects -> totalBeingCreated > 0) {
426 integer IOBJECT;
427 praat_deselectAll ();
428 WHERE (theCurrentPraatObjects -> list [IOBJECT]. isBeingCreated) {
429 praat_select (IOBJECT);
430 theCurrentPraatObjects -> list [IOBJECT]. isBeingCreated = false;
431 }
432 theCurrentPraatObjects -> totalBeingCreated = 0;
433 praat_show ();
434 }
435 }
436
gui_cb_list_selectionChanged(Thing,GuiList_SelectionChangedEvent event)437 static void gui_cb_list_selectionChanged (Thing /* boss */, GuiList_SelectionChangedEvent event) {
438 Melder_assert (event -> list == praatList_objects);
439 integer IOBJECT;
440 bool first = true;
441 WHERE (SELECTED) {
442 SELECTED = false;
443 integer readableClassId = theCurrentPraatObjects -> list [IOBJECT]. object -> classInfo -> sequentialUniqueIdOfReadableClass;
444 theCurrentPraatObjects -> numberOfSelected [readableClassId] --;
445 Melder_assert (theCurrentPraatObjects -> numberOfSelected [readableClassId] >= 0);
446 }
447 theCurrentPraatObjects -> totalSelection = 0;
448 autoINTVEC selected = GuiList_getSelectedPositions (praatList_objects);
449 for (integer iselected = 1; iselected <= selected.size; iselected ++) {
450 IOBJECT = selected [iselected];
451 SELECTED = true;
452 integer readableClassId = theCurrentPraatObjects -> list [IOBJECT]. object -> classInfo -> sequentialUniqueIdOfReadableClass;
453 theCurrentPraatObjects -> numberOfSelected [readableClassId] ++;
454 Melder_assert (theCurrentPraatObjects -> numberOfSelected [readableClassId] > 0);
455 UiHistory_write (first ? U"\nselectObject: \"" : U"\nplusObject: \"");
456 UiHistory_write_expandQuotes (FULL_NAME);
457 UiHistory_write (U"\"");
458 first = false;
459 theCurrentPraatObjects -> totalSelection += 1;
460 }
461 praat_show ();
462 }
463
praat_list_renameAndSelect(integer position,conststring32 name)464 void praat_list_renameAndSelect (integer position, conststring32 name) {
465 if (! theCurrentPraatApplication -> batch) {
466 GuiList_replaceItem (praatList_objects, name, position); // void if name equal
467 if (! Melder_backgrounding)
468 GuiList_selectItem (praatList_objects, position);
469 }
470 }
471
472 /***** objects *****/
473
praat_name2(char32 * name,ClassInfo klas1,ClassInfo klas2)474 void praat_name2 (char32 *name, ClassInfo klas1, ClassInfo klas2) {
475 int i1 = 1;
476 while (theCurrentPraatObjects -> list [i1]. isSelected == 0 || theCurrentPraatObjects -> list [i1]. klas != klas1)
477 i1 ++;
478 int i2 = 1;
479 while (theCurrentPraatObjects -> list [i2]. isSelected == 0 || theCurrentPraatObjects -> list [i2]. klas != klas2)
480 i2 ++;
481 char32 *name1 = str32chr (theCurrentPraatObjects -> list [i1]. name.get(), U' ') + 1;
482 char32 *name2 = str32chr (theCurrentPraatObjects -> list [i2]. name.get(), U' ') + 1;
483 if (str32equ (name1, name2))
484 Melder_sprint (name,200, name1);
485 else
486 Melder_sprint (name,200, name1, U"_", name2);
487 }
488
praat_removeObject(integer i)489 void praat_removeObject (integer i) {
490 praat_remove (i, true); // dangle
491 for (integer j = i; j < theCurrentPraatObjects -> n; j ++)
492 theCurrentPraatObjects -> list [j] = std::move (theCurrentPraatObjects -> list [j + 1]); // undangle but create second references
493 theCurrentPraatObjects -> list [theCurrentPraatObjects -> n]. name. reset ();
494 theCurrentPraatObjects -> list [theCurrentPraatObjects -> n]. object = nullptr; // undangle or remove second reference
495 theCurrentPraatObjects -> list [theCurrentPraatObjects -> n]. isSelected = 0;
496 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
497 theCurrentPraatObjects -> list [theCurrentPraatObjects -> n]. editors [ieditor] = nullptr; // undangle or remove second reference
498 MelderFile_setToNull (& theCurrentPraatObjects -> list [theCurrentPraatObjects -> n]. file); // undangle or remove second reference
499 -- theCurrentPraatObjects -> n;
500 if (! theCurrentPraatApplication -> batch)
501 GuiList_deleteItem (praatList_objects, i);
502 }
503
praat_exit(int exit_code)504 static void praat_exit (int exit_code) {
505 //Melder_setTracing (true);
506 integer IOBJECT;
507 trace (U"destroy the picture window");
508 praat_picture_exit ();
509 praat_statistics_exit (); // record total memory use across sessions
510
511 if (! praatP.ignorePreferenceFiles) {
512 trace (U"stop receiving messages");
513 #if defined (UNIX)
514 /*
515 * We are going to delete the process id ("pid") file, if it's ours.
516 */
517 if (pidFile. path [0]) {
518 try {
519 /*
520 * To see whether we own the pid file,
521 * we look into it to see whether its pid equals our pid.
522 * If not, then we are probably living in an old invocation of the program,
523 * and the pid file was written by the latest invocation of the program,
524 * which owns the pid (this means sendpraat can only send to the latest Praat if more than one are open).
525 */
526 autofile f = Melder_fopen (& pidFile, "r");
527 long_not_integer pid;
528 if (fscanf (f, "%ld", & pid) < 1)
529 throw MelderError ();
530 f.close (& pidFile);
531 if (pid == getpid ()) // is the pid in the pid file equal to our pid?
532 MelderFile_delete (& pidFile); // ...then we own the pid file and can delete it
533 } catch (MelderError) {
534 Melder_clearError (); // if the pid file is somehow missing or corrupted, we just ignore that
535 }
536 }
537 #endif
538
539 trace (U"save the preferences");
540 Melder_assert (str32equ (Melder_double (1.5), U"1.5")); // refuse to write the preferences if the locale is wrong (even if tracing is on)
541 Preferences_write (& prefsFile);
542
543 trace (U"save the script buttons");
544 if (! theCurrentPraatApplication -> batch) {
545 try {
546 autoMelderString buffer;
547 MelderString_append (& buffer, U"# Buttons (1).\n");
548 MelderString_append (& buffer, U"# This file is generated automatically when you quit the ", praatP.title.get(), U" program.\n");
549 MelderString_append (& buffer, U"# It contains the buttons that you added interactively to the fixed or dynamic menus,\n");
550 MelderString_append (& buffer, U"# and the buttons that you hid or showed.\n\n");
551 praat_saveAddedMenuCommands (& buffer);
552 praat_saveToggledMenuCommands (& buffer);
553 praat_saveAddedActions (& buffer);
554 praat_saveToggledActions (& buffer);
555 MelderFile_writeText (& buttonsFile, buffer.string, kMelder_textOutputEncoding::ASCII_THEN_UTF16);
556 } catch (MelderError) {
557 Melder_clearError ();
558 }
559 }
560 }
561
562 trace (U"flush the file-based objects");
563 WHERE_DOWN (! MelderFile_isNull (& theCurrentPraatObjects -> list [IOBJECT]. file)) {
564 trace (U"removing object based on file ", & theCurrentPraatObjects -> list [IOBJECT]. file);
565 praat_remove (IOBJECT, false);
566 }
567 Melder_files_cleanUp (); // in case a URL is open
568
569 trace (U"leave the program");
570 praat_menuCommands_exit_optimizeByLeaking (); // these calls are superflous if subsequently _Exit() is called instead of exit()
571 praat_actions_exit_optimizeByLeaking ();
572 Preferences_exit_optimizeByLeaking ();
573 /*
574 OPTIMIZE with an exercise in self-documenting code
575 */
576 constexpr bool weWouldLikeToOptimizeExitingSpeed = ((true));
577 constexpr bool callingExitTimeDestructorsIsSlow = (true);
578 constexpr bool notCallingExitTimeDestructorsCausesCorrectBehaviour = (true);
579 constexpr bool weAreReallySureAboutThat = (true);
580 constexpr bool weWillUseUnderscoreExitInsteadOfExit =
581 weWouldLikeToOptimizeExitingSpeed &&
582 callingExitTimeDestructorsIsSlow &&
583 notCallingExitTimeDestructorsCausesCorrectBehaviour &&
584 weAreReallySureAboutThat;
585 if ((weWillUseUnderscoreExitInsteadOfExit)) {
586 constexpr bool underscoreExitHasMoreSideEffectsThanJustNotCallingExitTimeDestructors = (true);
587 constexpr bool avoidOtherSideEffectsOfUnderscoreExit =
588 underscoreExitHasMoreSideEffectsThanJustNotCallingExitTimeDestructors;
589 if ((avoidOtherSideEffectsOfUnderscoreExit)) {
590 constexpr bool oneSideEffectIsThatOpenOutputFilesAreNotFlushed = true;
591 constexpr bool weShouldFlushAllOpenOutputFilesWhoseNonflushingWouldCauseIncorrectBehaviour =
592 oneSideEffectIsThatOpenOutputFilesAreNotFlushed;
593 if ((weShouldFlushAllOpenOutputFilesWhoseNonflushingWouldCauseIncorrectBehaviour)) {
594 constexpr bool stdoutIsOpen = (true);
595 constexpr bool stderrIsOpen = (true);
596 constexpr bool stdoutIsBufferedByDefault = true;
597 constexpr bool stderrIsBufferedByDefault = false;
598 constexpr bool weKnowThatSetbufHasNotBeenCalledOnStdout = (false);
599 constexpr bool weKnowThatSetbufHasNotBeenCalledOnStderr = (false);
600 constexpr bool stdoutHasCertainlyBeenFlushed =
601 ! stdoutIsBufferedByDefault && weKnowThatSetbufHasNotBeenCalledOnStdout;
602 constexpr bool stderrHasCertainlyBeenFlushed =
603 ! stderrIsBufferedByDefault && weKnowThatSetbufHasNotBeenCalledOnStderr;
604 constexpr bool notFlushingStdoutCouldCauseIncorrectBehaviour =
605 stdoutIsOpen && ! stdoutHasCertainlyBeenFlushed;
606 constexpr bool notFlushingStderrCouldCauseIncorrectBehaviour =
607 stderrIsOpen && ! stderrHasCertainlyBeenFlushed;
608 constexpr bool shouldFlushStdout = notFlushingStdoutCouldCauseIncorrectBehaviour;
609 constexpr bool shouldFlushStderr = notFlushingStderrCouldCauseIncorrectBehaviour;
610 if ((shouldFlushStdout))
611 fflush (stdout);
612 if ((shouldFlushStderr))
613 fflush (stderr);
614 constexpr bool thereAreOtherOpenFiles = (false);
615 constexpr bool thereAreOtherOpenFilesWhoseNonflushingCouldCauseIncorrectBehaviour =
616 thereAreOtherOpenFiles;
617 if ((! thereAreOtherOpenFilesWhoseNonflushingCouldCauseIncorrectBehaviour)) {}
618 }
619 constexpr bool thereAreNoOtherSideEffectsBesideNotCallingExitDestructorsAndNotFlushingOpenFiles = (true);
620 if ((thereAreNoOtherSideEffectsBesideNotCallingExitDestructorsAndNotFlushingOpenFiles)) {}
621 }
622 _Exit (exit_code);
623 } else {
624 exit (exit_code);
625 }
626 }
627
cb_Editor_destruction(Editor me)628 static void cb_Editor_destruction (Editor me) {
629 removeAllReferencesToMoribundEditor (me);
630 }
631
cb_Editor_dataChanged(Editor me)632 static void cb_Editor_dataChanged (Editor me) {
633 for (integer iobject = 1; iobject <= theCurrentPraatObjects -> n; iobject ++) {
634 /*
635 Am I editing this object?
636 */
637 bool editingThisObject = false;
638 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
639 editingThisObject |= ( theCurrentPraatObjects -> list [iobject]. editors [ieditor] == me );
640 if (editingThisObject) {
641 /*
642 Notify all other editors associated with this object.
643 */
644 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
645 Editor otherEditor = theCurrentPraatObjects -> list [iobject]. editors [ieditor];
646 if (otherEditor && otherEditor != me)
647 Editor_dataChanged (otherEditor);
648 }
649 }
650 }
651 }
652
cb_Editor_publication(Editor,autoDaata publication)653 static void cb_Editor_publication (Editor /* me */, autoDaata publication) {
654 /*
655 The default publish callback.
656 Works nicely if the publisher invents a name.
657 */
658 try {
659 praat_new (publication.move(), U"");
660 } catch (MelderError) {
661 Melder_flushError ();
662 }
663 praat_updateSelection ();
664 }
665
praat_installEditor(Editor editor,integer IOBJECT)666 void praat_installEditor (Editor editor, integer IOBJECT) {
667 if (! editor)
668 return;
669 for (integer ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
670 if (! EDITOR [ieditor]) {
671 EDITOR [ieditor] = editor;
672 Editor_setDestructionCallback (editor, cb_Editor_destruction);
673 Editor_setDataChangedCallback (editor, cb_Editor_dataChanged);
674 if (! editor -> d_publicationCallback)
675 Editor_setPublicationCallback (editor, cb_Editor_publication);
676 //Thing_setName (editor, Melder_cat (editor -> name.get(), U" [", ieditor + 1, U"]")); // would break existing scripts
677 return;
678 }
679 }
680 Melder_throw (U"(praat_installEditor:) Cannot have more than ", praat_MAXNUM_EDITORS, U" editors with one object.");
681 }
682
praat_installEditor2(Editor editor,integer i1,integer i2)683 void praat_installEditor2 (Editor editor, integer i1, integer i2) {
684 if (! editor)
685 return;
686 integer ieditor1 = 0;
687 for (; ieditor1 < praat_MAXNUM_EDITORS; ieditor1 ++)
688 if (! theCurrentPraatObjects -> list [i1]. editors [ieditor1])
689 break;
690 integer ieditor2 = 0;
691 for (; ieditor2 < praat_MAXNUM_EDITORS; ieditor2 ++)
692 if (! theCurrentPraatObjects -> list [i2]. editors [ieditor2])
693 break;
694 if (ieditor1 < praat_MAXNUM_EDITORS && ieditor2 < praat_MAXNUM_EDITORS) {
695 theCurrentPraatObjects -> list [i1]. editors [ieditor1] = theCurrentPraatObjects -> list [i2]. editors [ieditor2] = editor;
696 Editor_setDestructionCallback (editor, cb_Editor_destruction);
697 Editor_setDataChangedCallback (editor, cb_Editor_dataChanged);
698 if (! editor -> d_publicationCallback)
699 Editor_setPublicationCallback (editor, cb_Editor_publication);
700 } else {
701 Melder_throw (U"(praat_installEditor2:) Cannot have more than ", praat_MAXNUM_EDITORS, U" editors with one object.");
702 }
703 }
704
praat_installEditor3(Editor editor,integer i1,integer i2,integer i3)705 void praat_installEditor3 (Editor editor, integer i1, integer i2, integer i3) {
706 if (! editor)
707 return;
708 integer ieditor1 = 0;
709 for (; ieditor1 < praat_MAXNUM_EDITORS; ieditor1 ++)
710 if (! theCurrentPraatObjects -> list [i1]. editors [ieditor1])
711 break;
712 integer ieditor2 = 0;
713 for (; ieditor2 < praat_MAXNUM_EDITORS; ieditor2 ++)
714 if (! theCurrentPraatObjects -> list [i2]. editors [ieditor2])
715 break;
716 integer ieditor3 = 0;
717 for (; ieditor3 < praat_MAXNUM_EDITORS; ieditor3 ++)
718 if (! theCurrentPraatObjects -> list [i3]. editors [ieditor3])
719 break;
720 if (ieditor1 < praat_MAXNUM_EDITORS && ieditor2 < praat_MAXNUM_EDITORS && ieditor3 < praat_MAXNUM_EDITORS) {
721 theCurrentPraatObjects -> list [i1]. editors [ieditor1] = theCurrentPraatObjects -> list [i2]. editors [ieditor2] = theCurrentPraatObjects -> list [i3]. editors [ieditor3] = editor;
722 Editor_setDestructionCallback (editor, cb_Editor_destruction);
723 Editor_setDataChangedCallback (editor, cb_Editor_dataChanged);
724 if (! editor -> d_publicationCallback)
725 Editor_setPublicationCallback (editor, cb_Editor_publication);
726 } else {
727 Melder_throw (U"(praat_installEditor3:) Cannot have more than ", praat_MAXNUM_EDITORS, U" editors with one object.");
728 }
729 }
730
praat_installEditorN(Editor editor,DaataList objects)731 void praat_installEditorN (Editor editor, DaataList objects) {
732 if (! editor)
733 return;
734 /*
735 First check whether all objects in the Ordered are also in the List of Objects (Praat crashes if not),
736 and check whether there is room to add an editor for each.
737 */
738 for (integer iOrderedObject = 1; iOrderedObject <= objects->size; iOrderedObject ++) {
739 Daata object = objects->at [iOrderedObject];
740 integer iPraatObject = 1;
741 for (; iPraatObject <= theCurrentPraatObjects -> n; iPraatObject ++) {
742 if (object == theCurrentPraatObjects -> list [iPraatObject]. object) {
743 int ieditor = 0;
744 for (; ieditor < praat_MAXNUM_EDITORS; ieditor ++)
745 if (! theCurrentPraatObjects -> list [iPraatObject]. editors [ieditor])
746 break;
747 if (ieditor >= praat_MAXNUM_EDITORS)
748 Melder_throw (U"Cannot view the same object in more than ", praat_MAXNUM_EDITORS, U" windows.");
749 break;
750 }
751 }
752 Melder_assert (iPraatObject <= theCurrentPraatObjects -> n); // an element of the Ordered does not occur in the List of Objects
753 }
754 /*
755 There appears to be room for all elements of the Ordered. The editor window can appear. Install the editor in all objects.
756 */
757 for (integer iOrderedObject = 1; iOrderedObject <= objects->size; iOrderedObject ++) {
758 Daata object = objects->at [iOrderedObject];
759 integer iPraatObject = 1;
760 for (; iPraatObject <= theCurrentPraatObjects -> n; iPraatObject ++) {
761 if (object == theCurrentPraatObjects -> list [iPraatObject]. object) {
762 int ieditor = 0;
763 for (; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
764 if (! theCurrentPraatObjects -> list [iPraatObject]. editors [ieditor]) {
765 theCurrentPraatObjects -> list [iPraatObject]. editors [ieditor] = editor;
766 Editor_setDestructionCallback (editor, cb_Editor_destruction);
767 Editor_setDataChangedCallback (editor, cb_Editor_dataChanged);
768 if (! editor -> d_publicationCallback)
769 Editor_setPublicationCallback (editor, cb_Editor_publication);
770 break;
771 }
772 }
773 Melder_assert (ieditor < praat_MAXNUM_EDITORS); // we just checked, but nevertheless
774 break;
775 }
776 }
777 Melder_assert (iPraatObject <= theCurrentPraatObjects -> n); // we already checked, but still
778 }
779 }
780
praat_dataChanged(Daata object)781 void praat_dataChanged (Daata object) {
782 /*
783 This function can be called at error time, which is weird.
784 */
785 autostring32 saveError;
786 const bool duringError = Melder_hasError ();
787 if (duringError) {
788 if (Melder_hasCrash ())
789 return; // straight to the exit, without attempting to notify any editors of any data, which might well be corrupted
790 saveError = Melder_dup_f (Melder_getError ());
791 Melder_clearError ();
792 }
793 integer IOBJECT;
794 WHERE (OBJECT == object) {
795 for (int ieditor = 0; ieditor < praat_MAXNUM_EDITORS; ieditor ++) {
796 Editor editor = EDITOR [ieditor];
797 if (editor) {
798 Editor_dataChanged (editor);
799 if (duringError)
800 Melder_clearError (); // accept only the original error, and not the extra ones generated in the editors
801 }
802 }
803 }
804 if (duringError) {
805 Melder_appendError_noLine (saveError.get()); // restore the original error message
806 /*
807 If we are during error time, then this error should be caught
808 either by `throw` (see praatM.h, two occurrences) or by Melder_flushError (also see praatM.h, one occurrence).
809 LAST CHECKED 2021-12-02
810 */
811 }
812 }
813
helpProc(conststring32 query)814 static void helpProc (conststring32 query) {
815 if (theCurrentPraatApplication -> batch) {
816 Melder_flushError (U"Cannot view manual from batch.");
817 return;
818 }
819 try {
820 autoManual manual = Manual_create (query, theCurrentPraatApplication -> manPages, false);
821 manual.releaseToUser();
822 } catch (MelderError) {
823 Melder_flushError (U"help: no help on \"", query, U"\".");
824 }
825 }
826
publishProc(autoDaata me)827 static int publishProc (autoDaata me) {
828 try {
829 praat_new (me.move(), U"");
830 praat_updateSelection ();
831 return 1;
832 } catch (MelderError) {
833 Melder_throw (U"Not published.");
834 }
835 }
836
837 /***** QUIT *****/
838
839 FORM (DO_Quit, U"Confirm Quit", U"Quit") {
840 MUTABLE_LABEL (label, U"You have objects in your list!")
841 OK
842 {
843 char32 prompt [300];
844 if (ScriptEditors_dirty ()) {
845 if (theCurrentPraatObjects -> n)
846 Melder_sprint (prompt,300, U"You have objects and unsaved scripts! Do you still want to quit ", praatP.title.get(), U"?");
847 else
848 Melder_sprint (prompt,300, U"You have unsaved scripts! Do you still want to quit ", praatP.title.get(), U"?");
849 SET_STRING (label, prompt)
850 } else if (theCurrentPraatObjects -> n) {
851 Melder_sprint (prompt,300, U"You have objects in your list! Do you still want to quit ", praatP.title.get(), U"?");
852 SET_STRING (label, prompt)
853 } else {
854 praat_exit (0);
855 }
856 }
857 DO
858 praat_exit (0);
859 END_NO_NEW_DATA
860 }
861
gui_cb_quit(Thing)862 static void gui_cb_quit (Thing /* me */) {
863 DO_Quit (nullptr, 0, nullptr, nullptr, nullptr, nullptr, false, nullptr);
864 }
865
praat_dontUsePictureWindow()866 void praat_dontUsePictureWindow () { praatP.dontUsePictureWindow = true; }
867
868 /********** INITIALIZATION OF THE PRAAT SHELL **********/
869
870 #if defined (UNIX) && ! defined (NO_GUI)
871 /*
872 sendpraat messages can only enter via SIGUSR1 at interrupt time.
873 We generate an event that should enter the main event loop at loop time.
874 */
cb_sigusr1(int signum)875 static void cb_sigusr1 (int signum) {
876 Melder_assert (signum == SIGUSR1);
877 signal (SIGUSR1, cb_sigusr1); // keep this handler in the air
878 GdkEventProperty gevent; // or GdkEventSetting, once we can find its signal name
879 gevent. type = GDK_PROPERTY_NOTIFY; // or GDK_SETTING
880 gevent. window = gtk_widget_get_window (GTK_WIDGET (theCurrentPraatApplication -> topShell -> d_gtkWindow));
881 gevent. send_event = 1;
882 gevent. atom = gdk_atom_intern_static_string ("SENDPRAAT");
883 gevent. time = 0;
884 gevent. state = GDK_PROPERTY_NEW_VALUE;
885 // Melder_casual (U"event put");
886 gdk_event_put ((GdkEvent *) & gevent); // this is safe only if gdk_event_put is reentrant, which is unlikely because the event queue is global
887 }
888 #endif
889
890 #if defined (UNIX)
891 #if ! defined (NO_GUI)
cb_userMessage(GtkWidget,GdkEventProperty *,gpointer)892 static gboolean cb_userMessage (GtkWidget /* widget */, GdkEventProperty * /* event */, gpointer /* userData */) {
893 trace (U"client event called");
894 autofile f;
895 try {
896 f.reset (Melder_fopen (& messageFile, "r"));
897 } catch (MelderError) {
898 Melder_clearError ();
899 return true; // OK
900 }
901 long_not_integer pid = 0;
902 int narg = fscanf (f, "#%ld", & pid);
903 f.close (& messageFile);
904 {// scope
905 autoPraatBackground background;
906 try {
907 praat_executeScriptFromFile (& messageFile, nullptr);
908 } catch (MelderError) {
909 Melder_flushError (praatP.title.get(), U": message not completely handled.");
910 }
911 }
912 if (narg != 0 && pid != 0)
913 kill (pid, SIGUSR2);
914 return true;
915 }
916 #endif
917 #elif defined (_WIN32)
cb_userMessage()918 static int cb_userMessage () {
919 autoPraatBackground background;
920 try {
921 praat_executeScriptFromFile (& messageFile, nullptr);
922 } catch (MelderError) {
923 Melder_flushError (praatP.title.get(), U": message not completely handled.");
924 }
925 return 0;
926 }
927 extern "C" char *sendpraat (void *display, const char *programName, long timeOut, const char *text);
cb_openDocument(MelderFile file)928 static void cb_openDocument (MelderFile file) {
929 char32 text [kMelder_MAXPATH+25];
930 /*
931 The user dropped a file on the Praat icon,
932 or double-clicked a Praat file,
933 while Praat is already running.
934 */
935 Melder_sprint (text,500, U"Read from file: ~", file -> path);
936 sendpraat (nullptr, Melder_peek32to8 (praatP.title.get()), 0, Melder_peek32to8 (text));
937 }
cb_finishedOpeningDocuments()938 static void cb_finishedOpeningDocuments () {
939 praat_updateSelection ();
940 }
941 #elif macintosh
942 static int (*theUserMessageCallback) (char32 *message);
mac_setUserMessageCallback(int (* userMessageCallback)(char32 * message))943 static void mac_setUserMessageCallback (int (*userMessageCallback) (char32 *message)) {
944 theUserMessageCallback = userMessageCallback;
945 }
mac_processSignal8(const AppleEvent * theAppleEvent,AppleEvent *,long)946 static pascal OSErr mac_processSignal8 (const AppleEvent *theAppleEvent, AppleEvent * /* reply */, long /* handlerRefCon */) {
947 static bool duringAppleEvent = false; // FIXME: may have to be atomic?
948 if (! duringAppleEvent) {
949 char *buffer;
950 Size actualSize;
951 duringAppleEvent = true;
952 //AEInteractWithUser (kNoTimeOut, nullptr, nullptr); // use time out of 0 to execute immediately (without bringing to foreground)
953 ProcessSerialNumber psn;
954 GetCurrentProcess (& psn);
955 SetFrontProcess (& psn);
956 AEGetParamPtr (theAppleEvent, 1, typeUTF8Text, nullptr, nullptr, 0, & actualSize);
957 buffer = (char *) malloc ((size_t) actualSize);
958 AEGetParamPtr (theAppleEvent, 1, typeUTF8Text, nullptr, & buffer [0], actualSize, nullptr);
959 if (theUserMessageCallback) {
960 autostring32 buffer32 = Melder_8to32 (buffer);
961 theUserMessageCallback (buffer32.get());
962 }
963 free (buffer);
964 duringAppleEvent = false;
965 }
966 return noErr;
967 }
cb_userMessage(char32 * message)968 static int cb_userMessage (char32 *message) {
969 autoPraatBackground background;
970 try {
971 praat_executeScriptFromText (message);
972 } catch (MelderError) {
973 Melder_flushError (praatP.title.get(), U": message not completely handled.");
974 }
975 return 0;
976 }
cb_quitApplication()977 static int cb_quitApplication () {
978 DO_Quit (nullptr, 0, nullptr, nullptr, nullptr, nullptr, false, nullptr);
979 return 0;
980 }
981 #endif
982
983 static conststring32 thePraatStandAloneScriptText = nullptr;
984
praat_setStandAloneScriptText(conststring32 text)985 void praat_setStandAloneScriptText (conststring32 text) {
986 thePraatStandAloneScriptText = text;
987 }
988
tryToAttachToTheCommandLine()989 static bool tryToAttachToTheCommandLine ()
990 {
991 bool weHaveSucceeded = false;
992 #if defined (_WIN32)
993 /*
994 * On Windows, console applications are automatically attached to the command line,
995 * but Praat is always a Windows application instead, so command line attachment
996 * has to be handled explicitly, as here.
997 */
998 if (AttachConsole (ATTACH_PARENT_PROCESS)) { // was Praat called from either a console window or a "system" command?
999 weHaveSucceeded = true;
1000 }
1001 #else
1002 weHaveSucceeded = isatty (fileno (stdin)) || isatty (fileno (stdout)) || isatty (fileno (stderr));
1003 /*
1004 The result is `true` if Praat was called from a terminal window or some system() commands or Xcode,
1005 and `false` if Praat was called from the Finder by double-clicking or dropping a file.
1006
1007 FIXME:
1008 The result is incorrectly `false` if the output is redirected to a file or pipe.
1009 A proposed improvement is therefore:
1010 isatty (fileno (stdin)) || isatty (fileno (stdout)) || isatty (fileno (stderr))
1011 This might be incorrectly false only if all three streams are redirected, but this hasn't been tested yet.
1012 */
1013 #endif
1014 return weHaveSucceeded;
1015 }
1016
setThePraatLocale()1017 static void setThePraatLocale () {
1018 #if defined (UNIX)
1019 setlocale (LC_ALL, "C");
1020 //setenv ("PULSE_LATENCY_MSEC", "1", 0); // Rafael Laboissiere, August 2014
1021 #elif defined (_WIN32)
1022 setlocale (LC_ALL, "C"); // said to be superfluous
1023 #elif defined (macintosh)
1024 setlocale (LC_ALL, "en_US"); // required to make swprintf work correctly; the default "C" locale does not do that!
1025 #endif
1026 }
1027
installPraatShellPreferences()1028 static void installPraatShellPreferences () {
1029 praat_statistics_prefs (); // number of sessions, memory used...
1030 praat_picture_prefs (); // font...
1031 Graphics_prefs ();
1032 Ui_prefs ();
1033 structEditor :: f_preferences (); // erase picture first...
1034 structHyperPage :: f_preferences (); // font...
1035 Site_prefs (); // print command...
1036 Melder_audio_prefs (); // asynchronicity, silence after...
1037 Melder_textEncoding_prefs ();
1038 Printer_prefs (); // paper size, printer command...
1039 structTextEditor :: f_preferences (); // font size...
1040 }
1041
praatlib_init()1042 extern "C" void praatlib_init () {
1043 setThePraatLocale (); // FIXME: don't use the global locale
1044 Melder_init ();
1045 Melder_rememberShellDirectory ();
1046 installPraatShellPreferences (); // needed in the library, because this sets the defaults
1047 praatP.argc = 0;
1048 praatP.argv = nullptr;
1049 praatP.argumentNumber = 1;
1050 Melder_batch = true;
1051 praatP.userWantsToOpen = false;
1052 praatP.title = Melder_dup (U"Praatlib");
1053 theCurrentPraatApplication -> batch = true;
1054 Melder_getHomeDir (& homeDir);
1055 praat_actions_init ();
1056 praat_menuCommands_init ();
1057 Thing_recognizeClassesByName (classCollection, classStrings, classManPages, classStringSet, nullptr);
1058 Thing_recognizeClassByOtherName (classStringSet, U"SortedSetOfString");
1059 Melder_backgrounding = true;
1060 praat_addMenus (nullptr);
1061 praat_addFixedButtons (nullptr);
1062 praat_addMenus2 ();
1063 }
1064
injectMessageAndInformationProcs(GuiWindow parent)1065 static void injectMessageAndInformationProcs (GuiWindow parent) {
1066 Gui_injectMessageProcs (parent);
1067 InfoEditor_injectInformationProc ();
1068 }
1069
praat_init(conststring32 title,int argc,char ** argv)1070 void praat_init (conststring32 title, int argc, char **argv)
1071 {
1072 bool weWereStartedFromTheCommandLine = tryToAttachToTheCommandLine ();
1073
1074 for (int iarg = 0; iarg < argc; iarg ++) {
1075 //Melder_casual (U"arg ", iarg, U": <<", Melder_peek8to32 (argv [iarg]), U">>");
1076 }
1077 setThePraatLocale ();
1078 Melder_init ();
1079
1080 /*
1081 Remember the current directory. Useful only for scripts run from batch.
1082 */
1083 Melder_rememberShellDirectory ();
1084
1085 installPraatShellPreferences ();
1086
1087 praatP.argc = argc;
1088 praatP.argv = argv;
1089 praatP.argumentNumber = 1;
1090 autostring32 unknownCommandLineOption;
1091
1092 /*
1093 * Running Praat from the command line.
1094 */
1095 bool foundTheOpenOption = false, foundTheRunOption = false, foundTheTraceOption = false;
1096 while (praatP.argumentNumber < argc && argv [praatP.argumentNumber] [0] == '-') {
1097 if (strequ (argv [praatP.argumentNumber], "-")) {
1098 praatP.hasCommandLineInput = true;
1099 praatP.argumentNumber += 1;
1100 } else if (strequ (argv [praatP.argumentNumber], "--open")) {
1101 foundTheOpenOption = true;
1102 praatP.argumentNumber += 1;
1103 } else if (strequ (argv [praatP.argumentNumber], "--run")) {
1104 foundTheRunOption = true;
1105 praatP.argumentNumber += 1;
1106 } else if (strequ (argv [praatP.argumentNumber], "--no-pref-files")) {
1107 praatP.ignorePreferenceFiles = true;
1108 praatP.argumentNumber += 1;
1109 } else if (strequ (argv [praatP.argumentNumber], "--no-plugins")) {
1110 praatP.ignorePlugins = true;
1111 praatP.argumentNumber += 1;
1112 } else if (strnequ (argv [praatP.argumentNumber], "--pref-dir=", 11)) {
1113 Melder_pathToDir (Melder_peek8to32 (argv [praatP.argumentNumber] + 11), & Melder_preferencesFolder);
1114 praatP.argumentNumber += 1;
1115 } else if (strequ (argv [praatP.argumentNumber], "--version")) {
1116 #define xstr(s) str(s)
1117 #define str(s) #s
1118 Melder_information (title, U" " xstr (PRAAT_VERSION_STR) " (" xstr (PRAAT_MONTH) " ", PRAAT_DAY, U" ", PRAAT_YEAR, U")");
1119 exit (0);
1120 } else if (strequ (argv [praatP.argumentNumber], "--trace")) {
1121 foundTheTraceOption = true;
1122 praatP.argumentNumber += 1;
1123 } else if (strequ (argv [praatP.argumentNumber], "--help")) {
1124 MelderInfo_open ();
1125 MelderInfo_writeLine (U"Usage: praat [options] script-file-name [script-arguments]");
1126 MelderInfo_writeLine (U"Options:");
1127 MelderInfo_writeLine (U" --open regard the command line as files to be opened in the GUI");
1128 MelderInfo_writeLine (U" --run regard the command line as a script to run, with its arguments");
1129 MelderInfo_writeLine (U" (--run is superfluous when you use a Console or Terminal)");
1130 MelderInfo_writeLine (U" --no-pref-files don't read or write the preferences file and the buttons file");
1131 MelderInfo_writeLine (U" --no-plugins don't activate the plugins");
1132 MelderInfo_writeLine (U" --pref-dir=DIR set the preferences directory to DIR");
1133 MelderInfo_writeLine (U" --version print the Praat version");
1134 MelderInfo_writeLine (U" --help print this list of command line options");
1135 MelderInfo_writeLine (U" -u, --utf16 use UTF-16LE output encoding, no BOM (the default on Windows)");
1136 MelderInfo_writeLine (U" -8, --utf8 use UTF-8 output encoding (the default on MacOS and Linux)");
1137 MelderInfo_writeLine (U" -a, --ansi use ISO Latin-1 output encoding (lossy, hence not recommended)");
1138 MelderInfo_writeLine (U" (on Windows, use -8 or -a when you redirect to a pipe or file)");
1139 MelderInfo_writeLine (U" --trace switch tracing on at start-up (see Praat > Technical > Debug)");
1140 MelderInfo_close ();
1141 exit (0);
1142 } else if (strequ (argv [praatP.argumentNumber], "-8") || strequ (argv [praatP.argumentNumber], "--utf8")) {
1143 MelderConsole::setEncoding (MelderConsole::Encoding::UTF8);
1144 praatP.argumentNumber += 1;
1145 } else if (strequ (argv [praatP.argumentNumber], "-u") || strequ (argv [praatP.argumentNumber], "--utf16")) {
1146 MelderConsole::setEncoding (MelderConsole::Encoding::UTF16);
1147 praatP.argumentNumber += 1;
1148 } else if (strequ (argv [praatP.argumentNumber], "-a") || strequ (argv [praatP.argumentNumber], "--ansi")) {
1149 MelderConsole::setEncoding (MelderConsole::Encoding::ANSI);
1150 praatP.argumentNumber += 1;
1151 #if defined (macintosh)
1152 } else if (strequ (argv [praatP.argumentNumber], "-NSDocumentRevisionsDebugMode")) {
1153 (void) 0; // ignore this option, which was added by Xcode
1154 praatP.argumentNumber += 2; // jump over the argument, which is usually "YES" (this jump works correctly even if this argument is missing)
1155 } else if (strnequ (argv [praatP.argumentNumber], "-psn_", 5)) {
1156 (void) 0; // ignore this option, which was added by the Finder, perhaps when dragging a file on Praat (Process Serial Number)
1157 praatP.argumentNumber += 1;
1158 #endif
1159 } else if (strequ (argv [praatP.argumentNumber], "-sgi") ||
1160 strequ (argv [praatP.argumentNumber], "-motif") ||
1161 strequ (argv [praatP.argumentNumber], "-solaris") ||
1162 strequ (argv [praatP.argumentNumber], "-hp") ||
1163 strequ (argv [praatP.argumentNumber], "-sum4") ||
1164 strequ (argv [praatP.argumentNumber], "-mac") ||
1165 strequ (argv [praatP.argumentNumber], "-win32") ||
1166 strequ (argv [praatP.argumentNumber], "-linux") ||
1167 strequ (argv [praatP.argumentNumber], "-cocoa") ||
1168 strequ (argv [praatP.argumentNumber], "-chrome")
1169 ) {
1170 praatP.argumentNumber += 1;
1171 } else {
1172 unknownCommandLineOption = Melder_8to32 (argv [praatP.argumentNumber]);
1173 praatP.argumentNumber = INT32_MAX; // ignore all other command line options
1174 break;
1175 }
1176 }
1177 weWereStartedFromTheCommandLine |= foundTheRunOption; // some external system()-like commands don't make isatty return true, so we have to help
1178
1179 const bool thereIsAFileNameInTheArgumentList = ( praatP.argumentNumber < argc );
1180 Melder_batch = weWereStartedFromTheCommandLine && thereIsAFileNameInTheArgumentList && ! foundTheOpenOption;
1181 const bool fileNamesCameInByDropping = ( thereIsAFileNameInTheArgumentList && ! weWereStartedFromTheCommandLine ); // doesn't happen on the Mac
1182 praatP.userWantsToOpen = foundTheOpenOption || fileNamesCameInByDropping;
1183
1184 if (Melder_batch) {
1185 Melder_assert (praatP.argumentNumber < argc);
1186 /*
1187 * We now get the script file name. It is next on the command line
1188 * (not necessarily *last* on the line, because there may be script arguments after it).
1189 */
1190 MelderString_copy (& theCurrentPraatApplication -> batchName, Melder_peek8to32 (argv [praatP.argumentNumber ++]));
1191 if (praatP.hasCommandLineInput)
1192 Melder_throw (U"Cannot have both command line input and a script file.");
1193 } else {
1194 MelderString_copy (& theCurrentPraatApplication -> batchName, U"");
1195 }
1196 //Melder_casual (U"Script file name <<", theCurrentPraatApplication -> batchName.string, U">>");
1197
1198 Melder_batch |= !! thePraatStandAloneScriptText;
1199
1200 /*
1201 * Running the Praat shell from the command line:
1202 * praat -
1203 */
1204 Melder_batch |= praatP.hasCommandLineInput;
1205
1206 praatP.title = Melder_dup (title && title [0] != U'\0' ? title : U"Praat");
1207
1208 theCurrentPraatApplication -> batch = Melder_batch;
1209
1210 /*
1211 * Construct a program name like "Praat" for file and directory names.
1212 */
1213 str32cpy (programName, praatP.title.get());
1214
1215 /*
1216 * Construct a main-window title like "Praat 6.1".
1217 */
1218 programName [0] = Melder_toLowerCase (programName [0]);
1219
1220 /*
1221 * Get home directory, e.g. "/home/miep/", or "/Users/miep/", or just "/".
1222 */
1223 Melder_getHomeDir (& homeDir);
1224
1225 /*
1226 * Get the program's private directory (if not yet set by the --pref-dir option):
1227 * "/home/miep/.praat-dir" (Unix)
1228 * "/Users/miep/Library/Preferences/Praat Prefs" (MacOS)
1229 * "C:\Users\Miep\Praat" (Windows)
1230 * and construct a preferences-file name and a script-buttons-file name like
1231 * /home/miep/.praat-dir/prefs5
1232 * /home/miep/.praat-dir/buttons5
1233 * or
1234 * /Users/miep/Library/Preferences/Praat Prefs/Prefs5
1235 * /Users/miep/Library/Preferences/Praat Prefs/Buttons5
1236 * or
1237 * C:\Users\Miep\Praat\Preferences5.ini
1238 * C:\Users\Miep\Praat\Buttons5.ini
1239 * Also create names for message and tracing files.
1240 */
1241 if (MelderDir_isNull (& Melder_preferencesFolder)) { // not yet set by the --pref-dir option?
1242 structMelderDir prefParentDir { }; // directory under which to store our preferences directory
1243 Melder_getPrefDir (& prefParentDir);
1244
1245 /*
1246 * Make sure that the program's private directory exists.
1247 */
1248 char32 name [256];
1249 #if defined (UNIX)
1250 Melder_sprint (name,256, U".", programName, U"-dir"); // for example .praat-dir
1251 #elif defined (macintosh)
1252 Melder_sprint (name,256, praatP.title.get(), U" Prefs"); // for example Praat Prefs
1253 #elif defined (_WIN32)
1254 Melder_sprint (name,256, praatP.title.get()); // for example Praat
1255 #endif
1256 try {
1257 #if defined (UNIX) || defined (macintosh)
1258 Melder_createDirectory (& prefParentDir, name, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
1259 #else
1260 Melder_createDirectory (& prefParentDir, name, 0);
1261 #endif
1262 MelderDir_getSubdir (& prefParentDir, name, & Melder_preferencesFolder);
1263 } catch (MelderError) {
1264 /*
1265 * If we arrive here, the directory could not be created,
1266 * and all the files are null. Praat should nevertheless start up.
1267 */
1268 Melder_clearError ();
1269 }
1270 }
1271 if (! MelderDir_isNull (& Melder_preferencesFolder)) {
1272 #if defined (UNIX)
1273 MelderDir_getFile (& Melder_preferencesFolder, U"prefs5", & prefsFile);
1274 MelderDir_getFile (& Melder_preferencesFolder, U"buttons5", & buttonsFile);
1275 MelderDir_getFile (& Melder_preferencesFolder, U"pid", & pidFile);
1276 MelderDir_getFile (& Melder_preferencesFolder, U"message", & messageFile);
1277 MelderDir_getFile (& Melder_preferencesFolder, U"tracing", & tracingFile);
1278 #elif defined (_WIN32)
1279 MelderDir_getFile (& Melder_preferencesFolder, U"Preferences5.ini", & prefsFile);
1280 MelderDir_getFile (& Melder_preferencesFolder, U"Buttons5.ini", & buttonsFile);
1281 MelderDir_getFile (& Melder_preferencesFolder, U"Message.txt", & messageFile);
1282 MelderDir_getFile (& Melder_preferencesFolder, U"Tracing.txt", & tracingFile);
1283 #elif defined (macintosh)
1284 MelderDir_getFile (& Melder_preferencesFolder, U"Prefs5", & prefsFile);
1285 MelderDir_getFile (& Melder_preferencesFolder, U"Buttons5", & buttonsFile);
1286 MelderDir_getFile (& Melder_preferencesFolder, U"Tracing.txt", & tracingFile);
1287 #endif
1288 Melder_tracingToFile (& tracingFile);
1289 if (foundTheTraceOption)
1290 Melder_setTracing (true);
1291 }
1292
1293 #if defined (NO_GUI)
1294 if (! Melder_batch) {
1295 fprintf (stderr, "A no-GUI edition of Praat cannot be used interactively. "
1296 "Supply \"--run\" and a script file name on the command line.\n");
1297 exit (1);
1298 }
1299 #endif
1300 #if defined (UNIX)
1301 if (! Melder_batch) {
1302 /*
1303 * Make sure that the directory /home/miep/.praat-dir exists,
1304 * and write our process id into the pid file.
1305 * Messages from "sendpraat" are caught very early this way,
1306 * though they will be responded to much later.
1307 */
1308 try {
1309 autofile f = Melder_fopen (& pidFile, "w");
1310 fprintf (f, "%s", Melder8_integer (getpid ()));
1311 f.close (& pidFile);
1312 } catch (MelderError) {
1313 Melder_clearError ();
1314 }
1315 }
1316 #elif defined (_WIN32)
1317 if (! Melder_batch)
1318 motif_win_setUserMessageCallback (cb_userMessage);
1319 #elif defined (macintosh)
1320 if (! Melder_batch) {
1321 mac_setUserMessageCallback (cb_userMessage);
1322 Gui_setQuitApplicationCallback (cb_quitApplication); // BUG: the Quit Application message (from the system) is never actually handled
1323 }
1324 #endif
1325
1326 /*
1327 * Make room for commands.
1328 */
1329 trace (U"initing actions");
1330 praat_actions_init ();
1331 trace (U"initing menu commands");
1332 praat_menuCommands_init ();
1333
1334 GuiWindow raam = nullptr;
1335 if (Melder_batch) {
1336 MelderString_empty (& theCurrentPraatApplication -> batchName);
1337 for (int i = praatP.argumentNumber - 1; i < argc; i ++) {
1338 if (i >= praatP.argumentNumber)
1339 MelderString_append (& theCurrentPraatApplication -> batchName, U" ");
1340 bool needsQuoting = !! strchr (argv [i], ' ') && (i == praatP.argumentNumber - 1 || i < argc - 1);
1341 if (needsQuoting)
1342 MelderString_append (& theCurrentPraatApplication -> batchName, U"\"");
1343 MelderString_append (& theCurrentPraatApplication -> batchName, Melder_peek8to32 (argv [i]));
1344 if (needsQuoting)
1345 MelderString_append (& theCurrentPraatApplication -> batchName, U"\"");
1346 }
1347 } else {
1348 trace (U"starting the application");
1349 Machine_initLookAndFeel (argc, argv);
1350 /*
1351 * Start the application.
1352 */
1353 #if gtk
1354 trace (U"locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1355 g_set_application_name (Melder_peek32to8 (title));
1356 trace (U"locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1357 #elif motif
1358 argv [0] = Melder_32to8 (praatP. title.get()).transfer(); // argc == 4
1359 Gui_setOpenDocumentCallback (cb_openDocument, cb_finishedOpeningDocuments);
1360 GuiAppInitialize ("Praatwulg", argc, argv);
1361 #elif cocoa
1362 //[NSApplication sharedApplication];
1363 NSApplication *theApp = [GuiCocoaApplication sharedApplication];
1364 /*
1365 We want to get rid of the Search field in the help menu.
1366 By default, such a Search field will come up automatically when we create a menu with the title "Help".
1367 By changing this title to "SomeFakeTitleOfAMenuThatWillNeverBeInstantiated",
1368 we trick macOS into thinking that our help menu is called "SomeFakeTitleOfAMenuThatWillNeverBeInstantiated".
1369 As a result, the Search field will come up only when we create a menu
1370 titled "SomeFakeTitleOfAMenuThatWillNeverBeInstantiated", which is never.
1371 */
1372 theApp.helpMenu = [[NSMenu alloc] initWithTitle:@"SomeFakeTitleOfAMenuThatWillNeverBeInstantiated"];
1373 #endif
1374
1375 trace (U"creating and installing the Objects window");
1376 char32 objectWindowTitle [100];
1377 Melder_sprint (objectWindowTitle,100, praatP.title.get(), U" Objects");
1378 double x, y;
1379 trace (U"locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1380 Gui_getWindowPositioningBounds (& x, & y, nullptr, nullptr);
1381 trace (U"locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1382 theCurrentPraatApplication -> topShell = raam =
1383 GuiWindow_create (x + 10, y, WINDOW_WIDTH, WINDOW_HEIGHT, 450, 250,
1384 objectWindowTitle, gui_cb_quit, nullptr, 0);
1385 trace (U"locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1386 #if motif
1387 GuiApp_setApplicationShell (theCurrentPraatApplication -> topShell -> d_xmShell);
1388 #endif
1389 trace (U"before objects window shows locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1390 GuiThing_show (raam);
1391 trace (U"after objects window shows locale ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1392 }
1393 Thing_recognizeClassesByName (classCollection, classStrings, classManPages, classStringSet, nullptr);
1394 Thing_recognizeClassByOtherName (classStringSet, U"SortedSetOfString");
1395 if (Melder_batch) {
1396 Melder_backgrounding = true;
1397 trace (U"adding menus without GUI");
1398 praat_addMenus (nullptr);
1399 trace (U"adding fixed buttons without GUI");
1400 praat_addFixedButtons (nullptr);
1401 } else {
1402
1403 #ifdef macintosh
1404 AEInstallEventHandler (758934755, 0, (AEEventHandlerProcPtr) (mac_processSignal8), 0, false); // for receiving sendpraat
1405 injectMessageAndInformationProcs (raam); // BUG: default Melder_assert would call printf recursively!!!
1406 #endif
1407 trace (U"creating the menu bar in the Objects window");
1408 GuiWindow_addMenuBar (raam);
1409 praatP.menuBar = raam;
1410 praat_addMenus (praatP.menuBar);
1411
1412 trace (U"creating the object list in the Objects window");
1413 GuiLabel_createShown (raam, 3, -250, Machine_getMenuBarBottom () + 5, Machine_getMenuBarBottom () + 5 + Gui_LABEL_HEIGHT, U"Objects:", 0);
1414 praatList_objects = GuiList_create (raam, 0, -250, Machine_getMenuBarBottom () + 26, -100, true, U" Objects ");
1415 GuiList_setSelectionChangedCallback (praatList_objects, gui_cb_list_selectionChanged, nullptr);
1416 GuiThing_show (praatList_objects);
1417 praat_addFixedButtons (raam);
1418
1419 trace (U"creating the dynamic menu in the Objects window");
1420 praat_actions_createDynamicMenu (raam);
1421 trace (U"showing the Objects window");
1422 GuiThing_show (raam);
1423 #ifdef UNIX
1424 if (! praatP.ignorePreferenceFiles)
1425 Preferences_read (& prefsFile);
1426 #endif
1427 #if ! defined (macintosh)
1428 trace (U"initializing the Gui late");
1429 injectMessageAndInformationProcs (theCurrentPraatApplication -> topShell); // Mac: done this earlier
1430 #endif
1431 Melder_setHelpProc (helpProc);
1432 }
1433 Data_setPublishProc (publishProc);
1434 theCurrentPraatApplication -> manPages = ManPages_create ().releaseToAmbiguousOwner();
1435
1436 trace (U"creating the Picture window");
1437 trace (U"before picture window shows: locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1438 if (! praatP.dontUsePictureWindow)
1439 praat_picture_init ();
1440 trace (U"after picture window shows: locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1441
1442 if (unknownCommandLineOption)
1443 Melder_fatal (U"Unrecognized command line option ", unknownCommandLineOption.get());
1444 }
1445
executeStartUpFile(MelderDir startUpDirectory,conststring32 fileNameHead,conststring32 fileNameTail)1446 static void executeStartUpFile (MelderDir startUpDirectory, conststring32 fileNameHead, conststring32 fileNameTail) {
1447 char32 name [256];
1448 Melder_sprint (name,256, fileNameHead, programName, fileNameTail);
1449 if (! MelderDir_isNull (startUpDirectory)) { // should not occur on modern systems
1450 structMelderFile startUp { };
1451 MelderDir_getFile (startUpDirectory, name, & startUp);
1452 if (! MelderFile_readable (& startUp))
1453 return; // it's OK if the file doesn't exist
1454 try {
1455 praat_executeScriptFromFile (& startUp, nullptr);
1456 } catch (MelderError) {
1457 Melder_flushError (praatP.title.get(), U": start-up file ", & startUp, U" not completed.");
1458 }
1459 }
1460 }
1461
1462 #if gtk
1463 #include <gdk/gdkkeysyms.h>
theKeySnooper(GtkWidget * widget,GdkEventKey * event,gpointer data)1464 static gint theKeySnooper (GtkWidget *widget, GdkEventKey *event, gpointer data) {
1465 trace (U"keyval ", event -> keyval, U", type ", event -> type);
1466 if ((event -> keyval == GDK_KEY_Tab || event -> keyval == GDK_KEY_ISO_Left_Tab) && event -> type == GDK_KEY_PRESS) {
1467 using TabCallback = void (*) (GuiObject, gpointer);
1468 trace (U"tab key pressed in window ", Melder_pointer (widget));
1469 constexpr bool theTabKeyShouldWorkEvenIfNumLockIsOn = true;
1470 constexpr uint32 theProbableNumLockModifierMask = GDK_MOD2_MASK;
1471 constexpr uint32 modifiersToIgnore = ( theTabKeyShouldWorkEvenIfNumLockIsOn ? theProbableNumLockModifierMask : 0 );
1472 constexpr uint32 modifiersNotToIgnore = GDK_MODIFIER_MASK & ~ modifiersToIgnore;
1473 if ((event -> state & modifiersNotToIgnore) == 0) {
1474 if (GTK_IS_WINDOW (widget)) {
1475 GtkWidget *shell = gtk_widget_get_toplevel (GTK_WIDGET (widget));
1476 trace (U"tab pressed in GTK window ", Melder_pointer (shell));
1477 TabCallback tabCallback = (TabCallback) g_object_get_data (G_OBJECT (widget), "tabCallback");
1478 if (tabCallback) {
1479 trace (U"a tab callback exists");
1480 void *tabClosure = g_object_get_data (G_OBJECT (widget), "tabClosure");
1481 tabCallback (widget, tabClosure);
1482 return true;
1483 }
1484 }
1485 } else if ((event -> state & modifiersNotToIgnore) == GDK_SHIFT_MASK) {
1486 if (GTK_IS_WINDOW (widget)) {
1487 GtkWidget *shell = gtk_widget_get_toplevel (GTK_WIDGET (widget));
1488 trace (U"shift-tab pressed in GTK window ", Melder_pointer (shell));
1489 TabCallback tabCallback = (TabCallback) g_object_get_data (G_OBJECT (widget), "shiftTabCallback");
1490 if (tabCallback) {
1491 trace (U"a shift tab callback exists");
1492 void *tabClosure = g_object_get_data (G_OBJECT (widget), "shiftTabClosure");
1493 tabCallback (widget, tabClosure);
1494 return true;
1495 }
1496 }
1497 }
1498 }
1499 trace (U"end");
1500 return false; // pass event on
1501 }
1502 #endif
1503
praat_run()1504 void praat_run () {
1505 trace (U"adding menus, second round");
1506 praat_addMenus2 ();
1507 trace (U"locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1508
1509 trace (U"adding the Quit command");
1510 praat_addMenuCommand (U"Objects", U"Praat", U"-- quit --", nullptr, 0, nullptr);
1511 praat_addMenuCommand (U"Objects", U"Praat", U"Quit", nullptr, praat_UNHIDABLE | 'Q' | praat_NO_API, DO_Quit);
1512
1513 trace (U"read the preferences file, and notify those who want to be notified of this");
1514 /* ...namely, those who already have a window (namely, the Picture window),
1515 * and those that regard the start of a new session as a meaningful event
1516 * (namely, the session counter and the cross-session memory counter).
1517 */
1518 if (! praatP.ignorePreferenceFiles) {
1519 Preferences_read (& prefsFile);
1520 if (! praatP.dontUsePictureWindow)
1521 praat_picture_prefsChanged ();
1522 praat_statistics_prefsChanged ();
1523 }
1524
1525 praatP.phase = praat_STARTING_UP;
1526
1527 trace (U"execute start-up file(s)");
1528 /*
1529 * On Unix and the Mac, we try no less than three start-up file names.
1530 */
1531 #if defined (UNIX) || defined (macintosh)
1532 structMelderDir usrLocal { };
1533 Melder_pathToDir (U"/usr/local", & usrLocal);
1534 executeStartUpFile (& usrLocal, U"", U"-startUp");
1535 #endif
1536 #if defined (UNIX) || defined (macintosh)
1537 executeStartUpFile (& homeDir, U".", U"-user-startUp"); // not on Windows (empty file name error)
1538 #endif
1539 #if defined (UNIX) || defined (macintosh) || defined (_WIN32)
1540 executeStartUpFile (& homeDir, U"", U"-user-startUp");
1541 #endif
1542
1543 if (! MelderDir_isNull (& Melder_preferencesFolder) && ! praatP.ignorePlugins) {
1544 trace (U"install plug-ins");
1545 trace (U"locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1546 /* The Praat phase should remain praat_STARTING_UP,
1547 * because any added commands must not be included in the buttons file.
1548 */
1549 structMelderFile searchPattern { };
1550 MelderDir_getFile (& Melder_preferencesFolder, U"plugin_*", & searchPattern);
1551 try {
1552 autoSTRVEC folderNames = folderNames_STRVEC (Melder_fileToPath (& searchPattern));
1553 for (integer i = 1; i <= folderNames.size; i ++) {
1554 structMelderDir pluginDir { };
1555 structMelderFile plugin { };
1556 MelderDir_getSubdir (& Melder_preferencesFolder, folderNames [i].get(), & pluginDir);
1557 MelderDir_getFile (& pluginDir, U"setup.praat", & plugin);
1558 if (MelderFile_readable (& plugin)) {
1559 Melder_backgrounding = true;
1560 try {
1561 praat_executeScriptFromFile (& plugin, nullptr);
1562 } catch (MelderError) {
1563 Melder_flushError (praatP.title.get(), U": plugin ", & plugin, U" contains an error.");
1564 }
1565 Melder_backgrounding = false;
1566 }
1567 }
1568 } catch (MelderError) {
1569 Melder_clearError (); // in case Strings_createAsDirectoryList () threw an error
1570 }
1571 }
1572
1573 /*
1574 Check the locale, to ensure identical behaviour on all computers.
1575 */
1576 Melder_assert (str32equ (Melder_double (1.5), U"1.5")); // check locale settings; because of the required file portability Praat cannot stand "1,5"
1577 Melder_assert (Melder_isHorizontalOrVerticalSpace (' '));
1578 Melder_assert (Melder_isHorizontalOrVerticalSpace ('\r'));
1579 Melder_assert (Melder_isHorizontalOrVerticalSpace ('\n'));
1580 Melder_assert (Melder_isHorizontalOrVerticalSpace ('\t'));
1581 Melder_assert (Melder_isHorizontalOrVerticalSpace ('\f'));
1582 Melder_assert (Melder_isHorizontalOrVerticalSpace ('\v'));
1583
1584 {
1585 double *a = nullptr;
1586 double *b = & a [0];
1587 Melder_assert (! b);
1588 }
1589
1590 /*
1591 According to ISO 30112, a non-breaking space is not a space.
1592 We do not agree, as long as spaces are assumed to be word breakers:
1593 non-breaking spaces are used to prevent line breaks, not to prevent word breaks.
1594 For instance, in English it's possible to insert a non-breaking space
1595 after "e.g." in "...take drastic measures, e.g. pose a ban on...",
1596 just because otherwise a line would end in a period that does not signal end of sentence;
1597 this use does not mean that "e.g." and "pose" aren't separate words.
1598 */
1599 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_NO_BREAK_SPACE));
1600
1601 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_OGHAM_SPACE_MARK)); // ISO 30112
1602
1603 /*
1604 According to ISO 30112, a Mongolian vowel separator is a space.
1605 However, this character is used to separate vowels *within* a word,
1606 so it should not be a word breaker.
1607 This means that as long as all spaces are assumed to be word breakers,
1608 this character cannot be a space.
1609 */
1610 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_MONGOLIAN_VOWEL_SEPARATOR));
1611
1612 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_EN_QUAD)); // ISO 30112
1613 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_EM_QUAD)); // ISO 30112
1614 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_EN_SPACE)); // ISO 30112
1615 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_EM_SPACE)); // ISO 30112
1616 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_THREE_PER_EM_SPACE)); // ISO 30112
1617 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_FOUR_PER_EM_SPACE)); // ISO 30112
1618 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_SIX_PER_EM_SPACE)); // ISO 30112
1619 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_FIGURE_SPACE)); // questionable
1620 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_PUNCTUATION_SPACE)); // ISO 30112
1621 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_THIN_SPACE)); // ISO 30112
1622 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_HAIR_SPACE)); // ISO 30112
1623 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ZERO_WIDTH_SPACE)); // questionable
1624 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ZERO_WIDTH_NON_JOINER));
1625 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ZERO_WIDTH_JOINER));
1626 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ZERO_WIDTH_NO_BREAK_SPACE)); // this is the byte-order mark!
1627 //Melder_assert (iswspace (UNICODE_LEFT_TO_RIGHT_MARK));
1628 //Melder_assert (iswspace (UNICODE_RIGHT_TO_LEFT_MARK));
1629 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_LINE_SEPARATOR)); // ISO 30112
1630 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_PARAGRAPH_SEPARATOR)); // ISO 30112
1631 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_NARROW_NO_BREAK_SPACE));
1632 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_LEFT_TO_RIGHT_EMBEDDING));
1633 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_RIGHT_TO_LEFT_EMBEDDING));
1634 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_POP_DIRECTIONAL_FORMATTING));
1635 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_LEFT_TO_RIGHT_OVERRIDE));
1636 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_RIGHT_TO_LEFT_OVERRIDE));
1637 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_MEDIUM_MATHEMATICAL_SPACE)); // ISO 30112
1638 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_WORD_JOINER));
1639 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_FUNCTION_APPLICATION));
1640 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_INVISIBLE_TIMES));
1641 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_INVISIBLE_SEPARATOR));
1642 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_INHIBIT_SYMMETRIC_SWAPPING));
1643 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ACTIVATE_SYMMETRIC_SWAPPING));
1644 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_INHIBIT_ARABIC_FORM_SHAPING));
1645 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_ACTIVATE_ARABIC_FORM_SHAPING));
1646 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_NATIONAL_DIGIT_SHAPES));
1647 Melder_assert (! Melder_isHorizontalOrVerticalSpace (UNICODE_NOMINAL_DIGIT_SHAPES));
1648 Melder_assert (Melder_isHorizontalOrVerticalSpace (UNICODE_IDEOGRAPHIC_SPACE)); // ISO 30112; occurs on Japanese computers
1649
1650 { unsigned char dummy = 200;
1651 Melder_assert ((int) dummy == 200);
1652 }
1653 Melder_assert (integer_abs (-1000) == integer (1000));
1654 { int dummy = 200;
1655 Melder_assert ((int) (signed char) dummy == -56); // bingeti8 relies on this
1656 Melder_assert ((int) (unsigned char) dummy == 200);
1657 Melder_assert ((double) dummy == 200.0);
1658 Melder_assert ((double) (signed char) dummy == -56.0);
1659 Melder_assert ((double) (unsigned char) dummy == 200.0);
1660 }
1661 { int64 dummy = 200;
1662 Melder_assert ((int) (signed char) dummy == -56);
1663 Melder_assert ((int) (unsigned char) dummy == 200);
1664 Melder_assert ((double) dummy == 200.0);
1665 Melder_assert ((double) (signed char) dummy == -56.0);
1666 Melder_assert ((double) (unsigned char) dummy == 200.0);
1667 }
1668 { uint16 dummy = 40000;
1669 Melder_assert ((int) (int16) dummy == -25536); // bingeti16 relies on this
1670 Melder_assert ((short) (int16) dummy == -25536); // bingete16 relies on this
1671 Melder_assert ((integer) dummy == 40000); // Melder_integer relies on this
1672 Melder_assert ((double) dummy == 40000.0);
1673 Melder_assert ((double) (int16) dummy == -25536.0);
1674 }
1675 { unsigned int dummy = 40000;
1676 Melder_assert ((int) (int16) dummy == -25536);
1677 Melder_assert ((short) (int16) dummy == -25536);
1678 Melder_assert ((integer) dummy == 40000); // Melder_integer relies on this
1679 Melder_assert ((double) dummy == 40000.0);
1680 Melder_assert ((double) (int16) dummy == -25536.0);
1681 }
1682 {
1683 int64 dummy = 1000000000000;
1684 if (! str32equ (Melder_integer (dummy), U"1000000000000"))
1685 Melder_fatal (U"The number 1000000000000 is mistakenly written on this machine as ", dummy, U".");
1686 }
1687 { uint32 dummy = 0xffffffff;
1688 Melder_assert ((int64) dummy == 4294967295LL);
1689 Melder_assert (str32equ (Melder_integer (dummy), U"4294967295"));
1690 Melder_assert (double (dummy) == 4294967295.0);
1691 }
1692 { double dummy = 3000000000.0;
1693 Melder_assert ((uint32) dummy == 3000000000);
1694 }
1695 {
1696 Melder_assert (str32len (U"hello") == 5);
1697 Melder_assert (str32ncmp (U"hellogoodbye", U"hellogee", 6) == 0);
1698 Melder_assert (str32ncmp (U"hellogoodbye", U"hellogee", 7) > 0);
1699 Melder_assert (str32str (U"hellogoodbye", U"ogo"));
1700 Melder_assert (! str32str (U"hellogoodbye", U"oygo"));
1701 }
1702 Melder_assert (isundef (undefined));
1703 Melder_assert (std::isinf (1.0 / 0.0));
1704 Melder_assert (std::isnan (0.0 / 0.0));
1705 {
1706 double x = sqrt (-10.0);
1707 //if (! std::isnan (x)) printf ("sqrt (-10.0) = %g\n", x); // -10.0 on Windows
1708 x = NUMsqrt (-10.0);
1709 Melder_assert (isundef (x));
1710 }
1711 Melder_assert (isdefined (0.0));
1712 Melder_assert (isdefined (1e300));
1713 Melder_assert (isundef (double (1e160 * 1e160)));
1714 Melder_assert (isundef (pow (10.0, 330)));
1715 Melder_assert (isundef (0.0 / 0.0));
1716 Melder_assert (isundef (1.0 / 0.0));
1717 Melder_assert (undefined != undefined);
1718 Melder_assert (undefined - undefined != 0.0);
1719 Melder_assert ((1.0/0.0) == (1.0/0.0));
1720 Melder_assert ((1.0/0.0) - (1.0/0.0) != 0.0);
1721 {
1722 /*
1723 Assumptions made in abcio.cpp:
1724 `frexp()` returns an infinity if its argument is an infinity,
1725 and not-a-number if its argument is not-a-number.
1726 */
1727 int exponent;
1728 Melder_assert (isundef (frexp (HUGE_VAL, & exponent)));
1729 Melder_assert (isundef (frexp (0.0/0.0, & exponent)));
1730 Melder_assert (isundef (frexp (undefined, & exponent)));
1731 /*
1732 The following relies on the facts that:
1733 - positive infinity is not less than 1.0 (because it is greater than 1.0)
1734 - NaN is not less than 1.0 (because it is not ordered)
1735
1736 Note: we cannot replace `! (... < 1.0)` with `... >= 1.0`,
1737 because `! (NaN < 1.0)` is true but `NaN >= 1.0` is false.
1738 */
1739 Melder_assert (! (frexp (HUGE_VAL, & exponent) < 1.0));
1740 Melder_assert (! (frexp (0.0/0.0, & exponent) < 1.0));
1741 Melder_assert (! (frexp (undefined, & exponent) < 1.0));
1742 }
1743 Melder_assert (str32equ (Melder_integer (1234567), U"1234567"));
1744 Melder_assert (str32equ (Melder_integer (-1234567), U"-1234567"));
1745 MelderColour notExplicitlyIniitialized;
1746 Melder_assert (str32equ (Melder_colour (notExplicitlyIniitialized), U"{0,0,0}"));
1747 Melder_assert (str32equ (Melder_colour (MelderColour (0.25, 0.50, 0.875)), U"{0.25,0.5,0.875}"));
1748 {
1749 VEC xn; // "uninitialized", but initializes x.cells to nullptr and x.size to 0
1750 Melder_assert (! xn.cells);
1751 Melder_assert (xn.size == 0);
1752 VEC xn2 = xn; // copy construction
1753 xn2 = xn; // copy assignment
1754 VEC x { };
1755 Melder_assert (! x.cells);
1756 Melder_assert (x.size == 0);
1757 constVEC xnc; // zero-initialized
1758 Melder_assert (! xnc.cells);
1759 Melder_assert (xnc.size == 0);
1760 constVEC xc { };
1761 Melder_assert (! xc.cells);
1762 Melder_assert (xc.size == 0);
1763 MAT yn;
1764 Melder_assert (! yn.cells);
1765 Melder_assert (yn.nrow == 0);
1766 Melder_assert (yn.ncol == 0);
1767 MAT y { };
1768 Melder_assert (! y.cells);
1769 Melder_assert (y.nrow == 0);
1770 Melder_assert (y.ncol == 0);
1771 //autoMAT z {y.cells,y.nrow,y.ncol}; // "No matching constructor for initialization of autoMAT" (2021-04-05)
1772 autoMAT a = autoMAT { };
1773 Melder_assert (! a.cells);
1774 Melder_assert (a.nrow == 0);
1775 Melder_assert (a.ncol == 0);
1776 //a = z.move();
1777 //double q [11];
1778 //autoVEC s { & q [1], 10 }; // "No matching constructor for initialization of autoVEC" (2021-04-05)
1779 //autoVEC b { x }; // "No matching constructor for initialization of autoVEC" (2021-04-05)
1780 //autoVEC c = x; // "No viable conversion from VEC to autoVEC" (2021-04-05)
1781 double aa [] = { 3.14, 2.718 };
1782 VEC x3 (aa, 2); // initializes x3 to 2 values from a base-0 array
1783 Melder_assert (x3 [2] == 2.718);
1784 autoVEC x4 = { 3.14, 2.718 };
1785 Melder_assert (x4 [2] == 2.718);
1786 }
1787 Melder_assert (Melder_iroundUpToPowerOfTwo (-10) == 1);
1788 Melder_assert (Melder_iroundUpToPowerOfTwo (-1) == 1);
1789 Melder_assert (Melder_iroundUpToPowerOfTwo (0) == 1);
1790 Melder_assert (Melder_iroundUpToPowerOfTwo (1) == 1);
1791 Melder_assert (Melder_iroundUpToPowerOfTwo (2) == 2);
1792 Melder_assert (Melder_iroundUpToPowerOfTwo (3) == 4);
1793 Melder_assert (Melder_iroundUpToPowerOfTwo (4) == 4);
1794 Melder_assert (Melder_iroundUpToPowerOfTwo (5) == 8);
1795 Melder_assert (Melder_iroundUpToPowerOfTwo (6) == 8);
1796 Melder_assert (Melder_iroundUpToPowerOfTwo (7) == 8);
1797 Melder_assert (Melder_iroundUpToPowerOfTwo (8) == 8);
1798 Melder_assert (Melder_iroundUpToPowerOfTwo (9) == 16);
1799 Melder_assert (Melder_iroundUpToPowerOfTwo (44100) == 65536);
1800 Melder_assert (Melder_iroundUpToPowerOfTwo (131071) == 131072);
1801 Melder_assert (Melder_iroundUpToPowerOfTwo (131072) == 131072);
1802 Melder_assert (Melder_iroundUpToPowerOfTwo (131073) == 262144);
1803 if (sizeof (integer) == 4) {
1804 Melder_assert (Melder_iroundUpToPowerOfTwo (1073741823) == 1073741824); // 2^30 - 1
1805 Melder_assert (Melder_iroundUpToPowerOfTwo (1073741824) == 1073741824); // 2^30
1806 Melder_assert (Melder_iroundUpToPowerOfTwo (1073741825) == 0); // 2^30 + 1
1807 Melder_assert (Melder_iroundUpToPowerOfTwo (2147483647) == 0); // 2^31 - 1, i.e. INTEGER_MAX
1808 } else {
1809 Melder_assert (Melder_iroundUpToPowerOfTwo (4611686018427387903LL) == 4611686018427387904LL); // 2^62 - 1
1810 Melder_assert (Melder_iroundUpToPowerOfTwo (4611686018427387904LL) == 4611686018427387904LL); // 2^62
1811 Melder_assert (Melder_iroundUpToPowerOfTwo (4611686018427387905LL) == 0); // 2^62 + 1
1812 Melder_assert (Melder_iroundUpToPowerOfTwo (9223372036854775807LL) == 0); // 2^63 - 1, i.e. INTEGER_MAX
1813 }
1814 Melder_assert (Melder_iroundUpToPowerOfTwo (4) == 4);
1815 {
1816 autoMAT mat = { { 10, 20, 30, 40 }, { 60, 70, 80, 90 }, { 170, 180, 190, -300 } };
1817 Melder_assert (mat [1] [1] == 10.0);
1818 Melder_assert (mat [1] [4] == 40.0);
1819 Melder_assert (mat [2] [3] == 80.0);
1820 Melder_assert (mat [3] [1] == 170.0);
1821 Melder_assert (mat [3] [4] == -300.0);
1822 }
1823 static_assert (sizeof (float) == 4,
1824 "sizeof(float) should be 4");
1825 static_assert (sizeof (double) == 8,
1826 "sizeof(double) should be 8");
1827 static_assert (sizeof (longdouble) >= 8,
1828 "sizeof(longdouble) should be at least 8"); // this can be 8, 12 or 16
1829 static_assert (sizeof (integer) == sizeof (void *),
1830 "sizeof(integer) should equal the size of a pointer");
1831 static_assert (sizeof (off_t) >= 8,
1832 "sizeof(off_t) is less than 8. Compile Praat with -D_FILE_OFFSET_BITS=64.");
1833
1834 if (Melder_batch) {
1835 if (thePraatStandAloneScriptText) {
1836 try {
1837 praat_executeScriptFromText (thePraatStandAloneScriptText);
1838 praat_exit (0);
1839 } catch (MelderError) {
1840 Melder_flushError (praatP.title.get(), U": stand-alone script session interrupted.");
1841 praat_exit (-1);
1842 }
1843 } else if (praatP.hasCommandLineInput) {
1844 try {
1845 praat_executeCommandFromStandardInput (praatP.title.get());
1846 praat_exit (0);
1847 } catch (MelderError) {
1848 Melder_flushError (praatP.title.get(), U": command line session interrupted.");
1849 praat_exit (-1);
1850 }
1851 } else {
1852 try {
1853 //Melder_casual (U"Script <<", theCurrentPraatApplication -> batchName.string, U">>");
1854 praat_executeScriptFromFileNameWithArguments (theCurrentPraatApplication -> batchName.string);
1855 praat_exit (0);
1856 } catch (MelderError) {
1857 Melder_flushError (praatP.title.get(), U": script command <<",
1858 theCurrentPraatApplication -> batchName.string, U">> not completed.");
1859 praat_exit (-1);
1860 }
1861 }
1862 } else /* GUI */ {
1863 if (! praatP.ignorePreferenceFiles) {
1864 trace (U"reading the added script buttons");
1865 /* Each line separately: every error should be ignored.
1866 */
1867 praatP.phase = praat_READING_BUTTONS;
1868 {// scope
1869 autostring32 buttons;
1870 try {
1871 buttons = MelderFile_readText (& buttonsFile);
1872 } catch (MelderError) {
1873 Melder_clearError ();
1874 }
1875 if (buttons) {
1876 char32 *line = buttons.get();
1877 for (;;) {
1878 char32 *newline = str32chr (line, U'\n');
1879 if (newline) *newline = U'\0';
1880 try {
1881 (void) praat_executeCommand (nullptr, line); // should contain no cases of "nocheck"
1882 } catch (MelderError) {
1883 Melder_clearError (); // ignore this line, but not necessarily the next
1884 }
1885 if (! newline)
1886 break;
1887 line = newline + 1;
1888 }
1889 }
1890 }
1891 }
1892
1893 trace (U"sorting the commands");
1894 trace (U"locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1895 praat_sortMenuCommands ();
1896 praat_sortActions ();
1897
1898 praatP.phase = praat_HANDLING_EVENTS;
1899
1900 if (praatP.userWantsToOpen) {
1901 for (; praatP.argumentNumber < praatP.argc; praatP.argumentNumber ++) {
1902 //Melder_casual (U"File to open <<", Melder_peek8to32 (theArgv [iarg]), U">>");
1903 /*
1904 The use double-clicked a Praat file,
1905 or dropped a file on the Praat icon,
1906 while Praat was not yet running.
1907 */
1908 autostring32 text = Melder_dup (Melder_cat (U"Read from file... ",
1909 Melder_peek8to32 (praatP.argv [praatP.argumentNumber])));
1910 try {
1911 praat_executeScriptFromText (text.get());
1912 } catch (MelderError) {
1913 Melder_flushError ();
1914 }
1915 }
1916 }
1917
1918 #if gtk
1919 //gtk_widget_add_events (G_OBJECT (theCurrentPraatApplication -> topShell), GDK_ALL_EVENTS_MASK);
1920 trace (U"install GTK key snooper");
1921 trace (U"locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1922 //g_signal_newv ("SENDPRAAT", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, NULL, NULL, NULL, NULL, G_TYPE_NONE, 0, NULL);
1923 g_signal_connect (G_OBJECT (theCurrentPraatApplication -> topShell -> d_gtkWindow), "property-notify-event",
1924 G_CALLBACK (cb_userMessage), nullptr);
1925 signal (SIGUSR1, cb_sigusr1);
1926 gtk_key_snooper_install (theKeySnooper, 0);
1927 trace (U"start the GTK event loop");
1928 trace (U"locale is ", Melder_peek8to32 (setlocale (LC_ALL, nullptr)));
1929 gtk_main ();
1930 #elif motif
1931 for (;;) {
1932 XEvent event;
1933 GuiNextEvent (& event);
1934 XtDispatchEvent (& event);
1935 }
1936 #elif cocoa
1937 [NSApp run];
1938 #endif
1939 }
1940 }
1941
1942 /* End of file praat.cpp */
1943