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.

389 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 horizontal_)
  25. : value (0), isProportion (false), horizontal (horizontal_)
  26. {
  27. }
  28. Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool horizontal_)
  29. : value (absoluteDistanceFromOrigin), isProportion (false), horizontal (horizontal_)
  30. {
  31. }
  32. Coordinate::Coordinate (double absoluteDistance, const String& source, bool horizontal_)
  33. : anchor1 (source), value (absoluteDistance), isProportion (false), horizontal (horizontal_)
  34. {
  35. }
  36. Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool horizontal_)
  37. : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), horizontal (horizontal_)
  38. {
  39. }
  40. Coordinate::~Coordinate()
  41. {
  42. }
  43. const Coordinate Coordinate::getAnchorPoint1() const
  44. {
  45. return Coordinate (0.0, anchor1, horizontal);
  46. }
  47. const Coordinate Coordinate::getAnchorPoint2() const
  48. {
  49. return Coordinate (0.0, anchor2, horizontal);
  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 horizontal ? parentLeftMarkerName : parentTopMarkerName;
  58. }
  59. const String Coordinate::getExtentMarkerName() const
  60. {
  61. return horizontal ? 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, const MarkerResolver& markerResolver, int recursionCounter) const
  68. {
  69. if (isOrigin (name))
  70. return 0.0;
  71. return markerResolver.findMarker (name, horizontal)
  72. .resolve (markerResolver, recursionCounter + 1);
  73. }
  74. struct RecursivePositionException
  75. {
  76. };
  77. double Coordinate::resolve (const 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 (const 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, const 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::referencesDirectly (const String& markerName) const
  118. {
  119. jassert (markerName.isNotEmpty());
  120. return anchor1 == markerName || anchor2 == markerName;
  121. }
  122. bool Coordinate::referencesIndirectly (const String& markerName, const MarkerResolver& markerResolver) const
  123. {
  124. if (isOrigin (anchor1) && ! isProportion)
  125. return isOrigin (markerName);
  126. return referencesDirectly (markerName)
  127. || markerResolver.findMarker (anchor1, horizontal).referencesIndirectly (markerName, markerResolver)
  128. || (isProportion && markerResolver.findMarker (anchor2, horizontal).referencesIndirectly (markerName, markerResolver));
  129. }
  130. void Coordinate::skipWhitespace (const String& s, int& i)
  131. {
  132. while (CharacterFunctions::isWhitespace (s[i]))
  133. ++i;
  134. }
  135. const String Coordinate::readMarkerName (const String& s, int& i)
  136. {
  137. skipWhitespace (s, i);
  138. if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
  139. {
  140. int start = i;
  141. while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
  142. ++i;
  143. return s.substring (start, i);
  144. }
  145. return String::empty;
  146. }
  147. double Coordinate::readNumber (const String& s, int& i)
  148. {
  149. skipWhitespace (s, i);
  150. int start = i;
  151. if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
  152. ++i;
  153. while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
  154. ++i;
  155. if ((s[i] == 'e' || s[i] == 'E')
  156. && (CharacterFunctions::isDigit (s[i + 1])
  157. || s[i + 1] == '-'
  158. || s[i + 1] == '+'))
  159. {
  160. i += 2;
  161. while (CharacterFunctions::isDigit (s[i]))
  162. ++i;
  163. }
  164. const double value = s.substring (start, i).getDoubleValue();
  165. while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
  166. ++i;
  167. return value;
  168. }
  169. Coordinate::Coordinate (const String& s, bool horizontal_)
  170. : value (0), isProportion (false), horizontal (horizontal_)
  171. {
  172. int i = 0;
  173. anchor1 = readMarkerName (s, i);
  174. if (anchor1.isNotEmpty())
  175. {
  176. skipWhitespace (s, i);
  177. if (s[i] == '+')
  178. value = readNumber (s, ++i);
  179. else if (s[i] == '-')
  180. value = -readNumber (s, ++i);
  181. }
  182. else
  183. {
  184. value = readNumber (s, i);
  185. skipWhitespace (s, i);
  186. if (s[i] == '%')
  187. {
  188. isProportion = true;
  189. value /= 100.0;
  190. skipWhitespace (s, ++i);
  191. if (s[i] == '*')
  192. {
  193. anchor1 = readMarkerName (s, ++i);
  194. skipWhitespace (s, i);
  195. if (s[i] == '-' && s[i + 1] == '>')
  196. {
  197. i += 2;
  198. anchor2 = readMarkerName (s, i);
  199. }
  200. else
  201. {
  202. anchor2 = anchor1;
  203. anchor1 = getOriginMarkerName();
  204. }
  205. }
  206. else
  207. {
  208. anchor1 = getOriginMarkerName();
  209. anchor2 = getExtentMarkerName();
  210. }
  211. }
  212. }
  213. }
  214. static const String limitedAccuracyString (const double n)
  215. {
  216. return String (n, 3).trimCharactersAtEnd ("0").trimCharactersAtEnd (".");
  217. }
  218. const String Coordinate::toString() const
  219. {
  220. if (isProportion)
  221. {
  222. const String percent (limitedAccuracyString (value * 100.0));
  223. if (isOrigin (anchor1))
  224. {
  225. if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName)
  226. return percent + "%";
  227. else
  228. return percent + "% * " + checkName (anchor2);
  229. }
  230. else
  231. return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2);
  232. }
  233. else
  234. {
  235. if (isOrigin (anchor1))
  236. return limitedAccuracyString (value);
  237. else if (value > 0)
  238. return checkName (anchor1) + " + " + limitedAccuracyString (value);
  239. else if (value < 0)
  240. return checkName (anchor1) + " - " + limitedAccuracyString (-value);
  241. else
  242. return checkName (anchor1);
  243. }
  244. }
  245. const double Coordinate::getEditableValue() const
  246. {
  247. return isProportion ? value * 100.0 : value;
  248. }
  249. void Coordinate::setEditableValue (const double newValue)
  250. {
  251. value = isProportion ? newValue / 100.0 : newValue;
  252. }
  253. void Coordinate::toggleProportionality (const MarkerResolver& markerResolver)
  254. {
  255. const double oldValue = resolve (markerResolver);
  256. isProportion = ! isProportion;
  257. anchor1 = getOriginMarkerName();
  258. anchor2 = getExtentMarkerName();
  259. moveToAbsolute (oldValue, markerResolver);
  260. }
  261. void Coordinate::changeAnchor1 (const String& newMarkerName, const MarkerResolver& markerResolver)
  262. {
  263. const double oldValue = resolve (markerResolver);
  264. anchor1 = newMarkerName;
  265. moveToAbsolute (oldValue, markerResolver);
  266. }
  267. void Coordinate::changeAnchor2 (const String& newMarkerName, const MarkerResolver& markerResolver)
  268. {
  269. const double oldValue = resolve (markerResolver);
  270. anchor2 = newMarkerName;
  271. moveToAbsolute (oldValue, markerResolver);
  272. }
  273. //==============================================================================
  274. RectangleCoordinates::RectangleCoordinates()
  275. : left (true), right (true), top (false), bottom (false)
  276. {
  277. }
  278. RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect, const String& componentName)
  279. : left (rect.getX(), true),
  280. right (rect.getWidth(), componentName + ".left", true),
  281. top (rect.getY(), false),
  282. bottom (rect.getHeight(), componentName + ".top", false)
  283. {
  284. }
  285. RectangleCoordinates::RectangleCoordinates (const String& stringVersion)
  286. : left (true), right (true), top (false), bottom (false)
  287. {
  288. StringArray tokens;
  289. tokens.addTokens (stringVersion, ",", String::empty);
  290. left = Coordinate (tokens [0], true);
  291. top = Coordinate (tokens [1], false);
  292. right = Coordinate (tokens [2], true);
  293. bottom = Coordinate (tokens [3], false);
  294. }
  295. const Rectangle<int> RectangleCoordinates::resolve (const Coordinate::MarkerResolver& markerResolver) const
  296. {
  297. const int l = roundToInt (left.resolve (markerResolver));
  298. const int r = roundToInt (right.resolve (markerResolver));
  299. const int t = roundToInt (top.resolve (markerResolver));
  300. const int b = roundToInt (bottom.resolve (markerResolver));
  301. return Rectangle<int> (l, t, r - l, b - t);
  302. }
  303. void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, const Coordinate::MarkerResolver& markerResolver)
  304. {
  305. left.moveToAbsolute (newPos.getX(), markerResolver);
  306. right.moveToAbsolute (newPos.getRight(), markerResolver);
  307. top.moveToAbsolute (newPos.getY(), markerResolver);
  308. bottom.moveToAbsolute (newPos.getBottom(), markerResolver);
  309. }
  310. const String RectangleCoordinates::toString() const
  311. {
  312. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  313. }