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.

900 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2017 - ROLI Ltd.
  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. #ifndef RUN_CLANG_IN_CHILD_PROCESS
  27. #error
  28. #endif
  29. //==============================================================================
  30. static File getProjucerTempFolder() noexcept
  31. {
  32. #if JUCE_MAC
  33. return { "~/Library/Caches/com.juce.projucer" };
  34. #else
  35. return File::getSpecialLocation (File::tempDirectory).getChildFile ("com.juce.projucer");
  36. #endif
  37. }
  38. static File getCacheLocationForProject (Project& project) noexcept
  39. {
  40. auto cacheFolderName = project.getProjectFilenameRootString() + "_" + project.getProjectUIDString();
  41. #if JUCE_DEBUG
  42. cacheFolderName += "_debug";
  43. #endif
  44. return getProjucerTempFolder().getChildFile ("Intermediate Files").getChildFile (cacheFolderName);
  45. }
  46. //==============================================================================
  47. class ClientIPC : public MessageHandler,
  48. private InterprocessConnection,
  49. private Timer
  50. {
  51. public:
  52. ClientIPC (CompileEngineChildProcess& cp)
  53. : InterprocessConnection (true), owner (cp)
  54. {
  55. launchServer();
  56. }
  57. ~ClientIPC()
  58. {
  59. #if RUN_CLANG_IN_CHILD_PROCESS
  60. if (childProcess.isRunning())
  61. {
  62. #if JUCE_DEBUG
  63. killServerPolitely();
  64. #else
  65. // in release builds we don't want to wait
  66. // for the server to clean up and shut down
  67. killServerWithoutMercy();
  68. #endif
  69. }
  70. #endif
  71. }
  72. void launchServer()
  73. {
  74. DBG ("Client: Launching Server...");
  75. auto pipeName = "ipc_" + String::toHexString (Random().nextInt64());
  76. auto command = createCommandLineForLaunchingServer (pipeName, owner.project.getProjectUIDString(),
  77. getCacheLocationForProject (owner.project));
  78. #if RUN_CLANG_IN_CHILD_PROCESS
  79. if (! childProcess.start (command))
  80. jassertfalse;
  81. #else
  82. server = createClangServer (command);
  83. #endif
  84. for (int i = 0; i < 20; ++i)
  85. {
  86. if (connectToPipe (pipeName, 10000))
  87. {
  88. MessageTypes::sendPing (*this);
  89. break;
  90. }
  91. Thread::sleep (50);
  92. }
  93. jassert (isConnected());
  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() override
  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 (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 ? ProjectType::Target::SharedCodeTarget
  306. : ProjectType::Target::unspecified);
  307. if (isPluginProject || isVSTHost)
  308. m->findAndAddCompiledUnits (*exporter, nullptr, compileUnits, ProjectType::Target::StandalonePlugIn);
  309. }
  310. break;
  311. }
  312. }
  313. }
  314. for (int i = 0; ; ++i)
  315. {
  316. auto binaryDataCpp = proj.getBinaryDataCppFile (i);
  317. if (! binaryDataCpp.exists())
  318. break;
  319. compileUnits.add (binaryDataCpp);
  320. }
  321. for (auto i = compileUnits.size(); --i >= 0;)
  322. if (compileUnits.getReference(i).hasFileExtension (".r"))
  323. compileUnits.remove (i);
  324. build.setFiles (compileUnits, userFiles);
  325. }
  326. static bool doesProjectMatchSavedHeaderState (Project& project)
  327. {
  328. auto liveModules = project.getProjectRoot().getChildWithName (Ids::MODULES);
  329. if (auto xml = parseXMLIfTagMatches (project.getFile(), Ids::JUCERPROJECT.toString()))
  330. {
  331. auto diskModules = ValueTree::fromXml (*xml).getChildWithName (Ids::MODULES);
  332. return liveModules.isEquivalentTo (diskModules);
  333. }
  334. return false;
  335. }
  336. static bool areAnyModulesMissing (Project& project)
  337. {
  338. OwnedArray<LibraryModule> modules;
  339. project.getEnabledModules().createRequiredModules (modules);
  340. for (auto* module : modules)
  341. if (! module->getFolder().isDirectory())
  342. return true;
  343. return false;
  344. }
  345. StringArray getUserIncludes()
  346. {
  347. StringArray paths;
  348. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  349. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getUserHeaderPathString()));
  350. return convertSearchPathsToAbsolute (paths);
  351. }
  352. StringArray getSystemIncludePaths()
  353. {
  354. StringArray paths;
  355. paths.add (project.getGeneratedCodeFolder().getFullPathName());
  356. paths.addArray (getSearchPathsFromString (project.getCompileEngineSettings().getSystemHeaderPathString()));
  357. auto isVSTHost = project.getEnabledModules().isModuleEnabled ("juce_audio_processors")
  358. && (project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST3", false)
  359. || project.isConfigFlagEnabled ("JUCE_PLUGINHOST_VST", false));
  360. auto customVst3Path = getAppSettings().getStoredPath (Ids::vst3Path, TargetOS::getThisOS()).get().toString();
  361. if (customVst3Path.isNotEmpty() && (project.isAudioPluginProject() || isVSTHost))
  362. paths.add (customVst3Path);
  363. OwnedArray<LibraryModule> modules;
  364. project.getEnabledModules().createRequiredModules (modules);
  365. for (auto* module : modules)
  366. {
  367. paths.addIfNotAlreadyThere (module->getFolder().getParentDirectory().getFullPathName());
  368. if (customVst3Path.isEmpty() && (project.isAudioPluginProject() || isVSTHost))
  369. if (module->getID() == "juce_audio_processors")
  370. paths.addIfNotAlreadyThere (module->getFolder().getChildFile ("format_types").getChildFile ("VST3_SDK").getFullPathName());
  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. process.reset();
  403. lastComponentList.clear();
  404. }
  405. void CompileEngineChildProcess::createProcess()
  406. {
  407. jassert (process == nullptr);
  408. process.reset (new ChildProcess (*this, project));
  409. if (! process->openedOk)
  410. process.reset();
  411. updateAllEditors();
  412. }
  413. void CompileEngineChildProcess::cleanAll()
  414. {
  415. if (process != nullptr)
  416. process->cleanAll();
  417. }
  418. void CompileEngineChildProcess::openPreview (const ClassDatabase::Class& comp)
  419. {
  420. if (process != nullptr)
  421. {
  422. MainWindow* projectWindow = nullptr;
  423. OwnedArray<MainWindow>& windows = ProjucerApplication::getApp().mainWindowList.windows;
  424. for (int i = 0; i < windows.size(); ++i)
  425. {
  426. if (MainWindow* w = windows[i])
  427. {
  428. if (w->getProject() == &project)
  429. {
  430. projectWindow = w;
  431. break;
  432. }
  433. }
  434. }
  435. Rectangle<int> mainWindowRect;
  436. if (projectWindow != nullptr)
  437. mainWindowRect = projectWindow->getBounds();
  438. MessageTypes::sendOpenPreview (*process->server, comp, mainWindowRect);
  439. }
  440. }
  441. void CompileEngineChildProcess::reinstantiatePreviews()
  442. {
  443. if (process != nullptr)
  444. process->reinstantiatePreviews();
  445. }
  446. void CompileEngineChildProcess::processActivationChanged (bool isForeground)
  447. {
  448. if (process != nullptr)
  449. MessageTypes::sendProcessActivationState (*process->server, isForeground);
  450. }
  451. //==============================================================================
  452. bool CompileEngineChildProcess::canLaunchApp() const
  453. {
  454. return process != nullptr
  455. && runningAppProcess == nullptr
  456. && activityList.getNumActivities() == 0
  457. && errorList.getNumErrors() == 0
  458. && project.getProjectType().isGUIApplication();
  459. }
  460. void CompileEngineChildProcess::launchApp()
  461. {
  462. if (process != nullptr)
  463. process->launchApp();
  464. }
  465. bool CompileEngineChildProcess::canKillApp() const
  466. {
  467. return runningAppProcess != nullptr;
  468. }
  469. void CompileEngineChildProcess::killApp()
  470. {
  471. runningAppProcess.reset();
  472. }
  473. void CompileEngineChildProcess::handleAppLaunched()
  474. {
  475. runningAppProcess.reset (process.release());
  476. runningAppProcess->isRunningApp = true;
  477. createProcess();
  478. }
  479. void CompileEngineChildProcess::handleAppQuit()
  480. {
  481. DBG ("handleAppQuit");
  482. runningAppProcess.reset();
  483. }
  484. bool CompileEngineChildProcess::isAppRunning() const noexcept
  485. {
  486. return runningAppProcess != nullptr && runningAppProcess->isRunningApp;
  487. }
  488. //==============================================================================
  489. struct CompileEngineChildProcess::Editor : private CodeDocument::Listener,
  490. private Timer
  491. {
  492. Editor (CompileEngineChildProcess& ccp, const File& f, CodeDocument& doc)
  493. : owner (ccp), file (f), document (doc), transactionTimer (doc)
  494. {
  495. sendFullUpdate();
  496. document.addListener (this);
  497. }
  498. ~Editor() override
  499. {
  500. document.removeListener (this);
  501. }
  502. void codeDocumentTextInserted (const String& newText, int insertIndex) override
  503. {
  504. CodeChange (Range<int> (insertIndex, insertIndex), newText).addToList (pendingChanges);
  505. startEditorChangeTimer();
  506. transactionTimer.stopTimer();
  507. owner.lastComponentList.globalNamespace
  508. .nudgeAllCodeRanges (file.getFullPathName(), insertIndex, newText.length());
  509. }
  510. void codeDocumentTextDeleted (int start, int end) override
  511. {
  512. CodeChange (Range<int> (start, end), String()).addToList (pendingChanges);
  513. startEditorChangeTimer();
  514. transactionTimer.stopTimer();
  515. owner.lastComponentList.globalNamespace
  516. .nudgeAllCodeRanges (file.getFullPathName(), start, start - end);
  517. }
  518. void sendFullUpdate()
  519. {
  520. reset();
  521. if (owner.process != nullptr)
  522. MessageTypes::sendFileContentFullUpdate (*owner.process->server, file, document.getAllContent());
  523. }
  524. bool flushEditorChanges()
  525. {
  526. if (pendingChanges.size() > 0)
  527. {
  528. if (owner.process != nullptr && owner.process->server != nullptr)
  529. MessageTypes::sendFileChanges (*owner.process->server, pendingChanges, file);
  530. reset();
  531. return true;
  532. }
  533. stopTimer();
  534. return false;
  535. }
  536. void reset()
  537. {
  538. stopTimer();
  539. pendingChanges.clear();
  540. }
  541. void startTransactionTimer()
  542. {
  543. transactionTimer.startTimer (1000);
  544. }
  545. void startEditorChangeTimer()
  546. {
  547. startTimer (200);
  548. }
  549. CompileEngineChildProcess& owner;
  550. File file;
  551. CodeDocument& document;
  552. private:
  553. Array<CodeChange> pendingChanges;
  554. void timerCallback() override
  555. {
  556. if (owner.project.getCompileEngineSettings().isContinuousRebuildEnabled())
  557. flushEditorChanges();
  558. else
  559. stopTimer();
  560. }
  561. struct TransactionTimer : public Timer
  562. {
  563. TransactionTimer (CodeDocument& doc) : document (doc) {}
  564. void timerCallback() override
  565. {
  566. stopTimer();
  567. document.newTransaction();
  568. }
  569. CodeDocument& document;
  570. };
  571. TransactionTimer transactionTimer;
  572. };
  573. void CompileEngineChildProcess::editorOpened (const File& file, CodeDocument& document)
  574. {
  575. editors.add (new Editor (*this, file, document));
  576. }
  577. bool CompileEngineChildProcess::documentAboutToClose (OpenDocumentManager::Document* document)
  578. {
  579. for (int i = editors.size(); --i >= 0;)
  580. {
  581. if (document->getFile() == editors.getUnchecked(i)->file)
  582. {
  583. const File f (editors.getUnchecked(i)->file);
  584. editors.remove (i);
  585. if (process != nullptr)
  586. MessageTypes::sendHandleFileReset (*process->server, f);
  587. }
  588. }
  589. return true;
  590. }
  591. void CompileEngineChildProcess::updateAllEditors()
  592. {
  593. for (int i = editors.size(); --i >= 0;)
  594. editors.getUnchecked(i)->sendFullUpdate();
  595. }
  596. //==============================================================================
  597. void CompileEngineChildProcess::handleCrash (const String& message)
  598. {
  599. Logger::writeToLog ("*** Child process crashed: " + message);
  600. if (crashHandler != nullptr)
  601. crashHandler (message);
  602. }
  603. void CompileEngineChildProcess::handleNewDiagnosticList (const ValueTree& l) { errorList.setList (l); }
  604. void CompileEngineChildProcess::handleActivityListChanged (const StringArray& l) { activityList.setList (l); }
  605. void CompileEngineChildProcess::handleCloseIDE()
  606. {
  607. if (JUCEApplication* app = JUCEApplication::getInstance())
  608. app->systemRequestedQuit();
  609. }
  610. void CompileEngineChildProcess::handleMissingSystemHeaders()
  611. {
  612. if (ProjectContentComponent* p = findProjectContentComponent())
  613. p->handleMissingSystemHeaders();
  614. }
  615. void CompileEngineChildProcess::handleKeyPress (const String& className, const KeyPress& key)
  616. {
  617. ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
  618. CommandID command = commandManager.getKeyMappings()->findCommandForKeyPress (key);
  619. if (command == StandardApplicationCommandIDs::undo)
  620. {
  621. handleUndoInEditor (className);
  622. }
  623. else if (command == StandardApplicationCommandIDs::redo)
  624. {
  625. handleRedoInEditor (className);
  626. }
  627. else if (ApplicationCommandTarget* const target = ApplicationCommandManager::findTargetForComponent (findProjectContentComponent()))
  628. {
  629. commandManager.setFirstCommandTarget (target);
  630. commandManager.getKeyMappings()->keyPressed (key, findProjectContentComponent());
  631. commandManager.setFirstCommandTarget (nullptr);
  632. }
  633. }
  634. void CompileEngineChildProcess::handleUndoInEditor (const String& /*className*/)
  635. {
  636. }
  637. void CompileEngineChildProcess::handleRedoInEditor (const String& /*className*/)
  638. {
  639. }
  640. void CompileEngineChildProcess::handleClassListChanged (const ValueTree& newList)
  641. {
  642. lastComponentList = ClassDatabase::ClassList::fromValueTree (newList);
  643. activityList.sendClassListChangedMessage (lastComponentList);
  644. }
  645. void CompileEngineChildProcess::handleBuildFailed()
  646. {
  647. ProjucerApplication::getCommandManager().commandStatusChanged();
  648. }
  649. void CompileEngineChildProcess::handleChangeCode (const SourceCodeRange& location, const String& newText)
  650. {
  651. if (Editor* ed = getOrOpenEditorFor (location.file))
  652. {
  653. if (ed->flushEditorChanges())
  654. return; // client-side editor changes were pending, so deal with them first, and discard
  655. // the incoming change, whose position may now be wrong.
  656. ed->document.deleteSection (location.range.getStart(), location.range.getEnd());
  657. ed->document.insertText (location.range.getStart(), newText);
  658. // deliberately clear the messages that we just added, to avoid these changes being
  659. // sent to the server (which will already have processed the same ones locally)
  660. ed->reset();
  661. ed->startTransactionTimer();
  662. }
  663. }
  664. void CompileEngineChildProcess::handlePing()
  665. {
  666. }
  667. //==============================================================================
  668. void CompileEngineChildProcess::flushEditorChanges()
  669. {
  670. for (Editor* ed : editors)
  671. ed->flushEditorChanges();
  672. }
  673. ProjectContentComponent* CompileEngineChildProcess::findProjectContentComponent() const
  674. {
  675. for (MainWindow* mw : ProjucerApplication::getApp().mainWindowList.windows)
  676. if (mw->getProject() == &project)
  677. return mw->getProjectContentComponent();
  678. return nullptr;
  679. }
  680. CompileEngineChildProcess::Editor* CompileEngineChildProcess::getOrOpenEditorFor (const File& file)
  681. {
  682. for (Editor* ed : editors)
  683. if (ed->file == file)
  684. return ed;
  685. if (ProjectContentComponent* pcc = findProjectContentComponent())
  686. if (pcc->showEditorForFile (file, false))
  687. return getOrOpenEditorFor (file);
  688. return nullptr;
  689. }
  690. void CompileEngineChildProcess::handleHighlightCode (const SourceCodeRange& location)
  691. {
  692. ProjectContentComponent* pcc = findProjectContentComponent();
  693. if (pcc != nullptr && pcc->showEditorForFile (location.file, false))
  694. {
  695. SourceCodeEditor* sce = dynamic_cast <SourceCodeEditor*> (pcc->getEditorComponent());
  696. if (sce != nullptr && sce->editor != nullptr)
  697. {
  698. sce->highlight (location.range, true);
  699. Process::makeForegroundProcess();
  700. CodeEditorComponent& ed = *sce->editor;
  701. ed.getTopLevelComponent()->toFront (false);
  702. ed.grabKeyboardFocus();
  703. }
  704. }
  705. }
  706. void CompileEngineChildProcess::cleanAllCachedFilesForProject (Project& p)
  707. {
  708. File cacheFolder (getCacheLocationForProject (p));
  709. if (cacheFolder.isDirectory())
  710. cacheFolder.deleteRecursively();
  711. }