/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2017 - ROLI Ltd. JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 5 End-User License Agreement and JUCE 5 Privacy Policy (both updated and effective as of the 27th April 2017). End User License Agreement: www.juce.com/juce-5-licence Privacy Policy: www.juce.com/juce-5-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ void createGUIEditorMenu (PopupMenu&); void handleGUIEditorMenuCommand (int); void registerGUIEditorCommands(); //============================================================================== struct ProjucerApplication::MainMenuModel : public MenuBarModel { MainMenuModel() { setApplicationCommandManagerToWatch (&getCommandManager()); } StringArray getMenuBarNames() override { return getApp().getMenuNames(); } PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override { PopupMenu menu; getApp().createMenu (menu, menuName); return menu; } void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override { getApp().handleMainMenuCommand (menuItemID); } }; //============================================================================== ProjucerApplication::ProjucerApplication() : isRunningCommandLine (false) { } void ProjucerApplication::initialise (const String& commandLine) { if (commandLine.trimStart().startsWith ("--server")) { initialiseLogger ("Compiler_Log_"); LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); #if JUCE_MAC Process::setDockIconVisible (false); #endif server = createClangServer (commandLine); } else { initialiseLogger ("IDE_Log_"); Logger::writeToLog (SystemStats::getOperatingSystemName()); Logger::writeToLog ("CPU: " + String (SystemStats::getCpuSpeedInMegahertz()) + "MHz Cores: " + String (SystemStats::getNumCpus()) + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB"); initialiseBasics(); isRunningCommandLine = commandLine.isNotEmpty() && ! commandLine.startsWith ("-NSDocumentRevisionsDebugMode"); licenseController.reset (new LicenseController); licenseController->addLicenseStatusChangedCallback (this); if (isRunningCommandLine) { auto appReturnCode = performCommandLine (ArgumentList ("Projucer", commandLine)); if (appReturnCode != commandLineNotPerformed) { setApplicationReturnValue (appReturnCode); quit(); return; } isRunningCommandLine = false; } if (sendCommandLineToPreexistingInstance()) { DBG ("Another instance is running - quitting..."); quit(); return; } rescanJUCEPathModules(); rescanUserPathModules(); openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2); childProcessCache.reset (new ChildProcessCache()); initCommandManager(); menuModel.reset (new MainMenuModel()); settings->appearance.refreshPresetSchemeList(); setColourScheme (settings->getGlobalProperties().getIntValue ("COLOUR SCHEME"), false); setEditorColourScheme (settings->getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false); updateEditorColourSchemeIfNeeded(); // do further initialisation in a moment when the message loop has started triggerAsyncUpdate(); } } void ProjucerApplication::initialiseBasics() { LookAndFeel::setDefaultLookAndFeel (&lookAndFeel); settings.reset (new StoredSettings()); ImageCache::setCacheTimeout (30 * 1000); icons.reset (new Icons()); tooltipWindow.setMillisecondsBeforeTipAppears (1200); } bool ProjucerApplication::initialiseLogger (const char* filePrefix) { if (logger == nullptr) { #if JUCE_LINUX String folder = "~/.config/Projucer/Logs"; #else String folder = "com.juce.projucer"; #endif logger.reset (FileLogger::createDateStampedLogger (folder, filePrefix, ".txt", getApplicationName() + " " + getApplicationVersion() + " --- Build date: " __DATE__)); Logger::setCurrentLogger (logger.get()); } return logger != nullptr; } void ProjucerApplication::handleAsyncUpdate() { if (licenseController != nullptr) licenseController->startWebviewIfNeeded(); #if JUCE_MAC PopupMenu extraAppleMenuItems; createExtraAppleMenuItems (extraAppleMenuItems); // workaround broken "Open Recent" submenu: not passing the // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem MenuBarModel::setMacMainMenu (menuModel.get(), &extraAppleMenuItems); //, "Open Recent"); #endif if (getGlobalProperties().getValue (Ids::dontQueryForUpdate, {}).isEmpty()) LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (false); if (licenseController != nullptr) { setAnalyticsEnabled (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::enabled); Analytics::getInstance()->logEvent ("Startup", {}, ProjucerAnalyticsEvent::appEvent); } if (! isRunningCommandLine && settings->shouldAskUserToSetJUCEPath()) showSetJUCEPathAlert(); } void ProjucerApplication::initialiseWindows (const String& commandLine) { const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", StringRef())); if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-')) anotherInstanceStarted (commandLine); else mainWindowList.reopenLastProjects(); mainWindowList.createWindowIfNoneAreOpen(); if (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::notChosenYet) showApplicationUsageDataAgreementPopup(); } static void deleteTemporaryFiles() { auto tempDirectory = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs"); if (tempDirectory.exists()) tempDirectory.deleteRecursively(); } void ProjucerApplication::shutdown() { if (server != nullptr) { destroyClangServer (server); Logger::writeToLog ("Server shutdown cleanly"); } utf8Window.reset(); svgPathWindow.reset(); aboutWindow.reset(); pathsWindow.reset(); editorColourSchemeWindow.reset(); pipCreatorWindow.reset(); if (licenseController != nullptr) { licenseController->removeLicenseStatusChangedCallback (this); licenseController.reset(); } mainWindowList.forceCloseAllWindows(); openDocumentManager.clear(); childProcessCache.reset(); #if JUCE_MAC MenuBarModel::setMacMainMenu (nullptr); #endif menuModel.reset(); commandManager.reset(); settings.reset(); LookAndFeel::setDefaultLookAndFeel (nullptr); // clean up after ourselves and delete any temp project files that may have // been created from PIPs deleteTemporaryFiles(); if (! isRunningCommandLine) Logger::writeToLog ("Shutdown"); deleteLogger(); Analytics::getInstance()->logEvent ("Shutdown", {}, ProjucerAnalyticsEvent::appEvent); } struct AsyncQuitRetrier : private Timer { AsyncQuitRetrier() { startTimer (500); } void timerCallback() override { stopTimer(); delete this; if (auto* app = JUCEApplicationBase::getInstance()) app->systemRequestedQuit(); } JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier) }; void ProjucerApplication::systemRequestedQuit() { if (server != nullptr) { sendQuitMessageToIDE (server); } else if (ModalComponentManager::getInstance()->cancelAllModalComponents()) { new AsyncQuitRetrier(); } else { if (closeAllMainWindows()) quit(); } } //============================================================================== void ProjucerApplication::licenseStateChanged (const LicenseState& state) { #if ! JUCER_ENABLE_GPL_MODE if (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::noLicenseChosenYet) #else ignoreUnused (state); #endif { initialiseWindows (getCommandLineParameters()); } } void ProjucerApplication::doLogout() { if (licenseController != nullptr) { const LicenseState& state = licenseController->getState(); if (state.type != LicenseState::Type::notLoggedIn && closeAllMainWindows()) licenseController->logout(); } } //============================================================================== String ProjucerApplication::getVersionDescription() const { String s; const Time buildDate (Time::getCompilationDate()); s << "Projucer " << ProjectInfo::versionString << newLine << "Build date: " << buildDate.getDayOfMonth() << " " << Time::getMonthName (buildDate.getMonth(), true) << " " << buildDate.getYear(); return s; } void ProjucerApplication::anotherInstanceStarted (const String& commandLine) { if (server == nullptr && ! commandLine.trim().startsWithChar ('-')) openFile (File (commandLine.unquoted())); } ProjucerApplication& ProjucerApplication::getApp() { ProjucerApplication* const app = dynamic_cast (JUCEApplication::getInstance()); jassert (app != nullptr); return *app; } ApplicationCommandManager& ProjucerApplication::getCommandManager() { auto* cm = ProjucerApplication::getApp().commandManager.get(); jassert (cm != nullptr); return *cm; } //============================================================================== enum { recentProjectsBaseID = 100, openWindowsBaseID = 300, activeDocumentsBaseID = 400, showPathsID = 1999, examplesBaseID = 2000 }; MenuBarModel* ProjucerApplication::getMenuModel() { return menuModel.get(); } StringArray ProjucerApplication::getMenuNames() { return { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" }; } void ProjucerApplication::createMenu (PopupMenu& menu, const String& menuName) { if (menuName == "File") createFileMenu (menu); else if (menuName == "Edit") createEditMenu (menu); else if (menuName == "View") createViewMenu (menu); else if (menuName == "Build") createBuildMenu (menu); else if (menuName == "Window") createWindowMenu (menu); else if (menuName == "Document") createDocumentMenu (menu); else if (menuName == "Tools") createToolsMenu (menu); else if (menuName == "Help") createHelpMenu (menu); else if (menuName == "GUI Editor") createGUIEditorMenu (menu); else jassertfalse; // names have changed? } void ProjucerApplication::createFileMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::newProject); menu.addCommandItem (commandManager.get(), CommandIDs::newProjectFromClipboard); menu.addCommandItem (commandManager.get(), CommandIDs::newPIP); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::open); { PopupMenu recentFiles; settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true); if (recentFiles.getNumItems() > 0) { recentFiles.addSeparator(); recentFiles.addCommandItem (commandManager.get(), CommandIDs::clearRecentFiles); } menu.addSubMenu ("Open Recent", recentFiles); } { PopupMenu examples; createExamplesPopupMenu (examples); menu.addSubMenu ("Open Example", examples); } menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument); menu.addCommandItem (commandManager.get(), CommandIDs::saveDocument); menu.addCommandItem (commandManager.get(), CommandIDs::saveDocumentAs); menu.addCommandItem (commandManager.get(), CommandIDs::saveAll); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::closeProject); menu.addCommandItem (commandManager.get(), CommandIDs::saveProject); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::openInIDE); menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE); menu.addSeparator(); #if ! JUCER_ENABLE_GPL_MODE menu.addCommandItem (commandManager.get(), CommandIDs::loginLogout); #endif #if ! JUCE_MAC menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); menu.addCommandItem (commandManager.get(), CommandIDs::showAppUsageWindow); menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); menu.addSeparator(); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::quit); #endif } void ProjucerApplication::createEditMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::undo); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::redo); menu.addSeparator(); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::cut); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::copy); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::paste); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::del); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::selectAll); menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::deselectAll); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::showFindPanel); menu.addCommandItem (commandManager.get(), CommandIDs::findSelection); menu.addCommandItem (commandManager.get(), CommandIDs::findNext); menu.addCommandItem (commandManager.get(), CommandIDs::findPrevious); } void ProjucerApplication::createViewMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::showProjectSettings); menu.addCommandItem (commandManager.get(), CommandIDs::showProjectTab); menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab); menu.addCommandItem (commandManager.get(), CommandIDs::showFileExplorerPanel); menu.addCommandItem (commandManager.get(), CommandIDs::showModulesPanel); menu.addCommandItem (commandManager.get(), CommandIDs::showExportersPanel); menu.addCommandItem (commandManager.get(), CommandIDs::showExporterSettings); menu.addSeparator(); createColourSchemeItems (menu); } void ProjucerApplication::createBuildMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::toggleBuildEnabled); menu.addCommandItem (commandManager.get(), CommandIDs::buildNow); menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::launchApp); menu.addCommandItem (commandManager.get(), CommandIDs::killApp); menu.addCommandItem (commandManager.get(), CommandIDs::cleanAll); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::reinstantiateComp); menu.addCommandItem (commandManager.get(), CommandIDs::showWarnings); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::nextError); menu.addCommandItem (commandManager.get(), CommandIDs::prevError); } void ProjucerApplication::createColourSchemeItems (PopupMenu& menu) { PopupMenu colourSchemeMenu; colourSchemeMenu.addItem (PopupMenu::Item ("Dark") .setTicked (selectedColourSchemeIndex == 0) .setAction ([this] { setColourScheme (0, true); updateEditorColourSchemeIfNeeded(); })); colourSchemeMenu.addItem (PopupMenu::Item ("Grey") .setTicked (selectedColourSchemeIndex == 1) .setAction ([this] { setColourScheme (1, true); updateEditorColourSchemeIfNeeded(); })); colourSchemeMenu.addItem (PopupMenu::Item ("Light") .setTicked (selectedColourSchemeIndex == 2) .setAction ([this] { setColourScheme (2, true); updateEditorColourSchemeIfNeeded(); })); menu.addSubMenu ("Colour Scheme", colourSchemeMenu); //========================================================================== PopupMenu editorColourSchemeMenu; auto& appearanceSettings = getAppSettings().appearance; appearanceSettings.refreshPresetSchemeList(); auto schemes = appearanceSettings.getPresetSchemes(); auto i = 0; for (auto& s : schemes) { editorColourSchemeMenu.addItem (PopupMenu::Item (s) .setEnabled (editorColourSchemeWindow == nullptr) .setTicked (selectedEditorColourSchemeIndex == i) .setAction ([this, i] { setEditorColourScheme (i, true); })); ++i; } editorColourSchemeMenu.addSeparator(); editorColourSchemeMenu.addItem (PopupMenu::Item ("Create...") .setEnabled (editorColourSchemeWindow == nullptr) .setAction ([this] { showEditorColourSchemeWindow(); })); menu.addSubMenu ("Editor Colour Scheme", editorColourSchemeMenu); } void ProjucerApplication::createWindowMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousWindow); menu.addCommandItem (commandManager.get(), CommandIDs::goToNextWindow); menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow); menu.addSeparator(); int counter = 0; for (auto* window : mainWindowList.windows) if (window != nullptr) if (auto* project = window->getProject()) menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString()); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::closeAllWindows); } void ProjucerApplication::createDocumentMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousDoc); menu.addCommandItem (commandManager.get(), CommandIDs::goToNextDoc); menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart); menu.addSeparator(); auto numDocs = jmin (50, openDocumentManager.getNumOpenDocuments()); for (int i = 0; i < numDocs; ++i) { OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i); menu.addItem (activeDocumentsBaseID + i, doc->getName()); } menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::closeAllDocuments); } void ProjucerApplication::createToolsMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::showUTF8Tool); menu.addCommandItem (commandManager.get(), CommandIDs::showSVGPathTool); menu.addCommandItem (commandManager.get(), CommandIDs::showTranslationTool); } void ProjucerApplication::createHelpMenu (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::showForum); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules); menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses); menu.addCommandItem (commandManager.get(), CommandIDs::showTutorials); } void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu) { menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow); menu.addCommandItem (commandManager.get(), CommandIDs::showAppUsageWindow); menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion); menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow); } void ProjucerApplication::createExamplesPopupMenu (PopupMenu& menu) noexcept { numExamples = 0; for (auto& dir : getSortedExampleDirectories()) { PopupMenu m; for (auto& f : getSortedExampleFilesInDirectory (dir)) { m.addItem (examplesBaseID + numExamples, f.getFileNameWithoutExtension()); ++numExamples; } menu.addSubMenu (dir.getFileName(), m); } if (numExamples == 0) { menu.addItem (showPathsID, "Set path to JUCE..."); } else { menu.addSeparator(); menu.addCommandItem (commandManager.get(), CommandIDs::launchDemoRunner); } } //========================================================================== static File getJUCEExamplesDirectoryPathFromGlobal() { auto globalPath = File::createFileWithoutCheckingPath (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString() .replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName())); if (globalPath.exists()) return File (globalPath).getChildFile ("examples"); return {}; } Array ProjucerApplication::getSortedExampleDirectories() noexcept { Array exampleDirectories; auto examplesPath = getJUCEExamplesDirectoryPathFromGlobal(); if (! isValidJUCEExamplesDirectory (examplesPath)) return {}; DirectoryIterator iter (examplesPath, false, "*", File::findDirectories); while (iter.next()) { auto exampleDirectory = iter.getFile(); if (exampleDirectory.getNumberOfChildFiles (File::findFiles | File::ignoreHiddenFiles) > 0 && exampleDirectory.getFileName() != "DemoRunner" && exampleDirectory.getFileName() != "Assets") exampleDirectories.add (exampleDirectory); } exampleDirectories.sort(); return exampleDirectories; } Array ProjucerApplication::getSortedExampleFilesInDirectory (const File& directory) const noexcept { Array exampleFiles; DirectoryIterator iter (directory, false, "*.h", File::findFiles); while (iter.next()) exampleFiles.add (iter.getFile()); exampleFiles.sort(); return exampleFiles; } bool ProjucerApplication::findWindowAndOpenPIP (const File& pip) { auto* window = mainWindowList.getFrontmostWindow(); bool shouldCloseWindow = false; if (window == nullptr) { window = mainWindowList.getOrCreateEmptyWindow(); shouldCloseWindow = true; } if (window->tryToOpenPIP (pip)) return true; if (shouldCloseWindow) mainWindowList.closeWindow (window); return false; } void ProjucerApplication::findAndLaunchExample (int selectedIndex) { File example; for (auto& dir : getSortedExampleDirectories()) { auto exampleFiles = getSortedExampleFilesInDirectory (dir); if (selectedIndex < exampleFiles.size()) { example = exampleFiles.getUnchecked (selectedIndex); break; } selectedIndex -= exampleFiles.size(); } // example doesn't exist? jassert (example != File()); findWindowAndOpenPIP (example); StringPairArray data; data.set ("label", example.getFileNameWithoutExtension()); Analytics::getInstance()->logEvent ("Example Opened", data, ProjucerAnalyticsEvent::exampleEvent); } //========================================================================== static String getPlatformSpecificFileExtension() { #if JUCE_MAC return ".app"; #elif JUCE_WINDOWS return ".exe"; #elif JUCE_LINUX return {}; #else jassertfalse; return {}; #endif } static File getPlatformSpecificProjectFolder() { auto examplesDir = getJUCEExamplesDirectoryPathFromGlobal(); if (examplesDir == File()) return {}; auto buildsFolder = examplesDir.getChildFile ("DemoRunner").getChildFile ("Builds"); #if JUCE_MAC return buildsFolder.getChildFile ("MacOSX"); #elif JUCE_WINDOWS return buildsFolder.getChildFile ("VisualStudio2017"); #elif JUCE_LINUX return buildsFolder.getChildFile ("LinuxMakefile"); #else jassertfalse; return {}; #endif } static File tryToFindDemoRunnerExecutableInBuilds() { auto projectFolder = getPlatformSpecificProjectFolder(); if (projectFolder == File()) return {}; #if JUCE_MAC projectFolder = projectFolder.getChildFile ("build"); auto demoRunnerExecutable = projectFolder.getChildFile ("Release").getChildFile ("DemoRunner.app"); if (demoRunnerExecutable.exists()) return demoRunnerExecutable; demoRunnerExecutable = projectFolder.getChildFile ("Debug").getChildFile ("DemoRunner.app"); if (demoRunnerExecutable.exists()) return demoRunnerExecutable; #elif JUCE_WINDOWS projectFolder = projectFolder.getChildFile ("x64"); auto demoRunnerExecutable = projectFolder.getChildFile ("Release").getChildFile ("App").getChildFile ("DemoRunner.exe"); if (demoRunnerExecutable.existsAsFile()) return demoRunnerExecutable; demoRunnerExecutable = projectFolder.getChildFile ("Debug").getChildFile ("App").getChildFile ("DemoRunner.exe"); if (demoRunnerExecutable.existsAsFile()) return demoRunnerExecutable; #elif JUCE_LINUX projectFolder = projectFolder.getChildFile ("LinuxMakefile").getChildFile ("build"); auto demoRunnerExecutable = projectFolder.getChildFile ("DemoRunner"); if (demoRunnerExecutable.existsAsFile()) return demoRunnerExecutable; #endif return {}; } static File tryToFindPrebuiltDemoRunnerExecutable() { auto prebuiltFile = File (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString()) .getChildFile ("DemoRunner" + getPlatformSpecificFileExtension()); #if JUCE_MAC if (prebuiltFile.exists()) #else if (prebuiltFile.existsAsFile()) #endif return prebuiltFile; return {}; } void ProjucerApplication::checkIfGlobalJUCEPathHasChanged() { auto globalJUCEPath = File (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get()); if (lastJUCEPath != globalJUCEPath) { hasScannedForDemoRunnerProject = false; hasScannedForDemoRunnerExecutable = false; lastJUCEPath = globalJUCEPath; } } File ProjucerApplication::tryToFindDemoRunnerExecutable() { checkIfGlobalJUCEPathHasChanged(); if (hasScannedForDemoRunnerExecutable) return lastDemoRunnerExectuableFile; hasScannedForDemoRunnerExecutable = true; auto demoRunnerExecutable = tryToFindDemoRunnerExecutableInBuilds(); if (demoRunnerExecutable == File()) demoRunnerExecutable = tryToFindPrebuiltDemoRunnerExecutable(); lastDemoRunnerExectuableFile = demoRunnerExecutable; return demoRunnerExecutable; } File ProjucerApplication::tryToFindDemoRunnerProject() { checkIfGlobalJUCEPathHasChanged(); if (hasScannedForDemoRunnerProject) return lastDemoRunnerProjectFile; hasScannedForDemoRunnerProject = true; auto projectFolder = getPlatformSpecificProjectFolder(); if (projectFolder == File()) { lastDemoRunnerProjectFile = File(); return {}; } #if JUCE_MAC auto demoRunnerProjectFile = projectFolder.getChildFile ("DemoRunner.xcodeproj"); #elif JUCE_WINDOWS auto demoRunnerProjectFile = projectFolder.getChildFile ("DemoRunner.sln"); #elif JUCE_LINUX auto demoRunnerProjectFile = projectFolder.getChildFile ("Makefile"); #endif #if JUCE_MAC if (! demoRunnerProjectFile.exists()) #else if (! demoRunnerProjectFile.existsAsFile()) #endif demoRunnerProjectFile = File(); lastDemoRunnerProjectFile = demoRunnerProjectFile; return demoRunnerProjectFile; } void ProjucerApplication::launchDemoRunner() { auto demoRunnerFile = tryToFindDemoRunnerExecutable(); if (demoRunnerFile != File()) { auto succeeded = demoRunnerFile.startAsProcess(); StringPairArray data; data.set ("label", succeeded ? "Success" : "Failure"); Analytics::getInstance()->logEvent ("Launch DemoRunner", data, ProjucerAnalyticsEvent::exampleEvent); if (succeeded) return; } demoRunnerFile = tryToFindDemoRunnerProject(); if (demoRunnerFile != File()) { auto& lf = Desktop::getInstance().getDefaultLookAndFeel(); demoRunnerAlert.reset (lf.createAlertWindow ("Open Project", "Couldn't find a compiled version of the Demo Runner." #if JUCE_LINUX " Do you want to build it now?", "Build project", "Cancel", #else " Do you want to open the project?", "Open project", "Cancel", #endif {}, AlertWindow::QuestionIcon, 2, mainWindowList.getFrontmostWindow (false))); demoRunnerAlert->enterModalState (true, ModalCallbackFunction::create ([this, demoRunnerFile] (int retVal) { demoRunnerAlert.reset (nullptr); StringPairArray data; data.set ("label", retVal == 1 ? "Opened" : "Cancelled"); Analytics::getInstance()->logEvent ("Open DemoRunner Project", data, ProjucerAnalyticsEvent::exampleEvent); if (retVal == 1) { #if JUCE_LINUX String command ("make -C " + demoRunnerFile.getParentDirectory().getFullPathName() + " CONFIG=Release -j3"); if (! makeProcess.start (command)) AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Error building Demo Runner."); #else demoRunnerFile.startAsProcess(); #endif } }), false); } } //========================================================================== void ProjucerApplication::handleMainMenuCommand (int menuItemID) { if (menuItemID >= recentProjectsBaseID && menuItemID < (recentProjectsBaseID + 100)) { // open a file from the "recent files" menu openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID)); } else if (menuItemID >= openWindowsBaseID && menuItemID < (openWindowsBaseID + 100)) { if (auto* window = mainWindowList.windows.getUnchecked (menuItemID - openWindowsBaseID)) window->toFront (true); } else if (menuItemID >= activeDocumentsBaseID && menuItemID < (activeDocumentsBaseID + 200)) { if (auto* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID)) mainWindowList.openDocument (doc, true); else jassertfalse; } else if (menuItemID == showPathsID) { showPathsWindow (true); } else if (menuItemID >= examplesBaseID && menuItemID < (examplesBaseID + numExamples)) { findAndLaunchExample (menuItemID - examplesBaseID); } else { handleGUIEditorMenuCommand (menuItemID); } } //============================================================================== void ProjucerApplication::getAllCommands (Array & commands) { JUCEApplication::getAllCommands (commands); const CommandID ids[] = { CommandIDs::newProject, CommandIDs::newProjectFromClipboard, CommandIDs::newPIP, CommandIDs::open, CommandIDs::launchDemoRunner, CommandIDs::closeAllWindows, CommandIDs::closeAllDocuments, CommandIDs::clearRecentFiles, CommandIDs::saveAll, CommandIDs::showGlobalPathsWindow, CommandIDs::showUTF8Tool, CommandIDs::showSVGPathTool, CommandIDs::showAboutWindow, CommandIDs::showAppUsageWindow, CommandIDs::checkForNewVersion, CommandIDs::showForum, CommandIDs::showAPIModules, CommandIDs::showAPIClasses, CommandIDs::showTutorials, CommandIDs::loginLogout }; commands.addArray (ids, numElementsInArray (ids)); } void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result) { switch (commandID) { case CommandIDs::newProject: result.setInfo ("New Project...", "Creates a new JUCE project", CommandCategories::general, 0); result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0)); break; case CommandIDs::newProjectFromClipboard: result.setInfo ("New Project From Clipboard...", "Creates a new JUCE project from the clipboard contents", CommandCategories::general, 0); result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0)); break; case CommandIDs::newPIP: result.setInfo ("New PIP...", "Opens the PIP Creator utility for creating a new PIP", CommandCategories::general, 0); result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0)); break; case CommandIDs::launchDemoRunner: #if JUCE_LINUX if (makeProcess.isRunning()) { result.setInfo ("Building Demo Runner...", "The Demo Runner project is currently building", CommandCategories::general, 0); result.setActive (false); } else #endif { result.setInfo ("Launch Demo Runner", "Launches the JUCE demo runner application, or the project if it can't be found", CommandCategories::general, 0); result.setActive (tryToFindDemoRunnerExecutable() != File() || tryToFindDemoRunnerProject() != File()); } break; case CommandIDs::open: result.setInfo ("Open...", "Opens a JUCE project", CommandCategories::general, 0); result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0)); break; case CommandIDs::showGlobalPathsWindow: result.setInfo ("Global Paths...", "Shows the window to change the stored global paths.", CommandCategories::general, 0); break; case CommandIDs::closeAllWindows: result.setInfo ("Close All Windows", "Closes all open windows", CommandCategories::general, 0); result.setActive (mainWindowList.windows.size() > 0); break; case CommandIDs::closeAllDocuments: result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0); result.setActive (openDocumentManager.getNumOpenDocuments() > 0); break; case CommandIDs::clearRecentFiles: result.setInfo ("Clear Recent Files", "Clears all recent files from the menu", CommandCategories::general, 0); result.setActive (settings->recentFiles.getNumFiles() > 0); break; case CommandIDs::saveAll: result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0); result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0)); break; case CommandIDs::showUTF8Tool: result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0); break; case CommandIDs::showSVGPathTool: result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0); break; case CommandIDs::showAboutWindow: result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0); break; case CommandIDs::showAppUsageWindow: result.setInfo ("Application Usage Data", "Shows the application usage data agreement window", CommandCategories::general, 0); break; case CommandIDs::checkForNewVersion: result.setInfo ("Check for New Version...", "Checks the web server for a new version of JUCE", CommandCategories::general, 0); break; case CommandIDs::showForum: result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0); break; case CommandIDs::showAPIModules: result.setInfo ("API Modules", "Shows the API modules documentation in a browser", CommandCategories::general, 0); break; case CommandIDs::showAPIClasses: result.setInfo ("API Classes", "Shows the API classes documentation in a browser", CommandCategories::general, 0); break; case CommandIDs::showTutorials: result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0); break; case CommandIDs::loginLogout: { bool isLoggedIn = false; String username; if (licenseController != nullptr) { const LicenseState state = licenseController->getState(); isLoggedIn = (state.type != LicenseState::Type::notLoggedIn && state.type != LicenseState::Type::GPL); username = state.username; } result.setInfo (isLoggedIn ? String ("Sign out ") + username + "..." : String ("Sign in..."), "Log out of your JUCE account", CommandCategories::general, 0); } break; default: JUCEApplication::getCommandInfo (commandID, result); break; } } bool ProjucerApplication::perform (const InvocationInfo& info) { switch (info.commandID) { case CommandIDs::newProject: createNewProject(); break; case CommandIDs::newProjectFromClipboard: createNewProjectFromClipboard(); break; case CommandIDs::newPIP: createNewPIP(); break; case CommandIDs::open: askUserToOpenFile(); break; case CommandIDs::launchDemoRunner: launchDemoRunner(); break; case CommandIDs::saveAll: saveAllDocuments(); break; case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break; case CommandIDs::closeAllDocuments: closeAllDocuments (true); break; case CommandIDs::clearRecentFiles: clearRecentFiles(); break; case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break; case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break; case CommandIDs::showAboutWindow: showAboutWindow(); break; case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break; case CommandIDs::checkForNewVersion: LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (true); break; case CommandIDs::showForum: launchForumBrowser(); break; case CommandIDs::showAPIModules: launchModulesBrowser(); break; case CommandIDs::showAPIClasses: launchClassesBrowser(); break; case CommandIDs::showTutorials: launchTutorialsBrowser(); break; case CommandIDs::loginLogout: doLogout(); break; default: return JUCEApplication::perform (info); } return true; } //============================================================================== void ProjucerApplication::createNewProject() { auto* mw = mainWindowList.getOrCreateEmptyWindow(); mw->showStartPage(); mainWindowList.avoidSuperimposedWindows (mw); } void ProjucerApplication::createNewProjectFromClipboard() { auto tempFile = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs").getChildFile ("Clipboard") .getChildFile ("PIPFile_" + String (std::abs (Random::getSystemRandom().nextInt())) + ".h"); if (tempFile.existsAsFile()) tempFile.deleteFile(); tempFile.create(); tempFile.appendText (SystemClipboard::getTextFromClipboard()); if (! findWindowAndOpenPIP (tempFile)) { AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Couldn't create project from clipboard contents."); tempFile.deleteFile(); } } void ProjucerApplication::createNewPIP() { showPIPCreatorWindow(); } void ProjucerApplication::askUserToOpenFile() { FileChooser fc ("Open File"); if (fc.browseForFileToOpen()) openFile (fc.getResult()); } bool ProjucerApplication::openFile (const File& file) { return mainWindowList.openFile (file); } void ProjucerApplication::saveAllDocuments() { openDocumentManager.saveAll(); for (int i = 0; i < mainWindowList.windows.size(); ++i) if (auto* pcc = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent()) pcc->refreshProjectTreeFileStatuses(); } bool ProjucerApplication::closeAllDocuments (bool askUserToSave) { return openDocumentManager.closeAll (askUserToSave); } bool ProjucerApplication::closeAllMainWindows() { return server != nullptr || mainWindowList.askAllWindowsToClose(); } void ProjucerApplication::closeAllMainWindowsAndQuitIfNeeded() { if (closeAllMainWindows()) { #if ! JUCE_MAC if (mainWindowList.windows.size() == 0) systemRequestedQuit(); #endif } } void ProjucerApplication::clearRecentFiles() { settings->recentFiles.clear(); settings->recentFiles.clearRecentFilesNatively(); settings->flush(); menuModel->menuItemsChanged(); } //============================================================================== void ProjucerApplication::showUTF8ToolWindow() { if (utf8Window != nullptr) utf8Window->toFront (true); else new FloatingToolWindow ("UTF-8 String Literal Converter", "utf8WindowPos", new UTF8Component(), utf8Window, true, 500, 500, 300, 300, 1000, 1000); } void ProjucerApplication::showSVGPathDataToolWindow() { if (svgPathWindow != nullptr) svgPathWindow->toFront (true); else new FloatingToolWindow ("SVG Path Converter", "svgPathWindowPos", new SVGPathDataComponent(), svgPathWindow, true, 500, 500, 300, 300, 1000, 1000); } void ProjucerApplication::showAboutWindow() { if (aboutWindow != nullptr) aboutWindow->toFront (true); else new FloatingToolWindow ({}, {}, new AboutWindowComponent(), aboutWindow, false, 500, 300, 500, 300, 500, 300); } void ProjucerApplication::showApplicationUsageDataAgreementPopup() { if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->toFront (true); else new FloatingToolWindow ("Application Usage Analytics", {}, new ApplicationUsageDataWindowComponent (isPaidOrGPL()), applicationUsageDataWindow, false, 400, 300, 400, 300, 400, 300); } void ProjucerApplication::dismissApplicationUsageDataAgreementPopup() { if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow.reset(); } void ProjucerApplication::showPathsWindow (bool highlightJUCEPath) { if (pathsWindow != nullptr) pathsWindow->toFront (true); else new FloatingToolWindow ("Global Paths", "pathsWindowPos", new GlobalPathsWindowComponent(), pathsWindow, false, 600, 700, 600, 700, 600, 700); if (highlightJUCEPath) if (auto* pathsComp = dynamic_cast (pathsWindow->getChildComponent (0))) pathsComp->highlightJUCEPath(); } void ProjucerApplication::showEditorColourSchemeWindow() { if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->toFront (true); else new FloatingToolWindow ("Editor Colour Scheme", "editorColourSchemeWindowPos", new EditorColourSchemeWindowComponent(), editorColourSchemeWindow, false, 500, 500, 500, 500, 500, 500); } void ProjucerApplication::showPIPCreatorWindow() { if (pipCreatorWindow != nullptr) pipCreatorWindow->toFront (true); else new FloatingToolWindow ("PIP Creator", "pipCreatorWindowPos", new PIPCreatorWindowComponent(), pipCreatorWindow, false, 600, 750, 600, 750, 600, 750); } void ProjucerApplication::launchForumBrowser() { URL forumLink ("https://forum.juce.com/"); if (forumLink.isWellFormed()) forumLink.launchInDefaultBrowser(); } void ProjucerApplication::launchModulesBrowser() { URL modulesLink ("https://docs.juce.com/master/modules.html"); if (modulesLink.isWellFormed()) modulesLink.launchInDefaultBrowser(); } void ProjucerApplication::launchClassesBrowser() { URL classesLink ("https://docs.juce.com/master/classes.html"); if (classesLink.isWellFormed()) classesLink.launchInDefaultBrowser(); } void ProjucerApplication::launchTutorialsBrowser() { URL tutorialsLink ("https://juce.com/learn/tutorials"); if (tutorialsLink.isWellFormed()) tutorialsLink.launchInDefaultBrowser(); } //============================================================================== struct FileWithTime { FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {} FileWithTime() {} bool operator< (const FileWithTime& other) const { return time < other.time; } bool operator== (const FileWithTime& other) const { return time == other.time; } File file; Time time; }; void ProjucerApplication::deleteLogger() { const int maxNumLogFilesToKeep = 50; Logger::setCurrentLogger (nullptr); if (logger != nullptr) { auto logFiles = logger->getLogFile().getParentDirectory().findChildFiles (File::findFiles, false); if (logFiles.size() > maxNumLogFilesToKeep) { Array files; for (auto& f : logFiles) files.addUsingDefaultSort (f); for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i) files.getReference(i).file.deleteFile(); } } logger.reset(); } PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const String& filename, bool isProjectSettings) { PropertiesFile::Options options; options.applicationName = filename; options.filenameSuffix = "settings"; options.osxLibrarySubFolder = "Application Support"; #if JUCE_LINUX options.folderName = "~/.config/Projucer"; #else options.folderName = "Projucer"; #endif if (isProjectSettings) options.folderName += "/ProjectSettings"; return options; } void ProjucerApplication::updateAllBuildTabs() { for (int i = 0; i < mainWindowList.windows.size(); ++i) if (ProjectContentComponent* p = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent()) p->rebuildProjectTabs(); } void ProjucerApplication::initCommandManager() { commandManager.reset (new ApplicationCommandManager()); commandManager->registerAllCommandsForTarget (this); { CodeDocument doc; CppCodeEditorComponent ed (File(), doc); commandManager->registerAllCommandsForTarget (&ed); } registerGUIEditorCommands(); } void ProjucerApplication::setAnalyticsEnabled (bool enabled) { resetAnalytics(); if (enabled) setupAnalytics(); } void ProjucerApplication::resetAnalytics() noexcept { auto analyticsInstance = Analytics::getInstance(); analyticsInstance->setUserId ({}); analyticsInstance->setUserProperties ({}); analyticsInstance->getDestinations().clear(); } void ProjucerApplication::setupAnalytics() { Analytics::getInstance()->addDestination (new ProjucerAnalyticsDestination()); auto deviceString = SystemStats::getDeviceIdentifiers().joinIntoString (":"); auto deviceIdentifier = String::toHexString (deviceString.hashCode64()); Analytics::getInstance()->setUserId (deviceIdentifier); StringPairArray userData; userData.set ("cd1", getApplicationName()); userData.set ("cd2", getApplicationVersion()); userData.set ("cd3", SystemStats::getDeviceDescription()); userData.set ("cd4", deviceString); userData.set ("cd5", SystemStats::getOperatingSystemName()); Analytics::getInstance()->setUserProperties (userData); } void ProjucerApplication::showSetJUCEPathAlert() { auto& lf = Desktop::getInstance().getDefaultLookAndFeel(); pathAlert.reset (lf.createAlertWindow ("Set JUCE Path", "Your global JUCE path is invalid. This path is used to access the JUCE examples and demo project - " "would you like to set it now?", "Set path", "Cancel", "Don't ask again", AlertWindow::WarningIcon, 3, mainWindowList.getFrontmostWindow (false))); pathAlert->enterModalState (true, ModalCallbackFunction::create ([this] (int retVal) { pathAlert.reset (nullptr); if (retVal == 1) showPathsWindow (true); else if (retVal == 0) settings->setDontAskAboutJUCEPathAgain(); })); } void rescanModules (AvailableModuleList& list, const Array& paths, bool async) { if (async) list.scanPathsAsync (paths); else list.scanPaths (paths); } void ProjucerApplication::rescanJUCEPathModules() { rescanModules (jucePathModuleList, { getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); } void ProjucerApplication::rescanUserPathModules() { rescanModules (userPathsModuleList, { getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine); } void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName) { auto& appearanceSettings = getAppSettings().appearance; auto schemes = appearanceSettings.getPresetSchemes(); auto schemeIndex = schemes.indexOf (schemeName); if (schemeIndex >= 0) setEditorColourScheme (schemeIndex, true); } void ProjucerApplication::setColourScheme (int index, bool saveSetting) { switch (index) { case 0: lookAndFeel.setColourScheme (LookAndFeel_V4::getDarkColourScheme()); break; case 1: lookAndFeel.setColourScheme (LookAndFeel_V4::getGreyColourScheme()); break; case 2: lookAndFeel.setColourScheme (LookAndFeel_V4::getLightColourScheme()); break; default: break; } lookAndFeel.setupColours(); mainWindowList.sendLookAndFeelChange(); if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange(); if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange(); if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange(); if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange(); if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange(); if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange(); if (pipCreatorWindow != nullptr) pipCreatorWindow->sendLookAndFeelChange(); auto* mcm = ModalComponentManager::getInstance(); for (auto i = 0; i < mcm->getNumModalComponents(); ++i) mcm->getModalComponent (i)->sendLookAndFeelChange(); if (saveSetting) { auto& properties = settings->getGlobalProperties(); properties.setValue ("COLOUR SCHEME", index); } selectedColourSchemeIndex = index; getCommandManager().commandStatusChanged(); } void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting) { auto& appearanceSettings = getAppSettings().appearance; auto schemes = appearanceSettings.getPresetSchemes(); index = jmin (index, schemes.size() - 1); appearanceSettings.selectPresetScheme (index); if (saveSetting) { auto& properties = settings->getGlobalProperties(); properties.setValue ("EDITOR COLOUR SCHEME", index); } selectedEditorColourSchemeIndex = index; getCommandManager().commandStatusChanged(); } bool ProjucerApplication::isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex) { auto& schemeName = schemes[editorColourSchemeIndex]; return (schemeName == "Default (Dark)" || schemeName == "Default (Light)"); } int ProjucerApplication::getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex) { auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)"); auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)"); // Can't find default code editor colour schemes! jassert (defaultDarkEditorIndex != -1 && defaultLightEditorIndex != -1); return (guiColourSchemeIndex == 2 ? defaultLightEditorIndex : defaultDarkEditorIndex); } void ProjucerApplication::updateEditorColourSchemeIfNeeded() { auto& appearanceSettings = getAppSettings().appearance; auto schemes = appearanceSettings.getPresetSchemes(); if (isEditorColourSchemeADefaultScheme (schemes, selectedEditorColourSchemeIndex)) setEditorColourScheme (getEditorColourSchemeForGUIColourScheme (schemes, selectedColourSchemeIndex), true); }