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.

831 lines
25KB

  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_Headers.h"
  19. //==============================================================================
  20. int64 calculateStreamHashCode (InputStream& in)
  21. {
  22. int64 t = 0;
  23. const int bufferSize = 4096;
  24. HeapBlock <uint8> buffer;
  25. buffer.malloc (bufferSize);
  26. for (;;)
  27. {
  28. const int num = in.read (buffer, bufferSize);
  29. if (num <= 0)
  30. break;
  31. for (int i = 0; i < num; ++i)
  32. t = t * 65599 + buffer[i];
  33. }
  34. return t;
  35. }
  36. int64 calculateFileHashCode (const File& file)
  37. {
  38. ScopedPointer <FileInputStream> stream (file.createInputStream());
  39. return stream != 0 ? calculateStreamHashCode (*stream) : 0;
  40. }
  41. bool areFilesIdentical (const File& file1, const File& file2)
  42. {
  43. return file1.getSize() == file2.getSize()
  44. && calculateFileHashCode (file1) == calculateFileHashCode (file2);
  45. }
  46. bool overwriteFileWithNewDataIfDifferent (const File& file, const char* data, int numBytes)
  47. {
  48. if (file.getSize() == numBytes)
  49. {
  50. MemoryInputStream newStream (data, numBytes, false);
  51. if (calculateStreamHashCode (newStream) == calculateFileHashCode (file))
  52. return true;
  53. }
  54. TemporaryFile temp (file);
  55. return temp.getFile().appendData (data, numBytes)
  56. && temp.overwriteTargetFileWithTemporary();
  57. }
  58. bool overwriteFileWithNewDataIfDifferent (const File& file, const MemoryOutputStream& newData)
  59. {
  60. return overwriteFileWithNewDataIfDifferent (file, newData.getData(), newData.getDataSize());
  61. }
  62. bool overwriteFileWithNewDataIfDifferent (const File& file, const String& newData)
  63. {
  64. return overwriteFileWithNewDataIfDifferent (file, newData.toUTF8(), strlen ((const char*) newData.toUTF8()));
  65. }
  66. bool containsAnyNonHiddenFiles (const File& folder)
  67. {
  68. DirectoryIterator di (folder, false);
  69. while (di.next())
  70. if (! di.getFile().isHidden())
  71. return true;
  72. return false;
  73. }
  74. //==============================================================================
  75. const int64 hashCode64 (const String& s)
  76. {
  77. return s.hashCode64() + s.length() * s.hashCode() + s.toUpperCase().hashCode();
  78. }
  79. const String createAlphaNumericUID()
  80. {
  81. String uid;
  82. static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  83. Random r (Random::getSystemRandom().nextInt64());
  84. for (int i = 9; --i >= 0;)
  85. {
  86. r.setSeedRandomly();
  87. uid << (juce_wchar) chars [r.nextInt (sizeof (chars))];
  88. }
  89. return uid;
  90. }
  91. const String randomHexString (Random& random, int numChars)
  92. {
  93. String s;
  94. const char hexChars[] = "0123456789ABCDEF";
  95. while (--numChars >= 0)
  96. s << hexChars [random.nextInt (16)];
  97. return s;
  98. }
  99. const String hexString8Digits (int value)
  100. {
  101. return String::toHexString (value).paddedLeft ('0', 8);
  102. }
  103. const String createGUID (const String& seed)
  104. {
  105. String guid;
  106. Random r (hashCode64 (seed + "_jucersalt"));
  107. guid << "{" << randomHexString (r, 8); // (written as separate statements to enforce the order of execution)
  108. guid << "-" << randomHexString (r, 4);
  109. guid << "-" << randomHexString (r, 4);
  110. guid << "-" << randomHexString (r, 4);
  111. guid << "-" << randomHexString (r, 12) << "}";
  112. return guid;
  113. }
  114. const String unixStylePath (const String& path)
  115. {
  116. return path.replaceCharacter ('\\', '/');
  117. }
  118. const String windowsStylePath (const String& path)
  119. {
  120. return path.replaceCharacter ('/', '\\');
  121. }
  122. const String appendPath (const String& path, const String& subpath)
  123. {
  124. if (File::isAbsolutePath (subpath)
  125. || subpath.startsWithChar ('$')
  126. || subpath.startsWithChar ('~')
  127. || (CharacterFunctions::isLetter (subpath[0]) && subpath[1] == ':'))
  128. return subpath.replaceCharacter ('\\', '/');
  129. String path1 (path.replaceCharacter ('\\', '/'));
  130. if (! path1.endsWithChar ('/'))
  131. path1 << '/';
  132. return path1 + subpath.replaceCharacter ('\\', '/');
  133. }
  134. bool shouldPathsBeRelative (String path1, String path2)
  135. {
  136. path1 = unixStylePath (path1);
  137. path2 = unixStylePath (path2);
  138. const int len = jmin (path1.length(), path2.length());
  139. int commonBitLength = 0;
  140. for (int i = 0; i < len; ++i)
  141. {
  142. if (CharacterFunctions::toLowerCase (path1[i]) != CharacterFunctions::toLowerCase (path2[i]))
  143. break;
  144. ++commonBitLength;
  145. }
  146. return path1.substring (0, commonBitLength).removeCharacters ("/:").isNotEmpty();
  147. }
  148. const String createIncludeStatement (const File& includeFile, const File& targetFile)
  149. {
  150. return "#include \"" + unixStylePath (includeFile.getRelativePathFrom (targetFile.getParentDirectory()))
  151. + "\"";
  152. }
  153. const String makeHeaderGuardName (const File& file)
  154. {
  155. return "__" + file.getFileName().toUpperCase()
  156. .replaceCharacters (" .", "__")
  157. .retainCharacters ("_ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
  158. + "_"
  159. + String::toHexString (file.hashCode()).toUpperCase()
  160. + "__";
  161. }
  162. //==============================================================================
  163. bool isJuceFolder (const File& folder)
  164. {
  165. return folder.getFileName().containsIgnoreCase ("juce")
  166. && folder.getChildFile ("juce.h").exists()
  167. && folder.getChildFile ("juce_Config.h").exists();
  168. }
  169. static const File lookInFolderForJuceFolder (const File& folder)
  170. {
  171. for (DirectoryIterator di (folder, false, "*juce*", File::findDirectories); di.next();)
  172. {
  173. if (isJuceFolder (di.getFile()))
  174. return di.getFile();
  175. }
  176. return File::nonexistent;
  177. }
  178. const File findParentJuceFolder (const File& file)
  179. {
  180. File f (file);
  181. while (f.exists() && f.getParentDirectory() != f)
  182. {
  183. if (isJuceFolder (f))
  184. return f;
  185. File found = lookInFolderForJuceFolder (f);
  186. if (found.exists())
  187. return found;
  188. f = f.getParentDirectory();
  189. }
  190. return File::nonexistent;
  191. }
  192. const File findDefaultJuceFolder()
  193. {
  194. File f = findParentJuceFolder (File::getSpecialLocation (File::currentApplicationFile));
  195. if (! f.exists())
  196. f = lookInFolderForJuceFolder (File::getSpecialLocation (File::userHomeDirectory));
  197. if (! f.exists())
  198. f = lookInFolderForJuceFolder (File::getSpecialLocation (File::userDocumentsDirectory));
  199. return f;
  200. }
  201. //==============================================================================
  202. const String replaceCEscapeChars (const String& s)
  203. {
  204. const int len = s.length();
  205. String r;
  206. r.preallocateStorage (len + 2);
  207. bool lastWasHexEscapeCode = false;
  208. for (int i = 0; i < len; ++i)
  209. {
  210. const tchar c = s[i];
  211. switch (c)
  212. {
  213. case '\t': r << "\\t"; lastWasHexEscapeCode = false; break;
  214. case '\r': r << "\\r"; lastWasHexEscapeCode = false; break;
  215. case '\n': r << "\\n"; lastWasHexEscapeCode = false; break;
  216. case '\\': r << "\\\\"; lastWasHexEscapeCode = false; break;
  217. case '\'': r << "\\\'"; lastWasHexEscapeCode = false; break;
  218. case '\"': r << "\\\""; lastWasHexEscapeCode = false; break;
  219. default:
  220. if (c < 128
  221. && ! (lastWasHexEscapeCode
  222. && String ("0123456789abcdefABCDEF").containsChar (c))) // (have to avoid following a hex escape sequence with a valid hex digit)
  223. {
  224. r << c;
  225. lastWasHexEscapeCode = false;
  226. }
  227. else
  228. {
  229. r << "\\x" << String::toHexString ((int) c);
  230. lastWasHexEscapeCode = true;
  231. }
  232. break;
  233. }
  234. }
  235. return r;
  236. }
  237. //==============================================================================
  238. const String makeValidCppIdentifier (String s,
  239. const bool capitalise,
  240. const bool removeColons,
  241. const bool allowTemplates)
  242. {
  243. if (removeColons)
  244. s = s.replaceCharacters (".,;:/@", "______");
  245. else
  246. s = s.replaceCharacters (".,;/@", "_____");
  247. int i;
  248. for (i = s.length(); --i > 0;)
  249. if (CharacterFunctions::isLetter (s[i])
  250. && CharacterFunctions::isLetter (s[i - 1])
  251. && CharacterFunctions::isUpperCase (s[i])
  252. && ! CharacterFunctions::isUpperCase (s[i - 1]))
  253. s = s.substring (0, i) + " " + s.substring (i);
  254. String allowedChars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_ 0123456789");
  255. if (allowTemplates)
  256. allowedChars += "<>";
  257. if (! removeColons)
  258. allowedChars += ":";
  259. StringArray words;
  260. words.addTokens (s.retainCharacters (allowedChars), false);
  261. words.trim();
  262. String n (words[0]);
  263. if (capitalise)
  264. n = n.toLowerCase();
  265. for (i = 1; i < words.size(); ++i)
  266. {
  267. if (capitalise && words[i].length() > 1)
  268. n << words[i].substring (0, 1).toUpperCase()
  269. << words[i].substring (1).toLowerCase();
  270. else
  271. n << words[i];
  272. }
  273. if (CharacterFunctions::isDigit (n[0]))
  274. n = "_" + n;
  275. // make sure it's not a reserved c++ keyword..
  276. static const tchar* const reservedWords[] =
  277. {
  278. T("auto"), T("const"), T("double"), T("float"), T("int"), T("short"), T("struct"),
  279. T("return"), T("static"), T("union"), T("while"), T("asm"), T("dynamic_cast"),
  280. T("unsigned"), T("break"), T("continue"), T("else"), T("for"), T("long"), T("signed"),
  281. T("switch"), T("void"), T("case"), T("default"), T("enum"), T("goto"), T("register"),
  282. T("sizeof"), T("typedef"), T("volatile"), T("char"), T("do"), T("extern"), T("if"),
  283. T("namespace"), T("reinterpret_cast"), T("try"), T("bool"), T("explicit"), T("new"),
  284. T("static_cast"), T("typeid"), T("catch"), T("false"), T("operator"), T("template"),
  285. T("typename"), T("class"), T("friend"), T("private"), T("this"), T("using"), T("const_cast"),
  286. T("inline"), T("public"), T("throw"), T("virtual"), T("delete"), T("mutable"), T("protected"),
  287. T("true"), T("wchar_t"), T("and"), T("bitand"), T("compl"), T("not_eq"), T("or_eq"),
  288. T("xor_eq"), T("and_eq"), T("bitor"), T("not"), T("or"), T("xor"), T("cin"), T("endl"),
  289. T("INT_MIN"), T("iomanip"), T("main"), T("npos"), T("std"), T("cout"), T("include"),
  290. T("INT_MAX"), T("iostream"), T("MAX_RAND"), T("NULL"), T("string"), T("id")
  291. };
  292. for (i = 0; i < numElementsInArray (reservedWords); ++i)
  293. if (n == reservedWords[i])
  294. n << '_';
  295. return n;
  296. }
  297. //==============================================================================
  298. const String floatToCode (const float v)
  299. {
  300. String s ((double) (float) v, 4);
  301. if (s.containsChar ('.'))
  302. s << 'f';
  303. else
  304. s << ".0f";
  305. return s;
  306. }
  307. const String doubleToCode (const double v)
  308. {
  309. String s (v, 7);
  310. if (! s.containsChar ('.'))
  311. s << ".0";
  312. return s;
  313. }
  314. const String boolToCode (const bool b)
  315. {
  316. return b ? "true" : "false";
  317. }
  318. const String colourToCode (const Colour& col)
  319. {
  320. const Colour colours[] =
  321. {
  322. #define COL(col) Colours::col,
  323. #include "jucer_Colours.h"
  324. #undef COL
  325. Colours::transparentBlack
  326. };
  327. static const char* colourNames[] =
  328. {
  329. #define COL(col) #col,
  330. #include "jucer_Colours.h"
  331. #undef COL
  332. 0
  333. };
  334. for (int i = 0; i < numElementsInArray (colourNames) - 1; ++i)
  335. if (col == colours[i])
  336. return "Colours::" + String (colourNames[i]);
  337. return "Colour (0x" + hexString8Digits ((int) col.getARGB()) + ')';
  338. }
  339. const String justificationToCode (const Justification& justification)
  340. {
  341. switch (justification.getFlags())
  342. {
  343. case Justification::centred: return "Justification::centred";
  344. case Justification::centredLeft: return "Justification::centredLeft";
  345. case Justification::centredRight: return "Justification::centredRight";
  346. case Justification::centredTop: return "Justification::centredTop";
  347. case Justification::centredBottom: return "Justification::centredBottom";
  348. case Justification::topLeft: return "Justification::topLeft";
  349. case Justification::topRight: return "Justification::topRight";
  350. case Justification::bottomLeft: return "Justification::bottomLeft";
  351. case Justification::bottomRight: return "Justification::bottomRight";
  352. case Justification::left: return "Justification::left";
  353. case Justification::right: return "Justification::right";
  354. case Justification::horizontallyCentred: return "Justification::horizontallyCentred";
  355. case Justification::top: return "Justification::top";
  356. case Justification::bottom: return "Justification::bottom";
  357. case Justification::verticallyCentred: return "Justification::verticallyCentred";
  358. case Justification::horizontallyJustified: return "Justification::horizontallyJustified";
  359. default: jassertfalse; break;
  360. }
  361. return "Justification (" + String (justification.getFlags()) + ")";
  362. }
  363. const String castToFloat (const String& expression)
  364. {
  365. if (expression.containsOnly ("0123456789.f"))
  366. {
  367. String s (expression.getFloatValue());
  368. if (s.containsChar (T('.')))
  369. return s + "f";
  370. return s + ".0f";
  371. }
  372. return "(float) (" + expression + ")";
  373. }
  374. const String indentCode (const String& code, const int numSpaces)
  375. {
  376. if (numSpaces == 0)
  377. return code;
  378. const String space (String::repeatedString (" ", numSpaces));
  379. StringArray lines;
  380. lines.addLines (code);
  381. for (int i = 1; i < lines.size(); ++i)
  382. {
  383. String s (lines[i].trimEnd());
  384. if (s.isNotEmpty())
  385. s = space + s;
  386. lines.set (i, s);
  387. }
  388. return lines.joinIntoString ("\n");
  389. }
  390. int indexOfLineStartingWith (const StringArray& lines, const String& text, int startIndex)
  391. {
  392. startIndex = jmax (0, startIndex);
  393. while (startIndex < lines.size())
  394. {
  395. if (lines[startIndex].trimStart().startsWithIgnoreCase (text))
  396. return startIndex;
  397. ++startIndex;
  398. }
  399. return -1;
  400. }
  401. //==============================================================================
  402. const char* Coordinate::parentLeftMarkerName = "parent.left";
  403. const char* Coordinate::parentRightMarkerName = "parent.right";
  404. const char* Coordinate::parentTopMarkerName = "parent.top";
  405. const char* Coordinate::parentBottomMarkerName = "parent.bottom";
  406. Coordinate::Coordinate (bool isHorizontal_)
  407. : value (0), isProportion (false), isHorizontal (isHorizontal_)
  408. {
  409. }
  410. Coordinate::Coordinate (double absoluteDistanceFromOrigin, bool isHorizontal_)
  411. : value (absoluteDistanceFromOrigin), isProportion (false), isHorizontal (isHorizontal_)
  412. {
  413. }
  414. Coordinate::Coordinate (double absoluteDistance, const String& source, bool isHorizontal_)
  415. : anchor1 (source), value (absoluteDistance), isProportion (false), isHorizontal (isHorizontal_)
  416. {
  417. }
  418. Coordinate::Coordinate (double relativeProportion, const String& pos1, const String& pos2, bool isHorizontal_)
  419. : anchor1 (pos1), anchor2 (pos2), value (relativeProportion), isProportion (true), isHorizontal (isHorizontal_)
  420. {
  421. }
  422. Coordinate::~Coordinate()
  423. {
  424. }
  425. const Coordinate Coordinate::getAnchorPoint1() const
  426. {
  427. return Coordinate (0.0, anchor1, isHorizontal);
  428. }
  429. const Coordinate Coordinate::getAnchorPoint2() const
  430. {
  431. return Coordinate (0.0, anchor2, isHorizontal);
  432. }
  433. bool Coordinate::isOrigin (const String& name)
  434. {
  435. return name.isEmpty() || name == parentLeftMarkerName || name == parentTopMarkerName;
  436. }
  437. const String Coordinate::getOriginMarkerName() const
  438. {
  439. return isHorizontal ? parentLeftMarkerName : parentTopMarkerName;
  440. }
  441. const String Coordinate::getExtentMarkerName() const
  442. {
  443. return isHorizontal ? parentRightMarkerName : parentBottomMarkerName;
  444. }
  445. const String Coordinate::checkName (const String& name) const
  446. {
  447. return name.isEmpty() ? getOriginMarkerName() : name;
  448. }
  449. double Coordinate::getPosition (const String& name, MarkerResolver& markerResolver, int recursionCounter) const
  450. {
  451. if (isOrigin (name))
  452. return 0.0;
  453. return markerResolver.findMarker (name, isHorizontal)
  454. .resolve (markerResolver, recursionCounter + 1);
  455. }
  456. struct RecursivePositionException
  457. {
  458. };
  459. double Coordinate::resolve (MarkerResolver& markerResolver, int recursionCounter) const
  460. {
  461. if (recursionCounter > 100)
  462. {
  463. jassertfalse
  464. throw RecursivePositionException();
  465. }
  466. const double pos1 = getPosition (anchor1, markerResolver, recursionCounter);
  467. return isProportion ? pos1 + (getPosition (anchor2, markerResolver, recursionCounter) - pos1) * value
  468. : pos1 + value;
  469. }
  470. double Coordinate::resolve (MarkerResolver& markerResolver) const
  471. {
  472. try
  473. {
  474. return resolve (markerResolver, 0);
  475. }
  476. catch (RecursivePositionException&)
  477. {}
  478. return 0.0;
  479. }
  480. void Coordinate::moveToAbsolute (double newPos, MarkerResolver& markerResolver)
  481. {
  482. try
  483. {
  484. const double pos1 = getPosition (anchor1, markerResolver, 0);
  485. if (isProportion)
  486. {
  487. const double size = getPosition (anchor2, markerResolver, 0) - pos1;
  488. if (size != 0)
  489. value = (newPos - pos1) / size;
  490. }
  491. else
  492. {
  493. value = newPos - pos1;
  494. }
  495. }
  496. catch (RecursivePositionException&)
  497. {}
  498. }
  499. bool Coordinate::isRecursive (MarkerResolver& markerResolver) const
  500. {
  501. try
  502. {
  503. resolve (markerResolver, 0);
  504. }
  505. catch (RecursivePositionException&)
  506. {
  507. return true;
  508. }
  509. return false;
  510. }
  511. void Coordinate::skipWhitespace (const String& s, int& i)
  512. {
  513. while (CharacterFunctions::isWhitespace (s[i]))
  514. ++i;
  515. }
  516. const String Coordinate::readMarkerName (const String& s, int& i)
  517. {
  518. skipWhitespace (s, i);
  519. if (CharacterFunctions::isLetter (s[i]) || s[i] == '_')
  520. {
  521. int start = i;
  522. while (CharacterFunctions::isLetterOrDigit (s[i]) || s[i] == '_' || s[i] == '.')
  523. ++i;
  524. return s.substring (start, i);
  525. }
  526. return String::empty;
  527. }
  528. double Coordinate::readNumber (const String& s, int& i)
  529. {
  530. skipWhitespace (s, i);
  531. int start = i;
  532. if (CharacterFunctions::isDigit (s[i]) || s[i] == '.' || s[i] == '-')
  533. ++i;
  534. while (CharacterFunctions::isDigit (s[i]) || s[i] == '.')
  535. ++i;
  536. if ((s[i] == 'e' || s[i] == 'E')
  537. && (CharacterFunctions::isDigit (s[i + 1])
  538. || s[i + 1] == '-'
  539. || s[i + 1] == '+'))
  540. {
  541. i += 2;
  542. while (CharacterFunctions::isDigit (s[i]))
  543. ++i;
  544. }
  545. const double value = s.substring (start, i).getDoubleValue();
  546. while (CharacterFunctions::isWhitespace (s[i]) || s[i] == ',')
  547. ++i;
  548. return value;
  549. }
  550. Coordinate::Coordinate (const String& s, bool isHorizontal_)
  551. : value (0), isProportion (false), isHorizontal (isHorizontal_)
  552. {
  553. int i = 0;
  554. anchor1 = readMarkerName (s, i);
  555. if (anchor1.isNotEmpty())
  556. {
  557. skipWhitespace (s, i);
  558. if (s[i] == '+')
  559. value = readNumber (s, ++i);
  560. else if (s[i] == '-')
  561. value = -readNumber (s, ++i);
  562. }
  563. else
  564. {
  565. value = readNumber (s, i);
  566. skipWhitespace (s, i);
  567. if (s[i] == '%')
  568. {
  569. isProportion = true;
  570. value /= 100.0;
  571. skipWhitespace (s, ++i);
  572. if (s[i] == '*')
  573. {
  574. anchor1 = readMarkerName (s, ++i);
  575. skipWhitespace (s, i);
  576. if (s[i] == '-' && s[i + 1] == '>')
  577. {
  578. i += 2;
  579. anchor2 = readMarkerName (s, i);
  580. }
  581. else
  582. {
  583. anchor2 = anchor1;
  584. anchor1 = getOriginMarkerName();
  585. }
  586. }
  587. else
  588. {
  589. anchor1 = getOriginMarkerName();
  590. anchor2 = getExtentMarkerName();
  591. }
  592. }
  593. }
  594. }
  595. const String Coordinate::toString() const
  596. {
  597. if (isProportion)
  598. {
  599. const String percent (value * 100.0);
  600. if (isOrigin (anchor1))
  601. {
  602. if (anchor2 == parentRightMarkerName || anchor2 == parentBottomMarkerName)
  603. return percent + "%";
  604. else
  605. return percent + "% * " + checkName (anchor2);
  606. }
  607. else
  608. return percent + "% * " + checkName (anchor1) + " -> " + checkName (anchor2);
  609. }
  610. else
  611. {
  612. if (isOrigin (anchor1))
  613. return String (value);
  614. else if (value > 0)
  615. return checkName (anchor1) + " + " + String (value);
  616. else if (value < 0)
  617. return checkName (anchor1) + " - " + String (-value);
  618. else
  619. return checkName (anchor1);
  620. }
  621. }
  622. //==============================================================================
  623. RectangleCoordinates::RectangleCoordinates()
  624. : left (true), right (true), top (false), bottom (false)
  625. {
  626. }
  627. RectangleCoordinates::RectangleCoordinates (const Rectangle<int>& rect)
  628. : left (rect.getX(), true),
  629. right (rect.getWidth(), "left", true),
  630. top (rect.getY(), false),
  631. bottom (rect.getHeight(), "top", false)
  632. {
  633. }
  634. RectangleCoordinates::RectangleCoordinates (const String& stringVersion)
  635. : left (true), right (true), top (false), bottom (false)
  636. {
  637. StringArray tokens;
  638. tokens.addTokens (stringVersion, ",", String::empty);
  639. left = Coordinate (tokens [0], true);
  640. top = Coordinate (tokens [1], false);
  641. right = Coordinate (tokens [2], true);
  642. bottom = Coordinate (tokens [3], false);
  643. }
  644. bool RectangleCoordinates::isRecursive (Coordinate::MarkerResolver& markerResolver) const
  645. {
  646. return left.isRecursive (markerResolver) || right.isRecursive (markerResolver)
  647. || top.isRecursive (markerResolver) || bottom.isRecursive (markerResolver);
  648. }
  649. const Rectangle<int> RectangleCoordinates::resolve (Coordinate::MarkerResolver& markerResolver) const
  650. {
  651. const int l = roundToInt (left.resolve (markerResolver));
  652. const int r = roundToInt (right.resolve (markerResolver));
  653. const int t = roundToInt (top.resolve (markerResolver));
  654. const int b = roundToInt (bottom.resolve (markerResolver));
  655. return Rectangle<int> (l, t, r - l, b - t);
  656. }
  657. void RectangleCoordinates::moveToAbsolute (const Rectangle<int>& newPos, Coordinate::MarkerResolver& markerResolver)
  658. {
  659. left.moveToAbsolute (newPos.getX(), markerResolver);
  660. right.moveToAbsolute (newPos.getRight(), markerResolver);
  661. top.moveToAbsolute (newPos.getY(), markerResolver);
  662. bottom.moveToAbsolute (newPos.getBottom(), markerResolver);
  663. }
  664. const String RectangleCoordinates::toString() const
  665. {
  666. return left.toString() + ", " + top.toString() + ", " + right.toString() + ", " + bottom.toString();
  667. }