praat.cpp 73 KB

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