Extra "ports" of juce-based plugins using the distrho build system
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.

1147 lines
39KB

  1. #include "CabbageTable.h"
  2. #include "CabbageUtils.h"
  3. /*
  4. ====================================================================================
  5. Cabbage Envelope Handle
  6. ====================================================================================
  7. */
  8. CabbageEnvelopeHandleComponent::CabbageEnvelopeHandleComponent(int _index, Colour colour, bool fixed)
  9. :index(_index), x(0), y(0), colour(colour), fixed(fixed)
  10. {
  11. getProperties().set(String("curveType"), LINEAR);
  12. this->setInterceptsMouseClicks(true, false);
  13. }
  14. CabbageEnvelopeHandleComponent::~CabbageEnvelopeHandleComponent()
  15. {
  16. }
  17. void CabbageEnvelopeHandleComponent::paint (Graphics& g)
  18. {
  19. g.setColour (colour);
  20. if(!fixed)
  21. g.fillEllipse (1, 1, getWidth()-2, getHeight()-2);
  22. else
  23. g.fillRect(1, 1, getWidth()-2, getHeight()-2);
  24. }
  25. Table* CabbageEnvelopeHandleComponent::getParentComponent()
  26. {
  27. return (Table*)Component::getParentComponent();
  28. }
  29. void CabbageEnvelopeHandleComponent::removeThisHandle()
  30. {
  31. getParentComponent()->removeHandle (this);
  32. }
  33. void CabbageEnvelopeHandleComponent::mouseEnter (const MouseEvent& e)
  34. {
  35. setMouseCursor (MouseCursor::DraggingHandCursor);
  36. }
  37. void CabbageEnvelopeHandleComponent::mouseUp (const MouseEvent& e)
  38. {
  39. changeMessage = "updateOnMouseUp";
  40. sendChangeMessage();
  41. }
  42. void CabbageEnvelopeHandleComponent::mouseDown (const MouseEvent& e)
  43. {
  44. x = getX();
  45. y = getY();
  46. setMouseCursor (MouseCursor::DraggingHandCursor);
  47. dragger.startDraggingComponent (this, e);
  48. if ((e.mods.isShiftDown() == true) && (e.mods.isRightButtonDown() == true))
  49. removeThisHandle();
  50. PopupMenu pop, subm;
  51. pop.setLookAndFeel(&this->getTopLevelComponent()->getLookAndFeel());
  52. subm.setLookAndFeel(&this->getTopLevelComponent()->getLookAndFeel());
  53. if(e.mods.isRightButtonDown() == true)
  54. {
  55. //pop.addItem(1, "Linear");
  56. //subm.addItem(2, "Convex");
  57. //subm.addItem(3, "Concave");
  58. //pop.addSubMenu("Expon", subm);
  59. pop.addItem(4, "Delete");
  60. int result;
  61. #if !defined(AndroidBuild)
  62. result = pop.show();
  63. #endif
  64. if(result==1)
  65. getProperties().set(String("curveType"), LINEAR);
  66. else if(result==2)
  67. getProperties().set(String("curveType"), CONVEX);
  68. else if(result==3)
  69. getProperties().set(String("curveType"), CONCAVE);
  70. else if(result==4)
  71. {
  72. changeMessage = "removeHandle";
  73. sendChangeMessage();
  74. }
  75. }
  76. }
  77. CabbageEnvelopeHandleComponent* CabbageEnvelopeHandleComponent::getPreviousHandle()
  78. {
  79. return getParentComponent()->getPreviousHandle(this);
  80. }
  81. CabbageEnvelopeHandleComponent* CabbageEnvelopeHandleComponent::getNextHandle()
  82. {
  83. return getParentComponent()->getNextHandle(this);
  84. }
  85. void CabbageEnvelopeHandleComponent::mouseDrag (const MouseEvent& e)
  86. {
  87. CabbageEnvelopeHandleComponent* previousHandle = getPreviousHandle();
  88. CabbageEnvelopeHandleComponent* nextHandle = getNextHandle();
  89. bool fixed = this->getProperties().getWithDefault("fixedPos", false);
  90. int leftLimit = previousHandle == 0 ? 0 : previousHandle->getX()+1;
  91. int rightLimit = nextHandle == 0 ? getParentWidth()-previousHandle->getHeight() : nextHandle->getX()-1;
  92. int topLimit = previousHandle == 0 ? 0 : previousHandle->getX()+1;
  93. int bottomLimit = nextHandle == 0 ? getParentWidth()-previousHandle->getHeight() : nextHandle->getX()-1;
  94. int dragX = x+e.getDistanceFromDragStartX();
  95. int dragY = y+e.getDistanceFromDragStartY();
  96. //dragger.dragComponent(this, e, &resizeLimits);
  97. if(dragX < leftLimit)
  98. dragX = leftLimit;
  99. if(dragX > rightLimit)
  100. dragX = rightLimit;
  101. if(dragY< 0)
  102. dragY = 0;
  103. if(dragY > height-5)
  104. dragY = height-5;
  105. if(fixed)
  106. dragX = x;
  107. this->setTopLeftPosition(dragX, dragY);
  108. getParentComponent()->repaint();
  109. }
  110. /*
  111. ====================================================================================
  112. Table class
  113. ====================================================================================
  114. */
  115. Table::Table (String chan, int tblNumber, int tableSize, bool fixedEnv, bool drawHorizontal, bool drawOriginal, Colour colour) :
  116. tableSize(tableSize),
  117. zoom(1.0),
  118. tableNumber(tblNumber),
  119. channel(chan),
  120. activeColour(colour),
  121. draggingHandle (0),
  122. scrubberPosition(0),
  123. isCurrentlyOnTop(false),
  124. drawOriginalTableData(drawOriginal),
  125. drawHorizontalSegments(drawHorizontal),
  126. fixedEnvelope(fixedEnv),
  127. handleWidth(8),
  128. drawFill(false),
  129. isResizing(true),
  130. useAmpRanges(false),
  131. editMode(false)
  132. {
  133. }
  134. Table::~Table()
  135. {
  136. handles.clear();
  137. }
  138. //====================================================
  139. void Table::resized()
  140. {
  141. //We need to make room for the h scrollbar, therefore table data
  142. //can't use the full height of the canvas
  143. tableTop = getHeight()*0.01;
  144. tableBottom = getHeight()*0.99;
  145. tableHeight = tableBottom - tableTop;
  146. }
  147. //====================================================
  148. void Table::setOriginalWidth(int w)
  149. {
  150. //Sets the original width from when the table was initialised
  151. origWidth = w;
  152. maxZoomForOverview = tableSize / origWidth;
  153. }
  154. //====================================================
  155. void Table::setGlobalAmpRange(float globalMaxAmp, float globalMinAmp, float globalAmpRange)
  156. {
  157. minAmp = globalMinAmp;
  158. maxAmp = globalMaxAmp;
  159. ampRange = globalAmpRange;
  160. }
  161. //====================================================
  162. void Table::createAmpOverviews (Array<float, CriticalSection> csndInputData)
  163. {
  164. //This method creates smaller amp overview arrays using the
  165. //original table data...
  166. tableData.amps.clear();
  167. tableData.y.clear();
  168. overview.maxAmps.clear();
  169. overview.minAmps.clear();
  170. tableData.amps = csndInputData;
  171. //Logger::writeToLog(String(tableData.amps.size()));
  172. zeroAmpPosition = convertAmpToPixel(0);
  173. // Filling overview arrays. The original table data is broken into
  174. //separate blocks. The max and min values are then stored from each block.
  175. //Block size is dependent on the max zoom level we use for overviews.
  176. float incr = (float)tableSize / (getWidth()*maxZoomForOverview);
  177. float maxBlockValue, minBlockValue;
  178. for (float i=0; i<tableSize; i+=incr)
  179. {
  180. maxBlockValue = minBlockValue = tableData.amps[(int)i+0.5];
  181. for (int j=0; j<(int)incr+0.5; j++) //loop for current block
  182. {
  183. if (tableData.amps[(int)i+j+0.5] > maxBlockValue)
  184. maxBlockValue = tableData.amps[(int)i+j+0.5];
  185. if (tableData.amps[(int)i+j+0.5] < minBlockValue)
  186. minBlockValue = tableData.amps[(int)i+j+0.5];
  187. }
  188. overview.maxAmps.add (maxBlockValue);
  189. overview.minAmps.add (minBlockValue);
  190. }
  191. setDataSource (zoom);
  192. //if(drawOriginalTableData==false)
  193. //createEnvPath();
  194. //makeTableEditable();
  195. }
  196. //====================================================
  197. void Table::setDataSource (int zoomValue)
  198. {
  199. //If current zoom <= max zoom for overview, then the
  200. //overview arrays are used and converted to y coordinates.
  201. //Otherwise the initial table data is used.
  202. if (zoomValue <= maxZoomForOverview)
  203. {
  204. int incrSize = maxZoomForOverview/zoomValue;
  205. float maxBlockValue, minBlockValue;
  206. overview.maxY.clear();
  207. overview.minY.clear();
  208. for (int i=0; i<overview.maxAmps.size(); i+=incrSize)
  209. {
  210. maxBlockValue = overview.maxAmps[i];
  211. minBlockValue = overview.minAmps[i];
  212. for (int j=0; j<incrSize; j++) //loop for current block
  213. {
  214. if (overview.maxAmps[i+j] > maxBlockValue)
  215. maxBlockValue = overview.maxAmps[i+j];
  216. if (overview.minAmps[i+j] < minBlockValue)
  217. minBlockValue = overview.minAmps[i+j];
  218. }
  219. overview.maxY.add (convertAmpToPixel(maxBlockValue));
  220. overview.minY.add (convertAmpToPixel(minBlockValue));
  221. useOverview = true;
  222. }
  223. }
  224. // Else we use original table data for painting
  225. else
  226. {
  227. //numPixelsPerIndex = (zoomValue*origWidth) / (float)tableSize;
  228. numPixelsPerIndex = (float)getWidth() / (float)tableSize;
  229. useOverview = false;
  230. }
  231. repaint();
  232. }
  233. //====================================================
  234. float Table::convertAmpToPixel (float ampValue)
  235. {
  236. float normalisedAmp = (ampValue-minAmp) / ampRange; //first change to normalised value
  237. if(this->toggleMaxMin)
  238. {
  239. if(ampValue == maxAmp)
  240. return 0;
  241. else if(ampValue==minAmp)
  242. return getHeight();
  243. else
  244. {
  245. return jmax(0.f, ((1-normalisedAmp) * tableHeight) + tableTop);
  246. }
  247. }
  248. else
  249. return jmax(0.f, ((1-normalisedAmp) * tableHeight) + tableTop);
  250. }
  251. //====================================================
  252. float Table::convertPixelToAmp(float pixelYValue)
  253. {
  254. //scale values between 0 and 1
  255. double yAmp = (float)(this->getHeight()-(pixelYValue))/(float)this->getHeight();
  256. //now bring them in range
  257. yAmp = (yAmp*ampRange)+minAmp;
  258. yAmp = (yAmp < minAmp ? minAmp : yAmp);
  259. yAmp = (yAmp > maxAmp ? maxAmp : yAmp);
  260. return yAmp;
  261. }
  262. //====================================================
  263. void Table::cacheBackgroundImage()
  264. {
  265. //This method creates the background image that the table data will be
  266. //painted on to. It is then stored in cache.
  267. img = Image(Image::ARGB, origWidth, getHeight(), true);
  268. Graphics g (img);
  269. ImageCache::addImageToCache (img, 15);
  270. }
  271. //====================================================
  272. void Table::paint (Graphics& g)
  273. {
  274. //Image bg = ImageCache::getFromHashCode(15);
  275. //g.drawImage (bg, viewX, 0, origWidth, getHeight(),
  276. // 0, 0, bg.getWidth(), bg.getHeight(), false);
  277. if (isCurrentlyOnTop)
  278. currColour = activeColour.withMultipliedSaturation(3);
  279. else
  280. currColour = activeColour;
  281. int startIndx, endIndx;
  282. // If using overview...
  283. if (useOverview && drawOriginalTableData)
  284. {
  285. startIndx = (viewX/getWidth()) * overview.maxY.size();
  286. endIndx = startIndx + viewWidth;
  287. float bottomYPixelValue, topYPixelValue;
  288. bottomYPixelValue = topYPixelValue = 0;
  289. // For loop which will draw a vertical line between the min and max value
  290. //for each x pixel
  291. int xPixelValue = 0;
  292. for (int i=startIndx; i<endIndx; i++)
  293. {
  294. //We need to make sure that the current min value is not greater than the
  295. //next max value. Otherwise there would be a gap in the wave form...
  296. if (overview.minY[i] < overview.maxY[i+1])
  297. bottomYPixelValue = overview.maxY[i+1];
  298. else
  299. bottomYPixelValue = overview.minY[i];
  300. if (overview.maxY[i] > overview.minY[i+1])
  301. topYPixelValue = overview.minY[i+1];
  302. else
  303. topYPixelValue = overview.maxY[i];
  304. float minGap = 0.4;
  305. float diff = bottomYPixelValue - topYPixelValue;
  306. if (diff < minGap)
  307. {
  308. bottomYPixelValue += (minGap-diff)/2;
  309. topYPixelValue -= (minGap-diff)/2;
  310. }
  311. if (CabbageUtils::isNumber(topYPixelValue) && CabbageUtils::isNumber(bottomYPixelValue))
  312. {
  313. g.setColour(currColour);
  314. topYPixelValue -= minWaveHeight/2;
  315. bottomYPixelValue += minWaveHeight/2;
  316. g.drawVerticalLine (xPixelValue+viewX, topYPixelValue, bottomYPixelValue);
  317. xPixelValue += 1;
  318. // Fill
  319. if (tableSize <= 4096)
  320. {
  321. g.setColour(currColour.withAlpha(0.1f));
  322. if (bottomYPixelValue < zeroAmpPosition)
  323. g.drawVerticalLine (xPixelValue+viewX, bottomYPixelValue, zeroAmpPosition);
  324. else if (bottomYPixelValue > zeroAmpPosition)
  325. g.drawVerticalLine (xPixelValue+viewX, zeroAmpPosition, bottomYPixelValue);
  326. if (isCurrentlyOnTop)
  327. currColour = activeColour.withMultipliedSaturation(3);
  328. else
  329. currColour = activeColour;
  330. }
  331. }
  332. }
  333. }
  334. //Else if using original array values for painting...
  335. else if (!useOverview && drawOriginalTableData)
  336. {
  337. g.setColour(currColour);
  338. startIndx = ((viewX/getWidth()) * tableData.amps.size()) + 0.5; //0.5 for rounding
  339. endIndx = (startIndx + (viewWidth/numPixelsPerIndex))+0.5;
  340. float prevX = viewX;
  341. float prevY = convertAmpToPixel (tableData.amps[startIndx]);
  342. float currY;
  343. for (int i=startIndx+1; i<=endIndx; i++)
  344. {
  345. currY = convertAmpToPixel (tableData.amps[i]);
  346. if(drawHorizontalSegments==true)
  347. {
  348. g.setColour(currColour);
  349. g.drawLine(prevX, prevY, prevX+numPixelsPerIndex, prevY, minWaveHeight);
  350. g.setColour(currColour.darker(.7));
  351. if(drawFill==true)
  352. g.fillRect(prevX, (prevY>convertAmpToPixel(0) ? prevY-minWaveHeight : prevY+minWaveHeight), numPixelsPerIndex, convertAmpToPixel(0)-prevY);
  353. }
  354. else
  355. {
  356. g.drawLine(prevX, prevY, prevX+numPixelsPerIndex, currY, minWaveHeight);
  357. // For drawing index markers
  358. if (numPixelsPerIndex > 4)
  359. g.drawVerticalLine (prevX+numPixelsPerIndex, currY-3, currY+3);
  360. }
  361. prevX = prevX + numPixelsPerIndex;
  362. prevY = currY;
  363. }
  364. }
  365. // g.setColour(Colours::lightblue);
  366. // envPath.letoleToFit (0, tableTop, getWidth(), tableHeight, false);
  367. // g.strokePath (envPath, PathStrokeType(2.0f));
  368. //----- For handles....
  369. else if(!drawOriginalTableData)
  370. {
  371. envPath.clear();
  372. for(int i = 0; i < handles.size(); i++)
  373. {
  374. CabbageEnvelopeHandleComponent* handle = handles.getUnchecked(i);
  375. int prevX, prevY;
  376. if((int)handle->getProperties().getWithDefault("curveType", 1)==0)
  377. {
  378. //for linear envelopes
  379. if(i==0)
  380. {
  381. envPath.startNewSubPath((handle->getX() + handle->getRight()) / 2,
  382. (handle->getY() + handle->getBottom()) / 2);
  383. if(toggleMaxMin)
  384. g.setColour(currColour);
  385. else
  386. g.setColour(currColour.darker(.9f));
  387. if(drawHorizontalSegments==true && fixedEnvelope == true)
  388. if(drawFill==true && !toggleMaxMin)
  389. g.fillRect(0, jmax(handle->getY(), 0), jmax(handle->getWidth(), 0), jmax(0, getHeight()-1));
  390. else if(drawFill==true && toggleMaxMin)
  391. {
  392. //g.setColour(Colours::red);
  393. g.fillRect(0, jmax(handle->getY(), 0), jmax(handle->getWidth(), 0), jmax(0, getHeight()-1));
  394. //g.drawRect(0, jmax(handle->getY(), 0), jmax(handle->getWidth(), 0), jmax(0, getHeight()-1), 2);
  395. g.setColour(currColour);
  396. }
  397. prevX = handle->getX();
  398. prevY = handle->getY();
  399. }
  400. else
  401. {
  402. if(drawFill==true)
  403. g.fillRect(jmax(handle->getX(), 0), jmax(handle->getY(), 0), jmax(handle->getWidth()-1, 0), jmax(0, getHeight()-1));
  404. if(toggleMaxMin)
  405. g.setColour(currColour);
  406. else
  407. g.setColour(currColour.darker(.9f));
  408. if(toggleMaxMin)
  409. {
  410. g.drawRect(jmax(handle->getX(), 0), jmax(handle->getY(), 0), jmax(handle->getWidth()-1, 0), jmax(0, getHeight()-1), 2);
  411. g.setColour(currColour);
  412. }
  413. if(drawHorizontalSegments==true && fixedEnvelope == false)
  414. {
  415. envPath.lineTo((handle->getX() + handle->getRight()) / 2, (prevY+(handle->getHeight()/2)));
  416. envPath.lineTo((handle->getX() + handle->getRight()) / 2, (handle->getY() + handle->getBottom()) / 2);
  417. }
  418. else if(drawHorizontalSegments==true && fixedEnvelope==true)
  419. {
  420. envPath.lineTo((handle->getX()+1), (prevY+(handle->getHeight()/2)));
  421. envPath.lineTo((handle->getX()+1), (handle->getY() + handle->getBottom()) / 2);
  422. }
  423. else
  424. {
  425. envPath.lineTo((handle->getX() + handle->getRight()) / 2,
  426. (handle->getY() + handle->getBottom()) / 2);
  427. }
  428. prevY = handle->getY();
  429. prevX = handle->getX();
  430. }
  431. }
  432. else
  433. {
  434. //for bezier envelopes
  435. if(i==0)
  436. {
  437. envPath.startNewSubPath((handle->getX() + handle->getRight()) / 2,
  438. (handle->getY() + handle->getBottom()) / 2);
  439. }
  440. else
  441. {
  442. int curX = handle->getX();
  443. int curY = handle->getY();
  444. int prevX = envPath.getCurrentPosition().getX();
  445. int prevY = envPath.getCurrentPosition().getY();
  446. //int controlPoint1
  447. if(curY>prevY)
  448. {
  449. if((int)handle->getProperties().getWithDefault("curveType", 0)==CONVEX)
  450. envPath.quadraticTo(prevX+(curX/3), curY,
  451. (curX + handle->getRight()) / 2,
  452. (curY + handle->getBottom()) / 2);
  453. else if((int)handle->getProperties().getWithDefault("curveType", 0)==CONCAVE)
  454. envPath.quadraticTo(curX-(curX/3), prevY,
  455. (curX + handle->getRight()) / 2,
  456. (curY + handle->getBottom()) / 2);
  457. }
  458. else if(curY<prevY)
  459. if((int)handle->getProperties().getWithDefault("curveType", 0)==CONVEX)
  460. envPath.quadraticTo(curX-(curX/3.f), prevY,
  461. (curX + handle->getRight()) / 2,
  462. (curY + handle->getBottom()) / 2);
  463. else if((int)handle->getProperties().getWithDefault("curveType", 0)==CONCAVE)
  464. envPath.quadraticTo(prevX+(prevX/3.f), curY,
  465. (curX + handle->getRight()) / 2,
  466. (curY + handle->getBottom()) / 2);
  467. }
  468. }
  469. //String coordinate;
  470. //coordinate = String(handle->getX()) + String(", ") + String(handle->getY());
  471. //g.setColour(Colours::lime);
  472. //g.drawFittedText(coordinate, handle->getX(), handle->getY(), 50, 20, Justification::centred, 1);
  473. //envPath.lineTo(getWidth(), getHeight());
  474. //envPath.lineTo(0, getHeight());
  475. //envPath.closeSubPath();
  476. //g.setColour(currColour.darker(.7f));
  477. //if(toggleMaxMin)
  478. // int test;
  479. //else if((drawHorizontalSegments==true && fixedEnvelope == false))
  480. //g.fillPath(envPath);
  481. //else
  482. if(!drawFill)
  483. g.strokePath (envPath, PathStrokeType(2.0f));
  484. }
  485. }
  486. //if current table is on top, place text on widget to indicate so..
  487. /*
  488. if(onTop){
  489. String text = "ftable:"+String(tableNumber);
  490. g.setColour(currColour);
  491. g.setFont(CabbageUtils::getComponentFont());
  492. int length = CabbageUtils::getComponentFont().getStringWidth(text)+10;
  493. g.drawText(text, getWidth()-length,
  494. 2,
  495. length, 12,
  496. Justification::centred, 1);
  497. }*/
  498. //draw the crubber if needed
  499. if(scrubberPosition>0)
  500. {
  501. g.setColour(currColour);
  502. scrubberPosition = scrubberPosition*getWidth();
  503. g.drawLine(scrubberPosition, 0, scrubberPosition, getHeight(), 2);
  504. }
  505. }
  506. //====================================================
  507. void Table::mouseDown (const MouseEvent& e)
  508. {
  509. if(editMode==true)
  510. if(fixedEnvelope==false)
  511. if(e.mods.isShiftDown() == true)
  512. draggingHandle = addHandle (e.x, e.y, false, handleWidth, activeColour.contrasting(.5));
  513. }
  514. //====================================================
  515. void Table::applyZoom (int zoomInput)
  516. {
  517. zoom = zoomInput;
  518. setDataSource(zoom);
  519. this->repaint();
  520. }
  521. //====================================================
  522. void Table::setToEnabled(bool isEnabled)
  523. {
  524. isCurrentlyOnTop = isEnabled;
  525. if (isCurrentlyOnTop) //if on top
  526. minWaveHeight = 3;
  527. else
  528. minWaveHeight = 1.5;
  529. }
  530. //====================================================
  531. void Table::setViewStart(float x)
  532. {
  533. viewX = x;
  534. }
  535. //====================================================
  536. void Table::setViewWidth(float width)
  537. {
  538. viewWidth = width;
  539. }
  540. //====================================================
  541. void Table::modifyHandlePos (float j)
  542. {
  543. //This function changes the x position of each envelope handle
  544. //after a zoom in or out. j will either be 2 or 0.5.
  545. for (int i=0; i<handles.size(); i++)
  546. {
  547. CabbageEnvelopeHandleComponent* handle = handles.getUnchecked (i);
  548. handle->setBounds (handle->getX() * j, handle->getY(), HANDLESIZE, HANDLESIZE);
  549. }
  550. }
  551. //====================================================
  552. void Table::createHandlesFromTable(int points)
  553. {
  554. handles.clear();
  555. Colour col;
  556. editMode=true;
  557. int x;
  558. float end = getWidth();
  559. float scaleX = .99;
  560. if(points>tableSize)
  561. {
  562. points=tableSize;
  563. fixedEnvelope = true;
  564. }
  565. float segmentIncr = (float)tableSize/(float)points;
  566. if(fixedEnvelope && drawHorizontalSegments)
  567. {
  568. handleWidth = getWidth()/tableSize;
  569. col = activeColour;
  570. scaleX = 1;
  571. }
  572. else
  573. col = activeColour.contrasting(.5);
  574. addHandle(0, convertAmpToPixel(tableData.amps[0]), true, handleWidth-2, col);
  575. for(int i=segmentIncr; i<tableSize; i+=segmentIncr)
  576. {
  577. int x = int((float(i)/(float)tableSize)*getWidth()*scaleX);
  578. addHandle(x, convertAmpToPixel(tableData.amps[i]), (i==0 || i==tableSize, fixedEnvelope), handleWidth, col);
  579. }
  580. addHandle(end, convertAmpToPixel(tableData.amps[0]), true, handleWidth, col);
  581. }
  582. //====================================================
  583. void Table::toggleMinMaxAmp(int x)
  584. {
  585. for(int i=0; i<handles.size(); i++)
  586. if((x> handles[i]->getX()) && (x<handles[i]->getRight()))
  587. {
  588. if(handles[i]->getPosition().getY()>1)
  589. handles[i]->setTopLeftPosition((i==0 ? handles[i]->getX() : handles[i]->getX()), 0);
  590. else
  591. handles[i]->setTopLeftPosition((i==0 ? handles[i]->getX() : handles[i]->getX()), getHeight());
  592. repaint();
  593. }
  594. }
  595. //====================================================
  596. void Table::setYValueOfHandle(int x, int y)
  597. {
  598. for(int i=0; i<handles.size(); i++)
  599. if((x>= handles[i]->getX()) && (x<=handles[i]->getRight()))
  600. {
  601. handles[i]->setTopLeftPosition(handles[i]->getX(), y);
  602. repaint();
  603. }
  604. }
  605. //====================================================
  606. // delete handles if user selects delete from handle popup menu
  607. //====================================================
  608. void Table::changeListenerCallback(juce::ChangeBroadcaster *source)
  609. {
  610. CabbageEnvelopeHandleComponent* hand = dynamic_cast <CabbageEnvelopeHandleComponent*> (source);
  611. if(hand)
  612. {
  613. //PathFlatteningIterator flatpath(envPath);
  614. Logger::writeToLog("Table Length in Pixels:"+String(getWidth()));
  615. Logger::writeToLog("Table Length in samples:"+String(tableSize));
  616. if(hand->changeMessage=="removeHandle")
  617. {
  618. removeHandle(hand);
  619. sendChangeMessage();
  620. }
  621. else if(hand->changeMessage=="updateOnMouseUp")
  622. {
  623. sendChangeMessage();
  624. }
  625. }
  626. //change messages being sent from this component
  627. else
  628. sendChangeMessage();
  629. }
  630. CabbageEnvelopeHandleComponent* Table::addHandle(int x, int y, bool fixedPos, int width, Colour col)
  631. {
  632. int i;
  633. for (i=0; i<handles.size(); i++)
  634. {
  635. CabbageEnvelopeHandleComponent* handle = handles.getUnchecked (i);
  636. if (handle->getX() > x)
  637. break;
  638. }
  639. CabbageEnvelopeHandleComponent* handle = new CabbageEnvelopeHandleComponent(handles.size(), col, fixedEnvelope);
  640. handle->addChangeListener(this);
  641. handle->getProperties().set("fixedPos", fixedPos);
  642. handle->height = getHeight();
  643. handle->width = getWidth();
  644. //if(this->toggleMaxMin)
  645. //handle->setBounds (x, y, width, 3);
  646. //else
  647. handle->setBounds (x, y, width, HANDLESIZE);
  648. addAndMakeVisible (handle);
  649. handles.insert (i, handle);
  650. repaint();
  651. return handle;
  652. }
  653. int Table::getHandleIndex(CabbageEnvelopeHandleComponent* thisHandle)
  654. {
  655. return handles.indexOf(thisHandle);
  656. }
  657. CabbageEnvelopeHandleComponent* Table::getPreviousHandle(CabbageEnvelopeHandleComponent* thisHandle)
  658. {
  659. int thisHandleIndex = handles.indexOf(thisHandle);
  660. if(thisHandleIndex <= 0)
  661. return 0;
  662. else
  663. return handles.getUnchecked(thisHandleIndex-1);
  664. }
  665. CabbageEnvelopeHandleComponent* Table::getNextHandle(CabbageEnvelopeHandleComponent* thisHandle)
  666. {
  667. int thisHandleIndex = handles.indexOf(thisHandle);
  668. if(thisHandleIndex == -1 || thisHandleIndex >= handles.size()-1)
  669. return 0;
  670. else
  671. return handles.getUnchecked(thisHandleIndex+1);
  672. }
  673. void Table::removeHandle (CabbageEnvelopeHandleComponent* thisHandle)
  674. {
  675. if (handles.size() > 0)
  676. {
  677. handles.removeObject(thisHandle, true);
  678. repaint();
  679. }
  680. }
  681. /*
  682. ====================================================================================
  683. Table Manager class
  684. ====================================================================================
  685. */
  686. CabbageTableManager::CabbageTableManager(int tableSize)
  687. : alpha(1.0f), zoom(1), maxZoom(1), maxNumPixelsPerIndex(1),
  688. globalMaxAmp(0.f), globalMinAmp(0.f), tableSize(tableSize), readOnly(false)
  689. {
  690. }
  691. CabbageTableManager::~CabbageTableManager()
  692. {
  693. tables.clear();
  694. }
  695. void CabbageTableManager::resized()
  696. {
  697. //We need to make room for the h scrollbar, therefore table data
  698. //can't use the full height of the canvas
  699. tableTop = getHeight()*0.15;
  700. tableBottom = getHeight()*0.85;
  701. tableHeight = tableBottom - tableTop;
  702. }
  703. void CabbageTableManager::setOriginalWidth(float width)
  704. {
  705. originalWidth = width;
  706. }
  707. Table* CabbageTableManager::getTable(int index)
  708. {
  709. if(index > 0 && index < tables.size());
  710. return tables[index];
  711. }
  712. void CabbageTableManager::paint(Graphics& g)
  713. {
  714. g.setColour(CabbageUtils::getDarkerBackgroundSkin());
  715. g.fillAll();
  716. //this needs to be updated so that we can stables on top of each other
  717. if(toggleMode)
  718. {
  719. float incr = (float)getWidth()/(float)tableSize;
  720. g.setColour (Colours::whitesmoke.withAlpha(.5f));
  721. for(float i=0; i<getWidth()+1; i+=(incr))
  722. g.drawLine((i==0 ? 0 : i-1), 0, (i==0 ? 0 : i-1), getHeight()-.1, .5);
  723. int rowHeight = getHeight()/(getNumberOfTables());
  724. for(int i=0; i<getNumberOfTables()+1; i++)
  725. g.drawLine(0, i*rowHeight, getWidth(), i*rowHeight, .5);
  726. }
  727. // Amp horizontal markers...
  728. else
  729. {
  730. if(getHeight()>40)
  731. {
  732. g.setColour (Colour::fromRGBA (220, 220, 240, 255));
  733. g.drawLine (0, convertAmpToPixel(globalMaxAmp), getWidth(), convertAmpToPixel(globalMaxAmp), 0.1);
  734. g.drawLine (0, convertAmpToPixel(globalMinAmp), getWidth(), convertAmpToPixel(globalMinAmp), 0.1);
  735. g.drawLine (0, convertAmpToPixel(globalMaxAmp*0.5), getWidth(), convertAmpToPixel(globalMaxAmp*0.5), 0.1);
  736. g.drawLine (0, convertAmpToPixel(globalMinAmp*0.5), getWidth(), convertAmpToPixel(globalMinAmp*0.5), 0.1);
  737. if (globalMinAmp < 0)
  738. g.drawLine (0, convertAmpToPixel(0), getWidth(), convertAmpToPixel(0), 0.3);
  739. }
  740. else
  741. {
  742. //draw vertical markers
  743. /*
  744. float incr = (float)getWidth()/(float)tableSize;
  745. g.setColour (Colour::fromRGBA (220, 220, 240, 255));
  746. for(int i=0;i<getWidth();i+=(incr))
  747. g.drawLine(i, 0, i, getHeight()-.1, 0.1);
  748. g.drawLine (0, getHeight()-.1, getWidth(), getHeight()-.1, 0.1);
  749. g.drawLine (0, 0, getWidth(), 0, 0.1);
  750. */
  751. }
  752. }
  753. // update tables
  754. for (int i=0; i<tables.size(); ++i)
  755. tables[i]->setViewStart(0);
  756. }
  757. float CabbageTableManager::convertAmpToPixel (float ampValue)
  758. {
  759. // This method converts amps to y pixel values
  760. float normalisedAmp = (ampValue-globalMinAmp) / globalAmpRange; //first change to normalised value
  761. return jmax(0.f, ((1-normalisedAmp) * tableHeight) + tableTop);
  762. }
  763. void CabbageTableManager::addTable (String name,
  764. String channel,
  765. int tableNumber,
  766. int tableSize,
  767. bool fixedEnv,
  768. bool drawHoriz,
  769. bool drawOrig,
  770. bool toggleMaxMin,
  771. bool drawFill,
  772. int resizeMode,
  773. Point<float> minMax,
  774. Colour colour,
  775. bool readonly,
  776. bool stackMode,
  777. int numberOfTables,
  778. ChangeListener* listener)
  779. {
  780. int i = tables.size();
  781. tables.add (new Table(channel, tableNumber, tableSize, fixedEnv, drawHoriz, drawOrig, colour));
  782. if(stackMode==true)
  783. tables[i]->setBounds (0, i*(getHeight()/(numberOfTables)), getWidth(), (getHeight()/(numberOfTables)));
  784. else
  785. tables[i]->setBounds (0, 0, getWidth(), getHeight());
  786. tables[i]->addChangeListener(listener);
  787. tables[i]->setOriginalWidth (getWidth());
  788. tables[i]->setViewWidth(getWidth());
  789. tables[i]->toggleMaxMin = toggleMaxMin;
  790. toggleMode = toggleMaxMin;
  791. tables[i]->drawFill = drawFill;
  792. readOnly = readonly;
  793. addAndMakeVisible (tables[i]);
  794. tables[i]->addMouseListener(this, false); //"this" will now also get mouse events for tables[i]
  795. tableToTop(i); //setting this table to the top
  796. Logger::writeToLog("MinRange"+String(minMax.getX()));
  797. Logger::writeToLog("MaxRange"+String(minMax.getY()));
  798. if(resizeMode==1)
  799. tables[i]->isResizing = true;
  800. if(minMax.getX()!=minMax.getY())
  801. tables[i]->useAmpRanges=true;
  802. tables[i]->minMaxAmps = minMax;
  803. //the max possible zoom is based on the table with the most indices. It will have a max zoom
  804. //of 50 pixels for every index.
  805. if ((tableSize*50) > maxNumPixelsPerIndex)
  806. {
  807. maxNumPixelsPerIndex = (tableSize*50);
  808. maxZoom = sqrt(maxNumPixelsPerIndex);
  809. }
  810. }
  811. void CabbageTableManager::tableToTop (int tableIndex)
  812. {
  813. jassert(tables[tableIndex]);
  814. tables[tableIndex]->toFront(true);
  815. Logger::writeToLog("Table on top is"+String(tables[tableIndex]->tableNumber));
  816. for(int i=0; i<tables.size(); i++)
  817. {
  818. tables[i]->setToEnabled(false); //disabling the previously active table
  819. tables[i]->setAlpha(0.7);
  820. }
  821. if (tables.size() > 1)
  822. {
  823. tables.swap(tableIndex, tables.size()-1); //swapping currently active table to end of array
  824. }
  825. tables.getLast()->setToEnabled(true);
  826. tables.getLast()->setAlpha(1.0);
  827. }
  828. void CabbageTableManager::fillTable (int tableIndex, Array<float, CriticalSection> csndInputData)
  829. {
  830. if(isPositiveAndBelow(tableIndex, tables.size()))
  831. {
  832. float currTableMaxAmp, currTableMinAmp;
  833. if(tables[tableIndex]->isResizing==0)
  834. globalMaxAmp = globalMinAmp = 0;
  835. // Getting the min and max amplitude values....
  836. currTableMinAmp = currTableMaxAmp = csndInputData.getFirst();
  837. for (int i=0; i<csndInputData.size(); ++i)
  838. {
  839. //Logger::writeToLog(String(csndInputData[i]));
  840. if (csndInputData[i] > currTableMaxAmp)
  841. currTableMaxAmp = csndInputData[i];
  842. if (csndInputData[i] < currTableMinAmp)
  843. currTableMinAmp = csndInputData[i];
  844. }
  845. //if min and max amps are the same value....
  846. if (currTableMinAmp == currTableMaxAmp)
  847. {
  848. if (currTableMinAmp > 0)
  849. currTableMinAmp = 0;
  850. else if (currTableMinAmp < 0)
  851. {
  852. currTableMaxAmp = 0;
  853. }
  854. else //else if both are 0
  855. {
  856. currTableMinAmp = -1;
  857. currTableMaxAmp = 1;
  858. }
  859. }
  860. if (currTableMaxAmp > globalMaxAmp)
  861. globalMaxAmp = currTableMaxAmp;
  862. if (currTableMinAmp < globalMinAmp)
  863. globalMinAmp = currTableMinAmp;
  864. if(tables[tableIndex]->useAmpRanges)
  865. {
  866. globalMinAmp = tables[tableIndex]->minMaxAmps.getX();
  867. globalMaxAmp = tables[tableIndex]->minMaxAmps.getY();
  868. }
  869. globalAmpRange = globalMaxAmp - globalMinAmp;
  870. for (int i=0; i<tables.size(); ++i)
  871. tables[i]->setGlobalAmpRange(globalMaxAmp, globalMinAmp, globalAmpRange);
  872. tables[tableIndex]->createAmpOverviews((csndInputData));
  873. if(tables[tableIndex]->drawHorizontalSegments &&
  874. tables[tableIndex]->fixedEnvelope &&
  875. tables[tableIndex]->toggleMaxMin)
  876. {
  877. tables[tableIndex]->createHandlesFromTable(16);
  878. tables[tableIndex]->setInterceptsMouseClicks(false,false);
  879. }
  880. }
  881. //Logger::writeToLog("MaxAmp="+String(globalMaxAmp));
  882. //Logger::writeToLog("MinAmp="+String(globalMinAmp));
  883. }
  884. void CabbageTableManager::mouseDrag(const MouseEvent& e)
  885. {
  886. //if in toggle mode, draw on mouse move
  887. if(e.mods.isLeftButtonDown())
  888. for(int i=0; i<tables.size(); i++)
  889. {
  890. if(tables[i]->toggleMaxMin)
  891. {
  892. if(tables[i]->getBounds().contains(Point<int>(e.getPosition().getX(), e.getPosition().getY())))
  893. {
  894. tables[i]->toggleMinMaxAmp(e.getPosition().getX());
  895. tables[i]->changeMessage = "updateFunctionTable";
  896. tables[i]->sendChangeMessage();
  897. }
  898. }
  899. }
  900. }
  901. void CabbageTableManager::mouseMove(const MouseEvent& e)
  902. {
  903. }
  904. void CabbageTableManager::mouseDown (const MouseEvent& e)
  905. {
  906. if(e.mods.isPopupMenu())
  907. {
  908. PopupMenu pop, subMenu1, subMenu2;
  909. pop.setLookAndFeel(&getTopLevelComponent()->getLookAndFeel());
  910. subMenu1.setLookAndFeel(&getTopLevelComponent()->getLookAndFeel());
  911. subMenu2.setLookAndFeel(&getTopLevelComponent()->getLookAndFeel());
  912. subMenu1.addItem(101, "1 segment");
  913. subMenu1.addItem(102, "2 segments");
  914. subMenu1.addItem(104, "4 segments");
  915. subMenu1.addItem(108, "8 segments");
  916. subMenu1.addItem(112, "12 segments");
  917. subMenu1.addItem(116, "16 segments");
  918. subMenu1.addItem(120, "20 segments");
  919. subMenu1.addItem(124, "24 segments");
  920. subMenu1.addItem(128, "28 segments");
  921. subMenu1.addItem(132, "32 segments");
  922. for(int i=0; i<tables.size(); i++)
  923. subMenu2.addColouredItem(200+i, "fTable:"+String(tables[i]->tableNumber), tables[i]->currColour);
  924. if(!readOnly)
  925. {
  926. pop.addSubMenu(TRANS("Edit..."), subMenu1);
  927. pop.addSubMenu(TRANS("Table to front..."), subMenu2);
  928. pop.addItem(300, "Replace existing table");
  929. pop.addItem(301, "Add table to score");
  930. }
  931. int choice;
  932. #if !defined(AndroidBuild)
  933. choice = pop.show();
  934. #endif
  935. if((choice>=100) && (choice<200))
  936. {
  937. for(int i=0; i<tables.size(); i++)
  938. {
  939. if(tables[i]->isCurrentlyOnTop)
  940. {
  941. //Logger::writeToLog("currentOnTop:"+String(tables[i]->tableNumber));
  942. tables[i]->createHandlesFromTable(choice-100);
  943. tables[i]->drawOriginalTableData = false;
  944. }
  945. }
  946. }
  947. else if(choice>=200 && choice<300)
  948. {
  949. this->tableToTop(choice-200);
  950. }
  951. else if(choice==300)
  952. {
  953. for(int i=0; i<tables.size(); i++)
  954. {
  955. if(tables[i]->isCurrentlyOnTop)
  956. {
  957. tables[i]->changeMessage = "overwriteFunctionTable";
  958. tables[i]->sendChangeMessage();
  959. }
  960. }
  961. }
  962. else if(choice==301)
  963. {
  964. for(int i=0; i<tables.size(); i++)
  965. {
  966. if(tables[i]->isCurrentlyOnTop)
  967. {
  968. tables[i]->changeMessage = "writeNewFunctionTable";
  969. tables[i]->sendChangeMessage();
  970. }
  971. }
  972. }
  973. }
  974. //if left button is pressed..
  975. else
  976. {
  977. for(int i=0; i<tables.size(); i++)
  978. {
  979. if(tables[i]->editMode)
  980. if(tables[i]->toggleMaxMin)
  981. {
  982. if(tables[i]->getBounds().contains(Point<int>(e.getMouseDownX(), e.getMouseDownY())))
  983. {
  984. tables[i]->toggleMinMaxAmp(e.getMouseDownX());
  985. tables[i]->changeMessage = "updateFunctionTable";
  986. tables[i]->sendChangeMessage();
  987. }
  988. }
  989. else if(tables[i]->isCurrentlyOnTop)
  990. {
  991. tables[i]->toggleMinMaxAmp(e.getMouseDownX());
  992. tables[i]->changeMessage = "updateFunctionTable";
  993. tables[i]->sendChangeMessage();
  994. }
  995. }
  996. }
  997. }