The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

393 lines
11KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-9 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #include "jucer_Coordinate.h"
  19. //==============================================================================
  20. const char* Coordinate::parentLeftMarkerName = "parent.left";
  21. const char* Coordinate::parentRightMarkerName = "parent.right";
  22. const char* Coordinate::parentTopMarkerName = "parent.top";
  23. const char* Coordinate::parentBottomMarkerName = "parent.bottom";
  24. Coordinate::Coordinate (bool isHorizontal_)
  25. : value (0), isProportion (false), isHorizontal (isHorizontal_)
  26. {
  27. }
  28. Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_)
  29. : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_)
  30. {
  31. }
  32. Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_)
  33. : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_)
  34. {
  35. }
  36. Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_)
  37. : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_)
  38. {
  39. }
  40. Coordinate::~Coordinate()
  41. {
  42. }
  43. const Coordinate Coordinate::getAnchorPoint1() const
  44. {
  45. return Coordinate (0.0, anchor1, isHorizontal);
  46. }
  47. const Coordinate Coordinate::getAnchorPoint2() const
  48. {
  49. return Coordinate (0.0, anchor2, isHorizontal);
  50. }
  51. bool Coordinate::isOrigin (const String& name)
  52. {
  53. return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName;
  54. }
  55. const String Coordinate::getOriginMarkerName() const
  56. {
  57. return isHorizontal ? parentLeftMarkerName : parentTopMarkerName;
  58. }
  59. const String Coordinate::getExtentMarkerName() const
  60. {
  61. return isHorizontal ? parentRightMarkerName : parentBottomMarkerName;
  62. }
  63. const String Coordinate::checkName (const String& name) const
  64. {
  65. return name.isEmpty() ? getOriginMarkerName() : name;
  66. }
  67. double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const
  68. {
  69. if (isOrigin (name))
  70. return 0.0;
  71. return markerResolver.findMarker (name, isHorizontal)
  72. .resolve (markerResolver, recursionCounter + 1);
  73. }
  74. struct RecursivePositionException
  75. {
  76. };
  77. double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const
  78. {
  79. if (recursionCounter > 100)
  80. {
  81. jassertfalse
  82. throw RecursivePositionException();
  83. }
  84. const double pos1 = getPosition (anchor1, markerResolver, recursionCounter);
  85. return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value
  86. : pos1 + value;
  87. }
  88. double Coordinate::resolve (MarkerResolver& markerResolver) const
  89. {
  90. try
  91. {
  92. return resolve (markerResolver, 0);
  93. }
  94. catch (RecursivePositionException&)
  95. {}
  96. return 0.0;
  97. }
  98. void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver)
  99. {
  100. try
  101. {
  102. const double pos1 = getPosition (anchor1, markerResolver, 0);
  103. if (isProportion)
  104. {
  105. const double size = getPosition (anchor2, markerResolver, 0) - pos1;
  106. if (size != 0)
  107. value = (newPos - pos1) / size;
  108. }
  109. else
  110. {
  111. value = newPos - pos1;
  112. }
  113. }
  114. catch (RecursivePositionException&)
  115. {}
  116. }
  117. bool Coordinate::isRecursive (MarkerResolver& markerResolver) const
  118. {
  119. try
  120. {
  121. resolve (markerResolver, 0);
  122. }
  123. catch (RecursivePositionException&)
  124. {
  125. return true;
  126. }
  127. return false;
  128. }
  129. void Coordinate::skipWhitespace (const String& s, int& i)
  130. {
  131. while (CharacterFunctions::isWhitespace (s[i]))
  132. ++i;
  133. }
  134. const String Coordinate::readMarkerName (const String& s, int& i)
  135. {
  136. skipWhitespace (s, i);
  137. if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
  138. {
  139. int start = i;
  140. while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
  141. ++i;
  142. return s.substring (start, i);
  143. }
  144. return String::empty;
  145. }
  146. double Coordinate::readNumber (const String& s, int& i)
  147. {
  148. skipWhitespace (s, i);
  149. int start = i;
  150. if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
  151. ++i;
  152. while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
  153. ++i;
  154. if ((s[i] == 'e' || s[i] == 'E')
  155. && (CharacterFunctions::isDigit (s[i + 1])
  156. || s[i + 1] == '-'
  157. || s[i + 1] == '+'))
  158. {
  159. i += 2;
  160. while (CharacterFunctions::isDigit (s[i]))
  161. ++i;
  162. }
  163. const double value = s.substring (start, i).getDoubleValue();
  164. while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
  165. ++i;
  166. return value;
  167. }
  168. Coordinate::Coordinate (const String& s, bool isHorizontal_)
  169. : value (0), isProportion (false), isHorizontal (isHorizontal_)
  170. {
  171. int i = 0;
  172. anchor1 = readMarkerName (s, i);
  173. if (anchor1.isNotEmpty())
  174. {
  175. skipWhitespace (s, i);
  176. if (s[i] == '+')
  177. value = readNumber (s, ++i);
  178. else if (s[i] == '-')
  179. value = -readNumber (s, ++i);
  180. }
  181. else
  182. {
  183. value = readNumber (s, i);
  184. skipWhitespace (s, i);
  185. if (s[i] == '%')
  186. {
  187. isProportion = true;
  188. value /= 100.0;
  189. skipWhitespace (s, ++i);
  190. if (s[i] == '*')
  191. {
  192. anchor1 = readMarkerName (s, ++i);
  193. skipWhitespace (s, i);
  194. if (s[i] == '-' && s[i + 1] == '>')
  195. {
  196. i += 2;
  197. anchor2 = readMarkerName (s, i);
  198. }
  199. else
  200. {
  201. anchor2 = anchor1;
  202. anchor1 = getOriginMarkerName();
  203. }
  204. }
  205. else
  206. {
  207. anchor1 = getOriginMarkerName();
  208. anchor2 = getExtentMarkerName();
  209. }
  210. }
  211. }
  212. }
  213. static const String limitedAccuracyString (const double n)
  214. {
  215. return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd (".");
  216. }
  217. const String Coordinate::toString() const
  218. {
  219. if (isProportion)
  220. {
  221. const String percent (limitedAccuracyString (value * 100.0));
  222. if (isOrigin (anchor1))
  223. {
  224. if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName)
  225. return percent + "%";
  226. else
  227. return percent + "% * " + checkName (anchor2);
  228. }
  229. else
  230. return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2);
  231. }
  232. else
  233. {
  234. if (isOrigin (anchor1))
  235. return limitedAccuracyString (value);
  236. else if (value > 0)
  237. return checkName (anchor1) + " + " + limitedAccuracyString (value);
  238. else if (value < 0)
  239. return checkName (anchor1) + " - " + limitedAccuracyString (-value);
  240. else
  241. return checkName (anchor1);
  242. }
  243. }
  244. const double Coordinate::getEditableValue() const
  245. {
  246. return isProportion ? value * 100.0 : value;
  247. }
  248. void Coordinate::setEditableValue (const double newValue)
  249. {
  250. value = isProportion ? newValue / 100.0 : newValue;
  251. }
  252. void Coordinate::toggleProportionality (MarkerResolver& markerResolver)
  253. {
  254. const double oldValue = resolve (markerResolver);
  255. isProportion = ! isProportion;
  256. anchor1 = getOriginMarkerName();
  257. anchor2 = getExtentMarkerName();
  258. moveToAbsolute (oldValue, markerResolver);
  259. }
  260. void Coordinate::changeAnchor1 (const String& newMarkerName, MarkerResolver& markerResolver)
  261. {
  262. const double oldValue = resolve (markerResolver);
  263. anchor1 = newMarkerName;
  264. moveToAbsolute (oldValue, markerResolver);
  265. }
  266. void Coordinate::changeAnchor2 (const String& newMarkerName, MarkerResolver& markerResolver)
  267. {
  268. const double oldValue = resolve (markerResolver);
  269. anchor2 = newMarkerName;
  270. moveToAbsolute (oldValue, markerResolver);
  271. }
  272. //==============================================================================
  273. RectangleCoordinates::RectangleCoordinates()
  274. : left (true), right (true), top (false), bottom (false)
  275. {
  276. }
  277. RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect)
  278. : left (rect.getX(), true),
  279. right (rect.getWidth(), "left", true),
  280. top (rect.getY(), false),
  281. bottom (rect.getHeight(), "top", false)
  282. {
  283. }
  284. RectangleCoordinates::RectangleCoordinates (const String& stringVersion)
  285. : left (true), right (true), top (false), bottom (false)
  286. {
  287. StringArray tokens;
  288. tokens.addTokens (stringVersion, ",", String::empty);
  289. left = Coordinate (tokens [0], true);
  290. top = Coordinate (tokens [1], false);
  291. right = Coordinate (tokens [2], true);
  292. bottom = Coordinate (tokens [3], false);
  293. }
  294. bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const
  295. {
  296. return left.isRecursive (markerResolver) || right.isRecursive (markerResolver)
  297. || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver);
  298. }
  299. const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) const
  300. {
  301. const int l = roundToInt (left.resolve (markerResolver));
  302. const int r = roundToInt (right.resolve (markerResolver));
  303. const int t = roundToInt (top.resolve (markerResolver));
  304. const int b = roundToInt (bottom.resolve (markerResolver));
  305. return Rectangle<int> (l, t, r - l, b - t);
  306. }
  307. void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver)
  308. {
  309. left.moveToAbsolute (newPos.getX(), markerResolver);
  310. right.moveToAbsolute (newPos.getRight(), markerResolver);
  311. top.moveToAbsolute (newPos.getY(), markerResolver);
  312. bottom.moveToAbsolute (newPos.getBottom(), markerResolver);
  313. }
  314. const String RectangleCoordinates::toString() const
  315. {
  316. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  317. }