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.

768 lines
27KB

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