TableEditor.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. /* TableEditor.cpp
  2. *
  3. * Copyright (C) 2006-2013,2015-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 "TableEditor.h"
  19. #include "machine.h"
  20. #include "EditorM.h"
  21. Thing_implement (TableEditor, Editor, 0);
  22. #include "prefs_define.h"
  23. #include "TableEditor_prefs.h"
  24. #include "prefs_install.h"
  25. #include "TableEditor_prefs.h"
  26. #include "prefs_copyToInstance.h"
  27. #include "TableEditor_prefs.h"
  28. #define SIZE_INCHES 40
  29. /********** EDITOR METHODS **********/
  30. void structTableEditor :: v_destroy () noexcept {
  31. TableEditor_Parent :: v_destroy ();
  32. }
  33. void structTableEditor :: v_info () {
  34. our TableEditor_Parent :: v_info ();
  35. MelderInfo_writeLine (U"Table uses text styles: ", our p_useTextStyles);
  36. //MelderInfo_writeLine (U"Table font size: ", our p_fontSize);
  37. }
  38. static void updateVerticalScrollBar (TableEditor me) {
  39. Table table = static_cast<Table> (my data);
  40. GuiScrollBar_set (my verticalScrollBar, undefined, table -> rows.size + 1, my topRow, undefined, undefined, undefined);
  41. }
  42. static void updateHorizontalScrollBar (TableEditor me) {
  43. Table table = static_cast<Table> (my data);
  44. GuiScrollBar_set (my horizontalScrollBar, undefined, table -> numberOfColumns + 1, my leftColumn, undefined, undefined, undefined);
  45. }
  46. void structTableEditor :: v_dataChanged () {
  47. Table table = static_cast<Table> (our data);
  48. if (topRow > table -> rows.size) topRow = table -> rows.size;
  49. if (leftColumn > table -> numberOfColumns) leftColumn = table -> numberOfColumns;
  50. updateVerticalScrollBar (this);
  51. updateHorizontalScrollBar (this);
  52. Graphics_updateWs (our graphics.get());
  53. }
  54. /********** FILE MENU **********/
  55. static void menu_cb_preferences (TableEditor me, EDITOR_ARGS_FORM) {
  56. EDITOR_FORM (U"TableEditor preferences", nullptr);
  57. OPTIONMENU (useTextStyles, U"The symbols %#_^ in labels", my default_useTextStyles () + 1)
  58. OPTION (U"are shown as typed")
  59. OPTION (U"mean italic/bold/sub/super")
  60. EDITOR_OK
  61. SET_OPTION (useTextStyles, my p_useTextStyles + 1)
  62. EDITOR_DO
  63. my pref_useTextStyles () = my p_useTextStyles = useTextStyles - 1;
  64. Graphics_updateWs (my graphics.get());
  65. EDITOR_END
  66. }
  67. /********** EDIT MENU **********/
  68. #ifndef macintosh
  69. /*
  70. On macOS, Cut/Copy/Paste are already available in the Praat:Edit menu.
  71. */
  72. static void menu_cb_CutText (TableEditor me, EDITOR_ARGS_DIRECT) {
  73. GuiText_cut (my text);
  74. }
  75. static void menu_cb_CopyText (TableEditor me, EDITOR_ARGS_DIRECT) {
  76. GuiText_copy (my text);
  77. }
  78. static void menu_cb_PasteText (TableEditor me, EDITOR_ARGS_DIRECT) {
  79. GuiText_paste (my text);
  80. }
  81. static void menu_cb_EraseText (TableEditor me, EDITOR_ARGS_DIRECT) {
  82. GuiText_remove (my text);
  83. }
  84. #endif
  85. /********** VIEW MENU **********/
  86. /********** HELP MENU **********/
  87. static void menu_cb_TableEditorHelp (TableEditor, EDITOR_ARGS_DIRECT) {
  88. Melder_help (U"TableEditor");
  89. }
  90. /********** DRAWING AREA **********/
  91. void structTableEditor :: v_draw () {
  92. Table table = static_cast<Table> (data);
  93. double spacing = 2.0; // millimetres at both edges
  94. double columnWidth, cellWidth;
  95. /*
  96. * We fit 200 rows in 40 inches, which is 14.4 points per row.
  97. */
  98. integer rowmin = topRow, rowmax = rowmin + 197;
  99. integer colmin = leftColumn, colmax = colmin + (kTableEditor_MAXNUM_VISIBLE_COLUMNS - 1);
  100. if (rowmax > table -> rows.size) rowmax = table -> rows.size;
  101. if (colmax > table -> numberOfColumns) colmax = table -> numberOfColumns;
  102. Graphics_clearWs (graphics.get());
  103. Graphics_setTextAlignment (graphics.get(), Graphics_CENTRE, Graphics_HALF);
  104. Graphics_setWindow (graphics.get(), 0.0, 1.0, rowmin + 197.5, rowmin - 2.5);
  105. Graphics_setColour (graphics.get(), Graphics_SILVER);
  106. Graphics_fillRectangle (graphics.get(), 0.0, 1.0, rowmin - 2.5, rowmin - 0.5);
  107. Graphics_setColour (graphics.get(), Graphics_BLACK);
  108. Graphics_line (graphics.get(), 0.0, rowmin - 0.5, 1.0, rowmin - 0.5);
  109. Graphics_setWindow (graphics.get(), 0.0, Graphics_dxWCtoMM (graphics.get(), 1.0), rowmin + 197.5, rowmin - 2.5);
  110. /*
  111. * Determine the width of the column with the row numbers.
  112. */
  113. columnWidth = Graphics_textWidth (graphics.get(), U"row");
  114. for (integer irow = rowmin; irow <= rowmax; irow ++) {
  115. cellWidth = Graphics_textWidth (graphics.get(), Melder_integer (irow));
  116. if (cellWidth > columnWidth) columnWidth = cellWidth;
  117. }
  118. columnLeft [0] = columnWidth + 2 * spacing;
  119. Graphics_setColour (graphics.get(), Graphics_SILVER);
  120. Graphics_fillRectangle (graphics.get(), 0.0, columnLeft [0], rowmin - 0.5, rowmin + 197.5);
  121. Graphics_setColour (graphics.get(), Graphics_BLACK);
  122. Graphics_line (graphics.get(), columnLeft [0], rowmin - 0.5, columnLeft [0], rowmin + 197.5);
  123. /*
  124. * Determine the width of the columns.
  125. */
  126. for (integer icol = colmin; icol <= colmax; icol ++) {
  127. conststring32 columnLabel = table -> columnHeaders [icol]. label.get();
  128. columnWidth = Graphics_textWidth (graphics.get(), Melder_integer (icol));
  129. if (! columnLabel) columnLabel = U"";
  130. cellWidth = Graphics_textWidth (graphics.get(), columnLabel);
  131. if (cellWidth > columnWidth) columnWidth = cellWidth;
  132. for (integer irow = rowmin; irow <= rowmax; irow ++) {
  133. conststring32 cell = Table_getStringValue_Assert (table, irow, icol);
  134. Melder_assert (cell);
  135. if (cell [0] == U'\0') cell = U"?";
  136. cellWidth = Graphics_textWidth (graphics.get(), cell);
  137. if (cellWidth > columnWidth) columnWidth = cellWidth;
  138. }
  139. columnRight [icol - colmin] = columnLeft [icol - colmin] + columnWidth + 2 * spacing;
  140. if (icol < colmax) columnLeft [icol - colmin + 1] = columnRight [icol - colmin];
  141. }
  142. /*
  143. Text can be "graphic" or not.
  144. */
  145. Graphics_setPercentSignIsItalic (our graphics.get(), our p_useTextStyles);
  146. Graphics_setNumberSignIsBold (our graphics.get(), our p_useTextStyles);
  147. Graphics_setCircumflexIsSuperscript (our graphics.get(), our p_useTextStyles);
  148. Graphics_setUnderscoreIsSubscript (our graphics.get(), our p_useTextStyles);
  149. /*
  150. * Show the row numbers.
  151. */
  152. Graphics_text (graphics.get(), columnLeft [0] / 2, rowmin - 1, U"row");
  153. for (integer irow = rowmin; irow <= rowmax; irow ++) {
  154. Graphics_text (graphics.get(), columnLeft [0] / 2, irow, irow);
  155. }
  156. /*
  157. * Show the column labels.
  158. */
  159. for (integer icol = colmin; icol <= colmax; icol ++) {
  160. double mid = (columnLeft [icol - colmin] + columnRight [icol - colmin]) / 2;
  161. conststring32 columnLabel = table -> columnHeaders [icol]. label.get();
  162. if (! columnLabel || columnLabel [0] == U'\0') columnLabel = U"?";
  163. Graphics_text (graphics.get(), mid, rowmin - 2, icol);
  164. Graphics_text (graphics.get(), mid, rowmin - 1, columnLabel);
  165. }
  166. /*
  167. * Show the cell contents.
  168. */
  169. for (integer irow = rowmin; irow <= rowmax; irow ++) {
  170. for (integer icol = colmin; icol <= colmax; icol ++) {
  171. if (irow == selectedRow && icol == selectedColumn) {
  172. Graphics_setColour (graphics.get(), Graphics_YELLOW);
  173. double dx = Graphics_dxMMtoWC (graphics.get(), 0.3);
  174. Graphics_fillRectangle (graphics.get(),
  175. columnLeft [icol - colmin] + dx, columnRight [icol - colmin] - dx, irow - 0.45, irow + 0.55);
  176. Graphics_setColour (graphics.get(), Graphics_BLACK);
  177. }
  178. double mid = (columnLeft [icol - colmin] + columnRight [icol - colmin]) / 2;
  179. conststring32 cell = Table_getStringValue_Assert (table, irow, icol);
  180. Melder_assert (cell);
  181. if (cell [0] == U'\0') cell = U"?";
  182. Graphics_text (graphics.get(), mid, irow, cell);
  183. }
  184. }
  185. }
  186. bool structTableEditor :: v_clickCell (integer row, integer column, bool /* shiftKeyPressed */) {
  187. Table table = static_cast<Table> (our data);
  188. our selectedRow = row;
  189. our selectedColumn = column;
  190. return true;
  191. }
  192. static void gui_text_cb_changed (TableEditor me, GuiTextEvent /* event */) {
  193. Editor_broadcastDataChanged (me);
  194. }
  195. static void gui_drawingarea_cb_expose (TableEditor me, GuiDrawingArea_ExposeEvent /* event */) {
  196. if (! my graphics) return;
  197. my v_draw ();
  198. }
  199. static void gui_drawingarea_cb_click (TableEditor me, GuiDrawingArea_ClickEvent event) {
  200. Table table = static_cast<Table> (my data);
  201. if (! my graphics) return; // could be the case in the very beginning
  202. integer rowmin = my topRow, rowmax = rowmin + 197;
  203. integer colmin = my leftColumn, colmax = colmin + (kTableEditor_MAXNUM_VISIBLE_COLUMNS - 1);
  204. if (rowmax > table -> rows.size) rowmax = table -> rows.size;
  205. if (colmax > table -> numberOfColumns) colmax = table -> numberOfColumns;
  206. double xWC, yWC;
  207. Graphics_DCtoWC (my graphics.get(), event -> x, event -> y, & xWC, & yWC);
  208. if (yWC < rowmin - 0.45 || yWC > rowmax + 0.55)
  209. return;
  210. for (integer icol = colmin; icol <= colmax; icol ++) {
  211. if (xWC > my columnLeft [icol - colmin] && xWC < my columnRight [icol - colmin]) {
  212. integer selectedRow = Melder_iround (yWC);
  213. integer selectedColumn = icol;
  214. if (my v_clickCell (selectedRow, selectedColumn, event -> shiftKeyPressed))
  215. Graphics_updateWs (my graphics.get());
  216. return;
  217. }
  218. }
  219. }
  220. static void gui_drawingarea_cb_resize (TableEditor me, GuiDrawingArea_ResizeEvent /* event */) {
  221. if (! my graphics) return;
  222. Graphics_updateWs (my graphics.get());
  223. }
  224. static void gui_cb_scrollHorizontal (TableEditor me, GuiScrollBarEvent event) {
  225. integer value = GuiScrollBar_getValue (event -> scrollBar);
  226. if (value != my leftColumn) {
  227. my leftColumn = value;
  228. #if cocoa || gtk || motif
  229. Graphics_updateWs (my graphics.get()); // wait for expose event
  230. #else
  231. Graphics_clearWs (my graphics.get());
  232. my v_draw (); // do not wait for expose event
  233. #endif
  234. }
  235. }
  236. static void gui_cb_scrollVertical (TableEditor me, GuiScrollBarEvent event) {
  237. integer value = GuiScrollBar_getValue (event -> scrollBar);
  238. if (value != my topRow) {
  239. my topRow = value;
  240. #if cocoa || gtk || motif
  241. Graphics_updateWs (my graphics.get()); // wait for expose event
  242. #else
  243. Graphics_clearWs (my graphics.get());
  244. my v_draw (); // do not wait for expose event
  245. #endif
  246. }
  247. }
  248. void structTableEditor :: v_createChildren () {
  249. Table table = static_cast<Table> (data);
  250. int y = Machine_getMenuBarHeight () + 4, scrollWidth = Machine_getScrollBarWidth ();
  251. our text = GuiText_createShown (our windowForm, 0, 0, y, y + Machine_getTextHeight (), 0);
  252. GuiText_setChangedCallback (our text, gui_text_cb_changed, this);
  253. y += Machine_getTextHeight () + 4;
  254. our drawingArea = GuiDrawingArea_createShown (our windowForm, 0, - scrollWidth, y, - scrollWidth,
  255. gui_drawingarea_cb_expose, gui_drawingarea_cb_click, NULL, gui_drawingarea_cb_resize, this, 0);
  256. our verticalScrollBar = GuiScrollBar_createShown (our windowForm, - scrollWidth, 0, y, - scrollWidth,
  257. 1, table -> rows.size + 1, 1, 1, 1, 10, gui_cb_scrollVertical, this, 0);
  258. our horizontalScrollBar = GuiScrollBar_createShown (our windowForm, 0, - scrollWidth, - scrollWidth, 0,
  259. 1, table -> numberOfColumns + 1, 1, 1, 1, 3, gui_cb_scrollHorizontal, this, GuiScrollBar_HORIZONTAL);
  260. GuiDrawingArea_setSwipable (our drawingArea, our horizontalScrollBar, our verticalScrollBar);
  261. }
  262. void structTableEditor :: v_createMenus () {
  263. TableEditor_Parent :: v_createMenus ();
  264. Editor_addCommand (this, U"File", U"Preferences...", 0, menu_cb_preferences);
  265. Editor_addCommand (this, U"File", U"-- before scripting --", 0, nullptr);
  266. #ifndef macintosh
  267. Editor_addCommand (this, U"Edit", U"-- cut copy paste text --", 0, nullptr);
  268. Editor_addCommand (this, U"Edit", U"Cut text", 'X', menu_cb_CutText);
  269. Editor_addCommand (this, U"Edit", U"Copy text", 'C', menu_cb_CopyText);
  270. Editor_addCommand (this, U"Edit", U"Paste text", 'V', menu_cb_PasteText);
  271. Editor_addCommand (this, U"Edit", U"Erase text", 0, menu_cb_EraseText);
  272. #endif
  273. }
  274. void structTableEditor :: v_createHelpMenuItems (EditorMenu menu) {
  275. TableEditor_Parent :: v_createHelpMenuItems (menu);
  276. EditorMenu_addCommand (menu, U"TableEditor help", U'?', menu_cb_TableEditorHelp);
  277. }
  278. autoTableEditor TableEditor_create (conststring32 title, Table table) {
  279. try {
  280. autoTableEditor me = Thing_new (TableEditor);
  281. Editor_init (me.get(), 0, 0, 700, 500, title, table);
  282. #if motif
  283. Melder_assert (XtWindow (my drawingArea -> d_widget));
  284. #endif
  285. my topRow = 1;
  286. my leftColumn = 1;
  287. my selectedColumn = 1;
  288. my selectedRow = 1;
  289. my graphics = Graphics_create_xmdrawingarea (my drawingArea);
  290. double size_pixels = SIZE_INCHES * Graphics_getResolution (my graphics.get());
  291. Graphics_setWsViewport (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
  292. Graphics_setWsWindow (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
  293. Graphics_setViewport (my graphics.get(), 0.0, size_pixels, 0.0, size_pixels);
  294. Graphics_setFont (my graphics.get(), kGraphics_font::COURIER);
  295. Graphics_setFontSize (my graphics.get(), 12);
  296. Graphics_setUnderscoreIsSubscript (my graphics.get(), false);
  297. Graphics_setAtSignIsLink (my graphics.get(), true);
  298. return me;
  299. } catch (MelderError) {
  300. Melder_throw (U"TableEditor not created.");
  301. }
  302. }
  303. /* End of file TableEditor.cpp */