not really known
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

362 lines
14 KiB

  1. // Copyright (c) 2016-17 Walter Bender
  2. //
  3. // This program is free software; you can redistribute it and/or
  4. // modify it under the terms of the The GNU Affero General Public
  5. // License as published by the Free Software Foundation; either
  6. // version 3 of the License, or (at your option) any later version.
  7. //
  8. // You should have received a copy of the GNU Affero General Public
  9. // License along with this library; if not, write to the Free Software
  10. // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
  11. // This widget makes displays the status of selected parameters and
  12. // notes as they are being played.
  13. function StatusMatrix() {
  14. const BUTTONDIVWIDTH = 128;
  15. const BUTTONSIZE = 53;
  16. const ICONSIZE = 32;
  17. const OUTERWINDOWWIDTH = 620;
  18. const INNERWINDOWWIDTH = OUTERWINDOWWIDTH - BUTTONSIZE * 1.5;
  19. docById('statusDiv').style.visibility = 'hidden';
  20. this.init = function(logo) {
  21. // Initializes the status matrix. First removes the
  22. // previous matrix and them make another one in DOM (document
  23. // object model)
  24. this._logo = logo;
  25. var w = window.innerWidth;
  26. this._cellScale = w / 1200;
  27. var iconSize = ICONSIZE * this._cellScale;
  28. var canvas = docById('myCanvas');
  29. // Position the widget and make it visible.
  30. var statusDiv = docById('statusDiv');
  31. statusDiv.style.visibility = 'visible';
  32. statusDiv.setAttribute('draggable', 'true');
  33. statusDiv.style.left = '200px';
  34. statusDiv.style.top = '150px';
  35. // The status buttons
  36. var statusButtonsDiv = docById('statusButtonsDiv');
  37. statusButtonsDiv.style.display = 'inline';
  38. statusButtonsDiv.style.visibility = 'visible';
  39. statusButtonsDiv.style.width = BUTTONDIVWIDTH;
  40. statusButtonsDiv.innerHTML = '<table cellpadding="0px" id="statusButtonTable"></table>';
  41. var buttonTable = docById('statusButtonTable');
  42. var header = buttonTable.createTHead();
  43. var row = header.insertRow(0);
  44. // For the button callbacks
  45. var that = this;
  46. var cell = this._addButton(row, 'close-button.svg', ICONSIZE, _('close'));
  47. cell.onclick=function() {
  48. statusTableDiv.style.visibility = 'hidden';
  49. statusButtonsDiv.style.visibility = 'hidden';
  50. statusDiv.style.visibility = 'hidden';
  51. }
  52. // We use this cell as a handle for dragging.
  53. var dragCell = this._addButton(row, 'grab.svg', ICONSIZE, _('drag'));
  54. dragCell.style.cursor = 'move';
  55. this._dx = dragCell.getBoundingClientRect().left - statusDiv.getBoundingClientRect().left;
  56. this._dy = dragCell.getBoundingClientRect().top - statusDiv.getBoundingClientRect().top;
  57. this._dragging = false;
  58. this._target = false;
  59. this._dragCellHTML = dragCell.innerHTML;
  60. dragCell.onmouseover = function(e) {
  61. // In order to prevent the dragged item from triggering a
  62. // browser reload in Firefox, we empty the cell contents
  63. // before dragging.
  64. dragCell.innerHTML = '';
  65. };
  66. dragCell.onmouseout = function(e) {
  67. if (!that._dragging) {
  68. dragCell.innerHTML = that._dragCellHTML;
  69. }
  70. };
  71. canvas.ondragover = function(e) {
  72. e.preventDefault();
  73. };
  74. canvas.ondrop = function(e) {
  75. if (that._dragging) {
  76. that._dragging = false;
  77. var x = e.clientX - that._dx;
  78. statusDiv.style.left = x + 'px';
  79. var y = e.clientY - that._dy;
  80. statusDiv.style.top = y + 'px';
  81. dragCell.innerHTML = that._dragCellHTML;
  82. }
  83. };
  84. statusDiv.ondragover = function(e) {
  85. e.preventDefault();
  86. };
  87. statusDiv.ondrop = function(e) {
  88. if (that._dragging) {
  89. that._dragging = false;
  90. var x = e.clientX - that._dx;
  91. statusDiv.style.left = x + 'px';
  92. var y = e.clientY - that._dy;
  93. statusDiv.style.top = y + 'px';
  94. dragCell.innerHTML = that._dragCellHTML;
  95. }
  96. };
  97. statusDiv.onmousedown = function(e) {
  98. that._dragging = true;
  99. that._target = e.target;
  100. };
  101. statusDiv.ondragstart = function(e) {
  102. if (dragCell.contains(that._target)) {
  103. e.dataTransfer.setData('text/plain', '');
  104. } else {
  105. e.preventDefault();
  106. }
  107. };
  108. // The status table
  109. var statusTableDiv = docById('statusTableDiv');
  110. statusTableDiv.style.display = 'inline';
  111. statusTableDiv.style.visibility = 'visible';
  112. statusTableDiv.style.border = '0px';
  113. // We use an outer div to scroll vertically and an inner div to
  114. // scroll horizontally.
  115. statusTableDiv.innerHTML = '<div id="statusOuterDiv"><div id="statusInnerDiv"><table cellpadding="0px" id="statusTable"></table></div></div>';
  116. var n = Math.max(Math.floor((window.innerHeight * 0.5) / 100), 8);
  117. var outerDiv = docById('statusOuterDiv');
  118. if (this._logo.turtles.turtleList.length > n) {
  119. outerDiv.style.height = this._cellScale * MATRIXSOLFEHEIGHT * (n + 2) + 'px';
  120. var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH), BUTTONDIVWIDTH);
  121. outerDiv.style.width = w + 'px';
  122. } else {
  123. if (this._logo.statusFields.length > 4) { // Assume we need a horizontal slider
  124. outerDiv.style.height = this._cellScale * (MATRIXBUTTONHEIGHT2 + (2 + MATRIXSOLFEHEIGHT) * this._logo.turtles.turtleList.length) + 30 + 'px';
  125. } else {
  126. outerDiv.style.height = this._cellScale * (MATRIXBUTTONHEIGHT2 + (2 + MATRIXSOLFEHEIGHT) * this._logo.turtles.turtleList.length) + 'px';
  127. }
  128. var w = Math.max(Math.min(window.innerWidth, this._cellScale * OUTERWINDOWWIDTH - 20), BUTTONDIVWIDTH);
  129. outerDiv.style.width = w + 'px';
  130. }
  131. var w = Math.max(Math.min(window.innerWidth, this._cellScale * INNERWINDOWWIDTH), BUTTONDIVWIDTH - BUTTONSIZE);
  132. var innerDiv = docById('statusInnerDiv');
  133. innerDiv.style.width = w + 'px';
  134. innerDiv.style.marginLeft = (BUTTONSIZE * this._cellScale) + 'px';
  135. // Each row in the status table contains a note label in the
  136. // first column and a table of buttons in the second column.
  137. var statusTable = docById('statusTable');
  138. var header = statusTable.createTHead();
  139. var row = header.insertRow();
  140. var iconSize = Math.floor(this._cellScale * 24);
  141. var cell = row.insertCell();
  142. cell.style.backgroundColor = MATRIXBUTTONCOLOR;
  143. cell.className = 'headcol';
  144. cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
  145. cell.style.width = (BUTTONSIZE * this._cellScale) + 'px';
  146. cell.innerHTML = '&nbsp;';
  147. // One column per field
  148. for (var i = 0; i < this._logo.statusFields.length; i++) {
  149. var cell = row.insertCell(i + 1);
  150. cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
  151. switch (this._logo.statusFields[i][1]) {
  152. case 'plus':
  153. case 'minus':
  154. case 'neg':
  155. case 'divide':
  156. case 'power':
  157. case 'multiply':
  158. case 'sqrt':
  159. case 'int':
  160. case 'mod':
  161. var label = '';
  162. break;
  163. case 'namedbox':
  164. var label = this._logo.blocks.blockList[this._logo.statusFields[i][0]].privateData;
  165. break;
  166. default:
  167. var label = this._logo.blocks.blockList[this._logo.statusFields[i][0]].protoblock.staticLabels[0];
  168. break;
  169. }
  170. cell.innerHTML = '&nbsp;<b>' + label + '</b>&nbsp;'
  171. cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
  172. cell.style.backgroundColor = MATRIXBUTTONCOLOR;
  173. }
  174. if (_THIS_IS_MUSIC_BLOCKS_) {
  175. var cell = row.insertCell();
  176. cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
  177. cell.innerHTML = '&nbsp;<b>' + _('note') + '</b>&nbsp;'
  178. cell.style.height = Math.floor(MATRIXBUTTONHEIGHT * this._cellScale) + 'px';
  179. cell.style.backgroundColor = MATRIXBUTTONCOLOR;
  180. }
  181. // One row per voice (turtle)
  182. var activeTurtles = 0;
  183. for (var turtle = 0; turtle < this._logo.turtles.turtleList.length; turtle++) {
  184. if (this._logo.turtles.turtleList[turtle].trash) {
  185. continue;
  186. }
  187. var row = header.insertRow();
  188. var cell = row.insertCell();
  189. cell.style.backgroundColor = MATRIXLABELCOLOR;
  190. if (_THIS_IS_MUSIC_BLOCKS_) {
  191. cell.innerHTML = '&nbsp;&nbsp;<img src="images/mouse.svg" title="' + this._logo.turtles.turtleList[turtle].name + '" alt="' + this._logo.turtles.turtleList[turtle].name + '" height="' + iconSize + '" width="' + iconSize + '">&nbsp;&nbsp;';
  192. } else {
  193. cell.innerHTML = '&nbsp;&nbsp;<img src="header-icons/turtle-button.svg" title="' + this._logo.turtles.turtleList[turtle].name + '" alt="' + this._logo.turtles.turtleList[turtle].name + '" height="' + iconSize + '" width="' + iconSize + '">&nbsp;&nbsp;';
  194. }
  195. cell.style.width = (BUTTONSIZE * this._cellScale) + 'px';
  196. cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
  197. cell.className = 'headcol';
  198. if (_THIS_IS_MUSIC_BLOCKS_) {
  199. // + 1 is for the note column
  200. for (var i = 0; i < this._logo.statusFields.length + 1; i++) {
  201. var cell = row.insertCell();
  202. cell.style.backgroundColor = MATRIXRHYTHMCELLCOLOR;
  203. cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
  204. cell.innerHTML = '';
  205. cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
  206. }
  207. } else {
  208. for (var i = 0; i < this._logo.statusFields.length; i++) {
  209. var cell = row.insertCell();
  210. cell.style.backgroundColor = MATRIXRHYTHMCELLCOLOR;
  211. cell.style.fontSize = Math.floor(this._cellScale * 100) + '%';
  212. cell.innerHTML = '';
  213. cell.style.height = Math.floor(MATRIXSOLFEHEIGHT * this._cellScale) + 'px';
  214. }
  215. }
  216. activeTurtles += 1;
  217. }
  218. };
  219. this.updateAll = function() {
  220. // Update status of all of the voices in the matrix.
  221. var table = docById('statusTable');
  222. this._logo.updatingStatusMatrix = true;
  223. var activeTurtles = 0;
  224. for (var turtle = 0; turtle < this._logo.turtles.turtleList.length; turtle++) {
  225. if (this._logo.turtles.turtleList[turtle].trash) {
  226. continue;
  227. }
  228. for (var i = 0; i < this._logo.statusFields.length; i++) {
  229. var saveStatus = this._logo.inStatusMatrix;
  230. this._logo.inStatusMatrix = false;
  231. this._logo.parseArg(this._logo, turtle, this._logo.statusFields[i][0]);
  232. switch (this._logo.blocks.blockList[this._logo.statusFields[i][0]].name) {
  233. case 'x':
  234. case 'y':
  235. case 'heading':
  236. var value = this._logo.blocks.blockList[this._logo.statusFields[i][0]].value.toFixed(2);
  237. break;
  238. case 'elapsednotes':
  239. var value = mixedNumber(this._logo.blocks.blockList[this._logo.statusFields[i][0]].value);
  240. break;
  241. case 'namedbox':
  242. var name = this._logo.blocks.blockList[this._logo.statusFields[i][0]].privateData;
  243. if (name in this._logo.boxes) {
  244. var value = this._logo.boxes[name];
  245. } else {
  246. var value = '';
  247. }
  248. break;
  249. default:
  250. var value = this._logo.blocks.blockList[this._logo.statusFields[i][0]].value;
  251. break;
  252. }
  253. var innerHTML = value;
  254. this._logo.inStatusMatrix = saveStatus;
  255. var cell = table.rows[activeTurtles + 1].cells[i + 1];
  256. if (cell != null) {
  257. cell.innerHTML = innerHTML;
  258. }
  259. }
  260. if (_THIS_IS_MUSIC_BLOCKS_) {
  261. var note = '';
  262. var value = '';
  263. if (this._logo.noteStatus[turtle] != null) {
  264. var notes = this._logo.noteStatus[turtle][0];
  265. for (var j = 0; j < notes.length; j++) {
  266. note += notes[j];
  267. if (typeof(notes[j]) === 'number') {
  268. note += 'Hz ';
  269. } else {
  270. note += ' ';
  271. }
  272. }
  273. var value = this._logo.noteStatus[turtle][1];
  274. var obj = rationalToFraction(value);
  275. note += obj[1] + '/' + obj[0];
  276. }
  277. var cell = table.rows[activeTurtles + 1].cells[i + 1];
  278. if (cell != null) {
  279. cell.innerHTML = note.replace(/#/g, '♯').replace(/b/, '♭');
  280. }
  281. }
  282. activeTurtles += 1;
  283. }
  284. this._logo.updatingStatusMatrix = false;
  285. };
  286. this._addButton = function(row, icon, iconSize, label) {
  287. var cell = row.insertCell(-1);
  288. cell.innerHTML = '&nbsp;&nbsp;<img src="header-icons/' + icon + '" title="' + label + '" alt="' + label + '" height="' + iconSize + '" width="' + iconSize + '" vertical-align="middle" align-content="center">&nbsp;&nbsp;';
  289. cell.style.width = BUTTONSIZE + 'px';
  290. cell.style.minWidth = cell.style.width;
  291. cell.style.maxWidth = cell.style.width;
  292. cell.style.height = cell.style.width;
  293. cell.style.minHeight = cell.style.height;
  294. cell.style.maxHeight = cell.style.height;
  295. cell.style.backgroundColor = MATRIXBUTTONCOLOR;
  296. cell.onmouseover=function() {
  297. this.style.backgroundColor = MATRIXBUTTONCOLORHOVER;
  298. }
  299. cell.onmouseout=function() {
  300. this.style.backgroundColor = MATRIXBUTTONCOLOR;
  301. }
  302. return cell;
  303. };
  304. };