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.

911 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. 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. server.reset();
  180. }
  181. void restartServer()
  182. {
  183. server.reset (new ClientIPC (owner));
  184. sendRebuild();
  185. }
  186. void sendRebuild()
  187. {
  188. stopTimer();
  189. ProjectBuildInfo build;
  190. if (! doesProjectMatchSavedHeaderState (project))
  191. {
  192. MessageTypes::sendNewBuild (*server, build);
  193. owner.errorList.resetToError ("Project structure does not match the saved headers! "
  194. "Please re-save your project to enable compilation");
  195. return;
  196. }
  197. if (areAnyModulesMissing (project))
  198. {
  199. MessageTypes::sendNewBuild (*server, build);
  200. owner.errorList.resetToError ("Some of your JUCE modules can't be found! "
  201. "Please check that all the module paths are correct");
  202. return;
  203. }
  204. build.setSystemIncludes (getSystemIncludePaths());
  205. build.setUserIncludes (getUserIncludes());
  206. build.setGlobalDefs (getGlobalDefs());
  207. build.setCompileFlags (project.getCompileEngineSettings().getExtraCompilerFlagsString());
  208. build.setExtraDLLs (getExtraDLLs());
  209. build.setJuceModulesFolder (EnabledModuleList::findDefaultModulesFolder (project).getFullPathName());
  210. build.setUtilsCppInclude (project.getAppIncludeFile().getFullPathName());
  211. build.setWindowsTargetPlatformVersion (project.getCompileEngineSettings().getWindowsTargetPlatformVersionString());
  212. scanForProjectFiles (project, build);
  213. owner.updateAllEditors();
  214. MessageTypes::sendNewBuild (*server, build);
  215. }
  216. void cleanAll()
  217. {
  218. MessageTypes::sendCleanAll (*server);
  219. sendRebuild();
  220. }
  221. void reinstantiatePreviews()
  222. {
  223. MessageTypes::sendReinstantiate (*server);
  224. }
  225. bool launchApp()
  226. {
  227. MessageTypes::sendLaunchApp (*server);
  228. return true;
  229. }
  230. std::unique_ptr<ClientIPC> server;
  231. bool openedOk = false;
  232. bool isRunningApp = false;
  233. private:
  234. CompileEngineChildProcess& owner;
  235. Project& project;
  236. ValueTree projectRoot;
  237. void projectStructureChanged()
  238. {
  239. startTimer (100);
  240. }
  241. void timerCallback() override
  242. {
  243. sendRebuild();
  244. }
  245. void valueTreePropertyChanged (ValueTree&, const Identifier&) override { projectStructureChanged(); }
  246. void valueTreeChildAdded (ValueTree&, ValueTree&) override { projectStructureChanged(); }
  247. void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { projectStructureChanged(); }
  248. void valueTreeParentChanged (ValueTree&) override { projectStructureChanged(); }
  249. String getGlobalDefs()
  250. {
  251. StringArray defs;
  252. defs.add (project.getCompileEngineSettings().getExtraPreprocessorDefsString());
  253. {
  254. auto projectDefines = project.getPreprocessorDefs();
  255. for (int i = 0; i < projectDefines.size(); ++i)
  256. {
  257. auto def = projectDefines.getAllKeys()[i];
  258. auto value = projectDefines.getAllValues()[i];
  259. if (value.isNotEmpty())
  260. def << "=" << value;
  261. defs.add (def);
  262. }
  263. }
  264. for (Project::ExporterIterator exporter (project); exporter.next();)
  265. if (exporter->canLaunchProject())
  266. defs.add (exporter->getExporterIdentifierMacro() + "=1");
  267. // Use the JUCE implementation of std::function until the live build
  268. // engine can compile the one from the standard library
  269. defs.add (" _LIBCPP_FUNCTIONAL=1");
  270. defs.removeEmptyStrings();
  271. return defs.joinIntoString (" ");
  272. }
  273. static void scanProjectItem (const Project::Item& projectItem, Array<File>& compileUnits, Array<File>& userFiles)
  274. {
  275. if (projectItem.isGroup())
  276. {
  277. for (int i = 0; i < projectItem.getNumChildren(); ++i)
  278. scanProjectItem (projectItem.getChild(i), compileUnits, userFiles);
  279. return;
  280. }
  281. if (projectItem.shouldBeCompiled())
  282. {
  283. auto f = projectItem.getFile();
  284. if (f.exists())
  285. compileUnits.add (f);
  286. }
  287. if (projectItem.shouldBeAddedToTargetProject() && ! projectItem.shouldBeAddedToBinaryResources())
  288. {
  289. auto f = projectItem.getFile();
  290. if (f.exists())
  291. userFiles.add (f);
  292. }
  293. }
  294. void scanForProjectFiles (Project& proj, ProjectBuildInfo& build)
  295. {
  296. Array<File> compileUnits, userFiles;
  297. scanProjectItem (proj.getMainGroup(), compileUnits, userFiles);
  298. {
  299. auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors")
  300. && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3") || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"));
  301. auto isPluginProject = proj.isAudioPluginProject();
  302. OwnedArray<LibraryModule> modules;
  303. proj.getEnabledModules().createRequiredModules (modules);
  304. for (Project::ExporterIterator exporter (proj); exporter.next();)
  305. {
  306. if (exporter->canLaunchProject())
  307. {
  308. for (auto* m : modules)
  309. {
  310. auto localModuleFolder = proj.getEnabledModules().shouldCopyModuleFilesLocally (m->moduleInfo.getID()).getValue()
  311. ? proj.getLocalModuleFolder (m->moduleInfo.getID())
  312. : m->moduleInfo.getFolder();
  313. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits,
  314. isPluginProject || isVSTHost ? ProjectType::Target::SharedCodeTarget
  315. : ProjectType::Target::unspecified);
  316. if (isPluginProject || isVSTHost)
  317. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits, ProjectType::Target::StandalonePlugIn);
  318. }
  319. break;
  320. }
  321. }
  322. }
  323. for (int i = 0; ; ++i)
  324. {
  325. auto binaryDataCpp = proj.getBinaryDataCppFile (i);
  326. if (! binaryDataCpp.exists())
  327. break;
  328. compileUnits.add (binaryDataCpp);
  329. }
  330. for (auto i = compileUnits.size(); --i >= 0;)
  331. if (compileUnits.getReference(i).hasFileExtension (".r"))
  332. compileUnits.remove (i);
  333. build.setFiles (compileUnits, userFiles);
  334. }
  335. static bool doesProjectMatchSavedHeaderState (Project& project)
  336. {
  337. auto liveModules = project.getProjectRoot().getChildWithName (Ids::MODULES);
  338. if (auto xml = parseXMLIfTagMatches (project.getFile(), Ids::JUCERPROJECT.toString()))
  339. {
  340. auto diskModules = ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES);
  341. return liveModules.isEquivalentTo (diskModules);
  342. }
  343. return false;
  344. }
  345. static bool areAnyModulesMissing (Project& project)
  346. {
  347. OwnedArray<LibraryModule> modules;
  348. project.getEnabledModules().createRequiredModules (modules);
  349. for (auto* module : modules)
  350. if (! module->getFolder().isDirectory())
  351. return true;
  352. return false;
  353. }
  354. StringArray getUserIncludes()
  355. {
  356. StringArray paths;
  357. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  358. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getUserHeaderPathString()));
  359. return convertSearchPathsToAbsolute (paths);
  360. }
  361. StringArray getSystemIncludePaths()
  362. {
  363. StringArray paths;
  364. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  365. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getSystemHeaderPathString()));
  366. auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors")
  367. && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3")
  368. || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST"));
  369. auto customVst3Path = getAppSettings().getStoredPath (Ids::vst3Path, TargetOS::getThisOS()).get().toString();
  370. if (customVst3Path.isNotEmpty() && (project.isAudioPluginProject() || isVSTHost))
  371. paths.add (customVst3Path);
  372. OwnedArray<LibraryModule> modules;
  373. project.getEnabledModules().createRequiredModules (modules);
  374. for (auto* module : modules)
  375. {
  376. paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
  377. if (customVst3Path.isEmpty() && (project.isAudioPluginProject() || isVSTHost))
  378. if (module->getID() == "juce_audio_processors")
  379. paths.addIfNotAlreadyThere (module->getFolder().getChildFile ("format_types").getChildFile ("VST3_SDK").getFullPathName());
  380. }
  381. return convertSearchPathsToAbsolute (paths);
  382. }
  383. StringArray convertSearchPathsToAbsolute (const StringArray& paths) const
  384. {
  385. StringArray s;
  386. const File root (project.getProjectFolder());
  387. for (String p : paths)
  388. s.add (root.getChildFile (p).getFullPathName());
  389. return s;
  390. }
  391. StringArray getExtraDLLs()
  392. {
  393. auto dlls = StringArray::fromTokens (project.getCompileEngineSettings().getExtraDLLsString(), "\n\r,", {});
  394. dlls.trim();
  395. dlls.removeEmptyStrings();
  396. return dlls;
  397. }
  398. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ChildProcess)
  399. };
  400. //==============================================================================
  401. CompileEngineChildProcess::CompileEngineChildProcess (Project& p)
  402. : project (p)
  403. {
  404. ProjucerApplication::getApp().openDocumentManager.addListener (this);
  405. createProcess();
  406. errorList.setWarningsEnabled (project.getCompileEngineSettings().areWarningsEnabled());
  407. }
  408. CompileEngineChildProcess::~CompileEngineChildProcess()
  409. {
  410. ProjucerApplication::getApp().openDocumentManager.removeListener (this);
  411. process.reset();
  412. lastComponentList.clear();
  413. }
  414. void CompileEngineChildProcess::createProcess()
  415. {
  416. jassert (process == nullptr);
  417. process.reset (new ChildProcess (*this, project));
  418. if (! process->openedOk)
  419. process.reset();
  420. updateAllEditors();
  421. }
  422. void CompileEngineChildProcess::cleanAll()
  423. {
  424. if (process != nullptr)
  425. process->cleanAll();
  426. }
  427. void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
  428. {
  429. if (process != nullptr)
  430. {
  431. MainWindow* projectWindow = nullptr;
  432. OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
  433. for (int i = 0; i < windows.size(); ++i)
  434. {
  435. if (MainWindow* w = windows[i])
  436. {
  437. if (w->getProject() == &project)
  438. {
  439. projectWindow = w;
  440. break;
  441. }
  442. }
  443. }
  444. Rectangle<int> mainWindowRect;
  445. if (projectWindow != nullptr)
  446. mainWindowRect = projectWindow->getBounds();
  447. MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
  448. }
  449. }
  450. void CompileEngineChildProcess::reinstantiatePreviews()
  451. {
  452. if (process != nullptr)
  453. process->reinstantiatePreviews();
  454. }
  455. void CompileEngineChildProcess::processActivationChanged (bool isForeground)
  456. {
  457. if (process != nullptr)
  458. MessageTypes::sendProcessActivationState (*process->server, isForeground);
  459. }
  460. //==============================================================================
  461. bool CompileEngineChildProcess::canLaunchApp() const
  462. {
  463. return process != nullptr
  464. && runningAppProcess == nullptr
  465. && activityList.getNumActivities() == 0
  466. && errorList.getNumErrors() == 0
  467. && project.getProjectType().isGUIApplication();
  468. }
  469. void CompileEngineChildProcess::launchApp()
  470. {
  471. if (process != nullptr)
  472. process->launchApp();
  473. }
  474. bool CompileEngineChildProcess::canKillApp() const
  475. {
  476. return runningAppProcess != nullptr;
  477. }
  478. void CompileEngineChildProcess::killApp()
  479. {
  480. runningAppProcess.reset();
  481. }
  482. void CompileEngineChildProcess::handleAppLaunched()
  483. {
  484. runningAppProcess.reset (process.release());
  485. runningAppProcess->isRunningApp = true;
  486. createProcess();
  487. }
  488. void CompileEngineChildProcess::handleAppQuit()
  489. {
  490. DBG ("handleAppQuit");
  491. runningAppProcess.reset();
  492. }
  493. bool CompileEngineChildProcess::isAppRunning() const noexcept
  494. {
  495. return runningAppProcess != nullptr && runningAppProcess->isRunningApp;
  496. }
  497. //==============================================================================
  498. struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
  499. private Timer
  500. {
  501. Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
  502. : owner (ccp), file (f), document (doc), transactionTimer (doc)
  503. {
  504. sendFullUpdate();
  505. document.addListener (this);
  506. }
  507. ~Editor() override
  508. {
  509. document.removeListener (this);
  510. }
  511. void codeDocumentTextInserted (const String& newText, int insertIndex) override
  512. {
  513. CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
  514. startEditorChangeTimer();
  515. transactionTimer.stopTimer();
  516. owner.lastComponentList.globalNamespace
  517. .nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
  518. }
  519. void codeDocumentTextDeleted (int start, int end) override
  520. {
  521. CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
  522. startEditorChangeTimer();
  523. transactionTimer.stopTimer();
  524. owner.lastComponentList.globalNamespace
  525. .nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
  526. }
  527. void sendFullUpdate()
  528. {
  529. reset();
  530. if (owner.process != nullptr)
  531. MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
  532. }
  533. bool flushEditorChanges()
  534. {
  535. if (pendingChanges.size() > 0)
  536. {
  537. if (owner.process != nullptr && owner.process->server != nullptr)
  538. MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
  539. reset();
  540. return true;
  541. }
  542. stopTimer();
  543. return false;
  544. }
  545. void reset()
  546. {
  547. stopTimer();
  548. pendingChanges.clear();
  549. }
  550. void startTransactionTimer()
  551. {
  552. transactionTimer.startTimer (1000);
  553. }
  554. void startEditorChangeTimer()
  555. {
  556. startTimer (200);
  557. }
  558. CompileEngineChildProcess& owner;
  559. File file;
  560. CodeDocument& document;
  561. private:
  562. Array<CodeChange> pendingChanges;
  563. void timerCallback() override
  564. {
  565. if (owner.project.getCompileEngineSettings().isContinuousRebuildEnabled())
  566. flushEditorChanges();
  567. else
  568. stopTimer();
  569. }
  570. struct TransactionTimer : public Timer
  571. {
  572. TransactionTimer (CodeDocument& doc) : document (doc) {}
  573. void timerCallback() override
  574. {
  575. stopTimer();
  576. document.newTransaction();
  577. }
  578. CodeDocument& document;
  579. };
  580. TransactionTimer transactionTimer;
  581. };
  582. void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
  583. {
  584. editors.add (new Editor (*this, file, document));
  585. }
  586. bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
  587. {
  588. for (int i = editors.size(); --i >= 0;)
  589. {
  590. if (document->getFile() == editors.getUnchecked(i)->file)
  591. {
  592. const File f (editors.getUnchecked(i)->file);
  593. editors.remove (i);
  594. if (process != nullptr)
  595. MessageTypes::sendHandleFileReset (*process->server, f);
  596. }
  597. }
  598. return true;
  599. }
  600. void CompileEngineChildProcess::updateAllEditors()
  601. {
  602. for (int i = editors.size(); --i >= 0;)
  603. editors.getUnchecked(i)->sendFullUpdate();
  604. }
  605. //==============================================================================
  606. void CompileEngineChildProcess::handleCrash (const String& message)
  607. {
  608. Logger::writeToLog ("*** Child process crashed: " + message);
  609. if (crashHandler != nullptr)
  610. crashHandler (message);
  611. }
  612. void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
  613. void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
  614. void CompileEngineChildProcess::handleCloseIDE()
  615. {
  616. if (JUCEApplication* app = JUCEApplication::getInstance())
  617. app->systemRequestedQuit();
  618. }
  619. void CompileEngineChildProcess::handleMissingSystemHeaders()
  620. {
  621. if (ProjectContentComponent* p = findProjectContentComponent())
  622. p->handleMissingSystemHeaders();
  623. }
  624. void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
  625. {
  626. ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
  627. CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
  628. if (command == StandardApplicationCommandIDs::undo)
  629. {
  630. handleUndoInEditor (className);
  631. }
  632. else if (command == StandardApplicationCommandIDs::redo)
  633. {
  634. handleRedoInEditor (className);
  635. }
  636. else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
  637. {
  638. commandManager.setFirstCommandTarget (target);
  639. commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
  640. commandManager.setFirstCommandTarget (nullptr);
  641. }
  642. }
  643. void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
  644. {
  645. }
  646. void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
  647. {
  648. }
  649. void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
  650. {
  651. lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
  652. activityList.sendClassListChangedMessage (lastComponentList);
  653. }
  654. void CompileEngineChildProcess::handleBuildFailed()
  655. {
  656. ProjucerApplication::getCommandManager().commandStatusChanged();
  657. }
  658. void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
  659. {
  660. if (Editor* ed = getOrOpenEditorFor (location.file))
  661. {
  662. if (ed->flushEditorChanges())
  663. return; // client-side editor changes were pending, so deal with them first, and discard
  664. // the incoming change, whose position may now be wrong.
  665. ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
  666. ed->document.insertText (location.range.getStart(), newText);
  667. // deliberately clear the messages that we just added, to avoid these changes being
  668. // sent to the server (which will already have processed the same ones locally)
  669. ed->reset();
  670. ed->startTransactionTimer();
  671. }
  672. }
  673. void CompileEngineChildProcess::handlePing()
  674. {
  675. }
  676. //==============================================================================
  677. void CompileEngineChildProcess::flushEditorChanges()
  678. {
  679. for (Editor* ed : editors)
  680. ed->flushEditorChanges();
  681. }
  682. ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
  683. {
  684. for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
  685. if (mw->getProject() == &project)
  686. return mw->getProjectContentComponent();
  687. return nullptr;
  688. }
  689. CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
  690. {
  691. for (Editor* ed : editors)
  692. if (ed->file == file)
  693. return ed;
  694. if (ProjectContentComponent* pcc = findProjectContentComponent())
  695. if (pcc->showEditorForFile (file, false))
  696. return getOrOpenEditorFor (file);
  697. return nullptr;
  698. }
  699. void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
  700. {
  701. ProjectContentComponent* pcc = findProjectContentComponent();
  702. if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
  703. {
  704. SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
  705. if (sce != nullptr && sce->editor != nullptr)
  706. {
  707. sce->highlight (location.range, true);
  708. Process::makeForegroundProcess();
  709. CodeEditorComponent& ed = *sce->editor;
  710. ed.getTopLevelComponent()->toFront (false);
  711. ed.grabKeyboardFocus();
  712. }
  713. }
  714. }
  715. void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
  716. {
  717. File cacheFolder (getCacheLocationForProject (p));
  718. if (cacheFolder.isDirectory())
  719. cacheFolder.deleteRecursively();
  720. }