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.

894 lines
27KB

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