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.

904 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. #include "../Application/jucer_Headers.h"
  20. #include "../Application/jucer_Application.h"
  21. #include "../ProjectSaving/jucer_ProjectExporter.h"
  22. #include "jucer_MessageIDs.h"
  23. #include "jucer_CppHelpers.h"
  24. #include "jucer_SourceCodeRange.h"
  25. #include "jucer_ClassDatabase.h"
  26. #include "jucer_DiagnosticMessage.h"
  27. #include "jucer_ProjectBuildInfo.h"
  28. #include "jucer_ClientServerMessages.h"
  29. #include "jucer_CompileEngineClient.h"
  30. #include "jucer_CompileEngineServer.h"
  31. #include "jucer_CompileEngineSettings.h"
  32. #ifndef RUN_CLANG_IN_CHILD_PROCESS
  33. #error
  34. #endif
  35. //==============================================================================
  36. static File getProjucerTempFolder() noexcept
  37. {
  38. #if JUCE_MAC
  39. return { "~/Library/Caches/com.juce.projucer" };
  40. #else
  41. return File::getSpecialLocation (File::tempDirectory).getChildFile ("com.juce.projucer");
  42. #endif
  43. }
  44. static File getCacheLocationForProject (Project& project) noexcept
  45. {
  46. auto cacheFolderName = project.getProjectFilenameRootString() + "_" + project.getProjectUIDString();
  47. #if JUCE_DEBUG
  48. cacheFolderName += "_debug";
  49. #endif
  50. return getProjucerTempFolder().getChildFile ("Intermediate Files").getChildFile (cacheFolderName);
  51. }
  52. //==============================================================================
  53. class ClientIPC : public MessageHandler,
  54. private InterprocessConnection,
  55. private Timer
  56. {
  57. public:
  58. ClientIPC (CompileEngineChildProcess& cp)
  59. : InterprocessConnection (true), owner (cp)
  60. {
  61. launchServer();
  62. }
  63. ~ClientIPC()
  64. {
  65. #if RUN_CLANG_IN_CHILD_PROCESS
  66. if (childProcess.isRunning())
  67. {
  68. #if JUCE_DEBUG
  69. killServerPolitely();
  70. #else
  71. // in release builds we don't want to wait
  72. // for the server to clean up and shut down
  73. killServerWithoutMercy();
  74. #endif
  75. }
  76. #endif
  77. }
  78. void launchServer()
  79. {
  80. DBG ("Client: Launching Server...");
  81. auto pipeName = "ipc_" + String::toHexString (Random().nextInt64());
  82. auto command = createCommandLineForLaunchingServer (pipeName, owner.project.getProjectUIDString(),
  83. getCacheLocationForProject (owner.project));
  84. #if RUN_CLANG_IN_CHILD_PROCESS
  85. if (! childProcess.start (command))
  86. jassertfalse;
  87. #else
  88. server = createClangServer (command);
  89. #endif
  90. if (connectToPipe (pipeName, 10000))
  91. MessageTypes::sendPing (*this);
  92. else
  93. jassertfalse;
  94. startTimer (serverKeepAliveTimeout);
  95. }
  96. void killServerPolitely()
  97. {
  98. DBG ("Client: Killing Server...");
  99. MessageTypes::sendQuit (*this);
  100. disconnect();
  101. stopTimer();
  102. #if RUN_CLANG_IN_CHILD_PROCESS
  103. childProcess.waitForProcessToFinish (5000);
  104. #endif
  105. killServerWithoutMercy();
  106. }
  107. void killServerWithoutMercy()
  108. {
  109. disconnect();
  110. stopTimer();
  111. #if RUN_CLANG_IN_CHILD_PROCESS
  112. childProcess.kill();
  113. #else
  114. destroyClangServer (server);
  115. server = nullptr;
  116. #endif
  117. }
  118. void connectionMade()
  119. {
  120. DBG ("Client: connected");
  121. stopTimer();
  122. }
  123. void connectionLost()
  124. {
  125. DBG ("Client: disconnected");
  126. startTimer (100);
  127. }
  128. bool sendMessage (const ValueTree& m)
  129. {
  130. return InterprocessConnection::sendMessage (MessageHandler::convertMessage (m));
  131. }
  132. void messageReceived (const MemoryBlock& message)
  133. {
  134. #if RUN_CLANG_IN_CHILD_PROCESS
  135. startTimer (serverKeepAliveTimeout);
  136. #else
  137. stopTimer();
  138. #endif
  139. MessageTypes::dispatchToClient (owner, MessageHandler::convertMessage (message));
  140. }
  141. enum { serverKeepAliveTimeout = 10000 };
  142. private:
  143. CompileEngineChildProcess& owner;
  144. #if RUN_CLANG_IN_CHILD_PROCESS
  145. ChildProcess childProcess;
  146. #else
  147. void* server;
  148. #endif
  149. void timerCallback()
  150. {
  151. stopTimer();
  152. owner.handleCrash (String());
  153. }
  154. };
  155. //==============================================================================
  156. class CompileEngineChildProcess::ChildProcess : private ValueTree::Listener,
  157. private Timer
  158. {
  159. public:
  160. ChildProcess (CompileEngineChildProcess& proc, Project& p)
  161. : owner (proc), project (p)
  162. {
  163. projectRoot = project.getProjectRoot();
  164. restartServer();
  165. projectRoot.addListener (this);
  166. openedOk = true;
  167. }
  168. ~ChildProcess()
  169. {
  170. projectRoot.removeListener (this);
  171. if (isRunningApp && server != nullptr)
  172. server->killServerWithoutMercy();
  173. server.reset();
  174. }
  175. void restartServer()
  176. {
  177. server.reset (new ClientIPC (owner));
  178. sendRebuild();
  179. }
  180. void sendRebuild()
  181. {
  182. stopTimer();
  183. ProjectBuildInfo build;
  184. if (! doesProjectMatchSavedHeaderState (project))
  185. {
  186. MessageTypes::sendNewBuild (*server, build);
  187. owner.errorList.resetToError ("Project structure does not match the saved headers! "
  188. "Please re-save your project to enable compilation");
  189. return;
  190. }
  191. if (areAnyModulesMissing (project))
  192. {
  193. MessageTypes::sendNewBuild (*server, build);
  194. owner.errorList.resetToError ("Some of your JUCE modules can't be found! "
  195. "Please check that all the module paths are correct");
  196. return;
  197. }
  198. build.setSystemIncludes (getSystemIncludePaths());
  199. build.setUserIncludes (getUserIncludes());
  200. build.setGlobalDefs (getGlobalDefs());
  201. build.setCompileFlags (project.getCompileEngineSettings().getExtraCompilerFlagsString());
  202. build.setExtraDLLs (getExtraDLLs());
  203. build.setJuceModulesFolder (EnabledModuleList::findDefaultModulesFolder (project).getFullPathName());
  204. build.setUtilsCppInclude (project.getAppIncludeFile().getFullPathName());
  205. build.setWindowsTargetPlatformVersion (project.getCompileEngineSettings().getWindowsTargetPlatformVersionString());
  206. scanForProjectFiles (project, build);
  207. owner.updateAllEditors();
  208. MessageTypes::sendNewBuild (*server, build);
  209. }
  210. void cleanAll()
  211. {
  212. MessageTypes::sendCleanAll (*server);
  213. sendRebuild();
  214. }
  215. void reinstantiatePreviews()
  216. {
  217. MessageTypes::sendReinstantiate (*server);
  218. }
  219. bool launchApp()
  220. {
  221. MessageTypes::sendLaunchApp (*server);
  222. return true;
  223. }
  224. std::unique_ptr<ClientIPC> server;
  225. bool openedOk = false;
  226. bool isRunningApp = false;
  227. private:
  228. CompileEngineChildProcess& owner;
  229. Project& project;
  230. ValueTree projectRoot;
  231. void projectStructureChanged()
  232. {
  233. startTimer (100);
  234. }
  235. void timerCallback() override
  236. {
  237. sendRebuild();
  238. }
  239. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { projectStructureChanged(); }
  240. void valueTreeChildAdded (ValueTree&, ValueTree&) override { projectStructureChanged(); }
  241. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { projectStructureChanged(); }
  242. void valueTreeParentChanged (ValueTree&) override { projectStructureChanged(); }
  243. void valueTreeChildOrderChanged (ValueTree&, int, int) override {}
  244. String getGlobalDefs()
  245. {
  246. StringArray defs;
  247. defs.add (project.getCompileEngineSettings().getExtraPreprocessorDefsString());
  248. {
  249. auto projectDefines = project.getPreprocessorDefs();
  250. for (int i = 0; i < projectDefines.size(); ++i)
  251. {
  252. auto def = projectDefines.getAllKeys()[i];
  253. auto value = projectDefines.getAllValues()[i];
  254. if (value.isNotEmpty())
  255. def << "=" << value;
  256. defs.add (def);
  257. }
  258. }
  259. for (Project::ExporterIterator exporter (project); exporter.next();)
  260. if (exporter->canLaunchProject())
  261. defs.add (exporter->getExporterIdentifierMacro() + "=1");
  262. // Use the JUCE implementation of std::function until the live build
  263. // engine can compile the one from the standard library
  264. defs.add (" _LIBCPP_FUNCTIONAL=1");
  265. defs.removeEmptyStrings();
  266. return defs.joinIntoString (" ");
  267. }
  268. static void scanProjectItem (const Project::Item& projectItem, Array<File>& compileUnits, Array<File>& userFiles)
  269. {
  270. if (projectItem.isGroup())
  271. {
  272. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  273. scanProjectItem (projectItem.getChild(i), compileUnits, userFiles);
  274. return;
  275. }
  276. if (projectItem.shouldBeCompiled())
  277. {
  278. auto f = projectItem.getFile();
  279. if (f.exists())
  280. compileUnits.add (f);
  281. }
  282. if (projectItem.shouldBeAddedToTargetProject() && ! projectItem.shouldBeAddedToBinaryResources())
  283. {
  284. auto f = projectItem.getFile();
  285. if (f.exists())
  286. userFiles.add (f);
  287. }
  288. }
  289. void scanForProjectFiles (Project& proj, ProjectBuildInfo& build)
  290. {
  291. Array<File> compileUnits, userFiles;
  292. scanProjectItem (proj.getMainGroup(), compileUnits, userFiles);
  293. {
  294. auto isVSTHost = project.getModules().isModuleEnabled ("juce_audio_processors")
  295. && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3") || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"));
  296. auto isPluginProject = proj.getProjectType().isAudioPlugin();
  297. OwnedArray<LibraryModule> modules;
  298. proj.getModules().createRequiredModules (modules);
  299. for (Project::ExporterIterator exporter (proj); exporter.next();)
  300. {
  301. if (exporter->canLaunchProject())
  302. {
  303. for (auto* m : modules)
  304. {
  305. auto localModuleFolder = proj.getModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue()
  306. ? proj.getLocalModuleFolder (m->moduleInfo.getID())
  307. : m->moduleInfo.getFolder();
  308. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits,
  309. isPluginProject || isVSTHost ? ProjectType::Target::SharedCodeTarget
  310. : ProjectType::Target::unspecified);
  311. if (isPluginProject || isVSTHost)
  312. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits, ProjectType::Target::StandalonePlugIn);
  313. }
  314. break;
  315. }
  316. }
  317. }
  318. for (int i = 0; ; ++i)
  319. {
  320. auto binaryDataCpp = proj.getBinaryDataCppFile (i);
  321. if (! binaryDataCpp.exists())
  322. break;
  323. compileUnits.add (binaryDataCpp);
  324. }
  325. for (auto i = compileUnits.size(); --i >= 0;)
  326. if (compileUnits.getReference(i).hasFileExtension (".r"))
  327. compileUnits.remove (i);
  328. build.setFiles (compileUnits, userFiles);
  329. }
  330. static bool doesProjectMatchSavedHeaderState (Project& project)
  331. {
  332. auto liveModules = project.getProjectRoot().getChildWithName (Ids::MODULES);
  333. std::unique_ptr<XmlElement> xml (XmlDocument::parse (project.getFile()));
  334. if (xml == nullptr || ! xml->hasTagName (Ids::JUCERPROJECT.toString()))
  335. return false;
  336. auto diskModules = ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES);
  337. return liveModules.isEquivalentTo (diskModules);
  338. }
  339. static bool areAnyModulesMissing (Project& project)
  340. {
  341. OwnedArray<LibraryModule> modules;
  342. project.getModules().createRequiredModules (modules);
  343. for (auto* module : modules)
  344. if (! module->getFolder().isDirectory())
  345. return true;
  346. return false;
  347. }
  348. StringArray getUserIncludes()
  349. {
  350. StringArray paths;
  351. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  352. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getUserHeaderPathString()));
  353. return convertSearchPathsToAbsolute (paths);
  354. }
  355. StringArray getSystemIncludePaths()
  356. {
  357. StringArray paths;
  358. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getSystemHeaderPathString()));
  359. auto isVSTHost = project.getModules().isModuleEnabled ("juce_audio_processors")
  360. && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3")
  361. || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"));
  362. auto customVst3Path = getAppSettings().getStoredPath (Ids::vst3Path).toString();
  363. if (customVst3Path.isNotEmpty() && (project.getProjectType().isAudioPlugin() || isVSTHost))
  364. paths.add (customVst3Path);
  365. OwnedArray<LibraryModule> modules;
  366. project.getModules().createRequiredModules (modules);
  367. for (auto* module : modules)
  368. {
  369. paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
  370. if (customVst3Path.isEmpty() && (project.getProjectType().isAudioPlugin() || isVSTHost))
  371. if (module->getID() == "juce_audio_processors")
  372. paths.addIfNotAlreadyThere (module->getFolder().getChildFile ("format_types").getChildFile ("VST3_SDK").getFullPathName());
  373. }
  374. return convertSearchPathsToAbsolute (paths);
  375. }
  376. StringArray convertSearchPathsToAbsolute (const StringArray& paths) const
  377. {
  378. StringArray s;
  379. const File root (project.getProjectFolder());
  380. for (String p : paths)
  381. s.add (root.getChildFile (p).getFullPathName());
  382. return s;
  383. }
  384. StringArray getExtraDLLs()
  385. {
  386. auto dlls = StringArray::fromTokens (project.getCompileEngineSettings().getExtraDLLsString(), "\n\r,", {});
  387. dlls.trim();
  388. dlls.removeEmptyStrings();
  389. return dlls;
  390. }
  391. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
  392. };
  393. //==============================================================================
  394. CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
  395. : project (p)
  396. {
  397. ProjucerApplication::getApp().openDocumentManager.addListener (this);
  398. createProcess();
  399. errorList.setWarningsEnabled (project.getCompileEngineSettings().areWarningsEnabled());
  400. }
  401. CompileEngineChildProcess::~CompileEngineChildProcess()
  402. {
  403. ProjucerApplication::getApp().openDocumentManager.removeListener (this);
  404. process.reset();
  405. lastComponentList.clear();
  406. }
  407. void CompileEngineChildProcess::createProcess()
  408. {
  409. jassert (process == nullptr);
  410. process.reset (new ChildProcess (*this, project));
  411. if (! process->openedOk)
  412. process.reset();
  413. updateAllEditors();
  414. }
  415. void CompileEngineChildProcess::cleanAll()
  416. {
  417. if (process != nullptr)
  418. process->cleanAll();
  419. }
  420. void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
  421. {
  422. if (process != nullptr)
  423. {
  424. MainWindow* projectWindow = nullptr;
  425. OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
  426. for (int i = 0; i < windows.size(); ++i)
  427. {
  428. if (MainWindow* w = windows[i])
  429. {
  430. if (w->getProject() == &project)
  431. {
  432. projectWindow = w;
  433. break;
  434. }
  435. }
  436. }
  437. Rectangle<int> mainWindowRect;
  438. if (projectWindow != nullptr)
  439. mainWindowRect = projectWindow->getBounds();
  440. MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
  441. }
  442. }
  443. void CompileEngineChildProcess::reinstantiatePreviews()
  444. {
  445. if (process != nullptr)
  446. process->reinstantiatePreviews();
  447. }
  448. void CompileEngineChildProcess::processActivationChanged (bool isForeground)
  449. {
  450. if (process != nullptr)
  451. MessageTypes::sendProcessActivationState (*process->server, isForeground);
  452. }
  453. //==============================================================================
  454. bool CompileEngineChildProcess::canLaunchApp() const
  455. {
  456. return process != nullptr
  457. && runningAppProcess == nullptr
  458. && activityList.getNumActivities() == 0
  459. && errorList.getNumErrors() == 0
  460. && project.getProjectType().isGUIApplication();
  461. }
  462. void CompileEngineChildProcess::launchApp()
  463. {
  464. if (process != nullptr)
  465. process->launchApp();
  466. }
  467. bool CompileEngineChildProcess::canKillApp() const
  468. {
  469. return runningAppProcess != nullptr;
  470. }
  471. void CompileEngineChildProcess::killApp()
  472. {
  473. runningAppProcess.reset();
  474. }
  475. void CompileEngineChildProcess::handleAppLaunched()
  476. {
  477. runningAppProcess.reset (process.release());
  478. runningAppProcess->isRunningApp = true;
  479. createProcess();
  480. }
  481. void CompileEngineChildProcess::handleAppQuit()
  482. {
  483. DBG ("handleAppQuit");
  484. runningAppProcess.reset();
  485. }
  486. bool CompileEngineChildProcess::isAppRunning() const noexcept
  487. {
  488. return runningAppProcess != nullptr && runningAppProcess->isRunningApp;
  489. }
  490. //==============================================================================
  491. struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
  492. private Timer
  493. {
  494. Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
  495. : owner (ccp), file (f), document (doc), transactionTimer (doc)
  496. {
  497. sendFullUpdate();
  498. document.addListener (this);
  499. }
  500. ~Editor()
  501. {
  502. document.removeListener (this);
  503. }
  504. void codeDocumentTextInserted (const String& newText, int insertIndex) override
  505. {
  506. CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
  507. startEditorChangeTimer();
  508. transactionTimer.stopTimer();
  509. owner.lastComponentList.globalNamespace
  510. .nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
  511. }
  512. void codeDocumentTextDeleted (int start, int end) override
  513. {
  514. CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
  515. startEditorChangeTimer();
  516. transactionTimer.stopTimer();
  517. owner.lastComponentList.globalNamespace
  518. .nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
  519. }
  520. void sendFullUpdate()
  521. {
  522. reset();
  523. if (owner.process != nullptr)
  524. MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
  525. }
  526. bool flushEditorChanges()
  527. {
  528. if (pendingChanges.size() > 0)
  529. {
  530. if (owner.process != nullptr && owner.process->server != nullptr)
  531. MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
  532. reset();
  533. return true;
  534. }
  535. stopTimer();
  536. return false;
  537. }
  538. void reset()
  539. {
  540. stopTimer();
  541. pendingChanges.clear();
  542. }
  543. void startTransactionTimer()
  544. {
  545. transactionTimer.startTimer (1000);
  546. }
  547. void startEditorChangeTimer()
  548. {
  549. startTimer (200);
  550. }
  551. CompileEngineChildProcess& owner;
  552. File file;
  553. CodeDocument& document;
  554. private:
  555. Array<CodeChange> pendingChanges;
  556. void timerCallback() override
  557. {
  558. if (owner.project.getCompileEngineSettings().isContinuousRebuildEnabled())
  559. flushEditorChanges();
  560. else
  561. stopTimer();
  562. }
  563. struct TransactionTimer : public Timer
  564. {
  565. TransactionTimer (CodeDocument& doc) : document (doc) {}
  566. void timerCallback() override
  567. {
  568. stopTimer();
  569. document.newTransaction();
  570. }
  571. CodeDocument& document;
  572. };
  573. TransactionTimer transactionTimer;
  574. };
  575. void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
  576. {
  577. editors.add (new Editor (*this, file, document));
  578. }
  579. bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
  580. {
  581. for (int i = editors.size(); --i >= 0;)
  582. {
  583. if (document->getFile() == editors.getUnchecked(i)->file)
  584. {
  585. const File f (editors.getUnchecked(i)->file);
  586. editors.remove (i);
  587. if (process != nullptr)
  588. MessageTypes::sendHandleFileReset (*process->server, f);
  589. }
  590. }
  591. return true;
  592. }
  593. void CompileEngineChildProcess::updateAllEditors()
  594. {
  595. for (int i = editors.size(); --i >= 0;)
  596. editors.getUnchecked(i)->sendFullUpdate();
  597. }
  598. //==============================================================================
  599. void CompileEngineChildProcess::handleCrash (const String& message)
  600. {
  601. Logger::writeToLog ("*** Child process crashed: " + message);
  602. if (crashHandler != nullptr)
  603. crashHandler (message);
  604. }
  605. void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
  606. void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
  607. void CompileEngineChildProcess::handleCloseIDE()
  608. {
  609. if (JUCEApplication* app = JUCEApplication::getInstance())
  610. app->systemRequestedQuit();
  611. }
  612. void CompileEngineChildProcess::handleMissingSystemHeaders()
  613. {
  614. if (ProjectContentComponent* p = findProjectContentComponent())
  615. p->handleMissingSystemHeaders();
  616. }
  617. void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
  618. {
  619. ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
  620. CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
  621. if (command == StandardApplicationCommandIDs::undo)
  622. {
  623. handleUndoInEditor (className);
  624. }
  625. else if (command == StandardApplicationCommandIDs::redo)
  626. {
  627. handleRedoInEditor (className);
  628. }
  629. else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
  630. {
  631. commandManager.setFirstCommandTarget (target);
  632. commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
  633. commandManager.setFirstCommandTarget (nullptr);
  634. }
  635. }
  636. void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
  637. {
  638. }
  639. void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
  640. {
  641. }
  642. void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
  643. {
  644. lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
  645. activityList.sendClassListChangedMessage (lastComponentList);
  646. }
  647. void CompileEngineChildProcess::handleBuildFailed()
  648. {
  649. ProjucerApplication::getCommandManager().commandStatusChanged();
  650. }
  651. void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
  652. {
  653. if (Editor* ed = getOrOpenEditorFor (location.file))
  654. {
  655. if (ed->flushEditorChanges())
  656. return; // client-side editor changes were pending, so deal with them first, and discard
  657. // the incoming change, whose position may now be wrong.
  658. ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
  659. ed->document.insertText (location.range.getStart(), newText);
  660. // deliberately clear the messages that we just added, to avoid these changes being
  661. // sent to the server (which will already have processed the same ones locally)
  662. ed->reset();
  663. ed->startTransactionTimer();
  664. }
  665. }
  666. void CompileEngineChildProcess::handlePing()
  667. {
  668. }
  669. //==============================================================================
  670. void CompileEngineChildProcess::flushEditorChanges()
  671. {
  672. for (Editor* ed : editors)
  673. ed->flushEditorChanges();
  674. }
  675. ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
  676. {
  677. for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
  678. if (mw->getProject() == &project)
  679. return mw->getProjectContentComponent();
  680. return nullptr;
  681. }
  682. CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
  683. {
  684. for (Editor* ed : editors)
  685. if (ed->file == file)
  686. return ed;
  687. if (ProjectContentComponent* pcc = findProjectContentComponent())
  688. if (pcc->showEditorForFile (file, false))
  689. return getOrOpenEditorFor (file);
  690. return nullptr;
  691. }
  692. void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
  693. {
  694. ProjectContentComponent* pcc = findProjectContentComponent();
  695. if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
  696. {
  697. SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
  698. if (sce != nullptr && sce->editor != nullptr)
  699. {
  700. sce->highlight (location.range, true);
  701. Process::makeForegroundProcess();
  702. CodeEditorComponent& ed = *sce->editor;
  703. ed.getTopLevelComponent()->toFront (false);
  704. ed.grabKeyboardFocus();
  705. }
  706. }
  707. }
  708. void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
  709. {
  710. File cacheFolder (getCacheLocationForProject (p));
  711. if (cacheFolder.isDirectory())
  712. cacheFolder.deleteRecursively();
  713. }