FunctionEditor.cpp 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644
  1. /* FunctionEditor.cpp
  2. *
  3. * Copyright (C) 1992-2011,2012,2013,2014,2015,2017 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 "FunctionEditor.h"
  19. #include "machine.h"
  20. #include "EditorM.h"
  21. #include "GuiP.h"
  22. Thing_implement (FunctionEditor, Editor, 0);
  23. #define maximumScrollBarValue 2000000000
  24. #define RELATIVE_PAGE_INCREMENT 0.8
  25. #define SCROLL_INCREMENT_FRACTION 20
  26. #define space 30
  27. #define MARGIN 107
  28. #define BOTTOM_MARGIN 2
  29. #define TOP_MARGIN 3
  30. #define TEXT_HEIGHT 50
  31. #define BUTTON_X 3
  32. #define BUTTON_WIDTH 40
  33. #define BUTTON_SPACING 8
  34. #include "prefs_define.h"
  35. #include "FunctionEditor_prefs.h"
  36. #include "prefs_install.h"
  37. #include "FunctionEditor_prefs.h"
  38. #include "prefs_copyToInstance.h"
  39. #include "FunctionEditor_prefs.h"
  40. namespace {
  41. constexpr int maxGroup { 100 };
  42. int nGroup = 0;
  43. FunctionEditor theGroup [1 + maxGroup];
  44. }
  45. static void drawWhileDragging (FunctionEditor me, double x1, double x2);
  46. static int group_equalDomain (double tmin, double tmax) {
  47. if (nGroup == 0) return 1;
  48. for (int i = 1; i <= maxGroup; i ++)
  49. if (theGroup [i])
  50. return tmin == theGroup [i] -> tmin && tmax == theGroup [i] -> tmax;
  51. return 0; // should not occur
  52. }
  53. static void updateScrollBar (FunctionEditor me) {
  54. /* We cannot call this immediately after creation. */
  55. double slider_size = (my endWindow - my startWindow) / (my tmax - my tmin) * maximumScrollBarValue - 1.0;
  56. double value = (my startWindow - my tmin) / (my tmax - my tmin) * maximumScrollBarValue + 1.0;
  57. if (slider_size < 1.0) slider_size = 1.0;
  58. if (value > maximumScrollBarValue - slider_size)
  59. value = maximumScrollBarValue - slider_size;
  60. if (value < 1.0) value = 1.0;
  61. double increment = slider_size / SCROLL_INCREMENT_FRACTION + 1.0;
  62. double page_increment = RELATIVE_PAGE_INCREMENT * slider_size + 1.0;
  63. GuiScrollBar_set (my scrollBar, undefined, maximumScrollBarValue, value, slider_size, increment, page_increment);
  64. }
  65. static void updateGroup (FunctionEditor me) {
  66. if (! my group) return;
  67. for (int i = 1; i <= maxGroup; i ++) if (theGroup [i] && theGroup [i] != me) {
  68. FunctionEditor thee = theGroup [i];
  69. if (my pref_synchronizedZoomAndScroll ()) {
  70. thy startWindow = my startWindow;
  71. thy endWindow = my endWindow;
  72. }
  73. thy startSelection = my startSelection;
  74. thy endSelection = my endSelection;
  75. FunctionEditor_updateText (thee);
  76. updateScrollBar (thee);
  77. Graphics_updateWs (thy graphics.get());
  78. }
  79. }
  80. static void drawNow (FunctionEditor me) {
  81. bool leftFromWindow = my startWindow > my tmin;
  82. bool rightFromWindow = my endWindow < my tmax;
  83. bool cursorVisible = my startSelection == my endSelection && my startSelection >= my startWindow && my startSelection <= my endWindow;
  84. bool selection = my endSelection > my startSelection;
  85. /* Update selection. */
  86. bool beginVisible = my startSelection > my startWindow && my startSelection < my endWindow;
  87. bool endVisible = my endSelection > my startWindow && my endSelection < my endWindow;
  88. /* Update markers. */
  89. my numberOfMarkers = 0;
  90. if (beginVisible)
  91. my marker [++ my numberOfMarkers] = my startSelection;
  92. if (endVisible && my endSelection != my startSelection)
  93. my marker [++ my numberOfMarkers] = my endSelection;
  94. my marker [++ my numberOfMarkers] = my endWindow;
  95. VECsort_inplace (VEC (my marker, my numberOfMarkers));
  96. /* Update rectangles. */
  97. for (int i = 0; i < 8; i++)
  98. my rect [i]. left = my rect [i]. right = 0;
  99. /* 0: rectangle for total. */
  100. my rect [0]. left = my functionViewerLeft + ( leftFromWindow ? 0 : MARGIN );
  101. my rect [0]. right = my functionViewerRight - ( rightFromWindow ? 0 : MARGIN );
  102. my rect [0]. bottom = BOTTOM_MARGIN;
  103. my rect [0]. top = BOTTOM_MARGIN + space;
  104. /* 1: rectangle for visible part. */
  105. my rect [1]. left = my functionViewerLeft + MARGIN;
  106. my rect [1]. right = my functionViewerRight - MARGIN;
  107. my rect [1]. bottom = BOTTOM_MARGIN + space;
  108. my rect [1]. top = BOTTOM_MARGIN + space * (my numberOfMarkers > 1 ? 2 : 3);
  109. /* 2: rectangle for left from visible part. */
  110. if (leftFromWindow) {
  111. my rect [2]. left = my functionViewerLeft;
  112. my rect [2]. right = my functionViewerLeft + MARGIN;
  113. my rect [2]. bottom = BOTTOM_MARGIN + space;
  114. my rect [2]. top = BOTTOM_MARGIN + space * 2;
  115. }
  116. /* 3: rectangle for right from visible part. */
  117. if (rightFromWindow) {
  118. my rect [3]. left = my functionViewerRight - MARGIN;
  119. my rect [3]. right = my functionViewerRight;
  120. my rect [3]. bottom = BOTTOM_MARGIN + space;
  121. my rect [3]. top = BOTTOM_MARGIN + space * 2;
  122. }
  123. /* 4, 5, 6: rectangles between markers visible in visible part. */
  124. if (my numberOfMarkers > 1) {
  125. double window = my endWindow - my startWindow;
  126. for (int i = 1; i <= my numberOfMarkers; i ++) {
  127. my rect [3 + i]. left = i == 1 ? my functionViewerLeft + MARGIN : my functionViewerLeft + MARGIN + (my functionViewerRight - my functionViewerLeft - MARGIN * 2) *
  128. (my marker [i - 1] - my startWindow) / window;
  129. my rect [3 + i]. right = my functionViewerLeft + MARGIN + (my functionViewerRight - my functionViewerLeft - MARGIN * 2) *
  130. (my marker [i] - my startWindow) / window;
  131. my rect [3 + i]. bottom = BOTTOM_MARGIN + space * 2;
  132. my rect [3 + i]. top = BOTTOM_MARGIN + space * 3;
  133. }
  134. }
  135. if (selection) {
  136. double window = my endWindow - my startWindow;
  137. double left =
  138. my startSelection == my startWindow ? my functionViewerLeft + MARGIN :
  139. my startSelection == my tmin ? my functionViewerLeft :
  140. my startSelection < my startWindow ? my functionViewerLeft + MARGIN * 0.3 :
  141. my startSelection < my endWindow ? my functionViewerLeft + MARGIN + (my functionViewerRight - my functionViewerLeft - MARGIN * 2) * (my startSelection - my startWindow) / window :
  142. my startSelection == my endWindow ? my functionViewerRight - MARGIN : my functionViewerRight - MARGIN * 0.7;
  143. double right =
  144. my endSelection < my startWindow ? my functionViewerLeft + MARGIN * 0.7 :
  145. my endSelection == my startWindow ? my functionViewerLeft + MARGIN :
  146. my endSelection < my endWindow ? my functionViewerLeft + MARGIN + (my functionViewerRight - my functionViewerLeft - MARGIN * 2) * (my endSelection - my startWindow) / window :
  147. my endSelection == my endWindow ? my functionViewerRight - MARGIN :
  148. my endSelection < my tmax ? my functionViewerRight - MARGIN * 0.3 : my functionViewerRight;
  149. my rect [7]. left = left;
  150. my rect [7]. right = right;
  151. my rect [7]. bottom = my height - space - TOP_MARGIN;
  152. my rect [7]. top = my height - TOP_MARGIN;
  153. }
  154. /*
  155. * Be responsive: update the markers now.
  156. */
  157. Graphics_setViewport (my graphics.get(), my functionViewerLeft, my functionViewerRight, 0.0, my height);
  158. Graphics_setWindow (my graphics.get(), my functionViewerLeft, my functionViewerRight, 0.0, my height);
  159. Graphics_setColour (my graphics.get(), Graphics_WINDOW_BACKGROUND_COLOUR);
  160. Graphics_fillRectangle (my graphics.get(), my functionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, my height - (TOP_MARGIN + space), my height);
  161. Graphics_fillRectangle (my graphics.get(), my functionViewerLeft, my functionViewerLeft + MARGIN, BOTTOM_MARGIN + ( leftFromWindow ? space * 2 : 0 ), my height);
  162. Graphics_fillRectangle (my graphics.get(), my functionViewerRight - MARGIN, my functionViewerRight, BOTTOM_MARGIN + ( rightFromWindow ? space * 2 : 0 ), my height);
  163. if (my p_showSelectionViewer) {
  164. Graphics_setViewport (my graphics.get(), my selectionViewerLeft, my selectionViewerRight, 0.0, my height);
  165. Graphics_setWindow (my graphics.get(), my selectionViewerLeft, my selectionViewerRight, 0.0, my height);
  166. Graphics_fillRectangle (my graphics.get(), my selectionViewerLeft, my selectionViewerLeft + MARGIN, BOTTOM_MARGIN, my height);
  167. Graphics_fillRectangle (my graphics.get(), my selectionViewerRight - MARGIN, my selectionViewerRight, BOTTOM_MARGIN, my height);
  168. Graphics_fillRectangle (my graphics.get(), my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, 0, BOTTOM_MARGIN + space * 3);
  169. }
  170. Graphics_setGrey (my graphics.get(), 0.0);
  171. #if defined (macintosh)
  172. Graphics_line (my graphics.get(), my functionViewerLeft, 2.0, my selectionViewerRight, 2.0);
  173. Graphics_line (my graphics.get(), my functionViewerLeft, my height - 2.0, my selectionViewerRight, my height - 2.0);
  174. #endif
  175. Graphics_setViewport (my graphics.get(), my functionViewerLeft, my functionViewerRight, 0.0, my height);
  176. Graphics_setWindow (my graphics.get(), my functionViewerLeft, my functionViewerRight, 0.0, my height);
  177. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
  178. for (int i = 0; i < 8; i ++) {
  179. double left = my rect [i]. left, right = my rect [i]. right;
  180. if (left < right)
  181. Graphics_button (my graphics.get(), left, right, my rect [i]. bottom, my rect [i]. top);
  182. }
  183. double verticalCorrection = my height / (my height - 111.0 + 11.0);
  184. #ifdef _WIN32
  185. verticalCorrection *= 1.5;
  186. #endif
  187. for (int i = 0; i < 8; i ++) {
  188. double left = my rect [i]. left, right = my rect [i]. right;
  189. double bottom = my rect [i]. bottom, top = my rect [i]. top;
  190. if (left < right) {
  191. const char *format = my v_format_long ();
  192. double value = undefined, inverseValue = 0.0;
  193. switch (i) {
  194. case 0: {
  195. format = my v_format_totalDuration ();
  196. value = my tmax - my tmin;
  197. } break; case 1: {
  198. format = my v_format_window ();
  199. value = my endWindow - my startWindow;
  200. /*
  201. Window domain text.
  202. */
  203. Graphics_setColour (my graphics.get(), Graphics_BLUE);
  204. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
  205. Graphics_text (my graphics.get(), left, 0.5 * (bottom + top) - verticalCorrection,
  206. Melder_fixed (my startWindow, my v_fixedPrecision_long ()));
  207. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  208. Graphics_text (my graphics.get(), right, 0.5 * (bottom + top) - verticalCorrection,
  209. Melder_fixed (my endWindow, my v_fixedPrecision_long ()));
  210. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  211. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
  212. } break; case 2: {
  213. value = my startWindow - my tmin;
  214. } break; case 3: {
  215. value = my tmax - my endWindow;
  216. } break; case 4: {
  217. value = my marker [1] - my startWindow;
  218. } break; case 5: {
  219. value = my marker [2] - my marker [1];
  220. } break; case 6: {
  221. value = my marker [3] - my marker [2];
  222. } break; case 7: {
  223. format = my v_format_selection ();
  224. value = my endSelection - my startSelection;
  225. inverseValue = 1.0 / value;
  226. }
  227. }
  228. char text8 [100];
  229. snprintf (text8, 100, format, value, inverseValue);
  230. autostring32 text = Melder_8to32 (text8);
  231. if (Graphics_textWidth (my graphics.get(), text.get()) < right - left) {
  232. Graphics_text (my graphics.get(), 0.5 * (left + right), 0.5 * (bottom + top) - verticalCorrection, text.get());
  233. } else if (format == my v_format_long ()) {
  234. snprintf (text8, 100, my v_format_short (), value);
  235. text = Melder_8to32 (text8);
  236. if (Graphics_textWidth (my graphics.get(), text.get()) < right - left)
  237. Graphics_text (my graphics.get(), 0.5 * (left + right), 0.5 * (bottom + top) - verticalCorrection, text.get());
  238. } else {
  239. snprintf (text8, 100, my v_format_long (), value);
  240. text = Melder_8to32 (text8);
  241. if (Graphics_textWidth (my graphics.get(), text.get()) < right - left) {
  242. Graphics_text (my graphics.get(), 0.5 * (left + right), 0.5 * (bottom + top) - verticalCorrection, text.get());
  243. } else {
  244. snprintf (text8, 100, my v_format_short (), my endSelection - my startSelection);
  245. text = Melder_8to32 (text8);
  246. if (Graphics_textWidth (my graphics.get(), text.get()) < right - left)
  247. Graphics_text (my graphics.get(), 0.5 * (left + right), 0.5 * (bottom + top) - verticalCorrection, text.get());
  248. }
  249. }
  250. }
  251. }
  252. Graphics_setViewport (my graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN, 0.0, my height);
  253. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, 0.0, my height);
  254. /*Graphics_setColour (my graphics.get(), Graphics_WHITE);
  255. Graphics_fillRectangle (my graphics.get(), my startWindow, my endWindow, BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));*/
  256. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  257. Graphics_rectangle (my graphics.get(), my startWindow, my endWindow, BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  258. /*
  259. * Red marker text.
  260. */
  261. Graphics_setColour (my graphics.get(), Graphics_RED);
  262. if (cursorVisible) {
  263. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_BOTTOM);
  264. Graphics_text (my graphics.get(), my startSelection, my height - (TOP_MARGIN + space) - verticalCorrection,
  265. Melder_fixed (my startSelection, my v_fixedPrecision_long ()));
  266. }
  267. if (beginVisible && selection) {
  268. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  269. Graphics_text (my graphics.get(), my startSelection, my height - (TOP_MARGIN + space/2) - verticalCorrection,
  270. Melder_fixed (my startSelection, my v_fixedPrecision_long ()));
  271. }
  272. if (endVisible && selection) {
  273. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
  274. Graphics_text (my graphics.get(), my endSelection, my height - (TOP_MARGIN + space/2) - verticalCorrection,
  275. Melder_fixed (my endSelection, my v_fixedPrecision_long ()));
  276. }
  277. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  278. /*
  279. * To reduce flashing, give our descendants the opportunity to prepare their data.
  280. */
  281. my v_prepareDraw ();
  282. /*
  283. * Start of inner drawing.
  284. */
  285. Graphics_setViewport (my graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN, BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  286. my v_draw ();
  287. Graphics_setViewport (my graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN, BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  288. /*
  289. * Red dotted marker lines.
  290. */
  291. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, 0.0, 1.0);
  292. Graphics_setColour (my graphics.get(), Graphics_RED);
  293. Graphics_setLineType (my graphics.get(), Graphics_DOTTED);
  294. double bottom = my v_getBottomOfSoundAndAnalysisArea ();
  295. if (cursorVisible)
  296. Graphics_line (my graphics.get(), my startSelection, bottom, my startSelection, 1.0);
  297. if (beginVisible)
  298. Graphics_line (my graphics.get(), my startSelection, bottom, my startSelection, 1.0);
  299. if (endVisible)
  300. Graphics_line (my graphics.get(), my endSelection, bottom, my endSelection, 1.0);
  301. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  302. Graphics_setLineType (my graphics.get(), Graphics_DRAWN);
  303. /*
  304. * Highlight selection.
  305. */
  306. if (selection && my startSelection < my endWindow && my endSelection > my startWindow) {
  307. double left = my startSelection, right = my endSelection;
  308. if (left < my startWindow) left = my startWindow;
  309. if (right > my endWindow) right = my endWindow;
  310. my v_highlightSelection (left, right, 0.0, 1.0);
  311. }
  312. /*
  313. * Draw the selection part.
  314. */
  315. if (my p_showSelectionViewer) {
  316. Graphics_setViewport (my graphics.get(), my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  317. my v_drawSelectionViewer ();
  318. }
  319. /*
  320. * End of inner drawing.
  321. */
  322. Graphics_flushWs (my graphics.get());
  323. Graphics_setViewport (my graphics.get(), my functionViewerLeft, my selectionViewerRight, 0.0, my height);
  324. }
  325. /********** METHODS **********/
  326. void structFunctionEditor :: v_destroy () noexcept {
  327. MelderAudio_stopPlaying (MelderAudio_IMPLICIT);
  328. if (our group) { // undangle
  329. int i = 1;
  330. while (theGroup [i] != this) {
  331. Melder_assert (i < maxGroup);
  332. i ++;
  333. }
  334. theGroup [i] = nullptr;
  335. nGroup --;
  336. }
  337. FunctionEditor_Parent :: v_destroy ();
  338. }
  339. void structFunctionEditor :: v_info () {
  340. FunctionEditor_Parent :: v_info ();
  341. MelderInfo_writeLine (U"Editor start: ", our tmin, U" ", v_format_units ());
  342. MelderInfo_writeLine (U"Editor end: ", our tmax, U" ", v_format_units ());
  343. MelderInfo_writeLine (U"Window start: ", our startWindow, U" ", v_format_units ());
  344. MelderInfo_writeLine (U"Window end: ", our endWindow, U" ", v_format_units ());
  345. MelderInfo_writeLine (U"Selection start: ", our startSelection, U" ", v_format_units ());
  346. MelderInfo_writeLine (U"Selection end: ", our endSelection, U" ", v_format_units ());
  347. MelderInfo_writeLine (U"Arrow scroll step: ", our p_arrowScrollStep, U" ", v_format_units ());
  348. MelderInfo_writeLine (U"Group: ", group ? U"yes" : U"no");
  349. }
  350. /********** FILE MENU **********/
  351. static void gui_drawingarea_cb_resize (FunctionEditor me, GuiDrawingArea_ResizeEvent event) {
  352. if (! my graphics) return; // could be the case in the very beginning
  353. Graphics_setWsViewport (my graphics.get(), 0, event -> width, 0, event -> height);
  354. int width = event -> width + 21;
  355. /*
  356. * Put the function viewer at the left and the selection viewer at the right.
  357. */
  358. my functionViewerLeft = 0;
  359. my functionViewerRight = my p_showSelectionViewer ? Melder_ifloor (width * (2.0/3.0)) : width;
  360. my selectionViewerLeft = my functionViewerRight;
  361. my selectionViewerRight = width;
  362. my height = event -> height + 111;
  363. Graphics_setWsWindow (my graphics.get(), 0.0, width, 0.0, my height);
  364. Graphics_setViewport (my graphics.get(), 0.0, width, 0.0, my height);
  365. Graphics_updateWs (my graphics.get());
  366. /* Save the current shell size as the user's preference for a new FunctionEditor. */
  367. my pref_shellWidth () = GuiShell_getShellWidth (my windowForm);
  368. my pref_shellHeight () = GuiShell_getShellHeight (my windowForm);
  369. }
  370. static void menu_cb_preferences (FunctionEditor me, EDITOR_ARGS_FORM) {
  371. EDITOR_FORM (U"Preferences", nullptr)
  372. BOOLEAN (synchronizeZoomAndScroll, U"Synchronize zoom and scroll", my default_synchronizedZoomAndScroll ())
  373. BOOLEAN (showSelectionViewer, U"Show selection viewer", my default_showSelectionViewer ())
  374. POSITIVE (arrowScrollStep, Melder_cat (U"Arrow scroll step (", my v_format_units (), U")"), my default_arrowScrollStep ())
  375. my v_prefs_addFields (cmd);
  376. EDITOR_OK
  377. SET_BOOLEAN (synchronizeZoomAndScroll, my pref_synchronizedZoomAndScroll ())
  378. SET_BOOLEAN (showSelectionViewer, my pref_showSelectionViewer())
  379. SET_REAL (arrowScrollStep, my p_arrowScrollStep)
  380. my v_prefs_setValues (cmd);
  381. EDITOR_DO
  382. bool oldSynchronizedZoomAndScroll = my pref_synchronizedZoomAndScroll ();
  383. bool oldShowSelectionViewer = my p_showSelectionViewer;
  384. my pref_synchronizedZoomAndScroll () = synchronizeZoomAndScroll;
  385. my pref_showSelectionViewer () = my p_showSelectionViewer = showSelectionViewer;
  386. my pref_arrowScrollStep () = my p_arrowScrollStep = arrowScrollStep;
  387. if (my p_showSelectionViewer != oldShowSelectionViewer) {
  388. struct structGuiDrawingArea_ResizeEvent event { my drawingArea, 0, 0 };
  389. event. width = GuiControl_getWidth (my drawingArea);
  390. event. height = GuiControl_getHeight (my drawingArea);
  391. gui_drawingarea_cb_resize (me, & event);
  392. }
  393. if (! oldSynchronizedZoomAndScroll && my pref_synchronizedZoomAndScroll ()) {
  394. updateGroup (me);
  395. }
  396. my v_prefs_getValues (cmd);
  397. EDITOR_END
  398. }
  399. static bool v_form_pictureSelection_drawSelectionTimes;
  400. static bool v_form_pictureSelection_drawSelectionHairs;
  401. void structFunctionEditor :: v_form_pictureSelection (EditorCommand cmd) {
  402. UiForm_addBoolean (cmd -> d_uiform.get(), & v_form_pictureSelection_drawSelectionTimes, nullptr, U"Draw selection times", true);
  403. UiForm_addBoolean (cmd -> d_uiform.get(), & v_form_pictureSelection_drawSelectionHairs, nullptr, U"Draw selection hairs", true);
  404. }
  405. void structFunctionEditor :: v_ok_pictureSelection (EditorCommand cmd) {
  406. FunctionEditor me = (FunctionEditor) cmd -> d_editor;
  407. SET_BOOLEAN (v_form_pictureSelection_drawSelectionTimes, my pref_picture_drawSelectionTimes ())
  408. SET_BOOLEAN (v_form_pictureSelection_drawSelectionHairs, my pref_picture_drawSelectionHairs ())
  409. }
  410. void structFunctionEditor :: v_do_pictureSelection (EditorCommand cmd) {
  411. FunctionEditor me = (FunctionEditor) cmd -> d_editor;
  412. my pref_picture_drawSelectionTimes () = v_form_pictureSelection_drawSelectionTimes;
  413. my pref_picture_drawSelectionHairs () = v_form_pictureSelection_drawSelectionHairs;
  414. }
  415. /********** QUERY MENU **********/
  416. static void menu_cb_getB (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  417. Melder_informationReal (my startSelection, my v_format_units ());
  418. }
  419. static void menu_cb_getCursor (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  420. Melder_informationReal (0.5 * (my startSelection + my endSelection), my v_format_units ());
  421. }
  422. static void menu_cb_getE (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  423. Melder_informationReal (my endSelection, my v_format_units ());
  424. }
  425. static void menu_cb_getSelectionDuration (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  426. Melder_informationReal (my endSelection - my startSelection, my v_format_units ());
  427. }
  428. /********** VIEW MENU **********/
  429. static void menu_cb_zoom (FunctionEditor me, EDITOR_ARGS_FORM) {
  430. EDITOR_FORM (U"Zoom", nullptr)
  431. REAL (from, U"From", U"0.0")
  432. REAL (to, U"To", U"1.0")
  433. EDITOR_OK
  434. SET_REAL (from, my startWindow)
  435. SET_REAL (to, my endWindow)
  436. EDITOR_DO
  437. my startWindow = from;
  438. if (my startWindow < my tmin + 1e-12)
  439. my startWindow = my tmin;
  440. my endWindow = to;
  441. if (my endWindow > my tmax - 1e-12)
  442. my endWindow = my tmax;
  443. my v_updateText ();
  444. updateScrollBar (me);
  445. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  446. updateGroup (me);
  447. EDITOR_END
  448. }
  449. static void do_showAll (FunctionEditor me) {
  450. my startWindow = my tmin;
  451. my endWindow = my tmax;
  452. my v_updateText ();
  453. updateScrollBar (me);
  454. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  455. if (my pref_synchronizedZoomAndScroll ()) {
  456. updateGroup (me);
  457. }
  458. }
  459. static void gui_button_cb_showAll (FunctionEditor me, GuiButtonEvent /* event */) {
  460. do_showAll (me);
  461. }
  462. static void do_zoomIn (FunctionEditor me) {
  463. double shift = (my endWindow - my startWindow) / 4;
  464. my startWindow += shift;
  465. my endWindow -= shift;
  466. my v_updateText ();
  467. updateScrollBar (me);
  468. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  469. if (my pref_synchronizedZoomAndScroll ()) {
  470. updateGroup (me);
  471. }
  472. }
  473. static void gui_button_cb_zoomIn (FunctionEditor me, GuiButtonEvent /* event */) {
  474. do_zoomIn (me);
  475. }
  476. static void do_zoomOut (FunctionEditor me) {
  477. double shift = (my endWindow - my startWindow) / 2;
  478. MelderAudio_stopPlaying (MelderAudio_IMPLICIT); // quickly, before window changes
  479. my startWindow -= shift;
  480. if (my startWindow < my tmin + 1e-12)
  481. my startWindow = my tmin;
  482. my endWindow += shift;
  483. if (my endWindow > my tmax - 1e-12)
  484. my endWindow = my tmax;
  485. my v_updateText ();
  486. updateScrollBar (me);
  487. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  488. if (my pref_synchronizedZoomAndScroll ()) {
  489. updateGroup (me);
  490. }
  491. }
  492. static void gui_button_cb_zoomOut (FunctionEditor me, GuiButtonEvent /*event*/) {
  493. do_zoomOut (me);
  494. }
  495. static void do_zoomToSelection (FunctionEditor me) {
  496. if (my endSelection > my startSelection) {
  497. my startZoomHistory = my startWindow; // remember for Zoom Back
  498. my endZoomHistory = my endWindow; // remember for Zoom Back
  499. trace (U"Zooming in to ", my startSelection, U" ~ ", my endSelection, U" seconds.");
  500. my startWindow = my startSelection;
  501. my endWindow = my endSelection;
  502. trace (U"Zoomed in to ", my startWindow, U" ~ ", my endWindow, U" seconds (1).");
  503. my v_updateText ();
  504. trace (U"Zoomed in to ", my startWindow, U" ~ ", my endWindow, U" seconds (2).");
  505. updateScrollBar (me);
  506. trace (U"Zoomed in to ", my startWindow, U" ~ ", my endWindow, U" seconds (3).");
  507. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  508. if (my pref_synchronizedZoomAndScroll ()) {
  509. updateGroup (me);
  510. }
  511. trace (U"Zoomed in to ", my startWindow, U" ~ ", my endWindow, U" seconds (4).");
  512. }
  513. }
  514. static void gui_button_cb_zoomToSelection (FunctionEditor me, GuiButtonEvent /* event */) {
  515. do_zoomToSelection (me);
  516. }
  517. static void do_zoomBack (FunctionEditor me) {
  518. if (my endZoomHistory > my startZoomHistory) {
  519. my startWindow = my startZoomHistory;
  520. my endWindow = my endZoomHistory;
  521. my v_updateText ();
  522. updateScrollBar (me);
  523. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  524. if (my pref_synchronizedZoomAndScroll ()) {
  525. updateGroup (me);
  526. }
  527. }
  528. }
  529. static void gui_button_cb_zoomBack (FunctionEditor me, GuiButtonEvent /* event */) {
  530. do_zoomBack (me);
  531. }
  532. static void menu_cb_showAll (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  533. do_showAll (me);
  534. }
  535. static void menu_cb_zoomIn (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  536. do_zoomIn (me);
  537. }
  538. static void menu_cb_zoomOut (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  539. do_zoomOut (me);
  540. }
  541. static void menu_cb_zoomToSelection (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  542. do_zoomToSelection (me);
  543. }
  544. static void menu_cb_zoomBack (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  545. do_zoomBack (me);
  546. }
  547. static void menu_cb_play (FunctionEditor me, EDITOR_ARGS_FORM) {
  548. EDITOR_FORM (U"Play", nullptr)
  549. REAL (from, U"From", U"0.0")
  550. REAL (to, U"To", U"1.0")
  551. EDITOR_OK
  552. SET_REAL (from, my startWindow)
  553. SET_REAL (to, my endWindow)
  554. EDITOR_DO
  555. MelderAudio_stopPlaying (MelderAudio_IMPLICIT);
  556. my v_play (from, to);
  557. EDITOR_END
  558. }
  559. static void menu_cb_playOrStop (FunctionEditor me, EDITOR_ARGS_FORM) {
  560. if (MelderAudio_isPlaying) {
  561. MelderAudio_stopPlaying (MelderAudio_EXPLICIT);
  562. } else if (my startSelection < my endSelection) {
  563. my playingSelection = true;
  564. my v_play (my startSelection, my endSelection);
  565. } else {
  566. my playingCursor = true;
  567. if (my startSelection == my endSelection && my startSelection > my startWindow && my startSelection < my endWindow)
  568. my v_play (my startSelection, my endWindow);
  569. else
  570. my v_play (my startWindow, my endWindow);
  571. }
  572. }
  573. static void menu_cb_playWindow (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  574. MelderAudio_stopPlaying (MelderAudio_IMPLICIT);
  575. my playingCursor = true;
  576. my v_play (my startWindow, my endWindow);
  577. }
  578. static void menu_cb_interruptPlaying (FunctionEditor /* me */, EDITOR_ARGS_DIRECT) {
  579. MelderAudio_stopPlaying (MelderAudio_IMPLICIT);
  580. }
  581. /********** SELECT MENU **********/
  582. static void menu_cb_select (FunctionEditor me, EDITOR_ARGS_FORM) {
  583. EDITOR_FORM (U"Select", nullptr)
  584. REAL (startOfSelection, U"Start of selection", U"0.0")
  585. REAL (endOfSelection, U"End of selection", U"1.0")
  586. EDITOR_OK
  587. SET_REAL (startOfSelection, my startSelection)
  588. SET_REAL (endOfSelection, my endSelection)
  589. EDITOR_DO
  590. my startSelection = startOfSelection;
  591. if (my startSelection < my tmin + 1e-12)
  592. my startSelection = my tmin;
  593. my endSelection = endOfSelection;
  594. if (my endSelection > my tmax - 1e-12)
  595. my endSelection = my tmax;
  596. if (my startSelection > my endSelection) {
  597. double dummy = my startSelection;
  598. my startSelection = my endSelection;
  599. my endSelection = dummy;
  600. }
  601. my v_updateText ();
  602. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  603. updateGroup (me);
  604. EDITOR_END
  605. }
  606. static void menu_cb_moveCursorToB (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  607. my endSelection = my startSelection;
  608. my v_updateText ();
  609. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  610. updateGroup (me);
  611. }
  612. static void menu_cb_moveCursorToE (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  613. my startSelection = my endSelection;
  614. my v_updateText ();
  615. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  616. updateGroup (me);
  617. }
  618. static void menu_cb_moveCursorTo (FunctionEditor me, EDITOR_ARGS_FORM) {
  619. EDITOR_FORM (U"Move cursor to", nullptr)
  620. REAL (position, U"Position", U"0.0")
  621. EDITOR_OK
  622. SET_REAL (position, 0.5 * (my startSelection + my endSelection))
  623. EDITOR_DO
  624. if (position < my tmin + 1e-12) position = my tmin;
  625. if (position > my tmax - 1e-12) position = my tmax;
  626. my startSelection = my endSelection = position;
  627. my v_updateText ();
  628. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  629. updateGroup (me);
  630. EDITOR_END
  631. }
  632. static void menu_cb_moveCursorBy (FunctionEditor me, EDITOR_ARGS_FORM) {
  633. EDITOR_FORM (U"Move cursor by", nullptr)
  634. REAL (distance, U"Distance", U"0.05")
  635. EDITOR_OK
  636. EDITOR_DO
  637. double position = 0.5 * (my startSelection + my endSelection) + distance;
  638. if (position < my tmin) position = my tmin;
  639. if (position > my tmax) position = my tmax;
  640. my startSelection = my endSelection = position;
  641. my v_updateText ();
  642. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  643. updateGroup (me);
  644. EDITOR_END
  645. }
  646. static void menu_cb_moveBby (FunctionEditor me, EDITOR_ARGS_FORM) {
  647. EDITOR_FORM (U"Move start of selection by", nullptr)
  648. REAL (distance, U"Distance", U"0.05")
  649. EDITOR_OK
  650. EDITOR_DO
  651. double position = my startSelection + distance;
  652. if (position < my tmin) position = my tmin;
  653. if (position > my tmax) position = my tmax;
  654. my startSelection = position;
  655. if (my startSelection > my endSelection) {
  656. double dummy = my startSelection;
  657. my startSelection = my endSelection;
  658. my endSelection = dummy;
  659. }
  660. my v_updateText ();
  661. /*Graphics_updateWs (my graphics,get());*/ drawNow (me);
  662. updateGroup (me);
  663. EDITOR_END
  664. }
  665. static void menu_cb_moveEby (FunctionEditor me, EDITOR_ARGS_FORM) {
  666. EDITOR_FORM (U"Move end of selection by", nullptr)
  667. REAL (distance, U"Distance", U"0.05")
  668. EDITOR_OK
  669. EDITOR_DO
  670. double position = my endSelection + distance;
  671. if (position < my tmin) position = my tmin;
  672. if (position > my tmax) position = my tmax;
  673. my endSelection = position;
  674. if (my startSelection > my endSelection) {
  675. double dummy = my startSelection;
  676. my startSelection = my endSelection;
  677. my endSelection = dummy;
  678. }
  679. my v_updateText ();
  680. /*Graphics_updateWs (my graphics.get());*/ drawNow (me);
  681. updateGroup (me);
  682. EDITOR_END
  683. }
  684. void FunctionEditor_shift (FunctionEditor me, double shift, bool needsUpdateGroup) {
  685. double windowLength = my endWindow - my startWindow;
  686. MelderAudio_stopPlaying (MelderAudio_IMPLICIT); // quickly, before window changes
  687. trace (U"shifting by ", shift);
  688. if (shift < 0.0) {
  689. my startWindow += shift;
  690. if (my startWindow < my tmin + 1e-12)
  691. my startWindow = my tmin;
  692. my endWindow = my startWindow + windowLength;
  693. if (my endWindow > my tmax - 1e-12)
  694. my endWindow = my tmax;
  695. } else {
  696. my endWindow += shift;
  697. if (my endWindow > my tmax - 1e-12)
  698. my endWindow = my tmax;
  699. my startWindow = my endWindow - windowLength;
  700. if (my startWindow < my tmin + 1e-12)
  701. my startWindow = my tmin;
  702. }
  703. FunctionEditor_marksChanged (me, needsUpdateGroup);
  704. }
  705. static void menu_cb_pageUp (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  706. FunctionEditor_shift (me, -RELATIVE_PAGE_INCREMENT * (my endWindow - my startWindow), true);
  707. }
  708. static void menu_cb_pageDown (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  709. FunctionEditor_shift (me, +RELATIVE_PAGE_INCREMENT * (my endWindow - my startWindow), true);
  710. }
  711. static void scrollToView (FunctionEditor me, double t) {
  712. if (t <= my startWindow) {
  713. FunctionEditor_shift (me, t - my startWindow - 0.618 * (my endWindow - my startWindow), true);
  714. } else if (t >= my endWindow) {
  715. FunctionEditor_shift (me, t - my endWindow + 0.618 * (my endWindow - my startWindow), true);
  716. } else {
  717. FunctionEditor_marksChanged (me, true);
  718. }
  719. }
  720. static void menu_cb_selectEarlier (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  721. my startSelection -= my p_arrowScrollStep;
  722. if (my startSelection < my tmin + 1e-12)
  723. my startSelection = my tmin;
  724. my endSelection -= my p_arrowScrollStep;
  725. if (my endSelection < my tmin + 1e-12)
  726. my endSelection = my tmin;
  727. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  728. }
  729. static void menu_cb_selectLater (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  730. my startSelection += my p_arrowScrollStep;
  731. if (my startSelection > my tmax - 1e-12)
  732. my startSelection = my tmax;
  733. my endSelection += my p_arrowScrollStep;
  734. if (my endSelection > my tmax - 1e-12)
  735. my endSelection = my tmax;
  736. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  737. }
  738. static void menu_cb_moveBleft (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  739. my startSelection -= my p_arrowScrollStep;
  740. if (my startSelection < my tmin + 1e-12)
  741. my startSelection = my tmin;
  742. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  743. }
  744. static void menu_cb_moveBright (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  745. my startSelection += my p_arrowScrollStep;
  746. if (my startSelection > my tmax - 1e-12)
  747. my startSelection = my tmax;
  748. if (my startSelection > my endSelection) {
  749. double dummy = my startSelection;
  750. my startSelection = my endSelection;
  751. my endSelection = dummy;
  752. }
  753. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  754. }
  755. static void menu_cb_moveEleft (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  756. my endSelection -= my p_arrowScrollStep;
  757. if (my endSelection < my tmin + 1e-12)
  758. my endSelection = my tmin;
  759. if (my startSelection > my endSelection) {
  760. double dummy = my startSelection;
  761. my startSelection = my endSelection;
  762. my endSelection = dummy;
  763. }
  764. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  765. }
  766. static void menu_cb_moveEright (FunctionEditor me, EDITOR_ARGS_DIRECT) {
  767. my endSelection += my p_arrowScrollStep;
  768. if (my endSelection > my tmax - 1e-12)
  769. my endSelection = my tmax;
  770. scrollToView (me, 0.5 * (my startSelection + my endSelection));
  771. }
  772. /********** GUI CALLBACKS **********/
  773. static void gui_cb_scroll (FunctionEditor me, GuiScrollBarEvent event) {
  774. if (! my graphics) return; // ignore events during creation
  775. double value = GuiScrollBar_getValue (event -> scrollBar);
  776. double shift = my tmin + (value - 1) * (my tmax - my tmin) / maximumScrollBarValue - my startWindow;
  777. bool shifted = shift != 0.0;
  778. double oldSliderSize = (my endWindow - my startWindow) / (my tmax - my tmin) * maximumScrollBarValue - 1.0;
  779. double newSliderSize = GuiScrollBar_getSliderSize (event -> scrollBar);
  780. bool zoomed = newSliderSize != oldSliderSize;
  781. #if ! cocoa
  782. zoomed = false;
  783. #endif
  784. if (shifted) {
  785. my startWindow += shift;
  786. if (my startWindow < my tmin + 1e-12) my startWindow = my tmin;
  787. my endWindow += shift;
  788. if (my endWindow > my tmax - 1e-12) my endWindow = my tmax;
  789. }
  790. if (zoomed) {
  791. double zoom = (newSliderSize + 1) * (my tmax - my tmin) / maximumScrollBarValue;
  792. my endWindow = my startWindow + zoom;
  793. if (my endWindow > my tmax - 1e-12) my endWindow = my tmax;
  794. }
  795. if (shifted || zoomed) {
  796. my v_updateText ();
  797. updateScrollBar (me);
  798. #if cocoa
  799. Graphics_updateWs (my graphics.get());
  800. #else
  801. /*Graphics_clearWs (my graphics.get());*/
  802. drawNow (me); // do not wait for expose event
  803. #endif
  804. if (! my group || ! my pref_synchronizedZoomAndScroll ()) return;
  805. for (int i = 1; i <= maxGroup; i ++) if (theGroup [i] && theGroup [i] != me) {
  806. theGroup [i] -> startWindow = my startWindow;
  807. theGroup [i] -> endWindow = my endWindow;
  808. FunctionEditor_updateText (theGroup [i]);
  809. updateScrollBar (theGroup [i]);
  810. #if cocoa
  811. Graphics_updateWs (theGroup [i] -> graphics.get());
  812. #else
  813. Graphics_clearWs (theGroup [i] -> graphics.get());
  814. drawNow (theGroup [i]);
  815. #endif
  816. }
  817. }
  818. }
  819. static void gui_checkbutton_cb_group (FunctionEditor me, GuiCheckButtonEvent /* event */) {
  820. int i;
  821. my group = ! my group;
  822. if (my group) {
  823. FunctionEditor thee;
  824. i = 1; while (theGroup [i]) i ++; theGroup [i] = me;
  825. if (++ nGroup == 1) { Graphics_updateWs (my graphics.get()); return; }
  826. i = 1; while (! theGroup [i] || theGroup [i] == me) i ++; thee = theGroup [i];
  827. if (my pref_synchronizedZoomAndScroll ()) {
  828. my startWindow = thy startWindow;
  829. my endWindow = thy endWindow;
  830. }
  831. my startSelection = thy startSelection;
  832. my endSelection = thy endSelection;
  833. if (my tmin > thy tmin || my tmax < thy tmax) {
  834. if (my tmin > thy tmin) my tmin = thy tmin;
  835. if (my tmax < thy tmax) my tmax = thy tmax;
  836. my v_updateText ();
  837. updateScrollBar (me);
  838. Graphics_updateWs (my graphics.get());
  839. } else {
  840. my v_updateText ();
  841. updateScrollBar (me);
  842. Graphics_updateWs (my graphics.get());
  843. if (my tmin < thy tmin || my tmax > thy tmax)
  844. for (i = 1; i <= maxGroup; i ++) if (theGroup [i] && theGroup [i] != me) {
  845. if (my tmin < thy tmin)
  846. theGroup [i] -> tmin = my tmin;
  847. if (my tmax > thy tmax)
  848. theGroup [i] -> tmax = my tmax;
  849. FunctionEditor_updateText (theGroup [i]);
  850. updateScrollBar (theGroup [i]);
  851. Graphics_updateWs (theGroup [i] -> graphics.get());
  852. }
  853. }
  854. } else {
  855. i = 1; while (theGroup [i] != me) i ++; theGroup [i] = nullptr;
  856. nGroup --;
  857. my v_updateText ();
  858. Graphics_updateWs (my graphics.get()); // for setting buttons in draw method
  859. }
  860. if (my group) updateGroup (me);
  861. }
  862. static void menu_cb_intro (FunctionEditor /* me */, EDITOR_ARGS_DIRECT) {
  863. Melder_help (U"Intro");
  864. }
  865. void structFunctionEditor :: v_createMenuItems_file (EditorMenu menu) {
  866. FunctionEditor_Parent :: v_createMenuItems_file (menu);
  867. EditorMenu_addCommand (menu, U"Preferences...", 0, menu_cb_preferences);
  868. EditorMenu_addCommand (menu, U"-- after preferences --", 0, nullptr);
  869. }
  870. void structFunctionEditor :: v_createMenuItems_view_timeDomain (EditorMenu menu) {
  871. EditorMenu_addCommand (menu, v_format_domain (), GuiMenu_INSENSITIVE, menu_cb_zoom /* dummy */);
  872. EditorMenu_addCommand (menu, U"Zoom...", 0, menu_cb_zoom);
  873. EditorMenu_addCommand (menu, U"Show all", 'A', menu_cb_showAll);
  874. EditorMenu_addCommand (menu, U"Zoom in", 'I', menu_cb_zoomIn);
  875. EditorMenu_addCommand (menu, U"Zoom out", 'O', menu_cb_zoomOut);
  876. EditorMenu_addCommand (menu, U"Zoom to selection", 'N', menu_cb_zoomToSelection);
  877. EditorMenu_addCommand (menu, U"Zoom back", 'B', menu_cb_zoomBack);
  878. EditorMenu_addCommand (menu, U"Scroll page back", GuiMenu_PAGE_UP, menu_cb_pageUp);
  879. EditorMenu_addCommand (menu, U"Scroll page forward", GuiMenu_PAGE_DOWN, menu_cb_pageDown);
  880. }
  881. void structFunctionEditor :: v_createMenuItems_view_audio (EditorMenu menu) {
  882. EditorMenu_addCommand (menu, U"-- play --", 0, nullptr);
  883. EditorMenu_addCommand (menu, U"Audio:", GuiMenu_INSENSITIVE, menu_cb_play /* dummy */);
  884. EditorMenu_addCommand (menu, U"Play...", 0, menu_cb_play);
  885. EditorMenu_addCommand (menu, U"Play or stop", GuiMenu_TAB, menu_cb_playOrStop);
  886. EditorMenu_addCommand (menu, U"Play window", GuiMenu_SHIFT | GuiMenu_TAB, menu_cb_playWindow);
  887. EditorMenu_addCommand (menu, U"Interrupt playing", GuiMenu_ESCAPE, menu_cb_interruptPlaying);
  888. }
  889. void structFunctionEditor :: v_createMenuItems_view (EditorMenu menu) {
  890. v_createMenuItems_view_timeDomain (menu);
  891. v_createMenuItems_view_audio (menu);
  892. }
  893. void structFunctionEditor :: v_createMenuItems_query (EditorMenu menu) {
  894. FunctionEditor_Parent :: v_createMenuItems_query (menu);
  895. EditorMenu_addCommand (menu, U"-- query selection --", 0, nullptr);
  896. EditorMenu_addCommand (menu, U"Get start of selection", 0, menu_cb_getB);
  897. EditorMenu_addCommand (menu, U"Get begin of selection", Editor_HIDDEN, menu_cb_getB);
  898. EditorMenu_addCommand (menu, U"Get cursor", GuiMenu_F6, menu_cb_getCursor);
  899. EditorMenu_addCommand (menu, U"Get end of selection", 0, menu_cb_getE);
  900. EditorMenu_addCommand (menu, U"Get selection length", 0, menu_cb_getSelectionDuration);
  901. }
  902. void structFunctionEditor :: v_createMenus () {
  903. FunctionEditor_Parent :: v_createMenus ();
  904. EditorMenu menu;
  905. menu = Editor_addMenu (this, U"View", 0);
  906. v_createMenuItems_view (menu);
  907. Editor_addMenu (this, U"Select", 0);
  908. Editor_addCommand (this, U"Select", U"Select...", 0, menu_cb_select);
  909. Editor_addCommand (this, U"Select", U"Move cursor to start of selection", 0, menu_cb_moveCursorToB);
  910. Editor_addCommand (this, U"Select", U"Move cursor to begin of selection", Editor_HIDDEN, menu_cb_moveCursorToB);
  911. Editor_addCommand (this, U"Select", U"Move cursor to end of selection", 0, menu_cb_moveCursorToE);
  912. Editor_addCommand (this, U"Select", U"Move cursor to...", 0, menu_cb_moveCursorTo);
  913. Editor_addCommand (this, U"Select", U"Move cursor by...", 0, menu_cb_moveCursorBy);
  914. Editor_addCommand (this, U"Select", U"Move start of selection by...", 0, menu_cb_moveBby);
  915. Editor_addCommand (this, U"Select", U"Move begin of selection by...", Editor_HIDDEN, menu_cb_moveBby);
  916. Editor_addCommand (this, U"Select", U"Move end of selection by...", 0, menu_cb_moveEby);
  917. /*Editor_addCommand (this, U"Select", U"Move cursor back by half a second", motif_, menu_cb_moveCursorBy);*/
  918. Editor_addCommand (this, U"Select", U"Select earlier", GuiMenu_UP_ARROW, menu_cb_selectEarlier);
  919. Editor_addCommand (this, U"Select", U"Select later", GuiMenu_DOWN_ARROW, menu_cb_selectLater);
  920. Editor_addCommand (this, U"Select", U"Move start of selection left", GuiMenu_SHIFT | GuiMenu_UP_ARROW, menu_cb_moveBleft);
  921. Editor_addCommand (this, U"Select", U"Move begin of selection left", Editor_HIDDEN, menu_cb_moveBleft);
  922. Editor_addCommand (this, U"Select", U"Move start of selection right", GuiMenu_SHIFT | GuiMenu_DOWN_ARROW, menu_cb_moveBright);
  923. Editor_addCommand (this, U"Select", U"Move begin of selection right", Editor_HIDDEN, menu_cb_moveBright);
  924. Editor_addCommand (this, U"Select", U"Move end of selection left", GuiMenu_COMMAND | GuiMenu_UP_ARROW, menu_cb_moveEleft);
  925. Editor_addCommand (this, U"Select", U"Move end of selection right", GuiMenu_COMMAND | GuiMenu_DOWN_ARROW, menu_cb_moveEright);
  926. }
  927. void structFunctionEditor :: v_createHelpMenuItems (EditorMenu menu) {
  928. FunctionEditor_Parent :: v_createHelpMenuItems (menu);
  929. EditorMenu_addCommand (menu, U"Intro", 0, menu_cb_intro);
  930. }
  931. static void gui_drawingarea_cb_expose (FunctionEditor me, GuiDrawingArea_ExposeEvent /* event */) {
  932. if (! my graphics) return; // could be the case in the very beginning
  933. if (my enableUpdates)
  934. drawNow (me);
  935. }
  936. static void gui_drawingarea_cb_click (FunctionEditor me, GuiDrawingArea_ClickEvent event) {
  937. if (! my graphics) return; // could be the case in the very beginning
  938. my shiftKeyPressed = event -> shiftKeyPressed;
  939. Graphics_setWindow (my graphics.get(), my functionViewerLeft, my selectionViewerRight, 0.0, my height);
  940. double xWC, yWC;
  941. Graphics_DCtoWC (my graphics.get(), event -> x, event -> y, & xWC, & yWC);
  942. if (xWC > my selectionViewerLeft)
  943. {
  944. Graphics_setViewport (my graphics.get(), my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN,
  945. BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  946. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  947. Graphics_DCtoWC (my graphics.get(), event -> x, event -> y, & xWC, & yWC);
  948. my v_clickSelectionViewer (xWC, yWC);
  949. //my v_updateText ();
  950. drawNow (me);
  951. updateGroup (me);
  952. }
  953. else if (yWC > BOTTOM_MARGIN + space * 3 && yWC < my height - (TOP_MARGIN + space)) { // in signal region?
  954. bool needsUpdate;
  955. Graphics_setViewport (my graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN,
  956. BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
  957. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, 0.0, 1.0);
  958. Graphics_DCtoWC (my graphics.get(), event -> x, event -> y, & xWC, & yWC);
  959. if (xWC < my startWindow) xWC = my startWindow;
  960. if (xWC > my endWindow) xWC = my endWindow;
  961. if (Melder_debug == 24) {
  962. Melder_casual (U"FunctionEditor::gui_drawingarea_cb_click:"
  963. U" button ", event -> button,
  964. U" shift ", my shiftKeyPressed,
  965. U" option ", event -> optionKeyPressed,
  966. U" command ", event -> commandKeyPressed,
  967. U" control ", event -> extraControlKeyPressed);
  968. }
  969. #if defined (macintosh)
  970. needsUpdate =
  971. event -> optionKeyPressed || event -> extraControlKeyPressed ? my v_clickB (xWC, yWC) :
  972. event -> commandKeyPressed ? my v_clickE (xWC, yWC) :
  973. my v_click (xWC, yWC, my shiftKeyPressed);
  974. #elif defined (_WIN32)
  975. needsUpdate =
  976. event -> commandKeyPressed ? my v_clickB (xWC, yWC) :
  977. event -> optionKeyPressed ? my v_clickE (xWC, yWC) :
  978. my v_click (xWC, yWC, my shiftKeyPressed);
  979. #else
  980. needsUpdate =
  981. event -> commandKeyPressed ? my v_clickB (xWC, yWC) :
  982. event -> optionKeyPressed ? my v_clickE (xWC, yWC) :
  983. event -> button == 1 ? my v_click (xWC, yWC, my shiftKeyPressed) :
  984. event -> button == 2 ? my v_clickB (xWC, yWC) : my v_clickE (xWC, yWC);
  985. #endif
  986. if (needsUpdate) my v_updateText ();
  987. Graphics_setViewport (my graphics.get(), my functionViewerLeft, my functionViewerRight, 0, my height);
  988. if (needsUpdate) {
  989. drawNow (me);
  990. }
  991. if (needsUpdate) updateGroup (me);
  992. }
  993. else // clicked outside signal region? Let us hear it
  994. {
  995. try {
  996. for (int i = 0; i < 8; i ++) {
  997. if (xWC > my rect [i]. left && xWC < my rect [i]. right &&
  998. yWC > my rect [i]. bottom && yWC < my rect [i]. top)
  999. switch (i) {
  1000. case 0: my v_play (my tmin, my tmax); break;
  1001. case 1: my v_play (my startWindow, my endWindow); break;
  1002. case 2: my v_play (my tmin, my startWindow); break;
  1003. case 3: my v_play (my endWindow, my tmax); break;
  1004. case 4: my v_play (my startWindow, my marker [1]); break;
  1005. case 5: my v_play (my marker [1], my marker [2]); break;
  1006. case 6: my v_play (my marker [2], my marker [3]); break;
  1007. case 7: my v_play (my startSelection, my endSelection); break;
  1008. }
  1009. }
  1010. } catch (MelderError) {
  1011. Melder_flushError ();
  1012. }
  1013. }
  1014. }
  1015. void structFunctionEditor :: v_createChildren () {
  1016. int x = BUTTON_X;
  1017. /***** Create zoom buttons. *****/
  1018. GuiButton_createShown (our windowForm, x, x + BUTTON_WIDTH, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1019. U"all", gui_button_cb_showAll, this, 0);
  1020. x += BUTTON_WIDTH + BUTTON_SPACING;
  1021. GuiButton_createShown (our windowForm, x, x + BUTTON_WIDTH, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1022. U"in", gui_button_cb_zoomIn, this, 0);
  1023. x += BUTTON_WIDTH + BUTTON_SPACING;
  1024. GuiButton_createShown (our windowForm, x, x + BUTTON_WIDTH, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1025. U"out", gui_button_cb_zoomOut, this, 0);
  1026. x += BUTTON_WIDTH + BUTTON_SPACING;
  1027. GuiButton_createShown (our windowForm, x, x + BUTTON_WIDTH, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1028. U"sel", gui_button_cb_zoomToSelection, this, 0);
  1029. x += BUTTON_WIDTH + BUTTON_SPACING;
  1030. GuiButton_createShown (our windowForm, x, x + BUTTON_WIDTH, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1031. U"bak", gui_button_cb_zoomBack, this, 0);
  1032. /***** Create scroll bar. *****/
  1033. our scrollBar = GuiScrollBar_createShown (our windowForm,
  1034. x += BUTTON_WIDTH + BUTTON_SPACING, -80 - BUTTON_SPACING, -4 - Gui_PUSHBUTTON_HEIGHT, 0,
  1035. 1, maximumScrollBarValue, 1, maximumScrollBarValue - 1, 1, 1,
  1036. gui_cb_scroll, this, GuiScrollBar_HORIZONTAL);
  1037. /***** Create Group button. *****/
  1038. our groupButton = GuiCheckButton_createShown (our windowForm, -80, 0, -4 - Gui_PUSHBUTTON_HEIGHT, -4,
  1039. U"Group", gui_checkbutton_cb_group, this, group_equalDomain (our tmin, our tmax) ? GuiCheckButton_SET : 0);
  1040. /***** Create optional text field. *****/
  1041. if (our v_hasText ()) {
  1042. our text = GuiText_createShown (our windowForm, 0, 0,
  1043. Machine_getMenuBarHeight (),
  1044. Machine_getMenuBarHeight () + TEXT_HEIGHT, GuiText_WORDWRAP | GuiText_MULTILINE);
  1045. #if gtk
  1046. Melder_assert (our text -> d_widget);
  1047. gtk_widget_grab_focus (GTK_WIDGET (our text -> d_widget)); // BUG: can hardly be correct (the text should grab the focus of the window, not the global focus)
  1048. #elif cocoa
  1049. Melder_assert ([(NSView *) our text -> d_widget window]);
  1050. //[[(NSView *) our text -> d_widget window] setInitialFirstResponder: (NSView *) our text -> d_widget];
  1051. [[(NSView *) our text -> d_widget window] makeFirstResponder: (NSView *) our text -> d_widget];
  1052. #endif
  1053. }
  1054. /***** Create drawing area. *****/
  1055. #if cocoa
  1056. int marginBetweenTextAndDrawingAreaToEnsureCorrectUnhighlighting = 3;
  1057. #else
  1058. int marginBetweenTextAndDrawingAreaToEnsureCorrectUnhighlighting = 0;
  1059. #endif
  1060. our drawingArea = GuiDrawingArea_createShown (our windowForm,
  1061. 0, 0,
  1062. Machine_getMenuBarHeight () + ( our v_hasText () ? TEXT_HEIGHT + marginBetweenTextAndDrawingAreaToEnsureCorrectUnhighlighting : 0), -8 - Gui_PUSHBUTTON_HEIGHT,
  1063. gui_drawingarea_cb_expose, gui_drawingarea_cb_click, nullptr, gui_drawingarea_cb_resize, this, 0);
  1064. GuiDrawingArea_setSwipable (our drawingArea, our scrollBar, nullptr);
  1065. }
  1066. void structFunctionEditor :: v_dataChanged () {
  1067. Function function = (Function) our data;
  1068. Melder_assert (Thing_isa (function, classFunction));
  1069. our tmin = function -> xmin;
  1070. our tmax = function -> xmax;
  1071. if (our startWindow < our tmin || our startWindow > our tmax) our startWindow = our tmin;
  1072. if (our endWindow < our tmin || our endWindow > our tmax) our endWindow = our tmax;
  1073. if (our startWindow >= our endWindow) { our startWindow = our tmin; our endWindow = our tmax; }
  1074. if (our startSelection < our tmin) our startSelection = our tmin;
  1075. if (our startSelection > our tmax) our startSelection = our tmax;
  1076. if (our endSelection < our tmin) our endSelection = our tmin;
  1077. if (our endSelection > our tmax) our endSelection = our tmax;
  1078. FunctionEditor_marksChanged (this, false);
  1079. }
  1080. static void drawWhileDragging (FunctionEditor me, double x1, double x2) {
  1081. /*
  1082. * We must draw this within the window, because the window tends to have a white background.
  1083. * We cannot draw this in the margins, because these tend to be grey, so that Graphics_xorOn does not work properly.
  1084. * We draw the text twice, because we expect that not ALL of the window is white...
  1085. */
  1086. double xleft, xright;
  1087. if (x1 > x2) xleft = x2, xright = x1; else xleft = x1, xright = x2;
  1088. Graphics_xorOn (my graphics.get(), Graphics_MAROON);
  1089. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_TOP);
  1090. Graphics_text (my graphics.get(), xleft, 1.0, Melder_fixed (xleft, 6));
  1091. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_TOP);
  1092. Graphics_text (my graphics.get(), xright, 1.0, Melder_fixed (xright, 6));
  1093. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_BOTTOM);
  1094. Graphics_text (my graphics.get(), xleft, 0.0, Melder_fixed (xleft, 6));
  1095. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_BOTTOM);
  1096. Graphics_text (my graphics.get(), xright, 0.0, Melder_fixed (xright, 6));
  1097. Graphics_setLineType (my graphics.get(), Graphics_DOTTED);
  1098. Graphics_line (my graphics.get(), xleft, 0.0, xleft, 1.0);
  1099. Graphics_line (my graphics.get(), xright, 0.0, xright, 1.0);
  1100. Graphics_setLineType (my graphics.get(), Graphics_DRAWN);
  1101. Graphics_xorOff (my graphics.get());
  1102. }
  1103. bool structFunctionEditor :: v_click (double xbegin, double ybegin, bool a_shiftKeyPressed) {
  1104. bool drag = false;
  1105. double x = xbegin, y = ybegin;
  1106. /*
  1107. * The 'anchor' is the point that will stay fixed during dragging.
  1108. * For instance, if she clicks and drags to the right,
  1109. * the location at which she originally clicked will be the anchor,
  1110. * even if she later chooses to drag the mouse to the left of it.
  1111. * Another example: if she shift-clicks near E, B will become (and stay) the anchor.
  1112. */
  1113. Graphics_setWindow (our graphics.get(), our startWindow, our endWindow, 0.0, 1.0);
  1114. double anchorForDragging = xbegin; // the default (for if the shift key isn't pressed)
  1115. if (a_shiftKeyPressed) {
  1116. /*
  1117. * Extend the selection.
  1118. * We should always end up with a real selection (B < E),
  1119. * even if we start with the reversed temporal order (E < B).
  1120. */
  1121. bool reversed = our startSelection > our endSelection;
  1122. double firstMark = reversed ? our endSelection : our startSelection;
  1123. double secondMark = reversed ? our startSelection : our endSelection;
  1124. /*
  1125. * Undraw the old selection.
  1126. */
  1127. if (our endSelection > our startSelection) {
  1128. /*
  1129. * Determine the visible part of the old selection.
  1130. */
  1131. double startVisible = our startSelection > our startWindow ? our startSelection : our startWindow;
  1132. double endVisible = our endSelection < our endWindow ? our endSelection : our endWindow;
  1133. /*
  1134. * Undraw the visible part of the old selection.
  1135. */
  1136. if (endVisible > startVisible) {
  1137. v_unhighlightSelection (startVisible, endVisible, 0, 1);
  1138. //Graphics_flushWs (our graphics.get());
  1139. }
  1140. }
  1141. if (xbegin >= secondMark) {
  1142. /*
  1143. * She clicked right from the second mark (usually E). We move E.
  1144. */
  1145. our endSelection = xbegin;
  1146. anchorForDragging = our startSelection;
  1147. } else if (xbegin <= firstMark) {
  1148. /*
  1149. * She clicked left from the first mark (usually B). We move B.
  1150. */
  1151. our startSelection = xbegin;
  1152. anchorForDragging = our endSelection;
  1153. } else {
  1154. /*
  1155. * She clicked in between the two marks. We move the nearest mark.
  1156. */
  1157. double distanceOfClickToFirstMark = fabs (xbegin - firstMark);
  1158. double distanceOfClickToSecondMark = fabs (xbegin - secondMark);
  1159. /*
  1160. * We make sure that the marks are in the unmarked B - E order.
  1161. */
  1162. if (reversed) {
  1163. /*
  1164. * Swap B and E.
  1165. */
  1166. our startSelection = firstMark;
  1167. our endSelection = secondMark;
  1168. }
  1169. /*
  1170. * Move the nearest mark.
  1171. */
  1172. if (distanceOfClickToFirstMark < distanceOfClickToSecondMark) {
  1173. our startSelection = xbegin;
  1174. anchorForDragging = our endSelection;
  1175. } else {
  1176. our endSelection = xbegin;
  1177. anchorForDragging = our startSelection;
  1178. }
  1179. }
  1180. /*
  1181. * Draw the new selection.
  1182. */
  1183. if (our endSelection > our startSelection) {
  1184. /*
  1185. * Determine the visible part of the new selection.
  1186. */
  1187. double startVisible = our startSelection > our startWindow ? our startSelection : our startWindow;
  1188. double endVisible = our endSelection < our endWindow ? our endSelection : our endWindow;
  1189. /*
  1190. * Draw the visible part of the new selection.
  1191. */
  1192. if (endVisible > startVisible)
  1193. v_highlightSelection (startVisible, endVisible, 0.0, 1.0);
  1194. }
  1195. }
  1196. /*
  1197. * Find out whether this is a click or a drag.
  1198. */
  1199. while (Graphics_mouseStillDown (our graphics.get())) {
  1200. Graphics_getMouseLocation (our graphics.get(), & x, & y);
  1201. if (x < our startWindow) x = our startWindow;
  1202. if (x > our endWindow) x = our endWindow;
  1203. if (fabs (Graphics_dxWCtoMM (our graphics.get(), x - xbegin)) > 1.5) {
  1204. drag = true;
  1205. break;
  1206. }
  1207. }
  1208. if (drag) {
  1209. /*
  1210. * First undraw the old selection.
  1211. */
  1212. if (our endSelection > our startSelection) {
  1213. /*
  1214. * Determine the visible part of the old selection.
  1215. */
  1216. double startVisible = our startSelection > our startWindow ? our startSelection : our startWindow;
  1217. double endVisible = our endSelection < our endWindow ? our endSelection : our endWindow;
  1218. /*
  1219. * Undraw the visible part of the old selection.
  1220. */
  1221. if (endVisible > startVisible)
  1222. v_unhighlightSelection (startVisible, endVisible, 0.0, 1.0);
  1223. }
  1224. /*
  1225. * Draw the text at least once.
  1226. */
  1227. /*if (x < our startWindow) x = our startWindow; else if (x > our endWindow) x = our endWindow;*/
  1228. drawWhileDragging (this, anchorForDragging, x);
  1229. /*
  1230. * Draw the dragged selection at least once.
  1231. */
  1232. {
  1233. double x1, x2;
  1234. if (x > anchorForDragging) x1 = anchorForDragging, x2 = x; else x1 = x, x2 = anchorForDragging;
  1235. v_highlightSelection (x1, x2, 0.0, 1.0);
  1236. }
  1237. /*
  1238. * Drag for the new selection.
  1239. */
  1240. while (Graphics_mouseStillDown (our graphics.get()))
  1241. {
  1242. double xold = x, x1, x2;
  1243. Graphics_getMouseLocation (our graphics.get(), & x, & y);
  1244. /*
  1245. * Clip to the visible window. Ideally, we should perform autoscrolling instead, though...
  1246. */
  1247. if (x < our startWindow) x = our startWindow; else if (x > our endWindow) x = our endWindow;
  1248. if (x == xold)
  1249. continue;
  1250. /*
  1251. * Undraw previous dragged selection.
  1252. */
  1253. if (xold > anchorForDragging) x1 = anchorForDragging, x2 = xold; else x1 = xold, x2 = anchorForDragging;
  1254. if (x1 != x2) v_unhighlightSelection (x1, x2, 0.0, 1.0);
  1255. /*
  1256. * Undraw the text.
  1257. */
  1258. drawWhileDragging (this, anchorForDragging, xold);
  1259. /*
  1260. * Redraw the text at the new location.
  1261. */
  1262. drawWhileDragging (this, anchorForDragging, x);
  1263. /*
  1264. * Draw new dragged selection.
  1265. */
  1266. if (x > anchorForDragging) x1 = anchorForDragging, x2 = x; else x1 = x, x2 = anchorForDragging;
  1267. if (x1 != x2) v_highlightSelection (x1, x2, 0.0, 1.0);
  1268. } ;
  1269. /*
  1270. * Set the new selection.
  1271. */
  1272. if (x > anchorForDragging) our startSelection = anchorForDragging, our endSelection = x;
  1273. else our startSelection = x, our endSelection = anchorForDragging;
  1274. } else if (! a_shiftKeyPressed) {
  1275. /*
  1276. * Move the cursor to the clicked position.
  1277. */
  1278. our startSelection = our endSelection = xbegin;
  1279. }
  1280. return FunctionEditor_UPDATE_NEEDED;
  1281. }
  1282. bool structFunctionEditor :: v_clickB (double xWC, double /* yWC */) {
  1283. our startSelection = xWC;
  1284. if (our startSelection > our endSelection) {
  1285. double dummy = our startSelection;
  1286. our startSelection = our endSelection;
  1287. our endSelection = dummy;
  1288. }
  1289. return FunctionEditor_UPDATE_NEEDED;
  1290. }
  1291. bool structFunctionEditor :: v_clickE (double xWC, double /* yWC */) {
  1292. our endSelection = xWC;
  1293. if (our startSelection > our endSelection) {
  1294. double dummy = our startSelection;
  1295. our startSelection = our endSelection;
  1296. our endSelection = dummy;
  1297. }
  1298. return FunctionEditor_UPDATE_NEEDED;
  1299. }
  1300. void structFunctionEditor :: v_clickSelectionViewer (double /* xWC */, double /* yWC */) {
  1301. }
  1302. int structFunctionEditor :: v_playCallback (int phase, double /* a_tmin */, double a_tmax, double t) {
  1303. /*
  1304. * This callback will often be called by the Melder workproc during playback.
  1305. * However, it will sometimes be called by Melder_stopPlaying with phase=3.
  1306. * This will occur at unpredictable times, perhaps when the LongSound is updated.
  1307. * So we had better make no assumptions about the current viewport.
  1308. */
  1309. double x1NDC, x2NDC, y1NDC, y2NDC;
  1310. Graphics_inqViewport (our graphics.get(), & x1NDC, & x2NDC, & y1NDC, & y2NDC);
  1311. Graphics_setViewport (our graphics.get(),
  1312. our functionViewerLeft + MARGIN, our functionViewerRight - MARGIN,
  1313. BOTTOM_MARGIN + space * 3, our height - (TOP_MARGIN + space));
  1314. Graphics_setWindow (our graphics.get(), our startWindow, our endWindow, 0.0, 1.0);
  1315. Graphics_xorOn (our graphics.get(), Graphics_MAROON);
  1316. /*
  1317. * Undraw the play cursor at its old location.
  1318. * BUG: during scrolling, zooming, and exposure, an ugly line may remain.
  1319. */
  1320. if (phase != 1 && playCursor >= our startWindow && playCursor <= our endWindow) {
  1321. Graphics_setLineWidth (our graphics.get(), 3.0);
  1322. Graphics_line (our graphics.get(), playCursor, 0.0, playCursor, 1.0);
  1323. Graphics_setLineWidth (our graphics.get(), 1.0);
  1324. }
  1325. /*
  1326. * Draw the play cursor at its new location.
  1327. */
  1328. if (phase != 3 && t >= our startWindow && t <= our endWindow) {
  1329. Graphics_setLineWidth (our graphics.get(), 3.0);
  1330. Graphics_line (our graphics.get(), t, 0.0, t, 1.0);
  1331. Graphics_setLineWidth (our graphics.get(), 1.0);
  1332. }
  1333. Graphics_xorOff (our graphics.get());
  1334. if (our p_showSelectionViewer) {
  1335. Graphics_setViewport (our graphics.get(),
  1336. our selectionViewerLeft + MARGIN, our selectionViewerRight - MARGIN,
  1337. BOTTOM_MARGIN + space * 3, our height - (TOP_MARGIN + space));
  1338. our v_drawRealTimeSelectionViewer (phase, t);
  1339. }
  1340. /*
  1341. * Usually, there will be an event test after each invocation of this callback,
  1342. * because the asynchronicity is kMelder_asynchronicityLevel_INTERRUPTABLE or kMelder_asynchronicityLevel_ASYNCHRONOUS.
  1343. * However, if the asynchronicity is just kMelder_asynchronicityLevel_CALLING_BACK,
  1344. * there is no event test. Which means: no server round trip.
  1345. * Which means: no automatic flushing of graphics output.
  1346. * So: we force the flushing ourselves, lest we see too few moving cursors.
  1347. *
  1348. * At the moment, Cocoa seems to require this flushing even if the asynchronicity is kMelder_asynchronicityLevel_ASYNCHRONOUS.
  1349. */
  1350. Graphics_flushWs (our graphics.get());
  1351. Graphics_setViewport (our graphics.get(), x1NDC, x2NDC, y1NDC, y2NDC);
  1352. playCursor = t;
  1353. if (phase == 3) {
  1354. if (t < a_tmax && MelderAudio_stopWasExplicit ()) {
  1355. if (t > our startSelection && t < our endSelection)
  1356. our startSelection = t;
  1357. else
  1358. our startSelection = our endSelection = t;
  1359. v_updateText ();
  1360. /*Graphics_updateWs (our graphics);*/ drawNow (this);
  1361. updateGroup (this);
  1362. }
  1363. playingCursor = false;
  1364. playingSelection = false;
  1365. }
  1366. return 1;
  1367. }
  1368. int theFunctionEditor_playCallback (FunctionEditor me, int phase, double a_tmin, double a_tmax, double t) {
  1369. return my v_playCallback (phase, a_tmin, a_tmax, t);
  1370. }
  1371. void structFunctionEditor :: v_highlightSelection (double left, double right, double bottom, double top) {
  1372. Graphics_highlight (our graphics.get(), left, right, bottom, top);
  1373. }
  1374. void structFunctionEditor :: v_unhighlightSelection (double left, double right, double bottom, double top) {
  1375. Graphics_unhighlight (our graphics.get(), left, right, bottom, top);
  1376. }
  1377. void FunctionEditor_init (FunctionEditor me, conststring32 title, Function data) {
  1378. my tmin = data -> xmin; // set before adding children (see group button)
  1379. my tmax = data -> xmax;
  1380. Editor_init (me, 0, 0, my pref_shellWidth (), my pref_shellHeight (), title, data);
  1381. my startWindow = my tmin;
  1382. my endWindow = my tmax;
  1383. my startSelection = my endSelection = 0.5 * (my tmin + my tmax);
  1384. #if motif
  1385. Melder_assert (XtWindow (my drawingArea -> d_widget));
  1386. #endif
  1387. my graphics = Graphics_create_xmdrawingarea (my drawingArea);
  1388. Graphics_setFontSize (my graphics.get(), 12);
  1389. // This exdents because it's a hack:
  1390. struct structGuiDrawingArea_ResizeEvent event { my drawingArea, 0, 0 };
  1391. event. width = GuiControl_getWidth (my drawingArea);
  1392. event. height = GuiControl_getHeight (my drawingArea);
  1393. gui_drawingarea_cb_resize (me, & event);
  1394. my v_updateText ();
  1395. if (group_equalDomain (my tmin, my tmax))
  1396. gui_checkbutton_cb_group (me, nullptr); // BUG: nullptr
  1397. my enableUpdates = true;
  1398. }
  1399. void FunctionEditor_marksChanged (FunctionEditor me, bool needsUpdateGroup) {
  1400. my v_updateText ();
  1401. updateScrollBar (me);
  1402. /*Graphics_updateWs (my graphics);*/ drawNow (me);
  1403. if (needsUpdateGroup)
  1404. updateGroup (me);
  1405. }
  1406. void FunctionEditor_updateText (FunctionEditor me) {
  1407. my v_updateText ();
  1408. }
  1409. void FunctionEditor_redraw (FunctionEditor me) {
  1410. //Graphics_updateWs (my graphics);
  1411. drawNow (me);
  1412. }
  1413. void FunctionEditor_enableUpdates (FunctionEditor me, bool enable) {
  1414. my enableUpdates = enable;
  1415. }
  1416. void FunctionEditor_ungroup (FunctionEditor me) {
  1417. if (! my group) return;
  1418. my group = false;
  1419. GuiCheckButton_setValue (my groupButton, false);
  1420. int i = 1;
  1421. while (theGroup [i] != me) i ++;
  1422. theGroup [i] = nullptr;
  1423. nGroup --;
  1424. my v_updateText ();
  1425. Graphics_updateWs (my graphics.get()); // for setting buttons in v_draw() method
  1426. }
  1427. void FunctionEditor_drawRangeMark (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units, int verticalAlignment) {
  1428. static MelderString text { };
  1429. MelderString_copy (& text, yWC_string, units);
  1430. double textWidth = Graphics_textWidth (my graphics.get(), text.string) + Graphics_dxMMtoWC (my graphics.get(), 0.5);
  1431. Graphics_setColour (my graphics.get(), Graphics_BLUE);
  1432. Graphics_line (my graphics.get(), my endWindow, yWC, my endWindow + textWidth, yWC);
  1433. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, verticalAlignment);
  1434. if (verticalAlignment == Graphics_BOTTOM)
  1435. yWC -= Graphics_dyMMtoWC (my graphics.get(), 0.5);
  1436. Graphics_text (my graphics.get(), my endWindow, yWC, text.string);
  1437. }
  1438. void FunctionEditor_drawCursorFunctionValue (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units) {
  1439. Graphics_setColour (my graphics.get(), Graphics_CYAN);
  1440. Graphics_line (my graphics.get(), my startWindow, yWC, 0.99 * my startWindow + 0.01 * my endWindow, yWC);
  1441. Graphics_fillCircle_mm (my graphics.get(), 0.5 * (my startSelection + my endSelection), yWC, 1.5);
  1442. Graphics_setColour (my graphics.get(), Graphics_BLUE);
  1443. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  1444. Graphics_text (my graphics.get(), my startWindow, yWC, yWC_string, units);
  1445. }
  1446. void FunctionEditor_insertCursorFunctionValue (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units, double minimum, double maximum) {
  1447. double textX = my endWindow, textY = yWC;
  1448. int tooHigh = Graphics_dyWCtoMM (my graphics.get(), maximum - textY) < 5.0;
  1449. int tooLow = Graphics_dyWCtoMM (my graphics.get(), textY - minimum) < 5.0;
  1450. if (yWC < minimum || yWC > maximum) return;
  1451. Graphics_setColour (my graphics.get(), Graphics_CYAN);
  1452. Graphics_line (my graphics.get(), 0.99 * my endWindow + 0.01 * my startWindow, yWC, my endWindow, yWC);
  1453. Graphics_fillCircle_mm (my graphics.get(), 0.5 * (my startSelection + my endSelection), yWC, 1.5);
  1454. if (tooHigh) {
  1455. if (tooLow) textY = 0.5 * (minimum + maximum);
  1456. else textY = maximum - Graphics_dyMMtoWC (my graphics.get(), 5.0);
  1457. } else if (tooLow) {
  1458. textY = minimum + Graphics_dyMMtoWC (my graphics.get(), 5.0);
  1459. }
  1460. static MelderString text { };
  1461. MelderString_copy (& text, yWC_string, units);
  1462. double textWidth = Graphics_textWidth (my graphics.get(), text.string);
  1463. Graphics_fillCircle_mm (my graphics.get(), my endWindow + textWidth + Graphics_dxMMtoWC (my graphics.get(), 1.5), textY, 1.5);
  1464. Graphics_setColour (my graphics.get(), Graphics_RED);
  1465. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
  1466. Graphics_text (my graphics.get(), textX, textY, text.string);
  1467. }
  1468. void FunctionEditor_drawHorizontalHair (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units) {
  1469. Graphics_setColour (my graphics.get(), Graphics_RED);
  1470. Graphics_line (my graphics.get(), my startWindow, yWC, my endWindow, yWC);
  1471. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  1472. Graphics_text (my graphics.get(), my startWindow, yWC, yWC_string, units);
  1473. }
  1474. void FunctionEditor_drawGridLine (FunctionEditor me, double yWC) {
  1475. Graphics_setColour (my graphics.get(), Graphics_CYAN);
  1476. Graphics_setLineType (my graphics.get(), Graphics_DOTTED);
  1477. Graphics_line (my graphics.get(), my startWindow, yWC, my endWindow, yWC);
  1478. Graphics_setLineType (my graphics.get(), Graphics_DRAWN);
  1479. }
  1480. void FunctionEditor_garnish (FunctionEditor me) {
  1481. if (my pref_picture_drawSelectionTimes ()) {
  1482. if (my startSelection >= my startWindow && my startSelection <= my endWindow)
  1483. Graphics_markTop (my pictureGraphics, my startSelection, true, true, false, nullptr);
  1484. if (my endSelection != my startSelection && my endSelection >= my startWindow && my endSelection <= my endWindow)
  1485. Graphics_markTop (my pictureGraphics, my endSelection, true, true, false, nullptr);
  1486. }
  1487. if (my pref_picture_drawSelectionHairs ()) {
  1488. if (my startSelection >= my startWindow && my startSelection <= my endWindow)
  1489. Graphics_markTop (my pictureGraphics, my startSelection, false, false, true, nullptr);
  1490. if (my endSelection != my startSelection && my endSelection >= my startWindow && my endSelection <= my endWindow)
  1491. Graphics_markTop (my pictureGraphics, my endSelection, false, false, true, nullptr);
  1492. }
  1493. }
  1494. /* End of file FunctionEditor.cpp */