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.

908 lines
27KB

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