DataEditor.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. /* DataEditor.cpp
  2. *
  3. * Copyright (C) 1995-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. #define NAME_X 30
  19. #define TEXT_X 250
  20. #define BUTTON_X 250
  21. #define LIST_Y (2 * Gui_TOP_DIALOG_SPACING + Gui_PUSHBUTTON_HEIGHT)
  22. #define EDITOR_WIDTH 820
  23. #define EDITOR_HEIGHT (LIST_Y + kDataSubEditor_MAXNUM_ROWS * ROW_HEIGHT + 29 + Machine_getMenuBarHeight ())
  24. #define ROW_HEIGHT 31
  25. #define SCROLL_BAR_WIDTH Machine_getScrollBarWidth ()
  26. #include "DataEditor.h"
  27. #include "EditorM.h"
  28. #include "Collection.h"
  29. #include "machine.h"
  30. static Data_Description Class_getDescription (ClassInfo table) {
  31. return ((Daata) _Thing_dummyObject (table)) -> v_description ();
  32. }
  33. static void VectorEditor_create (DataEditor root, conststring32 title, void *address,
  34. Data_Description description, integer minimum, integer maximum);
  35. static void MatrixEditor_create (DataEditor root, conststring32 title, void *address,
  36. Data_Description description, integer min1, integer max1, integer min2, integer max2);
  37. static void StructEditor_create (DataEditor root, conststring32 title, void *address, Data_Description description);
  38. static void ClassEditor_create (DataEditor root, conststring32 title, void *address, Data_Description description);
  39. static inline conststring32 strip_d (conststring32 s) {
  40. return s && s [0] == U'd' && s [1] == U'_' ? & s [2] : & s [0];
  41. }
  42. /********** DataSubEditor **********/
  43. Thing_implement (DataSubEditor, Editor, 0);
  44. void structDataSubEditor :: v_destroy () noexcept {
  45. //for (int i = 1; i <= kDataSubEditor_MAXNUM_ROWS; i ++)
  46. // Melder_free (d_fieldData [i]. history);
  47. if (our root)
  48. for (int i = our root -> children.size; i > 0; i --)
  49. if (our root -> children.at [i] == this)
  50. our root -> children.subtractItem_ref (i);
  51. DataSubEditor_Parent :: v_destroy ();
  52. }
  53. static void update (DataSubEditor me) {
  54. /* Hide all the existing widgets. */
  55. for (int i = 1; i <= kDataSubEditor_MAXNUM_ROWS; i ++) {
  56. my d_fieldData [i]. address = nullptr;
  57. my d_fieldData [i]. description = nullptr;
  58. GuiThing_hide (my d_fieldData [i]. label);
  59. GuiThing_hide (my d_fieldData [i]. button);
  60. GuiThing_hide (my d_fieldData [i]. text);
  61. }
  62. my d_irow = 0;
  63. my v_showMembers ();
  64. }
  65. static Data_Description DataSubEditor_findNumberUse (DataSubEditor me, conststring32 number) {
  66. Data_Description structDescription, result;
  67. char32 string [100];
  68. if (my classInfo == classMatrixEditor) return nullptr; // no structs inside
  69. if (my classInfo == classVectorEditor) {
  70. if (my d_description -> type != structwa) return nullptr; // no structs inside
  71. structDescription = * (Data_Description *) my d_description -> tagType;
  72. } else { /* StructEditor or ClassEditor or DataEditor. */
  73. structDescription = my d_description;
  74. }
  75. Melder_sprint (string,100, number);
  76. if ((result = Data_Description_findNumberUse (structDescription, string)) != nullptr) return result;
  77. Melder_sprint (string,100, number, U" - 1");
  78. if ((result = Data_Description_findNumberUse (structDescription, string)) != nullptr) return result;
  79. return nullptr;
  80. }
  81. static void gui_button_cb_change (DataSubEditor me, GuiButtonEvent /* event */) {
  82. int irow = 1;
  83. for (; irow <= kDataSubEditor_MAXNUM_ROWS; irow ++) {
  84. #if motif
  85. bool visible = XtIsManaged (my d_fieldData [irow]. text -> d_widget);
  86. #elif gtk
  87. gboolean visible;
  88. g_object_get (G_OBJECT (my d_fieldData [irow]. text), "visible", & visible, nullptr);
  89. #elif defined (macintosh)
  90. bool visible = ! [(GuiCocoaTextField *) my d_fieldData [irow]. text -> d_widget isHidden];
  91. #else
  92. bool visible = false;
  93. #endif
  94. if (visible) {
  95. int type = my d_fieldData [irow]. description -> type;
  96. if (type > maxsingletypewa) continue;
  97. autostring32 text = GuiText_getString (my d_fieldData [irow]. text);
  98. switch (type) {
  99. case bytewa: {
  100. signed char oldValue = * (signed char *) my d_fieldData [irow]. address, newValue = (signed char) Melder_atoi (text.get());
  101. if (newValue != oldValue) {
  102. Data_Description numberUse = DataSubEditor_findNumberUse (me, my d_fieldData [irow]. description -> name);
  103. if (numberUse) {
  104. Melder_flushError (U"Changing field \"", strip_d (my d_fieldData [irow]. description -> name),
  105. U"\" would damage the array \"", strip_d (numberUse -> name), U"\".");
  106. } else {
  107. * (signed char *) my d_fieldData [irow]. address = newValue;
  108. }
  109. }
  110. } break;
  111. case int16wa: {
  112. int16 oldValue = * (int16 *) my d_fieldData [irow]. address;
  113. int64 newValue = Melder_atoi (text.get());
  114. if (newValue != oldValue) {
  115. Data_Description numberUse = DataSubEditor_findNumberUse (me, my d_fieldData [irow]. description -> name);
  116. if (numberUse) {
  117. Melder_flushError (U"Changing field \"", strip_d (my d_fieldData [irow]. description -> name),
  118. U"\" would damage the array \"", strip_d (numberUse -> name), U"\".");
  119. } else if (newValue < INT16_MIN || newValue > INT16_MAX) {
  120. Melder_flushError (U"Field \"", strip_d (my d_fieldData [irow]. description -> name),
  121. U"\" can have no values less than ", INT16_MIN, U" or greater than ", INT16_MAX, U".");
  122. } else {
  123. * (int16 *) my d_fieldData [irow]. address = (int16) newValue; // guarded conversion
  124. }
  125. }
  126. } break;
  127. case intwa: {
  128. int oldValue = * (int *) my d_fieldData [irow]. address, newValue = Melder_atoi (text.get());
  129. if (newValue != oldValue) {
  130. Data_Description numberUse = DataSubEditor_findNumberUse (me, my d_fieldData [irow]. description -> name);
  131. if (numberUse) {
  132. Melder_flushError (U"Changing field \"", strip_d (my d_fieldData [irow]. description -> name),
  133. U"\" would damage the array \"", strip_d (numberUse -> name), U"\".");
  134. } else {
  135. * (int *) my d_fieldData [irow]. address = newValue;
  136. }
  137. }
  138. } break;
  139. case integerwa: {
  140. integer oldValue = * (integer *) my d_fieldData [irow]. address, newValue = Melder_atoi (text.get());
  141. if (newValue != oldValue) {
  142. Data_Description numberUse = DataSubEditor_findNumberUse (me, my d_fieldData [irow]. description -> name);
  143. if (numberUse) {
  144. Melder_flushError (U"Changing field \"", strip_d (my d_fieldData [irow]. description -> name),
  145. U"\" would damage the array \"", strip_d (numberUse -> name), U"\".");
  146. } else {
  147. * (integer *) my d_fieldData [irow]. address = newValue;
  148. }
  149. }
  150. } break;
  151. case ubytewa: { * (unsigned char *) my d_fieldData [irow]. address = (uint8) Melder_atoi (text.get()); } break;
  152. case uintwa: { * (unsigned int *) my d_fieldData [irow]. address = Melder_atoi (text.get()); } break;
  153. case uintegerwa: { * (uinteger *) my d_fieldData [irow]. address = (uinteger) Melder_atoi (text.get()); } break;
  154. case floatwa: { * (double *) my d_fieldData [irow]. address = Melder_atof (text.get()); } break;
  155. case doublewa: { * (double *) my d_fieldData [irow]. address = Melder_atof (text.get()); } break;
  156. case complexwa: { dcomplex *x = (dcomplex *) my d_fieldData [irow]. address;
  157. sscanf (Melder_peek32to8 (text.get()), "%lf + %lf i", & x -> re, & x -> im); } break;
  158. case enumwa: {
  159. if (str32len (text.get()) < 3) goto error;
  160. text [str32len (text.get()) - 1] = '\0'; // remove trailing ">"
  161. int value = ((int (*) (conststring32)) (my d_fieldData [irow]. description -> tagType)) (text.get() + 1); // skip leading "<"
  162. if (value < 0) goto error;
  163. * (signed char *) my d_fieldData [irow]. address = (signed char) value;
  164. } break;
  165. case lenumwa: {
  166. if (str32len (text.get()) < 3) goto error;
  167. text [str32len (text.get()) - 1] = '\0'; // remove trailing ">"
  168. int value = ((int (*) (conststring32)) (my d_fieldData [irow]. description -> tagType)) (text.get() + 1); // skip leading "<"
  169. if (value < 0) goto error;
  170. * (signed short *) my d_fieldData [irow]. address = (signed short) value;
  171. } break;
  172. case booleanwa: {
  173. bool value;
  174. if (str32nequ (text.get(), U"<true>", 6)) {
  175. value = true;
  176. } else if (str32nequ (text.get(), U"<false>", 7)) {
  177. value = false;
  178. } else {
  179. goto error;
  180. }
  181. * (bool *) my d_fieldData [irow]. address = value;
  182. } break;
  183. case questionwa: {
  184. bool value;
  185. if (str32nequ (text.get(), U"<yes>", 5)) {
  186. value = true;
  187. } else if (str32nequ (text.get(), U"<no>", 4)) {
  188. value = false;
  189. } else {
  190. goto error;
  191. }
  192. * (bool *) my d_fieldData [irow]. address = value;
  193. } break;
  194. case stringwa:
  195. case lstringwa: {
  196. char32 *old = * (char32 **) my d_fieldData [irow]. address;
  197. Melder_free (old);
  198. * (char32 **) my d_fieldData [irow]. address = Melder_dup_f (text.get()).transfer();
  199. } break;
  200. default: break;
  201. }
  202. }
  203. }
  204. /* Several collaborators have to be notified of this change:
  205. * 1. The owner (creator) of our root DataEditor: so that she can notify other editors, if any.
  206. * 2. All our sibling DataSubEditors.
  207. */
  208. Editor_broadcastDataChanged (my root);
  209. update (me);
  210. for (int isub = 1; isub <= my root -> children.size; isub ++) {
  211. DataSubEditor subeditor = my root -> children.at [isub];
  212. if (subeditor != me) update (subeditor);
  213. }
  214. return;
  215. error:
  216. Melder_flushError (U"Edit field \"", strip_d (my d_fieldData [irow]. description -> name), U"\" or click \"Cancel\".");
  217. }
  218. static void gui_button_cb_cancel (DataSubEditor me, GuiButtonEvent /* event */) {
  219. update (me);
  220. }
  221. static void gui_cb_scroll (DataSubEditor me, GuiScrollBarEvent event) {
  222. my d_topField = GuiScrollBar_getValue (event -> scrollBar) + 1;
  223. update (me);
  224. }
  225. static void gui_button_cb_open (DataSubEditor me, GuiButtonEvent event) {
  226. int ifield = 0;
  227. static MelderString name { };
  228. MelderString_empty (& name);
  229. /* Identify the pressed button; it must be one of those created in the list. */
  230. for (int i = 1; i <= kDataSubEditor_MAXNUM_ROWS; i ++)
  231. if (my d_fieldData [i]. button == event -> button) { ifield = i; break; }
  232. Melder_assert (ifield != 0);
  233. /* Launch the appropriate subeditor. */
  234. DataSubEditor_FieldData fieldData = & my d_fieldData [ifield];
  235. if (! fieldData -> description) {
  236. Melder_casual (U"Not yet implemented.");
  237. return; /* Not yet implemented. */
  238. }
  239. if (fieldData -> description -> rank == 1 || fieldData -> description -> rank == 3 || fieldData -> description -> rank < 0) {
  240. MelderString_append (& name, fieldData -> history.get(), U". ", strip_d (fieldData -> description -> name),
  241. U" [", fieldData -> minimum, U"..", fieldData -> maximum, U"]");
  242. VectorEditor_create (my root, name.string, fieldData -> address,
  243. fieldData -> description, fieldData -> minimum, fieldData -> maximum);
  244. } else if (fieldData -> description -> rank == 2) {
  245. MelderString_append (& name, fieldData -> history.get(), U". ", strip_d (fieldData -> description -> name),
  246. U" [", fieldData -> minimum, U"..", fieldData -> maximum, U"]");
  247. MelderString_append (& name, U" [", fieldData -> min2, U"..", fieldData -> max2, U"]");
  248. MatrixEditor_create (my root, name.string, fieldData -> address, fieldData -> description,
  249. fieldData -> minimum, fieldData -> maximum, fieldData -> min2, fieldData -> max2);
  250. } else if (fieldData -> description -> type == structwa) {
  251. MelderString_append (& name, fieldData -> history.get(), U". ", strip_d (fieldData -> description -> name));
  252. StructEditor_create (my root, name.string, fieldData -> address,
  253. * (Data_Description *) fieldData -> description -> tagType);
  254. } else if (fieldData -> description -> type == objectwa ||
  255. fieldData -> description -> type == collectionofwa ||
  256. fieldData -> description -> type == collectionwa) {
  257. MelderString_append (& name, fieldData -> history.get(), U". ", strip_d (fieldData -> description -> name));
  258. ClassEditor_create (my root, name.string, fieldData -> address,
  259. Class_getDescription ((ClassInfo) fieldData -> description -> tagType));
  260. } else /*if (fieldData -> description -> type == inheritwa)*/ {
  261. ClassEditor_create (my root, fieldData -> history.get(), fieldData -> address,
  262. fieldData -> description);
  263. /* } else {
  264. Melder_casual (
  265. U"Strange editor \"", strip_d (fieldData -> description -> name),
  266. U"\" required (type ", fieldData -> description -> type,
  267. U", rank ", fieldData -> description -> rank,
  268. U")."
  269. );*/
  270. }
  271. }
  272. void structDataSubEditor :: v_createChildren () {
  273. int x = Gui_LEFT_DIALOG_SPACING, y = Gui_TOP_DIALOG_SPACING + Machine_getMenuBarHeight (), buttonWidth = 120;
  274. GuiButton_createShown (our windowForm, x, x + buttonWidth, y, y + Gui_PUSHBUTTON_HEIGHT,
  275. U"Change", gui_button_cb_change, this, 0);
  276. x += buttonWidth + Gui_HORIZONTAL_DIALOG_SPACING;
  277. GuiButton_createShown (our windowForm, x, x + buttonWidth, y, y + Gui_PUSHBUTTON_HEIGHT,
  278. U"Cancel", gui_button_cb_cancel, this, 0);
  279. y = LIST_Y + Machine_getMenuBarHeight ();
  280. d_scrollBar = GuiScrollBar_createShown (our windowForm,
  281. - SCROLL_BAR_WIDTH, 0, y, 0,
  282. 0, d_numberOfFields, 0, d_numberOfFields < kDataSubEditor_MAXNUM_ROWS ? d_numberOfFields : kDataSubEditor_MAXNUM_ROWS, 1, kDataSubEditor_MAXNUM_ROWS - 1,
  283. gui_cb_scroll, this, 0);
  284. y += 10;
  285. for (int i = 1; i <= kDataSubEditor_MAXNUM_ROWS; i ++) {
  286. d_fieldData [i]. label = GuiLabel_create (our windowForm, 0, 200, y, y + Gui_TEXTFIELD_HEIGHT, U"label", 0); // no fixed x value: sometimes indent
  287. d_fieldData [i]. button = GuiButton_create (our windowForm, BUTTON_X, BUTTON_X + buttonWidth, y, y + Gui_TEXTFIELD_HEIGHT,
  288. U"Open", gui_button_cb_open, this, 0);
  289. d_fieldData [i]. text = GuiText_create (our windowForm, TEXT_X, -30, y, y + Gui_TEXTFIELD_HEIGHT, 0);
  290. d_fieldData [i]. y = y;
  291. y += ROW_HEIGHT;
  292. }
  293. }
  294. static void menu_cb_help (DataSubEditor, EDITOR_ARGS_DIRECT) { Melder_help (U"Inspect"); }
  295. void structDataSubEditor :: v_createHelpMenuItems (EditorMenu menu) {
  296. DataSubEditor_Parent :: v_createHelpMenuItems (menu);
  297. EditorMenu_addCommand (menu, U"DataEditor help", '?', menu_cb_help);
  298. }
  299. static void DataSubEditor_init (DataSubEditor me, DataEditor root, conststring32 title, void *address, Data_Description description) {
  300. my root = root;
  301. if (me != root) {
  302. root -> children.addItem_ref (me);
  303. }
  304. my d_address = address;
  305. my d_description = description;
  306. my d_topField = 1;
  307. my d_numberOfFields = my v_countFields ();
  308. Editor_init (me, 0, 0, EDITOR_WIDTH, EDITOR_HEIGHT, title, nullptr);
  309. update (me);
  310. }
  311. /********** StructEditor **********/
  312. Thing_implement (StructEditor, DataSubEditor, 0);
  313. integer structStructEditor :: v_countFields () {
  314. return Data_Description_countMembers (d_description);
  315. }
  316. static conststring32 singleTypeToText (void *address, int type, void *tagType, MelderString *buffer) {
  317. switch (type) {
  318. case bytewa: MelderString_append (buffer, Melder_integer (* (signed char *) address)); break;
  319. case int16wa: MelderString_append (buffer, Melder_integer (* (int16 *) address)); break;
  320. case intwa: MelderString_append (buffer, Melder_integer (* (int *) address)); break;
  321. case integerwa: MelderString_append (buffer, Melder_integer (* (integer *) address)); break;
  322. case ubytewa: MelderString_append (buffer, Melder_integer (* (unsigned char *) address)); break;
  323. case uintwa: MelderString_append (buffer, Melder_integer (* (unsigned int *) address)); break;
  324. case uintegerwa: MelderString_append (buffer, Melder_integer (* (uinteger *) address)); break;
  325. case floatwa: MelderString_append (buffer, Melder_single (* (double *) address)); break;
  326. case doublewa: MelderString_append (buffer, Melder_double (* (double *) address)); break;
  327. case complexwa: MelderString_append (buffer, Melder_dcomplex (* (dcomplex *) address)); break;
  328. case enumwa: MelderString_append (buffer, U"<", ((conststring32 (*) (int)) tagType) (* (signed char *) address), U">"); break;
  329. case lenumwa: MelderString_append (buffer, U"<", ((conststring32 (*) (int)) tagType) (* (signed short *) address), U">"); break;
  330. case booleanwa: MelderString_append (buffer, * (bool *) address ? U"<true>" : U"<false>"); break;
  331. case questionwa: MelderString_append (buffer, * (bool *) address ? U"<yes>" : U"<no>" ); break;
  332. case stringwa:
  333. case lstringwa: {
  334. char32 *string = * (char32 **) address;
  335. if (! string) { MelderString_empty (buffer); return buffer -> string; } // convert null string to empty string
  336. return string; // may be much longer than the usual size of 'buffer'
  337. } break;
  338. default: return U"(unknown)";
  339. }
  340. return buffer -> string; // Mind the special return for strings above.
  341. }
  342. static void showStructMember (
  343. void *structAddress, /* The address of (the first member of) the struct. */
  344. Data_Description structDescription, /* The description of (the first member of) the struct. */
  345. Data_Description memberDescription, /* The description of the current member. */
  346. DataSubEditor_FieldData fieldData, /* The widgets in which to show the info about the current member. */
  347. char32 *history)
  348. {
  349. int type = memberDescription -> type, rank = memberDescription -> rank, isSingleType = type <= maxsingletypewa && rank == 0;
  350. unsigned char *memberAddress = (unsigned char *) structAddress + memberDescription -> offset;
  351. if (type == inheritwa) {
  352. GuiLabel_setText (fieldData -> label,
  353. Melder_cat (U"Class part \"", memberDescription -> name, U"\":"));
  354. } else {
  355. GuiLabel_setText (fieldData -> label,
  356. Melder_cat (U" ", strip_d (memberDescription -> name),
  357. ( rank == 0 ? U"" : rank == 1 || rank == 3 || rank < 0 ? U" [ ]" : U" [ ] [ ]" )));
  358. }
  359. //GuiControl_move (fieldData -> label, type == inheritwa ? 0 : NAME_X, fieldData -> y);
  360. GuiThing_show (fieldData -> label);
  361. /* Show the value (for a single type) or a button (for a composite type). */
  362. if (isSingleType) {
  363. #if motif
  364. XtVaSetValues (fieldData -> text -> d_widget, XmNcolumns, 60, nullptr); // TODO: change to GuiObject_size
  365. #endif
  366. autoMelderString buffer;
  367. conststring32 text = singleTypeToText (memberAddress, type, memberDescription -> tagType, & buffer);
  368. GuiText_setString (fieldData -> text, text);
  369. GuiThing_show (fieldData -> text);
  370. fieldData -> address = memberAddress;
  371. fieldData -> description = memberDescription;
  372. fieldData -> rank = 0;
  373. } else if (rank == 1) {
  374. void *arrayAddress = * (void **) memberAddress;
  375. integer minimum, maximum;
  376. if (! arrayAddress) return; // no button for empty fields
  377. Data_Description_evaluateInteger (structAddress, structDescription,
  378. memberDescription -> min1, & minimum);
  379. Data_Description_evaluateInteger (structAddress, structDescription,
  380. memberDescription -> max1, & maximum);
  381. if (maximum < minimum) return; // no button if no elements
  382. fieldData -> address = arrayAddress; // indirect
  383. fieldData -> description = memberDescription;
  384. fieldData -> minimum = minimum; // normally 1
  385. fieldData -> maximum = maximum;
  386. fieldData -> rank = 1;
  387. fieldData -> history = Melder_dup_f (history);
  388. GuiThing_show (fieldData -> button);
  389. } else if (rank < 0) {
  390. /*
  391. * This represents an in-line array.
  392. */
  393. integer maximum; /* But: capacity = - rank */
  394. Data_Description_evaluateInteger (structAddress, structDescription,
  395. memberDescription -> max1, & maximum);
  396. if (-- maximum < 0) return; /* Subtract one for zero-based array; no button if no elements. */
  397. fieldData -> address = memberAddress; /* Direct. */
  398. fieldData -> description = memberDescription;
  399. fieldData -> minimum = 0; /* In-line arrays start with index 0. */
  400. fieldData -> maximum = maximum; /* Probably between -1 and capacity - 1. */
  401. fieldData -> rank = rank;
  402. fieldData -> history = Melder_dup_f (history);
  403. GuiThing_show (fieldData -> button);
  404. } else if (rank == 3) {
  405. /*
  406. * This represents an in-line set.
  407. */
  408. fieldData -> address = memberAddress; /* Direct. */
  409. fieldData -> description = memberDescription;
  410. fieldData -> minimum = str32equ (((conststring32 (*) (int)) memberDescription -> min1) (0), U"_") ? 1 : 0;
  411. fieldData -> maximum = ((int (*) (conststring32)) memberDescription -> max1) (U"\n");
  412. fieldData -> rank = rank;
  413. fieldData -> history = Melder_dup_f (history);
  414. GuiThing_show (fieldData -> button);
  415. } else if (rank == 2) {
  416. void *arrayAddress = * (void **) memberAddress;
  417. integer min1, max1, min2, max2;
  418. if (! arrayAddress) return; // no button for empty fields
  419. Data_Description_evaluateInteger (structAddress, structDescription,
  420. memberDescription -> min1, & min1);
  421. Data_Description_evaluateInteger (structAddress, structDescription,
  422. memberDescription -> max1, & max1);
  423. Data_Description_evaluateInteger (structAddress, structDescription,
  424. memberDescription -> min2, & min2);
  425. Data_Description_evaluateInteger (structAddress, structDescription,
  426. memberDescription -> max2, & max2);
  427. if (max1 < min1 || max2 < min2) return; // no button if no elements
  428. fieldData -> address = arrayAddress; // indirect
  429. fieldData -> description = memberDescription;
  430. fieldData -> minimum = min1; // normally 1
  431. fieldData -> maximum = max1;
  432. fieldData -> min2 = min2;
  433. fieldData -> max2 = max2;
  434. fieldData -> rank = 2;
  435. fieldData -> history = Melder_dup_f (history);
  436. GuiThing_show (fieldData -> button);
  437. } else if (type == structwa) { // in-line struct
  438. fieldData -> address = memberAddress; // direct
  439. fieldData -> description = memberDescription;
  440. fieldData -> rank = 0;
  441. fieldData -> history = Melder_dup_f (history);
  442. GuiThing_show (fieldData -> button);
  443. } else if (type == objectwa || type == collectionwa) {
  444. fieldData -> address = * (Daata *) memberAddress; // indirect // FIXME: not guaranteed for auto objects
  445. if (! fieldData -> address) return; // no button if no object
  446. fieldData -> description = memberDescription;
  447. fieldData -> rank = 0;
  448. fieldData -> history = Melder_dup_f (history);
  449. GuiThing_show (fieldData -> button);
  450. } else if (type == collectionofwa) {
  451. fieldData -> address = (Daata) memberAddress; // direct // FIXME: not guaranteed for auto objects
  452. Melder_casual (U"Daata ", Melder_pointer (fieldData -> address));
  453. Melder_casual (U"Class ", ((Daata) fieldData -> address) -> classInfo -> className);
  454. if (! fieldData -> address) return; // no button if no object
  455. fieldData -> description = memberDescription;
  456. fieldData -> rank = 0;
  457. fieldData -> history = Melder_dup_f (history);
  458. GuiThing_show (fieldData -> button);
  459. }
  460. }
  461. static void showStructMembers (DataSubEditor me, void *structAddress, Data_Description structDescription, int fromMember, char32 *history) {
  462. int i = 1;
  463. Data_Description memberDescription = structDescription;
  464. for (; i < fromMember && memberDescription -> name != nullptr; i ++, memberDescription ++)
  465. (void) 0;
  466. for (; memberDescription -> name != nullptr; memberDescription ++) {
  467. if (++ my d_irow > kDataSubEditor_MAXNUM_ROWS) return;
  468. showStructMember (structAddress, structDescription, memberDescription, & my d_fieldData [my d_irow], history);
  469. }
  470. }
  471. void structStructEditor :: v_showMembers () {
  472. showStructMembers (this, our d_address, our d_description, our d_topField, our name.get());
  473. }
  474. static void StructEditor_init (StructEditor me, DataEditor root, conststring32 title, void *address, Data_Description description) {
  475. DataSubEditor_init (me, root, title, address, description);
  476. }
  477. static void StructEditor_create (DataEditor root, conststring32 title, void *address, Data_Description description) {
  478. try {
  479. autoStructEditor me = Thing_new (StructEditor);
  480. StructEditor_init (me.get(), root, title, address, description);
  481. return me.releaseToUser();
  482. } catch (MelderError) {
  483. Melder_throw (U"Struct inspector window not created.");
  484. }
  485. }
  486. /********** VectorEditor **********/
  487. Thing_implement (VectorEditor, DataSubEditor, 0);
  488. integer structVectorEditor :: v_countFields () {
  489. integer numberOfElements = d_maximum - d_minimum + 1;
  490. if (d_description -> type == structwa)
  491. return numberOfElements * (Data_Description_countMembers (* (Data_Description *) d_description -> tagType) + 1);
  492. else
  493. return numberOfElements;
  494. }
  495. void structVectorEditor :: v_showMembers () {
  496. int type = our d_description -> type, isSingleType = type <= maxsingletypewa;
  497. int elementSize = type == structwa ?
  498. Data_Description_countMembers (* (Data_Description *) d_description -> tagType) + 1 : 1;
  499. integer firstElement = d_minimum + (d_topField - 1) / elementSize;
  500. for (integer ielement = firstElement; ielement <= d_maximum; ielement ++) {
  501. unsigned char *elementAddress = (unsigned char *) our d_address + ielement * our d_description -> size;
  502. int skip = ielement == firstElement ? (our d_topField - 1) % elementSize : 0;
  503. if (++ our d_irow > kDataSubEditor_MAXNUM_ROWS) return;
  504. DataSubEditor_FieldData fieldData = & our d_fieldData [d_irow];
  505. if (isSingleType) {
  506. GuiControl_move (fieldData -> label, 0, fieldData -> y);
  507. GuiLabel_setText (fieldData -> label,
  508. Melder_cat (strip_d (our d_description -> name), U" [",
  509. ( our d_description -> rank == 3 ? ((conststring32 (*) (int)) our d_description -> min1) (ielement) : Melder_integer (ielement) ),
  510. U"]"));
  511. GuiThing_show (fieldData -> label);
  512. autoMelderString buffer;
  513. conststring32 text = singleTypeToText (elementAddress, type, our d_description -> tagType, & buffer);
  514. #if motif
  515. XtVaSetValues (fieldData -> text -> d_widget, XmNcolumns, 60, nullptr); // TODO: change to GuiObject_size
  516. #endif
  517. GuiText_setString (fieldData -> text, text);
  518. GuiThing_show (fieldData -> text);
  519. fieldData -> address = elementAddress;
  520. fieldData -> description = d_description;
  521. } else if (type == structwa) {
  522. static MelderString history { };
  523. MelderString_copy (& history, our name.get());
  524. /* Replace things like [1..100] by things like [19]. */
  525. if (history.string [history.length - 1] == ']') {
  526. char32 *openingBracket = str32rchr (history.string, U'[');
  527. Melder_assert (openingBracket != nullptr);
  528. * openingBracket = '\0';
  529. history.length = openingBracket - history.string;
  530. }
  531. MelderString_append (& history, U"[", ielement, U"]");
  532. if (skip) {
  533. our d_irow --;
  534. } else {
  535. GuiControl_move (fieldData -> label, 0, fieldData -> y);
  536. GuiLabel_setText (fieldData -> label,
  537. Melder_cat (strip_d (d_description -> name), U" [", ielement, U"]: ---------------------------"));
  538. GuiThing_show (fieldData -> label);
  539. }
  540. showStructMembers (this, elementAddress, * (Data_Description *) d_description -> tagType, skip, history.string);
  541. } else if (type == objectwa) {
  542. static MelderString history { };
  543. MelderString_copy (& history, our name.get());
  544. if (history.string [history.length - 1] == U']') {
  545. char32 *openingBracket = str32rchr (history.string, U'[');
  546. Melder_assert (openingBracket != nullptr);
  547. * openingBracket = U'\0';
  548. history.length = openingBracket - history.string;
  549. }
  550. MelderString_append (& history, U"[", ielement, U"]");
  551. GuiControl_move (fieldData -> label, 0, fieldData -> y);
  552. GuiLabel_setText (fieldData -> label, Melder_cat (strip_d (our d_description -> name), U" [", ielement, U"]"));
  553. GuiThing_show (fieldData -> label);
  554. Daata object = * (Daata *) elementAddress;
  555. if (! object) return; // no button if no object
  556. if (! Class_getDescription (object -> classInfo)) return; // no button if no description for this class
  557. fieldData -> address = object;
  558. fieldData -> description = Class_getDescription (object -> classInfo);
  559. fieldData -> rank = 0;
  560. fieldData -> history = Melder_dup_f (history.string);
  561. GuiThing_show (fieldData -> button);
  562. }
  563. }
  564. }
  565. static void VectorEditor_create (DataEditor root, conststring32 title, void *address,
  566. Data_Description description, integer minimum, integer maximum)
  567. {
  568. try {
  569. autoVectorEditor me = Thing_new (VectorEditor);
  570. my d_minimum = minimum;
  571. my d_maximum = maximum;
  572. DataSubEditor_init (me.get(), root, title, address, description);
  573. return me.releaseToUser();
  574. } catch (MelderError) {
  575. Melder_throw (U"Vector inspector window not created.");
  576. }
  577. }
  578. /********** MatrixEditor **********/
  579. Thing_implement (MatrixEditor, DataSubEditor, 0);
  580. integer structMatrixEditor :: v_countFields () {
  581. integer numberOfElements = (d_maximum - d_minimum + 1) * (d_max2 - d_min2 + 1);
  582. if (d_description -> type == structwa)
  583. return numberOfElements * (Data_Description_countMembers (* (Data_Description *) d_description -> tagType) + 1);
  584. else
  585. return numberOfElements;
  586. }
  587. void structMatrixEditor :: v_showMembers () {
  588. int type = d_description -> type, isSingleType = type <= maxsingletypewa;
  589. int elementSize = type == structwa ?
  590. Data_Description_countMembers (* (Data_Description *) d_description -> tagType) + 1 : 1;
  591. int rowSize = elementSize * (d_max2 - d_min2 + 1);
  592. integer firstRow = d_minimum + (d_topField - 1) / rowSize;
  593. integer firstColumn = d_min2 + (d_topField - 1 - (firstRow - d_minimum) * rowSize) / elementSize;
  594. for (integer irow = firstRow; irow <= d_maximum; irow ++)
  595. for (integer icolumn = irow == firstRow ? firstColumn : d_min2; icolumn <= d_max2; icolumn ++) {
  596. unsigned char *elementAddress = * ((unsigned char **) d_address + irow) + icolumn * d_description -> size;
  597. if (++ d_irow > kDataSubEditor_MAXNUM_ROWS) return;
  598. DataSubEditor_FieldData fieldData = & d_fieldData [d_irow];
  599. if (isSingleType) {
  600. GuiControl_move (fieldData -> label, 0, fieldData -> y);
  601. GuiLabel_setText (fieldData -> label, Melder_cat (strip_d (d_description -> name), U" [", irow, U"] [", icolumn, U"]"));
  602. GuiThing_show (fieldData -> label);
  603. autoMelderString buffer;
  604. conststring32 text = singleTypeToText (elementAddress, type, d_description -> tagType, & buffer);
  605. #if motif
  606. XtVaSetValues (fieldData -> text -> d_widget, XmNcolumns, 60, nullptr); // TODO: change to GuiObject_size
  607. #endif
  608. GuiText_setString (fieldData -> text, text);
  609. GuiThing_show (fieldData -> text);
  610. fieldData -> address = elementAddress;
  611. fieldData -> description = d_description;
  612. }
  613. }
  614. }
  615. static void MatrixEditor_create (DataEditor root, conststring32 title, void *address,
  616. Data_Description description, integer min1, integer max1, integer min2, integer max2)
  617. {
  618. try {
  619. autoMatrixEditor me = Thing_new (MatrixEditor);
  620. my d_minimum = min1;
  621. my d_maximum = max1;
  622. my d_min2 = min2;
  623. my d_max2 = max2;
  624. DataSubEditor_init (me.get(), root, title, address, description);
  625. return me.releaseToUser();
  626. } catch (MelderError) {
  627. Melder_throw (U"Matrix inspector window not created.");
  628. }
  629. }
  630. /********** ClassEditor **********/
  631. Thing_implement (ClassEditor, StructEditor, 0);
  632. static void ClassEditor_showMembers_recursive (ClassEditor me, ClassInfo klas) {
  633. ClassInfo parentClass = klas -> semanticParent;
  634. Data_Description description = Class_getDescription (klas);
  635. int classFieldsTraversed = 0;
  636. while (Class_getDescription (parentClass) == description)
  637. parentClass = parentClass -> semanticParent;
  638. if (parentClass != classDaata) {
  639. ClassEditor_showMembers_recursive (me, parentClass);
  640. classFieldsTraversed = Data_Description_countMembers (Class_getDescription (parentClass));
  641. //Melder_casual (U"ClassEditor_showMembers_recursive: classFieldsTraversed = ", classFieldsTraversed);
  642. }
  643. showStructMembers (me, my d_address, description, my d_irow ? 1 : my d_topField - classFieldsTraversed, my name.get());
  644. }
  645. void structClassEditor :: v_showMembers () {
  646. ClassEditor_showMembers_recursive (this, ((Daata) d_address) -> classInfo);
  647. }
  648. static void ClassEditor_init (ClassEditor me, DataEditor root, conststring32 title, void *address, Data_Description description) {
  649. if (! description)
  650. Melder_throw (U"Class ", Thing_className ((Thing) address), U" cannot be inspected.");
  651. StructEditor_init (me, root, title, address, description);
  652. }
  653. static void ClassEditor_create (DataEditor root, conststring32 title, void *address, Data_Description description) {
  654. try {
  655. autoClassEditor me = Thing_new (ClassEditor);
  656. ClassEditor_init (me.get(), root, title, address, description);
  657. return me.releaseToUser();
  658. } catch (MelderError) {
  659. Melder_throw (U"Class inspector window not created.");
  660. }
  661. }
  662. /********** DataEditor **********/
  663. Thing_implement (DataEditor, ClassEditor, 0);
  664. static void DataEditor_destroyAllChildren (DataEditor me) {
  665. /*
  666. To destroy all children, we travel them from youngest to oldest,
  667. because the array of children will change from under us:
  668. */
  669. for (int i = my children.size; i >= 1; i --) {
  670. /*
  671. An optimization coming!
  672. Instead of
  673. DataSubEditor child = my children [i];
  674. forget (child);
  675. we isolate the child from the parent before destroying the child,
  676. so that the child won't try to remove the reference
  677. that the parent has to her.
  678. So first we make the parent forget the moribund child,
  679. which prevents a dangling pointer:
  680. */
  681. DataSubEditor child = my children.subtractItem_ref (i);
  682. /*
  683. That was fast, because subtracting the last item involves no shifting
  684. of the remaining items.
  685. Second, we make the child forget the parent,
  686. so that the child won't try to remove the reference
  687. that the parent had to her (but no longer has):
  688. */
  689. child -> root = nullptr;
  690. /*
  691. The child is now fully isolated, so we are ready to destroy her:
  692. */
  693. forget (child);
  694. /*
  695. This procedure was an optimization because if we just destroyed each child,
  696. each child would remove itself from the array by (1) searching for itself
  697. and (2) shifting the remaining children, both of which have a complexity
  698. that is linear in the number of children. So we would end up with quadratic complexity,
  699. whereas the procedure that we use above has linear complexity.
  700. This linear complexity makes this procedure good enough for `v_destroy()`
  701. (where obtaining linear complexity would have been easy anyway),
  702. and nice enough for `v_dataChanged()`.
  703. Something to note is that this procedure doesn't care whether the autoCollection
  704. `children` owns its items or not.
  705. */
  706. }
  707. }
  708. void structDataEditor :: v_destroy () noexcept {
  709. DataEditor_destroyAllChildren (this);
  710. DataEditor_Parent :: v_destroy ();
  711. }
  712. void structDataEditor :: v_dataChanged () {
  713. /*
  714. Someone else changed our data.
  715. We know that the top-level data is still accessible,
  716. so we update the top-level window to show the change:
  717. */
  718. update (this);
  719. /*
  720. Changing the data may have changed any part of the *structure* of the data,
  721. so we do not know if the data visible in any of the subeditors is still valid.
  722. We follow the most straightforward solution, which is to simply close all the child windows,
  723. which guarantees the removal of all dangling visual representations:
  724. */
  725. DataEditor_destroyAllChildren (this);
  726. }
  727. autoDataEditor DataEditor_create (conststring32 title, Daata data) {
  728. try {
  729. ClassInfo klas = data -> classInfo;
  730. if (Class_getDescription (klas) == nullptr)
  731. Melder_throw (U"Class ", klas -> className, U" cannot be inspected.");
  732. autoDataEditor me = Thing_new (DataEditor);
  733. ClassEditor_init (me.get(), me.get(), title, data, Class_getDescription (klas));
  734. return me;
  735. } catch (MelderError) {
  736. Melder_throw (U"Inspector window not created.");
  737. }
  738. }
  739. /* End of file DataEditor.cpp */