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.

777 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2013 - Raw Material Software Ltd.
  5. Permission is granted to use this software under the terms of either:
  6. a) the GPL v2 (or any later version)
  7. b) the Affero GPL v3
  8. Details of these licenses can be found at: www.gnu.org/licenses
  9. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  10. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  11. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. ------------------------------------------------------------------------------
  13. To release a closed-source product which uses JUCE, commercial licenses are
  14. available: visit www.juce.com for more information.
  15. ==============================================================================
  16. */
  17. #include "../jucer_Headers.h"
  18. #include "../Application/jucer_Application.h"
  19. #include "../Project/jucer_NewFileWizard.h"
  20. #include "jucer_JucerDocument.h"
  21. #include "jucer_ObjectTypes.h"
  22. #include "ui/jucer_JucerDocumentEditor.h"
  23. #include "ui/jucer_TestComponent.h"
  24. #include "jucer_UtilityFunctions.h"
  25. #include "documents/jucer_ComponentDocument.h"
  26. #include "documents/jucer_ButtonDocument.h"
  27. const char* const defaultClassName = "NewComponent";
  28. const char* const defaultParentClasses = "public Component";
  29. //==============================================================================
  30. JucerDocument::JucerDocument (SourceCodeDocument* c)
  31. : cpp (c),
  32. className (defaultClassName),
  33. parentClasses (defaultParentClasses),
  34. fixedSize (false),
  35. initialWidth (600),
  36. initialHeight (400),
  37. snapGridPixels (8),
  38. snapActive (true),
  39. snapShown (true),
  40. componentOverlayOpacity (0.33f)
  41. {
  42. jassert (cpp != nullptr);
  43. resources.setDocument (this);
  44. IntrojucerApp::getCommandManager().commandStatusChanged();
  45. cpp->getCodeDocument().addListener (this);
  46. IntrojucerApp::getApp().openDocumentManager.addListener (this);
  47. }
  48. JucerDocument::~JucerDocument()
  49. {
  50. IntrojucerApp::getApp().openDocumentManager.removeListener (this);
  51. cpp->getCodeDocument().removeListener (this);
  52. IntrojucerApp::getCommandManager().commandStatusChanged();
  53. }
  54. //==============================================================================
  55. void JucerDocument::changed()
  56. {
  57. sendChangeMessage();
  58. IntrojucerApp::getCommandManager().commandStatusChanged();
  59. startTimer (800);
  60. }
  61. struct UserDocChangeTimer : public Timer
  62. {
  63. UserDocChangeTimer (JucerDocument& d) : doc (d) {}
  64. void timerCallback() override { doc.reloadFromDocument(); }
  65. JucerDocument& doc;
  66. };
  67. bool JucerDocument::documentAboutToClose (OpenDocumentManager::Document* doc)
  68. {
  69. return doc != cpp;
  70. }
  71. void JucerDocument::userEditedCpp()
  72. {
  73. if (userDocChangeTimer == nullptr)
  74. userDocChangeTimer = new UserDocChangeTimer (*this);
  75. userDocChangeTimer->startTimer (500);
  76. }
  77. void JucerDocument::beginTransaction()
  78. {
  79. getUndoManager().beginNewTransaction();
  80. }
  81. void JucerDocument::beginTransaction (const String& name)
  82. {
  83. getUndoManager().beginNewTransaction (name);
  84. }
  85. void JucerDocument::timerCallback()
  86. {
  87. if (! Component::isMouseButtonDownAnywhere())
  88. {
  89. stopTimer();
  90. beginTransaction();
  91. flushChangesToDocuments();
  92. }
  93. }
  94. void JucerDocument::codeDocumentTextInserted (const String&, int) { userEditedCpp(); }
  95. void JucerDocument::codeDocumentTextDeleted (int, int) { userEditedCpp(); }
  96. bool JucerDocument::perform (UndoableAction* const action, const String& actionName)
  97. {
  98. return undoManager.perform (action, actionName);
  99. }
  100. void JucerDocument::refreshAllPropertyComps()
  101. {
  102. if (ComponentLayout* l = getComponentLayout())
  103. l->getSelectedSet().changed();
  104. for (int i = getNumPaintRoutines(); --i >= 0;)
  105. {
  106. getPaintRoutine (i)->getSelectedElements().changed();
  107. getPaintRoutine (i)->getSelectedPoints().changed();
  108. }
  109. }
  110. //==============================================================================
  111. void JucerDocument::setClassName (const String& newName)
  112. {
  113. if (newName != className
  114. && CodeHelpers::makeValidIdentifier (newName, false, false, true).isNotEmpty())
  115. {
  116. className = CodeHelpers::makeValidIdentifier (newName, false, false, true);
  117. changed();
  118. }
  119. }
  120. void JucerDocument::setComponentName (const String& newName)
  121. {
  122. if (newName != componentName)
  123. {
  124. componentName = newName;
  125. changed();
  126. }
  127. }
  128. void JucerDocument::setParentClasses (const String& classes)
  129. {
  130. if (classes != parentClasses)
  131. {
  132. StringArray parentClassLines;
  133. parentClassLines.addTokens (classes, ",", String::empty);
  134. parentClassLines.trim();
  135. parentClassLines.removeEmptyStrings();
  136. parentClassLines.removeDuplicates (false);
  137. for (int i = parentClassLines.size(); --i >= 0;)
  138. {
  139. String s (parentClassLines[i]);
  140. String type;
  141. if (s.startsWith ("public ")
  142. || s.startsWith ("protected ")
  143. || s.startsWith ("private "))
  144. {
  145. type = s.upToFirstOccurrenceOf (" ", true, false);
  146. s = s.fromFirstOccurrenceOf (" ", false, false);
  147. if (s.trim().isEmpty())
  148. type = s = String::empty;
  149. }
  150. s = type + CodeHelpers::makeValidIdentifier (s.trim(), false, false, true);
  151. parentClassLines.set (i, s);
  152. }
  153. parentClasses = parentClassLines.joinIntoString (", ");
  154. changed();
  155. }
  156. }
  157. void JucerDocument::setConstructorParams (const String& newParams)
  158. {
  159. if (constructorParams != newParams)
  160. {
  161. constructorParams = newParams;
  162. changed();
  163. }
  164. }
  165. void JucerDocument::setVariableInitialisers (const String& newInitlialisers)
  166. {
  167. if (variableInitialisers != newInitlialisers)
  168. {
  169. variableInitialisers = newInitlialisers;
  170. changed();
  171. }
  172. }
  173. void JucerDocument::setFixedSize (const bool isFixed)
  174. {
  175. if (fixedSize != isFixed)
  176. {
  177. fixedSize = isFixed;
  178. changed();
  179. }
  180. }
  181. void JucerDocument::setInitialSize (int w, int h)
  182. {
  183. w = jmax (1, w);
  184. h = jmax (1, h);
  185. if (initialWidth != w || initialHeight != h)
  186. {
  187. initialWidth = w;
  188. initialHeight = h;
  189. changed();
  190. }
  191. }
  192. //==============================================================================
  193. bool JucerDocument::isSnapActive (const bool disableIfCtrlKeyDown) const noexcept
  194. {
  195. return snapActive != (disableIfCtrlKeyDown && ModifierKeys::getCurrentModifiers().isCtrlDown());
  196. }
  197. int JucerDocument::snapPosition (int pos) const noexcept
  198. {
  199. if (isSnapActive (true))
  200. {
  201. jassert (snapGridPixels > 0);
  202. pos = ((pos + snapGridPixels * 1024 + snapGridPixels / 2) / snapGridPixels - 1024) * snapGridPixels;
  203. }
  204. return pos;
  205. }
  206. void JucerDocument::setSnappingGrid (const int numPixels, const bool active, const bool shown)
  207. {
  208. if (numPixels != snapGridPixels
  209. || active != snapActive
  210. || shown != snapShown)
  211. {
  212. snapGridPixels = numPixels;
  213. snapActive = active;
  214. snapShown = shown;
  215. changed();
  216. }
  217. }
  218. void JucerDocument::setComponentOverlayOpacity (const float alpha)
  219. {
  220. if (alpha != componentOverlayOpacity)
  221. {
  222. componentOverlayOpacity = alpha;
  223. changed();
  224. }
  225. }
  226. //==============================================================================
  227. void JucerDocument::addMethod (const String& base, const String& returnVal, const String& method, const String& initialContent,
  228. StringArray& baseClasses, StringArray& returnValues, StringArray& methods, StringArray& initialContents)
  229. {
  230. baseClasses.add (base);
  231. returnValues.add (returnVal);
  232. methods.add (method);
  233. initialContents.add (initialContent);
  234. }
  235. void JucerDocument::getOptionalMethods (StringArray& baseClasses,
  236. StringArray& returnValues,
  237. StringArray& methods,
  238. StringArray& initialContents) const
  239. {
  240. addMethod ("Component", "void", "visibilityChanged()", "", baseClasses, returnValues, methods, initialContents);
  241. addMethod ("Component", "void", "moved()", "", baseClasses, returnValues, methods, initialContents);
  242. addMethod ("Component", "void", "parentHierarchyChanged()", "", baseClasses, returnValues, methods, initialContents);
  243. addMethod ("Component", "void", "parentSizeChanged()", "", baseClasses, returnValues, methods, initialContents);
  244. addMethod ("Component", "void", "lookAndFeelChanged()", "", baseClasses, returnValues, methods, initialContents);
  245. addMethod ("Component", "bool", "hitTest (int x, int y)", "return true;", baseClasses, returnValues, methods, initialContents);
  246. addMethod ("Component", "void", "broughtToFront()", "", baseClasses, returnValues, methods, initialContents);
  247. addMethod ("Component", "void", "filesDropped (const StringArray& filenames, int mouseX, int mouseY)", "", baseClasses, returnValues, methods, initialContents);
  248. addMethod ("Component", "void", "handleCommandMessage (int commandId)", "", baseClasses, returnValues, methods, initialContents);
  249. addMethod ("Component", "void", "childrenChanged()", "", baseClasses, returnValues, methods, initialContents);
  250. addMethod ("Component", "void", "enablementChanged()", "", baseClasses, returnValues, methods, initialContents);
  251. addMethod ("Component", "void", "mouseMove (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  252. addMethod ("Component", "void", "mouseEnter (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  253. addMethod ("Component", "void", "mouseExit (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  254. addMethod ("Component", "void", "mouseDown (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  255. addMethod ("Component", "void", "mouseDrag (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  256. addMethod ("Component", "void", "mouseUp (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  257. addMethod ("Component", "void", "mouseDoubleClick (const MouseEvent& e)", "", baseClasses, returnValues, methods, initialContents);
  258. addMethod ("Component", "void", "mouseWheelMove (const MouseEvent& e, const MouseWheelDetails& wheel)", "", baseClasses, returnValues, methods, initialContents);
  259. addMethod ("Component", "bool", "keyPressed (const KeyPress& key)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
  260. addMethod ("Component", "bool", "keyStateChanged (const bool isKeyDown)", "return false; // Return true if your handler uses this key event, or false to allow it to be passed-on.", baseClasses, returnValues, methods, initialContents);
  261. addMethod ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
  262. addMethod ("Component", "void", "focusGained (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
  263. addMethod ("Component", "void", "focusLost (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
  264. addMethod ("Component", "void", "focusOfChildComponentChanged (FocusChangeType cause)", "", baseClasses, returnValues, methods, initialContents);
  265. addMethod ("Component", "void", "modifierKeysChanged (const ModifierKeys& modifiers)", "", baseClasses, returnValues, methods, initialContents);
  266. addMethod ("Component", "void", "inputAttemptWhenModal()", "", baseClasses, returnValues, methods, initialContents);
  267. }
  268. void JucerDocument::setOptionalMethodEnabled (const String& methodSignature, const bool enable)
  269. {
  270. if (enable)
  271. activeExtraMethods.addIfNotAlreadyThere (methodSignature);
  272. else
  273. activeExtraMethods.removeString (methodSignature);
  274. changed();
  275. }
  276. bool JucerDocument::isOptionalMethodEnabled (const String& sig) const noexcept
  277. {
  278. return activeExtraMethods.contains (sig);
  279. }
  280. void JucerDocument::addExtraClassProperties (PropertyPanel&)
  281. {
  282. }
  283. //==============================================================================
  284. const char* const JucerDocument::jucerCompXmlTag = "JUCER_COMPONENT";
  285. XmlElement* JucerDocument::createXml() const
  286. {
  287. XmlElement* doc = new XmlElement (jucerCompXmlTag);
  288. doc->setAttribute ("documentType", getTypeName());
  289. doc->setAttribute ("className", className);
  290. if (templateFile.trim().isNotEmpty())
  291. doc->setAttribute ("template", templateFile);
  292. doc->setAttribute ("componentName", componentName);
  293. doc->setAttribute ("parentClasses", parentClasses);
  294. doc->setAttribute ("constructorParams", constructorParams);
  295. doc->setAttribute ("variableInitialisers", variableInitialisers);
  296. doc->setAttribute ("snapPixels", snapGridPixels);
  297. doc->setAttribute ("snapActive", snapActive);
  298. doc->setAttribute ("snapShown", snapShown);
  299. doc->setAttribute ("overlayOpacity", String (componentOverlayOpacity, 3));
  300. doc->setAttribute ("fixedSize", fixedSize);
  301. doc->setAttribute ("initialWidth", initialWidth);
  302. doc->setAttribute ("initialHeight", initialHeight);
  303. if (activeExtraMethods.size() > 0)
  304. {
  305. XmlElement* extraMethods = new XmlElement ("METHODS");
  306. doc->addChildElement (extraMethods);
  307. for (int i = 0; i < activeExtraMethods.size(); ++i)
  308. {
  309. XmlElement* e = new XmlElement ("METHOD");
  310. extraMethods ->addChildElement (e);
  311. e->setAttribute ("name", activeExtraMethods[i]);
  312. }
  313. }
  314. return doc;
  315. }
  316. bool JucerDocument::loadFromXml (const XmlElement& xml)
  317. {
  318. if (xml.hasTagName (jucerCompXmlTag)
  319. && getTypeName().equalsIgnoreCase (xml.getStringAttribute ("documentType")))
  320. {
  321. className = xml.getStringAttribute ("className", defaultClassName);
  322. templateFile = xml.getStringAttribute ("template", String::empty);
  323. componentName = xml.getStringAttribute ("componentName", String::empty);
  324. parentClasses = xml.getStringAttribute ("parentClasses", defaultParentClasses);
  325. constructorParams = xml.getStringAttribute ("constructorParams", String::empty);
  326. variableInitialisers = xml.getStringAttribute ("variableInitialisers", String::empty);
  327. fixedSize = xml.getBoolAttribute ("fixedSize", false);
  328. initialWidth = xml.getIntAttribute ("initialWidth", 300);
  329. initialHeight = xml.getIntAttribute ("initialHeight", 200);
  330. snapGridPixels = xml.getIntAttribute ("snapPixels", snapGridPixels);
  331. snapActive = xml.getBoolAttribute ("snapActive", snapActive);
  332. snapShown = xml.getBoolAttribute ("snapShown", snapShown);
  333. componentOverlayOpacity = (float) xml.getDoubleAttribute ("overlayOpacity", 0.0);
  334. activeExtraMethods.clear();
  335. if (XmlElement* const methods = xml.getChildByName ("METHODS"))
  336. {
  337. forEachXmlChildElementWithTagName (*methods, e, "METHOD")
  338. {
  339. activeExtraMethods.addIfNotAlreadyThere (e->getStringAttribute ("name"));
  340. }
  341. }
  342. activeExtraMethods.trim();
  343. activeExtraMethods.removeEmptyStrings();
  344. changed();
  345. getUndoManager().clearUndoHistory();
  346. return true;
  347. }
  348. return false;
  349. }
  350. //==============================================================================
  351. void JucerDocument::fillInGeneratedCode (GeneratedCode& code) const
  352. {
  353. code.className = className;
  354. code.componentName = componentName;
  355. code.parentClasses = parentClasses;
  356. code.constructorParams = constructorParams;
  357. code.initialisers.addLines (variableInitialisers);
  358. if (! componentName.isEmpty())
  359. code.constructorCode << "setName (" + quotedString (componentName) + ")\n";
  360. // call these now, just to make sure they're the first two methods in the list.
  361. code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false)
  362. << "//[UserPrePaint] Add your own custom painting code here..\n//[/UserPrePaint]\n\n";
  363. code.getCallbackCode (String::empty, "void", "resized()", false);
  364. if (ComponentLayout* l = getComponentLayout())
  365. l->fillInGeneratedCode (code);
  366. fillInPaintCode (code);
  367. ScopedPointer<XmlElement> e (createXml());
  368. jassert (e != nullptr);
  369. code.jucerMetadata = e->createDocument (String::empty, false, false);
  370. resources.fillInGeneratedCode (code);
  371. code.constructorCode
  372. << "\n//[UserPreSize]\n"
  373. "//[/UserPreSize]\n";
  374. if (initialWidth > 0 || initialHeight > 0)
  375. code.constructorCode << "\nsetSize (" << initialWidth << ", " << initialHeight << ");\n";
  376. code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false)
  377. << "//[UserPaint] Add your own custom painting code here..\n//[/UserPaint]";
  378. code.getCallbackCode (String::empty, "void", "resized()", false)
  379. << "//[UserResized] Add your own custom resize handling here..\n//[/UserResized]";
  380. // add optional methods
  381. StringArray baseClasses, returnValues, methods, initialContents;
  382. getOptionalMethods (baseClasses, returnValues, methods, initialContents);
  383. for (int i = 0; i < methods.size(); ++i)
  384. {
  385. if (isOptionalMethodEnabled (methods[i]))
  386. {
  387. String baseClassToAdd (baseClasses[i]);
  388. if (baseClassToAdd == "Component" || baseClassToAdd == "Button")
  389. baseClassToAdd = String::empty;
  390. String& s = code.getCallbackCode (baseClassToAdd, returnValues[i], methods[i], false);
  391. if (! s.contains ("//["))
  392. {
  393. String userCommentTag ("UserCode_");
  394. userCommentTag += methods[i].upToFirstOccurrenceOf ("(", false, false).trim();
  395. s << "\n//["
  396. << userCommentTag
  397. << "] -- Add your code here...\n"
  398. << initialContents[i];
  399. if (initialContents[i].isNotEmpty() && ! initialContents[i].endsWithChar ('\n'))
  400. s << '\n';
  401. s << "//[/"
  402. << userCommentTag
  403. << "]\n";
  404. }
  405. }
  406. }
  407. }
  408. void JucerDocument::fillInPaintCode (GeneratedCode& code) const
  409. {
  410. for (int i = 0; i < getNumPaintRoutines(); ++i)
  411. getPaintRoutine (i)
  412. ->fillInGeneratedCode (code, code.getCallbackCode (String::empty, "void", "paint (Graphics& g)", false));
  413. }
  414. void JucerDocument::setTemplateFile (const String& newFile)
  415. {
  416. if (templateFile != newFile)
  417. {
  418. templateFile = newFile;
  419. changed();
  420. }
  421. }
  422. //==============================================================================
  423. bool JucerDocument::findTemplateFiles (String& headerContent, String& cppContent) const
  424. {
  425. if (templateFile.isNotEmpty())
  426. {
  427. const File f (getCppFile().getSiblingFile (templateFile));
  428. const File templateCpp (f.withFileExtension (".cpp"));
  429. const File templateH (f.withFileExtension (".h"));
  430. headerContent = templateH.loadFileAsString();
  431. cppContent = templateCpp.loadFileAsString();
  432. if (headerContent.isNotEmpty() && cppContent.isNotEmpty())
  433. return true;
  434. }
  435. headerContent = BinaryData::jucer_ComponentTemplate_h;
  436. cppContent = BinaryData::jucer_ComponentTemplate_cpp;
  437. return true;
  438. }
  439. static String fixLineEndings (const String& s)
  440. {
  441. StringArray lines;
  442. lines.addLines (s);
  443. for (int i = 0; i < lines.size(); ++i)
  444. lines.set (i, lines[i].trimEnd());
  445. while (lines.size() > 0 && lines [lines.size() - 1].trim().isEmpty())
  446. lines.remove (lines.size() - 1);
  447. lines.add (String::empty);
  448. return lines.joinIntoString ("\r\n");
  449. }
  450. bool JucerDocument::flushChangesToDocuments()
  451. {
  452. String headerTemplate, cppTemplate;
  453. if (! findTemplateFiles (headerTemplate, cppTemplate))
  454. return false;
  455. GeneratedCode generated (this);
  456. fillInGeneratedCode (generated);
  457. const File headerFile (getHeaderFile());
  458. generated.includeFilesCPP.insert (0, headerFile.getFileName());
  459. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  460. if (SourceCodeDocument* header = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, getHeaderFile())))
  461. {
  462. String existingHeader (header->getCodeDocument().getAllContent());
  463. String existingCpp (cpp->getCodeDocument().getAllContent());
  464. generated.applyToCode (headerTemplate, headerFile.getFileNameWithoutExtension(), false, existingHeader);
  465. generated.applyToCode (cppTemplate, headerFile.getFileNameWithoutExtension(), false, existingCpp);
  466. headerTemplate = fixLineEndings (headerTemplate);
  467. cppTemplate = fixLineEndings (cppTemplate);
  468. if (header->getCodeDocument().getAllContent() != headerTemplate)
  469. header->getCodeDocument().replaceAllContent (headerTemplate);
  470. if (cpp->getCodeDocument().getAllContent() != cppTemplate)
  471. cpp->getCodeDocument().replaceAllContent (cppTemplate);
  472. }
  473. userDocChangeTimer = nullptr;
  474. return true;
  475. }
  476. bool JucerDocument::reloadFromDocument()
  477. {
  478. const String cppContent (cpp->getCodeDocument().getAllContent());
  479. ScopedPointer<XmlElement> newXML (pullMetaDataFromCppFile (cppContent));
  480. if (newXML == nullptr || ! newXML->hasTagName (jucerCompXmlTag))
  481. return false;
  482. if (currentXML != nullptr && currentXML->isEquivalentTo (newXML, true))
  483. return true;
  484. currentXML = newXML;
  485. stopTimer();
  486. resources.loadFromCpp (getCppFile(), cppContent);
  487. return loadFromXml (*currentXML);
  488. }
  489. XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp)
  490. {
  491. StringArray lines;
  492. lines.addLines (cpp);
  493. const int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0);
  494. if (startLine > 0)
  495. {
  496. const int endLine = indexOfLineStartingWith (lines, "END_JUCER_METADATA", startLine);
  497. if (endLine > startLine)
  498. return XmlDocument::parse (lines.joinIntoString ("\n", startLine + 1,
  499. endLine - startLine - 1));
  500. }
  501. return nullptr;
  502. }
  503. bool JucerDocument::isValidJucerCppFile (const File& f)
  504. {
  505. if (f.hasFileExtension (".cpp"))
  506. {
  507. const ScopedPointer<XmlElement> xml (pullMetaDataFromCppFile (f.loadFileAsString()));
  508. return xml != nullptr && xml->hasTagName (jucerCompXmlTag);
  509. }
  510. return false;
  511. }
  512. static JucerDocument* createDocument (SourceCodeDocument* cpp)
  513. {
  514. CodeDocument& codeDoc = cpp->getCodeDocument();
  515. ScopedPointer<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent()));
  516. if (xml == nullptr || ! xml->hasTagName (JucerDocument::jucerCompXmlTag))
  517. return nullptr;
  518. const String docType (xml->getStringAttribute ("documentType"));
  519. ScopedPointer<JucerDocument> newDoc;
  520. if (docType.equalsIgnoreCase ("Button"))
  521. newDoc = new ButtonDocument (cpp);
  522. if (docType.equalsIgnoreCase ("Component") || docType.isEmpty())
  523. newDoc = new ComponentDocument (cpp);
  524. if (newDoc != nullptr && newDoc->reloadFromDocument())
  525. return newDoc.release();
  526. return nullptr;
  527. }
  528. JucerDocument* JucerDocument::createForCppFile (Project* p, const File& file)
  529. {
  530. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  531. if (SourceCodeDocument* cpp = dynamic_cast<SourceCodeDocument*> (odm.openFile (p, file)))
  532. if (dynamic_cast<SourceCodeDocument*> (odm.openFile (p, file.withFileExtension (".h"))) != nullptr)
  533. return createDocument (cpp);
  534. return nullptr;
  535. }
  536. //==============================================================================
  537. class JucerComponentDocument : public SourceCodeDocument
  538. {
  539. public:
  540. JucerComponentDocument (Project* p, const File& f)
  541. : SourceCodeDocument (p, f)
  542. {
  543. }
  544. bool save() override
  545. {
  546. return SourceCodeDocument::save() && saveHeader();
  547. }
  548. bool saveHeader()
  549. {
  550. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  551. if (OpenDocumentManager::Document* header = odm.openFile (nullptr, getFile().withFileExtension (".h")))
  552. return header->save();
  553. return false;
  554. }
  555. Component* createEditor() override
  556. {
  557. ScopedPointer<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile()));
  558. if (jucerDoc != nullptr)
  559. return new JucerDocumentEditor (jucerDoc.release());
  560. return SourceCodeDocument::createEditor();
  561. }
  562. class Type : public OpenDocumentManager::DocumentType
  563. {
  564. public:
  565. Type() {}
  566. bool canOpenFile (const File& f) override { return JucerDocument::isValidJucerCppFile (f); }
  567. Document* openFile (Project* p, const File& f) override { return new JucerComponentDocument (p, f); }
  568. };
  569. };
  570. OpenDocumentManager::DocumentType* createGUIDocumentType()
  571. {
  572. return new JucerComponentDocument::Type();
  573. }
  574. //==============================================================================
  575. class JucerFileWizard : public NewFileWizard::Type
  576. {
  577. public:
  578. JucerFileWizard() {}
  579. String getName() { return "GUI Component"; }
  580. void createNewFile (Project::Item parent)
  581. {
  582. const File newFile (askUserToChooseNewFile (String (defaultClassName) + ".h", "*.h;*.cpp", parent));
  583. if (newFile != File::nonexistent)
  584. {
  585. const File headerFile (newFile.withFileExtension (".h"));
  586. const File cppFile (newFile.withFileExtension (".cpp"));
  587. headerFile.replaceWithText (String::empty);
  588. cppFile.replaceWithText (String::empty);
  589. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  590. if (SourceCodeDocument* cpp = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, cppFile)))
  591. {
  592. if (SourceCodeDocument* header = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, headerFile)))
  593. {
  594. ScopedPointer<JucerDocument> jucerDoc (new ComponentDocument (cpp));
  595. if (jucerDoc != nullptr)
  596. {
  597. jucerDoc->flushChangesToDocuments();
  598. jucerDoc = nullptr;
  599. cpp->save();
  600. header->save();
  601. odm.closeDocument (cpp, true);
  602. odm.closeDocument (header, true);
  603. parent.addFile (headerFile, 0, true);
  604. parent.addFile (cppFile, 0, true);
  605. }
  606. }
  607. }
  608. }
  609. }
  610. };
  611. NewFileWizard::Type* createGUIComponentWizard()
  612. {
  613. return new JucerFileWizard();
  614. }