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.

724 lines
26KB

  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& methodSigniture, const bool enable)
  264. {
  265. if (enable)
  266. activeExtraMethods.addIfNotAlreadyThere (methodSigniture);
  267. else
  268. activeExtraMethods.removeString (methodSigniture);
  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. bool JucerDocument::flushChangesToDocuments()
  433. {
  434. String headerTemplate, cppTemplate;
  435. if (! findTemplateFiles (headerTemplate, cppTemplate))
  436. return false;
  437. GeneratedCode generated (this);
  438. fillInGeneratedCode (generated);
  439. const File headerFile (getHeaderFile());
  440. generated.includeFilesCPP.insert (0, headerFile.getFileName());
  441. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  442. if (SourceCodeDocument* header = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, getHeaderFile())))
  443. {
  444. String existingHeader (header->getCodeDocument().getAllContent());
  445. String existingCpp (cpp->getCodeDocument().getAllContent());
  446. generated.applyToCode (headerTemplate, headerFile.getFileNameWithoutExtension(), false, existingHeader);
  447. generated.applyToCode (cppTemplate, headerFile.getFileNameWithoutExtension(), false, existingCpp);
  448. header->getCodeDocument().applyChanges (headerTemplate);
  449. cpp->getCodeDocument().applyChanges (cppTemplate);
  450. }
  451. return true;
  452. }
  453. bool JucerDocument::reloadFromDocument()
  454. {
  455. ScopedPointer<XmlElement> newXML (pullMetaDataFromCppFile (cpp->getCodeDocument().getAllContent()));
  456. if (newXML == nullptr || ! newXML->hasTagName (jucerCompXmlTag))
  457. return false;
  458. if (currentXML != nullptr && currentXML->isEquivalentTo (newXML, true))
  459. return true;
  460. currentXML = newXML;
  461. stopTimer();
  462. return loadFromXml (*currentXML);
  463. }
  464. XmlElement* JucerDocument::pullMetaDataFromCppFile (const String& cpp)
  465. {
  466. StringArray lines;
  467. lines.addLines (cpp);
  468. const int startLine = indexOfLineStartingWith (lines, "BEGIN_JUCER_METADATA", 0);
  469. if (startLine > 0)
  470. {
  471. const int endLine = indexOfLineStartingWith (lines, "END_JUCER_METADATA", startLine);
  472. if (endLine > startLine)
  473. return XmlDocument::parse (lines.joinIntoString ("\n", startLine + 1,
  474. endLine - startLine - 1));
  475. }
  476. return nullptr;
  477. }
  478. bool JucerDocument::isValidJucerCppFile (const File& f)
  479. {
  480. if (f.hasFileExtension (".cpp"))
  481. {
  482. const ScopedPointer<XmlElement> xml (pullMetaDataFromCppFile (f.loadFileAsString()));
  483. return xml != nullptr && xml->hasTagName (jucerCompXmlTag);
  484. }
  485. return false;
  486. }
  487. static JucerDocument* createDocument (SourceCodeDocument* cpp)
  488. {
  489. CodeDocument& codeDoc = cpp->getCodeDocument();
  490. ScopedPointer<XmlElement> xml (JucerDocument::pullMetaDataFromCppFile (codeDoc.getAllContent()));
  491. if (xml == nullptr || ! xml->hasTagName (JucerDocument::jucerCompXmlTag))
  492. return nullptr;
  493. const String docType (xml->getStringAttribute ("documentType"));
  494. ScopedPointer<JucerDocument> newDoc;
  495. if (docType.equalsIgnoreCase ("Button"))
  496. newDoc = new ButtonDocument (cpp);
  497. if (docType.equalsIgnoreCase ("Component") || docType.isEmpty())
  498. newDoc = new ComponentDocument (cpp);
  499. if (newDoc != nullptr && newDoc->reloadFromDocument())
  500. return newDoc.release();
  501. return nullptr;
  502. }
  503. JucerDocument* JucerDocument::createForCppFile (Project* p, const File& file)
  504. {
  505. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  506. if (SourceCodeDocument* cpp = dynamic_cast <SourceCodeDocument*> (odm.openFile (p, file)))
  507. if (dynamic_cast <SourceCodeDocument*> (odm.openFile (p, file.withFileExtension (".h"))) != nullptr)
  508. return createDocument (cpp);
  509. return nullptr;
  510. }
  511. //==============================================================================
  512. class JucerComponentDocument : public SourceCodeDocument
  513. {
  514. public:
  515. JucerComponentDocument (Project* p, const File& f)
  516. : SourceCodeDocument (p, f)
  517. {
  518. }
  519. Component* createEditor()
  520. {
  521. ScopedPointer<JucerDocument> jucerDoc (JucerDocument::createForCppFile (getProject(), getFile()));
  522. if (jucerDoc != nullptr)
  523. return new JucerDocumentEditor (jucerDoc.release());
  524. return SourceCodeDocument::createEditor();
  525. }
  526. class Type : public OpenDocumentManager::DocumentType
  527. {
  528. public:
  529. Type() {}
  530. bool canOpenFile (const File& f) { return JucerDocument::isValidJucerCppFile (f); }
  531. Document* openFile (Project* p, const File& f) { return new JucerComponentDocument (p, f); }
  532. };
  533. };
  534. OpenDocumentManager::DocumentType* createGUIDocumentType()
  535. {
  536. return new JucerComponentDocument::Type();
  537. }
  538. //==============================================================================
  539. class JucerFileWizard : public NewFileWizard::Type
  540. {
  541. public:
  542. JucerFileWizard() {}
  543. String getName() { return "GUI Component"; }
  544. void createNewFile (Project::Item parent)
  545. {
  546. const File newFile (askUserToChooseNewFile (String (defaultClassName) + ".h", "*.h;*.cpp", parent));
  547. if (newFile != File::nonexistent)
  548. {
  549. const File headerFile (newFile.withFileExtension (".h"));
  550. const File cppFile (newFile.withFileExtension (".cpp"));
  551. headerFile.replaceWithText (String::empty);
  552. cppFile.replaceWithText (String::empty);
  553. OpenDocumentManager& odm = IntrojucerApp::getApp().openDocumentManager;
  554. if (SourceCodeDocument* cpp = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, cppFile)))
  555. {
  556. if (SourceCodeDocument* header = dynamic_cast <SourceCodeDocument*> (odm.openFile (nullptr, headerFile)))
  557. {
  558. ScopedPointer<JucerDocument> jucerDoc (new ComponentDocument (cpp));
  559. if (jucerDoc != nullptr)
  560. {
  561. jucerDoc->flushChangesToDocuments();
  562. jucerDoc = nullptr;
  563. cpp->save();
  564. header->save();
  565. odm.closeDocument (cpp, true);
  566. odm.closeDocument (header, true);
  567. parent.addFile (headerFile, 0, true);
  568. parent.addFile (cppFile, 0, true);
  569. }
  570. }
  571. }
  572. }
  573. }
  574. };
  575. NewFileWizard::Type* createGUIComponentWizard()
  576. {
  577. return new JucerFileWizard();
  578. }