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.

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