1 /*
2 ==============================================================================
3
4 This file is part of the JUCE library.
5 Copyright (c) 2020 - Raw Material Software Limited
6
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
9
10 By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11 Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12
13 End User License Agreement: www.juce.com/juce-6-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
15
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
18
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
22
23 ==============================================================================
24 */
25
26 PopupMenu createGUIEditorMenu();
27 void handleGUIEditorMenuCommand (int);
28 void registerGUIEditorCommands();
29
30
31 //==============================================================================
32 struct ProjucerApplication::MainMenuModel : public MenuBarModel
33 {
MainMenuModelProjucerApplication::MainMenuModel34 MainMenuModel()
35 {
36 setApplicationCommandManagerToWatch (&getCommandManager());
37 }
38
getMenuBarNamesProjucerApplication::MainMenuModel39 StringArray getMenuBarNames() override
40 {
41 return getApp().getMenuNames();
42 }
43
getMenuForIndexProjucerApplication::MainMenuModel44 PopupMenu getMenuForIndex (int /*topLevelMenuIndex*/, const String& menuName) override
45 {
46 return getApp().createMenu (menuName);
47 }
48
menuItemSelectedProjucerApplication::MainMenuModel49 void menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/) override
50 {
51 getApp().handleMainMenuCommand (menuItemID);
52 }
53 };
54
55 //==============================================================================
initialise(const String & commandLine)56 void ProjucerApplication::initialise (const String& commandLine)
57 {
58 if (commandLine.trimStart().startsWith ("--server"))
59 {
60 initialiseLogger ("Compiler_Log_");
61 LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
62
63 #if JUCE_MAC
64 Process::setDockIconVisible (false);
65 #endif
66
67 server = createClangServer (commandLine);
68 }
69 else
70 {
71 initialiseLogger ("IDE_Log_");
72 Logger::writeToLog (SystemStats::getOperatingSystemName());
73 Logger::writeToLog ("CPU: " + String (SystemStats::getCpuSpeedInMegahertz())
74 + "MHz Cores: " + String (SystemStats::getNumCpus())
75 + " " + String (SystemStats::getMemorySizeInMegabytes()) + "MB");
76
77 isRunningCommandLine = commandLine.isNotEmpty()
78 && ! commandLine.startsWith ("-NSDocumentRevisionsDebugMode");
79
80 settings = std::make_unique<StoredSettings>();
81
82 if (isRunningCommandLine)
83 {
84 auto appReturnCode = performCommandLine (ArgumentList ("Projucer", commandLine));
85
86 if (appReturnCode != commandLineNotPerformed)
87 {
88 setApplicationReturnValue (appReturnCode);
89 quit();
90 return;
91 }
92
93 isRunningCommandLine = false;
94 }
95
96 if (sendCommandLineToPreexistingInstance())
97 {
98 DBG ("Another instance is running - quitting...");
99 quit();
100 return;
101 }
102
103 doBasicApplicationSetup();
104
105 // do further initialisation in a moment when the message loop has started
106 triggerAsyncUpdate();
107 }
108 }
109
initialiseLogger(const char * filePrefix)110 bool ProjucerApplication::initialiseLogger (const char* filePrefix)
111 {
112 if (logger == nullptr)
113 {
114 #if JUCE_LINUX || JUCE_BSD
115 String folder = "~/.config/Projucer/Logs";
116 #else
117 String folder = "com.juce.projucer";
118 #endif
119
120 logger.reset (FileLogger::createDateStampedLogger (folder, filePrefix, ".txt",
121 getApplicationName() + " " + getApplicationVersion()
122 + " --- Build date: " __DATE__));
123 Logger::setCurrentLogger (logger.get());
124 }
125
126 return logger != nullptr;
127 }
128
initialiseWindows(const String & commandLine)129 void ProjucerApplication::initialiseWindows (const String& commandLine)
130 {
131 const String commandLineWithoutNSDebug (commandLine.replace ("-NSDocumentRevisionsDebugMode YES", StringRef()));
132
133 if (commandLineWithoutNSDebug.trim().isNotEmpty() && ! commandLineWithoutNSDebug.trim().startsWithChar ('-'))
134 anotherInstanceStarted (commandLine);
135 else if (mainWindowList.windows.size() == 0)
136 mainWindowList.reopenLastProjects();
137
138 mainWindowList.createWindowIfNoneAreOpen();
139 }
140
handleAsyncUpdate()141 void ProjucerApplication::handleAsyncUpdate()
142 {
143 rescanJUCEPathModules();
144 rescanUserPathModules();
145
146 openDocumentManager.registerType (new ProjucerAppClasses::LiveBuildCodeEditorDocument::Type(), 2);
147
148 menuModel.reset (new MainMenuModel());
149
150 #if JUCE_MAC
151 rebuildAppleMenu();
152 appleMenuRebuildListener = std::make_unique<AppleMenuRebuildListener>();
153 #endif
154
155 settings->appearance.refreshPresetSchemeList();
156 setColourScheme (getGlobalProperties().getIntValue ("COLOUR SCHEME"), false);
157 setEditorColourScheme (getGlobalProperties().getIntValue ("EDITOR COLOUR SCHEME"), false);
158 updateEditorColourSchemeIfNeeded();
159
160 ImageCache::setCacheTimeout (30 * 1000);
161 tooltipWindow = std::make_unique<TooltipWindow> (nullptr, 1200);
162
163 if (isAutomaticVersionCheckingEnabled())
164 LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (true);
165
166 initialiseWindows (getCommandLineParameters());
167 }
168
doBasicApplicationSetup()169 void ProjucerApplication::doBasicApplicationSetup()
170 {
171 licenseController = std::make_unique<LicenseController>();
172 LookAndFeel::setDefaultLookAndFeel (&lookAndFeel);
173 initCommandManager();
174 childProcessCache = std::make_unique<ChildProcessCache>();
175 icons = std::make_unique<Icons>();
176 }
177
deleteTemporaryFiles()178 static void deleteTemporaryFiles()
179 {
180 auto tempDirectory = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs");
181
182 if (tempDirectory.exists())
183 tempDirectory.deleteRecursively();
184 }
185
shutdown()186 void ProjucerApplication::shutdown()
187 {
188 if (server != nullptr)
189 {
190 destroyClangServer (server);
191 Logger::writeToLog ("Server shutdown cleanly");
192 }
193
194 utf8Window.reset();
195 svgPathWindow.reset();
196 aboutWindow.reset();
197 pathsWindow.reset();
198 editorColourSchemeWindow.reset();
199 pipCreatorWindow.reset();
200
201 mainWindowList.forceCloseAllWindows();
202 openDocumentManager.clear();
203
204 childProcessCache.reset();
205
206 #if JUCE_MAC
207 MenuBarModel::setMacMainMenu (nullptr);
208 #endif
209
210 menuModel.reset();
211 commandManager.reset();
212 settings.reset();
213
214 if (! isRunningCommandLine)
215 LookAndFeel::setDefaultLookAndFeel (nullptr);
216
217 // clean up after ourselves and delete any temp project files that may have
218 // been created from PIPs
219 deleteTemporaryFiles();
220
221 if (! isRunningCommandLine)
222 Logger::writeToLog ("Shutdown");
223
224 deleteLogger();
225 }
226
227 struct AsyncQuitRetrier : private Timer
228 {
AsyncQuitRetrierAsyncQuitRetrier229 AsyncQuitRetrier() { startTimer (500); }
230
timerCallbackAsyncQuitRetrier231 void timerCallback() override
232 {
233 stopTimer();
234 delete this;
235
236 if (auto* app = JUCEApplicationBase::getInstance())
237 app->systemRequestedQuit();
238 }
239
240 JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier)
241 };
242
systemRequestedQuit()243 void ProjucerApplication::systemRequestedQuit()
244 {
245 if (server != nullptr)
246 {
247 sendQuitMessageToIDE (server);
248 }
249 else if (ModalComponentManager::getInstance()->cancelAllModalComponents())
250 {
251 new AsyncQuitRetrier();
252 }
253 else
254 {
255 if (closeAllMainWindows())
256 quit();
257 }
258 }
259
260 //==============================================================================
getVersionDescription() const261 String ProjucerApplication::getVersionDescription() const
262 {
263 String s;
264
265 const Time buildDate (Time::getCompilationDate());
266
267 s << "Projucer " << ProjectInfo::versionString
268 << newLine
269 << "Build date: " << buildDate.getDayOfMonth()
270 << " " << Time::getMonthName (buildDate.getMonth(), true)
271 << " " << buildDate.getYear();
272
273 return s;
274 }
275
anotherInstanceStarted(const String & commandLine)276 void ProjucerApplication::anotherInstanceStarted (const String& commandLine)
277 {
278 if (server == nullptr && ! commandLine.trim().startsWithChar ('-'))
279 {
280 ArgumentList list ({}, commandLine);
281
282 for (auto& arg : list.arguments)
283 openFile (arg.resolveAsFile());
284 }
285 }
286
getApp()287 ProjucerApplication& ProjucerApplication::getApp()
288 {
289 ProjucerApplication* const app = dynamic_cast<ProjucerApplication*> (JUCEApplication::getInstance());
290 jassert (app != nullptr);
291 return *app;
292 }
293
getCommandManager()294 ApplicationCommandManager& ProjucerApplication::getCommandManager()
295 {
296 auto* cm = ProjucerApplication::getApp().commandManager.get();
297 jassert (cm != nullptr);
298 return *cm;
299 }
300
301
302 //==============================================================================
303 enum
304 {
305 recentProjectsBaseID = 100,
306 openWindowsBaseID = 300,
307 activeDocumentsBaseID = 400,
308 showPathsID = 1999,
309 examplesBaseID = 2000
310 };
311
getMenuModel()312 MenuBarModel* ProjucerApplication::getMenuModel()
313 {
314 return menuModel.get();
315 }
316
getMenuNames()317 StringArray ProjucerApplication::getMenuNames()
318 {
319 StringArray currentMenuNames { "File", "Edit", "View", "Build", "Window", "Document", "GUI Editor", "Tools", "Help" };
320
321 if (! isLiveBuildEnabled()) currentMenuNames.removeString ("Build");
322 if (! isGUIEditorEnabled()) currentMenuNames.removeString ("GUI Editor");
323
324 return currentMenuNames;
325 }
326
createMenu(const String & menuName)327 PopupMenu ProjucerApplication::createMenu (const String& menuName)
328 {
329 if (menuName == "File")
330 return createFileMenu();
331
332 if (menuName == "Edit")
333 return createEditMenu();
334
335 if (menuName == "View")
336 return createViewMenu();
337
338 if (menuName == "Build")
339 if (isLiveBuildEnabled())
340 return createBuildMenu();
341
342 if (menuName == "Window")
343 return createWindowMenu();
344
345 if (menuName == "Document")
346 return createDocumentMenu();
347
348 if (menuName == "Tools")
349 return createToolsMenu();
350
351 if (menuName == "Help")
352 return createHelpMenu();
353
354 if (menuName == "GUI Editor")
355 if (isGUIEditorEnabled())
356 return createGUIEditorMenu();
357
358 jassertfalse; // names have changed?
359 return {};
360 }
361
createFileMenu()362 PopupMenu ProjucerApplication::createFileMenu()
363 {
364 PopupMenu menu;
365 menu.addCommandItem (commandManager.get(), CommandIDs::newProject);
366 menu.addCommandItem (commandManager.get(), CommandIDs::newProjectFromClipboard);
367 menu.addCommandItem (commandManager.get(), CommandIDs::newPIP);
368 menu.addSeparator();
369 menu.addCommandItem (commandManager.get(), CommandIDs::open);
370
371 {
372 PopupMenu recentFiles;
373
374 settings->recentFiles.createPopupMenuItems (recentFiles, recentProjectsBaseID, true, true);
375
376 if (recentFiles.getNumItems() > 0)
377 {
378 recentFiles.addSeparator();
379 recentFiles.addCommandItem (commandManager.get(), CommandIDs::clearRecentFiles);
380 }
381
382 menu.addSubMenu ("Open Recent", recentFiles);
383 }
384
385 menu.addSubMenu ("Open Example", createExamplesPopupMenu());
386
387 menu.addSeparator();
388 menu.addCommandItem (commandManager.get(), CommandIDs::closeDocument);
389 menu.addCommandItem (commandManager.get(), CommandIDs::saveDocument);
390 menu.addCommandItem (commandManager.get(), CommandIDs::saveDocumentAs);
391 menu.addCommandItem (commandManager.get(), CommandIDs::saveAll);
392 menu.addSeparator();
393 menu.addCommandItem (commandManager.get(), CommandIDs::closeProject);
394 menu.addCommandItem (commandManager.get(), CommandIDs::saveProject);
395 menu.addSeparator();
396 menu.addCommandItem (commandManager.get(), CommandIDs::openInIDE);
397 menu.addCommandItem (commandManager.get(), CommandIDs::saveAndOpenInIDE);
398 menu.addSeparator();
399
400 #if ! JUCER_ENABLE_GPL_MODE
401 menu.addCommandItem (commandManager.get(), CommandIDs::loginLogout);
402 #endif
403
404 #if ! JUCE_MAC
405 menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow);
406 menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion);
407 menu.addCommandItem (commandManager.get(), CommandIDs::enableNewVersionCheck);
408 menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow);
409 menu.addSeparator();
410 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::quit);
411 #endif
412
413 return menu;
414 }
415
createEditMenu()416 PopupMenu ProjucerApplication::createEditMenu()
417 {
418 PopupMenu menu;
419 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::undo);
420 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::redo);
421 menu.addSeparator();
422 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::cut);
423 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::copy);
424 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::paste);
425 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::del);
426 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::selectAll);
427 menu.addCommandItem (commandManager.get(), StandardApplicationCommandIDs::deselectAll);
428 menu.addSeparator();
429 menu.addCommandItem (commandManager.get(), CommandIDs::showFindPanel);
430 menu.addCommandItem (commandManager.get(), CommandIDs::findSelection);
431 menu.addCommandItem (commandManager.get(), CommandIDs::findNext);
432 menu.addCommandItem (commandManager.get(), CommandIDs::findPrevious);
433 return menu;
434 }
435
createViewMenu()436 PopupMenu ProjucerApplication::createViewMenu()
437 {
438 PopupMenu menu;
439 menu.addCommandItem (commandManager.get(), CommandIDs::showProjectSettings);
440 menu.addCommandItem (commandManager.get(), CommandIDs::showProjectTab);
441 menu.addCommandItem (commandManager.get(), CommandIDs::showBuildTab);
442 menu.addCommandItem (commandManager.get(), CommandIDs::showFileExplorerPanel);
443 menu.addCommandItem (commandManager.get(), CommandIDs::showModulesPanel);
444 menu.addCommandItem (commandManager.get(), CommandIDs::showExportersPanel);
445 menu.addCommandItem (commandManager.get(), CommandIDs::showExporterSettings);
446
447 menu.addSeparator();
448 createColourSchemeItems (menu);
449
450 return menu;
451 }
452
createBuildMenu()453 PopupMenu ProjucerApplication::createBuildMenu()
454 {
455 PopupMenu menu;
456 menu.addCommandItem (commandManager.get(), CommandIDs::toggleBuildEnabled);
457 menu.addCommandItem (commandManager.get(), CommandIDs::buildNow);
458 menu.addCommandItem (commandManager.get(), CommandIDs::toggleContinuousBuild);
459 menu.addSeparator();
460 menu.addCommandItem (commandManager.get(), CommandIDs::launchApp);
461 menu.addCommandItem (commandManager.get(), CommandIDs::killApp);
462 menu.addCommandItem (commandManager.get(), CommandIDs::cleanAll);
463 menu.addSeparator();
464 menu.addCommandItem (commandManager.get(), CommandIDs::reinstantiateComp);
465 menu.addCommandItem (commandManager.get(), CommandIDs::showWarnings);
466 menu.addSeparator();
467 menu.addCommandItem (commandManager.get(), CommandIDs::nextError);
468 menu.addCommandItem (commandManager.get(), CommandIDs::prevError);
469 return menu;
470 }
471
createColourSchemeItems(PopupMenu & menu)472 void ProjucerApplication::createColourSchemeItems (PopupMenu& menu)
473 {
474 {
475 PopupMenu colourSchemeMenu;
476
477 colourSchemeMenu.addItem (PopupMenu::Item ("Dark")
478 .setTicked (selectedColourSchemeIndex == 0)
479 .setAction ([this] { setColourScheme (0, true); updateEditorColourSchemeIfNeeded(); }));
480
481 colourSchemeMenu.addItem (PopupMenu::Item ("Grey")
482 .setTicked (selectedColourSchemeIndex == 1)
483 .setAction ([this] { setColourScheme (1, true); updateEditorColourSchemeIfNeeded(); }));
484
485 colourSchemeMenu.addItem (PopupMenu::Item ("Light")
486 .setTicked (selectedColourSchemeIndex == 2)
487 .setAction ([this] { setColourScheme (2, true); updateEditorColourSchemeIfNeeded(); }));
488
489 menu.addSubMenu ("Colour Scheme", colourSchemeMenu);
490 }
491
492 {
493 PopupMenu editorColourSchemeMenu;
494
495 auto& appearanceSettings = getAppSettings().appearance;
496
497 appearanceSettings.refreshPresetSchemeList();
498 auto schemes = appearanceSettings.getPresetSchemes();
499
500 auto i = 0;
501
502 for (auto& s : schemes)
503 {
504 editorColourSchemeMenu.addItem (PopupMenu::Item (s)
505 .setEnabled (editorColourSchemeWindow == nullptr)
506 .setTicked (selectedEditorColourSchemeIndex == i)
507 .setAction ([this, i] { setEditorColourScheme (i, true); }));
508 ++i;
509 }
510
511 editorColourSchemeMenu.addSeparator();
512 editorColourSchemeMenu.addItem (PopupMenu::Item ("Create...")
513 .setEnabled (editorColourSchemeWindow == nullptr)
514 .setAction ([this] { showEditorColourSchemeWindow(); }));
515
516 menu.addSubMenu ("Editor Colour Scheme", editorColourSchemeMenu);
517 }
518 }
519
createWindowMenu()520 PopupMenu ProjucerApplication::createWindowMenu()
521 {
522 PopupMenu menu;
523 menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousWindow);
524 menu.addCommandItem (commandManager.get(), CommandIDs::goToNextWindow);
525 menu.addCommandItem (commandManager.get(), CommandIDs::closeWindow);
526 menu.addSeparator();
527
528 int counter = 0;
529
530 for (auto* window : mainWindowList.windows)
531 {
532 if (window != nullptr)
533 {
534 if (auto* project = window->getProject())
535 menu.addItem (openWindowsBaseID + counter++, project->getProjectNameString());
536 }
537 }
538
539 menu.addSeparator();
540 menu.addCommandItem (commandManager.get(), CommandIDs::closeAllWindows);
541 return menu;
542 }
543
createDocumentMenu()544 PopupMenu ProjucerApplication::createDocumentMenu()
545 {
546 PopupMenu menu;
547 menu.addCommandItem (commandManager.get(), CommandIDs::goToPreviousDoc);
548 menu.addCommandItem (commandManager.get(), CommandIDs::goToNextDoc);
549 menu.addCommandItem (commandManager.get(), CommandIDs::goToCounterpart);
550 menu.addSeparator();
551
552 auto numDocs = jmin (50, openDocumentManager.getNumOpenDocuments());
553
554 for (int i = 0; i < numDocs; ++i)
555 {
556 OpenDocumentManager::Document* doc = openDocumentManager.getOpenDocument(i);
557 menu.addItem (activeDocumentsBaseID + i, doc->getName());
558 }
559
560 menu.addSeparator();
561 menu.addCommandItem (commandManager.get(), CommandIDs::closeAllDocuments);
562 return menu;
563 }
564
createToolsMenu()565 PopupMenu ProjucerApplication::createToolsMenu()
566 {
567 PopupMenu menu;
568 menu.addCommandItem (commandManager.get(), CommandIDs::showUTF8Tool);
569 menu.addCommandItem (commandManager.get(), CommandIDs::showSVGPathTool);
570 menu.addCommandItem (commandManager.get(), CommandIDs::showTranslationTool);
571 menu.addSeparator();
572 menu.addCommandItem (commandManager.get(), CommandIDs::enableLiveBuild);
573 menu.addCommandItem (commandManager.get(), CommandIDs::enableGUIEditor);
574 return menu;
575 }
576
createHelpMenu()577 PopupMenu ProjucerApplication::createHelpMenu()
578 {
579 PopupMenu menu;
580 menu.addCommandItem (commandManager.get(), CommandIDs::showForum);
581 menu.addSeparator();
582 menu.addCommandItem (commandManager.get(), CommandIDs::showAPIModules);
583 menu.addCommandItem (commandManager.get(), CommandIDs::showAPIClasses);
584 menu.addCommandItem (commandManager.get(), CommandIDs::showTutorials);
585 return menu;
586 }
587
createExtraAppleMenuItems()588 PopupMenu ProjucerApplication::createExtraAppleMenuItems()
589 {
590 PopupMenu menu;
591 menu.addCommandItem (commandManager.get(), CommandIDs::showAboutWindow);
592 menu.addCommandItem (commandManager.get(), CommandIDs::checkForNewVersion);
593 menu.addCommandItem (commandManager.get(), CommandIDs::enableNewVersionCheck);
594 menu.addSeparator();
595 menu.addCommandItem (commandManager.get(), CommandIDs::showGlobalPathsWindow);
596 return menu;
597 }
598
createExamplesPopupMenu()599 PopupMenu ProjucerApplication::createExamplesPopupMenu() noexcept
600 {
601 PopupMenu menu;
602 numExamples = 0;
603 for (auto& dir : getSortedExampleDirectories())
604 {
605 PopupMenu m;
606 for (auto& f : getSortedExampleFilesInDirectory (dir))
607 {
608 m.addItem (examplesBaseID + numExamples, f.getFileNameWithoutExtension());
609 ++numExamples;
610 }
611
612 menu.addSubMenu (dir.getFileName(), m);
613 }
614
615 if (numExamples == 0)
616 {
617 menu.addItem (showPathsID, "Set path to JUCE...");
618 }
619 else
620 {
621 menu.addSeparator();
622 menu.addCommandItem (commandManager.get(), CommandIDs::launchDemoRunner);
623 }
624
625 return menu;
626 }
627
628 #if JUCE_MAC
rebuildAppleMenu()629 void ProjucerApplication::rebuildAppleMenu()
630 {
631 auto extraAppleMenuItems = createExtraAppleMenuItems();
632
633 // workaround broken "Open Recent" submenu: not passing the
634 // submenu's title here avoids the defect in JuceMainMenuHandler::addMenuItem
635 MenuBarModel::setMacMainMenu (menuModel.get(), &extraAppleMenuItems); //, "Open Recent");
636 }
637 #endif
638
639 //==============================================================================
getJUCEExamplesDirectoryPathFromGlobal()640 File ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal() noexcept
641 {
642 auto globalPath = File::createFileWithoutCheckingPath (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString()
643 .replace ("~", File::getSpecialLocation (File::userHomeDirectory).getFullPathName()));
644
645 if (globalPath.exists())
646 return File (globalPath).getChildFile ("examples");
647
648 return {};
649 }
650
getSortedExampleDirectories()651 Array<File> ProjucerApplication::getSortedExampleDirectories() noexcept
652 {
653 Array<File> exampleDirectories;
654
655 auto examplesPath = getJUCEExamplesDirectoryPathFromGlobal();
656
657 if (! isValidJUCEExamplesDirectory (examplesPath))
658 return {};
659
660 for (const auto& iter : RangedDirectoryIterator (examplesPath, false, "*", File::findDirectories))
661 {
662 auto exampleDirectory = iter.getFile();
663
664 if (exampleDirectory.getNumberOfChildFiles (File::findFiles | File::ignoreHiddenFiles) > 0
665 && exampleDirectory.getFileName() != "DemoRunner"
666 && exampleDirectory.getFileName() != "Assets"
667 && exampleDirectory.getFileName() != "CMake")
668 {
669 exampleDirectories.add (exampleDirectory);
670 }
671 }
672
673 exampleDirectories.sort();
674
675 return exampleDirectories;
676 }
677
getSortedExampleFilesInDirectory(const File & directory)678 Array<File> ProjucerApplication::getSortedExampleFilesInDirectory (const File& directory) noexcept
679 {
680 Array<File> exampleFiles;
681
682 for (const auto& iter : RangedDirectoryIterator (directory, false, "*.h", File::findFiles))
683 exampleFiles.add (iter.getFile());
684
685 exampleFiles.sort();
686
687 return exampleFiles;
688 }
689
findAndLaunchExample(int selectedIndex)690 void ProjucerApplication::findAndLaunchExample (int selectedIndex)
691 {
692 File example;
693
694 for (auto& dir : getSortedExampleDirectories())
695 {
696 auto exampleFiles = getSortedExampleFilesInDirectory (dir);
697
698 if (selectedIndex < exampleFiles.size())
699 {
700 example = exampleFiles.getUnchecked (selectedIndex);
701 break;
702 }
703
704 selectedIndex -= exampleFiles.size();
705 }
706
707 // example doesn't exist?
708 jassert (example != File());
709
710 openFile (example);
711 }
712
713 //==============================================================================
getPlatformSpecificFileExtension()714 static String getPlatformSpecificFileExtension()
715 {
716 #if JUCE_MAC
717 return ".app";
718 #elif JUCE_WINDOWS
719 return ".exe";
720 #elif JUCE_LINUX || JUCE_BSD
721 return {};
722 #else
723 jassertfalse;
724 return {};
725 #endif
726 }
727
getPlatformSpecificProjectFolder()728 static File getPlatformSpecificProjectFolder()
729 {
730 auto examplesDir = ProjucerApplication::getJUCEExamplesDirectoryPathFromGlobal();
731
732 if (examplesDir == File())
733 return {};
734
735 auto buildsFolder = examplesDir.getChildFile ("DemoRunner").getChildFile ("Builds");
736
737 #if JUCE_MAC
738 return buildsFolder.getChildFile ("MacOSX");
739 #elif JUCE_WINDOWS
740 return buildsFolder.getChildFile ("VisualStudio2017");
741 #elif JUCE_LINUX || JUCE_BSD
742 return buildsFolder.getChildFile ("LinuxMakefile");
743 #else
744 jassertfalse;
745 return {};
746 #endif
747 }
748
tryToFindDemoRunnerExecutableInBuilds()749 static File tryToFindDemoRunnerExecutableInBuilds()
750 {
751 auto projectFolder = getPlatformSpecificProjectFolder();
752
753 if (projectFolder == File())
754 return {};
755
756 #if JUCE_MAC
757 projectFolder = projectFolder.getChildFile ("build");
758 auto demoRunnerExecutable = projectFolder.getChildFile ("Release").getChildFile ("DemoRunner.app");
759
760 if (demoRunnerExecutable.exists())
761 return demoRunnerExecutable;
762
763 demoRunnerExecutable = projectFolder.getChildFile ("Debug").getChildFile ("DemoRunner.app");
764
765 if (demoRunnerExecutable.exists())
766 return demoRunnerExecutable;
767 #elif JUCE_WINDOWS
768 projectFolder = projectFolder.getChildFile ("x64");
769 auto demoRunnerExecutable = projectFolder.getChildFile ("Release").getChildFile ("App").getChildFile ("DemoRunner.exe");
770
771 if (demoRunnerExecutable.existsAsFile())
772 return demoRunnerExecutable;
773
774 demoRunnerExecutable = projectFolder.getChildFile ("Debug").getChildFile ("App").getChildFile ("DemoRunner.exe");
775
776 if (demoRunnerExecutable.existsAsFile())
777 return demoRunnerExecutable;
778 #elif JUCE_LINUX || JUCE_BSD
779 projectFolder = projectFolder.getChildFile ("LinuxMakefile").getChildFile ("build");
780 auto demoRunnerExecutable = projectFolder.getChildFile ("DemoRunner");
781
782 if (demoRunnerExecutable.existsAsFile())
783 return demoRunnerExecutable;
784 #endif
785
786 return {};
787 }
788
tryToFindPrebuiltDemoRunnerExecutable()789 static File tryToFindPrebuiltDemoRunnerExecutable()
790 {
791 auto prebuiltFile = File (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get().toString())
792 .getChildFile ("DemoRunner" + getPlatformSpecificFileExtension());
793
794 #if JUCE_MAC
795 if (prebuiltFile.exists())
796 #else
797 if (prebuiltFile.existsAsFile())
798 #endif
799 return prebuiltFile;
800
801 return {};
802 }
803
checkIfGlobalJUCEPathHasChanged()804 void ProjucerApplication::checkIfGlobalJUCEPathHasChanged()
805 {
806 auto globalJUCEPath = File (getAppSettings().getStoredPath (Ids::jucePath, TargetOS::getThisOS()).get());
807
808 if (lastJUCEPath != globalJUCEPath)
809 {
810 hasScannedForDemoRunnerProject = false;
811 hasScannedForDemoRunnerExecutable = false;
812
813 lastJUCEPath = globalJUCEPath;
814 }
815 }
816
tryToFindDemoRunnerExecutable()817 File ProjucerApplication::tryToFindDemoRunnerExecutable()
818 {
819 checkIfGlobalJUCEPathHasChanged();
820
821 if (hasScannedForDemoRunnerExecutable)
822 return lastDemoRunnerExectuableFile;
823
824 hasScannedForDemoRunnerExecutable = true;
825
826 auto demoRunnerExecutable = tryToFindDemoRunnerExecutableInBuilds();
827
828 if (demoRunnerExecutable == File())
829 demoRunnerExecutable = tryToFindPrebuiltDemoRunnerExecutable();
830
831 lastDemoRunnerExectuableFile = demoRunnerExecutable;
832
833 return demoRunnerExecutable;
834 }
835
tryToFindDemoRunnerProject()836 File ProjucerApplication::tryToFindDemoRunnerProject()
837 {
838 checkIfGlobalJUCEPathHasChanged();
839
840 if (hasScannedForDemoRunnerProject)
841 return lastDemoRunnerProjectFile;
842
843 hasScannedForDemoRunnerProject = true;
844
845 auto projectFolder = getPlatformSpecificProjectFolder();
846
847 if (projectFolder == File())
848 {
849 lastDemoRunnerProjectFile = File();
850 return {};
851 }
852
853 #if JUCE_MAC
854 auto demoRunnerProjectFile = projectFolder.getChildFile ("DemoRunner.xcodeproj");
855 #elif JUCE_WINDOWS
856 auto demoRunnerProjectFile = projectFolder.getChildFile ("DemoRunner.sln");
857 #elif JUCE_LINUX || JUCE_BSD
858 auto demoRunnerProjectFile = projectFolder.getChildFile ("Makefile");
859 #endif
860
861 #if JUCE_MAC
862 if (! demoRunnerProjectFile.exists())
863 #else
864 if (! demoRunnerProjectFile.existsAsFile())
865 #endif
866 demoRunnerProjectFile = File();
867
868 lastDemoRunnerProjectFile = demoRunnerProjectFile;
869
870 return demoRunnerProjectFile;
871 }
872
launchDemoRunner()873 void ProjucerApplication::launchDemoRunner()
874 {
875 auto demoRunnerFile = tryToFindDemoRunnerExecutable();
876
877 if (demoRunnerFile != File() && demoRunnerFile.startAsProcess())
878 return;
879
880 demoRunnerFile = tryToFindDemoRunnerProject();
881
882 if (demoRunnerFile != File())
883 {
884 auto& lf = Desktop::getInstance().getDefaultLookAndFeel();
885
886 demoRunnerAlert.reset (lf.createAlertWindow ("Open Project",
887 "Couldn't find a compiled version of the Demo Runner."
888 #if JUCE_LINUX || JUCE_BSD
889 " Do you want to build it now?", "Build project", "Cancel",
890 #else
891 " Do you want to open the project?", "Open project", "Cancel",
892 #endif
893 {},
894 AlertWindow::QuestionIcon, 2,
895 mainWindowList.getFrontmostWindow (false)));
896
897 demoRunnerAlert->enterModalState (true, ModalCallbackFunction::create ([this, demoRunnerFile] (int retVal)
898 {
899 demoRunnerAlert.reset (nullptr);
900
901 if (retVal == 1)
902 {
903 #if JUCE_LINUX || JUCE_BSD
904 String command ("make -C " + demoRunnerFile.getParentDirectory().getFullPathName() + " CONFIG=Release -j3");
905
906 if (! makeProcess.start (command))
907 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", "Error building Demo Runner.");
908 #else
909 demoRunnerFile.startAsProcess();
910 #endif
911 }
912 }), false);
913 }
914 }
915
916 //==============================================================================
handleMainMenuCommand(int menuItemID)917 void ProjucerApplication::handleMainMenuCommand (int menuItemID)
918 {
919 if (menuItemID >= recentProjectsBaseID && menuItemID < (recentProjectsBaseID + 100))
920 {
921 // open a file from the "recent files" menu
922 openFile (settings->recentFiles.getFile (menuItemID - recentProjectsBaseID));
923 }
924 else if (menuItemID >= openWindowsBaseID && menuItemID < (openWindowsBaseID + 100))
925 {
926 if (auto* window = mainWindowList.windows.getUnchecked (menuItemID - openWindowsBaseID))
927 window->toFront (true);
928 }
929 else if (menuItemID >= activeDocumentsBaseID && menuItemID < (activeDocumentsBaseID + 200))
930 {
931 if (auto* doc = openDocumentManager.getOpenDocument (menuItemID - activeDocumentsBaseID))
932 mainWindowList.openDocument (doc, true);
933 else
934 jassertfalse;
935 }
936 else if (menuItemID == showPathsID)
937 {
938 showPathsWindow (true);
939 }
940 else if (menuItemID >= examplesBaseID && menuItemID < (examplesBaseID + numExamples))
941 {
942 findAndLaunchExample (menuItemID - examplesBaseID);
943 }
944 else
945 {
946 handleGUIEditorMenuCommand (menuItemID);
947 }
948 }
949
950 //==============================================================================
getAllCommands(Array<CommandID> & commands)951 void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
952 {
953 JUCEApplication::getAllCommands (commands);
954
955 const CommandID ids[] = { CommandIDs::newProject,
956 CommandIDs::newProjectFromClipboard,
957 CommandIDs::newPIP,
958 CommandIDs::open,
959 CommandIDs::launchDemoRunner,
960 CommandIDs::closeAllWindows,
961 CommandIDs::closeAllDocuments,
962 CommandIDs::clearRecentFiles,
963 CommandIDs::saveAll,
964 CommandIDs::showGlobalPathsWindow,
965 CommandIDs::showUTF8Tool,
966 CommandIDs::showSVGPathTool,
967 CommandIDs::enableLiveBuild,
968 CommandIDs::enableGUIEditor,
969 CommandIDs::showAboutWindow,
970 CommandIDs::checkForNewVersion,
971 CommandIDs::enableNewVersionCheck,
972 CommandIDs::showForum,
973 CommandIDs::showAPIModules,
974 CommandIDs::showAPIClasses,
975 CommandIDs::showTutorials,
976 CommandIDs::loginLogout };
977
978 commands.addArray (ids, numElementsInArray (ids));
979 }
980
getCommandInfo(CommandID commandID,ApplicationCommandInfo & result)981 void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationCommandInfo& result)
982 {
983 switch (commandID)
984 {
985 case CommandIDs::newProject:
986 result.setInfo ("New Project...", "Creates a new JUCE project", CommandCategories::general, 0);
987 result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier, 0));
988 break;
989
990 case CommandIDs::newProjectFromClipboard:
991 result.setInfo ("New Project From Clipboard...", "Creates a new JUCE project from the clipboard contents", CommandCategories::general, 0);
992 result.defaultKeypresses.add (KeyPress ('n', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
993 break;
994
995 case CommandIDs::newPIP:
996 result.setInfo ("New PIP...", "Opens the PIP Creator utility for creating a new PIP", CommandCategories::general, 0);
997 result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
998 break;
999
1000 case CommandIDs::launchDemoRunner:
1001 #if JUCE_LINUX || JUCE_BSD
1002 if (makeProcess.isRunning())
1003 {
1004 result.setInfo ("Building Demo Runner...", "The Demo Runner project is currently building", CommandCategories::general, 0);
1005 result.setActive (false);
1006 }
1007 else
1008 #endif
1009 {
1010 result.setInfo ("Launch Demo Runner", "Launches the JUCE demo runner application, or the project if it can't be found", CommandCategories::general, 0);
1011 result.setActive (tryToFindDemoRunnerExecutable() != File() || tryToFindDemoRunnerProject() != File());
1012 }
1013 break;
1014
1015 case CommandIDs::open:
1016 result.setInfo ("Open...", "Opens a JUCE project", CommandCategories::general, 0);
1017 result.defaultKeypresses.add (KeyPress ('o', ModifierKeys::commandModifier, 0));
1018 break;
1019
1020 case CommandIDs::showGlobalPathsWindow:
1021 result.setInfo ("Global Paths...",
1022 "Shows the window to change the stored global paths.",
1023 CommandCategories::general, 0);
1024 break;
1025
1026 case CommandIDs::closeAllWindows:
1027 result.setInfo ("Close All Windows", "Closes all open windows", CommandCategories::general, 0);
1028 result.setActive (mainWindowList.windows.size() > 0);
1029 break;
1030
1031 case CommandIDs::closeAllDocuments:
1032 result.setInfo ("Close All Documents", "Closes all open documents", CommandCategories::general, 0);
1033 result.setActive (openDocumentManager.getNumOpenDocuments() > 0);
1034 break;
1035
1036 case CommandIDs::clearRecentFiles:
1037 result.setInfo ("Clear Recent Files", "Clears all recent files from the menu", CommandCategories::general, 0);
1038 result.setActive (settings->recentFiles.getNumFiles() > 0);
1039 break;
1040
1041 case CommandIDs::saveAll:
1042 result.setInfo ("Save All", "Saves all open documents", CommandCategories::general, 0);
1043 result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::altModifier, 0));
1044 break;
1045
1046 case CommandIDs::showUTF8Tool:
1047 result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0);
1048 break;
1049
1050 case CommandIDs::showSVGPathTool:
1051 result.setInfo ("SVG Path Converter", "Shows the SVG->Path data conversion utility", CommandCategories::general, 0);
1052 break;
1053
1054 case CommandIDs::enableLiveBuild:
1055 result.setInfo ("Live-Build Enabled",
1056 "Enables or disables the live-build functionality",
1057 CommandCategories::general,
1058 (isLiveBuildEnabled() ? ApplicationCommandInfo::isTicked : 0));
1059 break;
1060
1061 case CommandIDs::enableGUIEditor:
1062 result.setInfo ("GUI Editor Enabled",
1063 "Enables or disables the GUI editor functionality",
1064 CommandCategories::general,
1065 (isGUIEditorEnabled() ? ApplicationCommandInfo::isTicked : 0));
1066 break;
1067
1068 case CommandIDs::showAboutWindow:
1069 result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
1070 break;
1071
1072 case CommandIDs::checkForNewVersion:
1073 result.setInfo ("Check for New Version...", "Checks the web server for a new version of JUCE", CommandCategories::general, 0);
1074 break;
1075
1076 case CommandIDs::enableNewVersionCheck:
1077 result.setInfo ("Automatically Check for New Versions",
1078 "Enables automatic background checking for new versions of JUCE.",
1079 CommandCategories::general,
1080 (isAutomaticVersionCheckingEnabled() ? ApplicationCommandInfo::isTicked : 0));
1081 break;
1082
1083 case CommandIDs::showForum:
1084 result.setInfo ("JUCE Community Forum", "Shows the JUCE community forum in a browser", CommandCategories::general, 0);
1085 break;
1086
1087 case CommandIDs::showAPIModules:
1088 result.setInfo ("API Modules", "Shows the API modules documentation in a browser", CommandCategories::general, 0);
1089 break;
1090
1091 case CommandIDs::showAPIClasses:
1092 result.setInfo ("API Classes", "Shows the API classes documentation in a browser", CommandCategories::general, 0);
1093 break;
1094
1095 case CommandIDs::showTutorials:
1096 result.setInfo ("JUCE Tutorials", "Shows the JUCE tutorials in a browser", CommandCategories::general, 0);
1097 break;
1098
1099 case CommandIDs::loginLogout:
1100 {
1101 auto licenseState = licenseController->getCurrentState();
1102
1103 if (licenseState.isGPL())
1104 result.setInfo ("Disable GPL mode", "Disables GPL mode", CommandCategories::general, 0);
1105 else
1106 result.setInfo (licenseState.isSignedIn() ? String ("Sign out ") + licenseState.username + "..." : String ("Sign in..."),
1107 "Sign out of your JUCE account",
1108 CommandCategories::general, 0);
1109 break;
1110 }
1111
1112 default:
1113 JUCEApplication::getCommandInfo (commandID, result);
1114 break;
1115 }
1116 }
1117
perform(const InvocationInfo & info)1118 bool ProjucerApplication::perform (const InvocationInfo& info)
1119 {
1120 switch (info.commandID)
1121 {
1122 case CommandIDs::newProject: createNewProject(); break;
1123 case CommandIDs::newProjectFromClipboard: createNewProjectFromClipboard(); break;
1124 case CommandIDs::newPIP: createNewPIP(); break;
1125 case CommandIDs::open: askUserToOpenFile(); break;
1126 case CommandIDs::launchDemoRunner: launchDemoRunner(); break;
1127 case CommandIDs::saveAll: saveAllDocuments(); break;
1128 case CommandIDs::closeAllWindows: closeAllMainWindowsAndQuitIfNeeded(); break;
1129 case CommandIDs::closeAllDocuments: closeAllDocuments (OpenDocumentManager::SaveIfNeeded::yes); break;
1130 case CommandIDs::clearRecentFiles: clearRecentFiles(); break;
1131 case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break;
1132 case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
1133 case CommandIDs::enableLiveBuild: enableOrDisableLiveBuild(); break;
1134 case CommandIDs::enableGUIEditor: enableOrDisableGUIEditor(); break;
1135 case CommandIDs::showGlobalPathsWindow: showPathsWindow (false); break;
1136 case CommandIDs::showAboutWindow: showAboutWindow(); break;
1137 case CommandIDs::checkForNewVersion: LatestVersionCheckerAndUpdater::getInstance()->checkForNewVersion (false); break;
1138 case CommandIDs::enableNewVersionCheck: setAutomaticVersionCheckingEnabled (! isAutomaticVersionCheckingEnabled()); break;
1139 case CommandIDs::showForum: launchForumBrowser(); break;
1140 case CommandIDs::showAPIModules: launchModulesBrowser(); break;
1141 case CommandIDs::showAPIClasses: launchClassesBrowser(); break;
1142 case CommandIDs::showTutorials: launchTutorialsBrowser(); break;
1143 case CommandIDs::loginLogout: doLoginOrLogout(); break;
1144 default: return JUCEApplication::perform (info);
1145 }
1146
1147 return true;
1148 }
1149
1150 //==============================================================================
createNewProject()1151 void ProjucerApplication::createNewProject()
1152 {
1153 auto* mw = mainWindowList.getOrCreateEmptyWindow();
1154 jassert (mw != nullptr);
1155
1156 mw->showStartPage();
1157
1158 mainWindowList.checkWindowBounds (*mw);
1159 }
1160
createNewProjectFromClipboard()1161 void ProjucerApplication::createNewProjectFromClipboard()
1162 {
1163 auto tempFile = File::getSpecialLocation (File::SpecialLocationType::tempDirectory).getChildFile ("PIPs").getChildFile ("Clipboard")
1164 .getChildFile ("PIPFile_" + String (std::abs (Random::getSystemRandom().nextInt())) + ".h")
1165 .getNonexistentSibling();
1166
1167 if (tempFile.existsAsFile())
1168 tempFile.deleteFile();
1169
1170 tempFile.create();
1171 tempFile.appendText (SystemClipboard::getTextFromClipboard());
1172
1173 String errorString;
1174
1175 if (! isPIPFile (tempFile))
1176 {
1177 errorString = "Clipboard does not contain a valid PIP.";
1178 }
1179 else if (! openFile (tempFile))
1180 {
1181 errorString = "Couldn't create project from clipboard contents.";
1182 mainWindowList.closeWindow (mainWindowList.windows.getLast());
1183 }
1184
1185 if (errorString.isNotEmpty())
1186 {
1187 AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Error", errorString);
1188 tempFile.deleteFile();
1189 }
1190 }
1191
createNewPIP()1192 void ProjucerApplication::createNewPIP()
1193 {
1194 showPIPCreatorWindow();
1195 }
1196
askUserToOpenFile()1197 void ProjucerApplication::askUserToOpenFile()
1198 {
1199 FileChooser fc ("Open File");
1200
1201 if (fc.browseForFileToOpen())
1202 openFile (fc.getResult());
1203 }
1204
openFile(const File & file)1205 bool ProjucerApplication::openFile (const File& file)
1206 {
1207 return mainWindowList.openFile (file);
1208 }
1209
saveAllDocuments()1210 void ProjucerApplication::saveAllDocuments()
1211 {
1212 openDocumentManager.saveAll();
1213
1214 for (int i = 0; i < mainWindowList.windows.size(); ++i)
1215 if (auto* pcc = mainWindowList.windows.getUnchecked(i)->getProjectContentComponent())
1216 pcc->refreshProjectTreeFileStatuses();
1217 }
1218
closeAllDocuments(OpenDocumentManager::SaveIfNeeded askUserToSave)1219 bool ProjucerApplication::closeAllDocuments (OpenDocumentManager::SaveIfNeeded askUserToSave)
1220 {
1221 return openDocumentManager.closeAll (askUserToSave);
1222 }
1223
closeAllMainWindows()1224 bool ProjucerApplication::closeAllMainWindows()
1225 {
1226 return server != nullptr || mainWindowList.askAllWindowsToClose();
1227 }
1228
closeAllMainWindowsAndQuitIfNeeded()1229 void ProjucerApplication::closeAllMainWindowsAndQuitIfNeeded()
1230 {
1231 if (closeAllMainWindows())
1232 {
1233 #if ! JUCE_MAC
1234 if (mainWindowList.windows.size() == 0)
1235 systemRequestedQuit();
1236 #endif
1237 }
1238 }
1239
clearRecentFiles()1240 void ProjucerApplication::clearRecentFiles()
1241 {
1242 settings->recentFiles.clear();
1243 settings->recentFiles.clearRecentFilesNatively();
1244 settings->flush();
1245 menuModel->menuItemsChanged();
1246 }
1247
1248 //==============================================================================
showUTF8ToolWindow()1249 void ProjucerApplication::showUTF8ToolWindow()
1250 {
1251 if (utf8Window != nullptr)
1252 utf8Window->toFront (true);
1253 else
1254 new FloatingToolWindow ("UTF-8 String Literal Converter", "utf8WindowPos",
1255 new UTF8Component(), utf8Window, true,
1256 500, 500, 300, 300, 1000, 1000);
1257 }
1258
showSVGPathDataToolWindow()1259 void ProjucerApplication::showSVGPathDataToolWindow()
1260 {
1261 if (svgPathWindow != nullptr)
1262 svgPathWindow->toFront (true);
1263 else
1264 new FloatingToolWindow ("SVG Path Converter", "svgPathWindowPos",
1265 new SVGPathDataComponent(), svgPathWindow, true,
1266 500, 500, 300, 300, 1000, 1000);
1267 }
1268
isLiveBuildEnabled() const1269 bool ProjucerApplication::isLiveBuildEnabled() const
1270 {
1271 return getGlobalProperties().getBoolValue (Ids::liveBuildEnabled);
1272 }
1273
enableOrDisableLiveBuild()1274 void ProjucerApplication::enableOrDisableLiveBuild()
1275 {
1276 getGlobalProperties().setValue (Ids::liveBuildEnabled, ! isLiveBuildEnabled());
1277 }
1278
isGUIEditorEnabled() const1279 bool ProjucerApplication::isGUIEditorEnabled() const
1280 {
1281 return getGlobalProperties().getBoolValue (Ids::guiEditorEnabled);
1282 }
1283
enableOrDisableGUIEditor()1284 void ProjucerApplication::enableOrDisableGUIEditor()
1285 {
1286 getGlobalProperties().setValue (Ids::guiEditorEnabled, ! isGUIEditorEnabled());
1287 }
1288
showAboutWindow()1289 void ProjucerApplication::showAboutWindow()
1290 {
1291 if (aboutWindow != nullptr)
1292 aboutWindow->toFront (true);
1293 else
1294 new FloatingToolWindow ({}, {}, new AboutWindowComponent(),
1295 aboutWindow, false,
1296 500, 300, 500, 300, 500, 300);
1297 }
1298
showPathsWindow(bool highlightJUCEPath)1299 void ProjucerApplication::showPathsWindow (bool highlightJUCEPath)
1300 {
1301 if (pathsWindow != nullptr)
1302 pathsWindow->toFront (true);
1303 else
1304 new FloatingToolWindow ("Global Paths", "pathsWindowPos",
1305 new GlobalPathsWindowComponent(), pathsWindow, false,
1306 600, 700, 600, 700, 600, 700);
1307
1308 if (highlightJUCEPath)
1309 if (auto* pathsComp = dynamic_cast<GlobalPathsWindowComponent*> (pathsWindow->getChildComponent (0)))
1310 pathsComp->highlightJUCEPath();
1311 }
1312
showEditorColourSchemeWindow()1313 void ProjucerApplication::showEditorColourSchemeWindow()
1314 {
1315 if (editorColourSchemeWindow != nullptr)
1316 editorColourSchemeWindow->toFront (true);
1317 else
1318 new FloatingToolWindow ("Editor Colour Scheme", "editorColourSchemeWindowPos",
1319 new EditorColourSchemeWindowComponent(), editorColourSchemeWindow, false,
1320 500, 500, 500, 500, 500, 500);
1321 }
1322
showPIPCreatorWindow()1323 void ProjucerApplication::showPIPCreatorWindow()
1324 {
1325 if (pipCreatorWindow != nullptr)
1326 pipCreatorWindow->toFront (true);
1327 else
1328 new FloatingToolWindow ("PIP Creator", "pipCreatorWindowPos",
1329 new PIPCreatorWindowComponent(), pipCreatorWindow, false,
1330 600, 750, 600, 750, 600, 750);
1331 }
1332
launchForumBrowser()1333 void ProjucerApplication::launchForumBrowser()
1334 {
1335 URL forumLink ("https://forum.juce.com/");
1336
1337 if (forumLink.isWellFormed())
1338 forumLink.launchInDefaultBrowser();
1339 }
1340
launchModulesBrowser()1341 void ProjucerApplication::launchModulesBrowser()
1342 {
1343 URL modulesLink ("https://docs.juce.com/master/modules.html");
1344
1345 if (modulesLink.isWellFormed())
1346 modulesLink.launchInDefaultBrowser();
1347 }
1348
launchClassesBrowser()1349 void ProjucerApplication::launchClassesBrowser()
1350 {
1351 URL classesLink ("https://docs.juce.com/master/classes.html");
1352
1353 if (classesLink.isWellFormed())
1354 classesLink.launchInDefaultBrowser();
1355 }
1356
launchTutorialsBrowser()1357 void ProjucerApplication::launchTutorialsBrowser()
1358 {
1359 URL tutorialsLink ("https://juce.com/learn/tutorials");
1360
1361 if (tutorialsLink.isWellFormed())
1362 tutorialsLink.launchInDefaultBrowser();
1363 }
1364
doLoginOrLogout()1365 void ProjucerApplication::doLoginOrLogout()
1366 {
1367 if (licenseController->getCurrentState().isSignedIn())
1368 {
1369 licenseController->resetState();
1370 }
1371 else
1372 {
1373 if (auto* window = mainWindowList.getMainWindowWithLoginFormOpen())
1374 {
1375 window->toFront (true);
1376 }
1377 else
1378 {
1379 mainWindowList.createWindowIfNoneAreOpen();
1380 mainWindowList.getFrontmostWindow()->showLoginFormOverlay();
1381 }
1382 }
1383 }
1384
1385 //==============================================================================
1386 struct FileWithTime
1387 {
FileWithTimeFileWithTime1388 FileWithTime (const File& f) : file (f), time (f.getLastModificationTime()) {}
FileWithTimeFileWithTime1389 FileWithTime() {}
1390
operator <FileWithTime1391 bool operator< (const FileWithTime& other) const { return time < other.time; }
operator ==FileWithTime1392 bool operator== (const FileWithTime& other) const { return time == other.time; }
1393
1394 File file;
1395 Time time;
1396 };
1397
deleteLogger()1398 void ProjucerApplication::deleteLogger()
1399 {
1400 const int maxNumLogFilesToKeep = 50;
1401
1402 Logger::setCurrentLogger (nullptr);
1403
1404 if (logger != nullptr)
1405 {
1406 auto logFiles = logger->getLogFile().getParentDirectory().findChildFiles (File::findFiles, false);
1407
1408 if (logFiles.size() > maxNumLogFilesToKeep)
1409 {
1410 Array<FileWithTime> files;
1411
1412 for (auto& f : logFiles)
1413 files.addUsingDefaultSort (f);
1414
1415 for (int i = 0; i < files.size() - maxNumLogFilesToKeep; ++i)
1416 files.getReference(i).file.deleteFile();
1417 }
1418 }
1419
1420 logger.reset();
1421 }
1422
getPropertyFileOptionsFor(const String & filename,bool isProjectSettings)1423 PropertiesFile::Options ProjucerApplication::getPropertyFileOptionsFor (const String& filename, bool isProjectSettings)
1424 {
1425 PropertiesFile::Options options;
1426 options.applicationName = filename;
1427 options.filenameSuffix = "settings";
1428 options.osxLibrarySubFolder = "Application Support";
1429 #if JUCE_LINUX || JUCE_BSD
1430 options.folderName = "~/.config/Projucer";
1431 #else
1432 options.folderName = "Projucer";
1433 #endif
1434
1435 if (isProjectSettings)
1436 options.folderName += "/ProjectSettings";
1437
1438 return options;
1439 }
1440
initCommandManager()1441 void ProjucerApplication::initCommandManager()
1442 {
1443 commandManager.reset (new ApplicationCommandManager());
1444 commandManager->registerAllCommandsForTarget (this);
1445
1446 {
1447 CodeDocument doc;
1448 CppCodeEditorComponent ed (File(), doc);
1449 commandManager->registerAllCommandsForTarget (&ed);
1450 }
1451
1452 registerGUIEditorCommands();
1453 }
1454
rescanModules(AvailableModulesList & list,const Array<File> & paths,bool async)1455 static void rescanModules (AvailableModulesList& list, const Array<File>& paths, bool async)
1456 {
1457 if (async)
1458 list.scanPathsAsync (paths);
1459 else
1460 list.scanPaths (paths);
1461 }
1462
rescanJUCEPathModules()1463 void ProjucerApplication::rescanJUCEPathModules()
1464 {
1465 rescanModules (jucePathModulesList, { getAppSettings().getStoredPath (Ids::defaultJuceModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine);
1466 }
1467
rescanUserPathModules()1468 void ProjucerApplication::rescanUserPathModules()
1469 {
1470 rescanModules (userPathsModulesList, { getAppSettings().getStoredPath (Ids::defaultUserModulePath, TargetOS::getThisOS()).get().toString() }, ! isRunningCommandLine);
1471 }
1472
isAutomaticVersionCheckingEnabled() const1473 bool ProjucerApplication::isAutomaticVersionCheckingEnabled() const
1474 {
1475 return ! getGlobalProperties().getBoolValue (Ids::dontQueryForUpdate);
1476 }
1477
setAutomaticVersionCheckingEnabled(bool enabled)1478 void ProjucerApplication::setAutomaticVersionCheckingEnabled (bool enabled)
1479 {
1480 getGlobalProperties().setValue (Ids::dontQueryForUpdate, ! enabled);
1481 }
1482
shouldPromptUserAboutIncorrectJUCEPath() const1483 bool ProjucerApplication::shouldPromptUserAboutIncorrectJUCEPath() const
1484 {
1485 return ! getGlobalProperties().getBoolValue (Ids::dontAskAboutJUCEPath);
1486 }
1487
setShouldPromptUserAboutIncorrectJUCEPath(bool shouldPrompt)1488 void ProjucerApplication::setShouldPromptUserAboutIncorrectJUCEPath (bool shouldPrompt)
1489 {
1490 getGlobalProperties().setValue (Ids::dontAskAboutJUCEPath, ! shouldPrompt);
1491 }
1492
selectEditorColourSchemeWithName(const String & schemeName)1493 void ProjucerApplication::selectEditorColourSchemeWithName (const String& schemeName)
1494 {
1495 auto& appearanceSettings = getAppSettings().appearance;
1496 auto schemes = appearanceSettings.getPresetSchemes();
1497
1498 auto schemeIndex = schemes.indexOf (schemeName);
1499
1500 if (schemeIndex >= 0)
1501 setEditorColourScheme (schemeIndex, true);
1502 }
1503
setColourScheme(int index,bool saveSetting)1504 void ProjucerApplication::setColourScheme (int index, bool saveSetting)
1505 {
1506 switch (index)
1507 {
1508 case 0: lookAndFeel.setColourScheme (LookAndFeel_V4::getDarkColourScheme()); break;
1509 case 1: lookAndFeel.setColourScheme (LookAndFeel_V4::getGreyColourScheme()); break;
1510 case 2: lookAndFeel.setColourScheme (LookAndFeel_V4::getLightColourScheme()); break;
1511 default: break;
1512 }
1513
1514 lookAndFeel.setupColours();
1515 mainWindowList.sendLookAndFeelChange();
1516
1517 if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
1518 if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
1519 if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
1520 if (pathsWindow != nullptr) pathsWindow->sendLookAndFeelChange();
1521 if (editorColourSchemeWindow != nullptr) editorColourSchemeWindow->sendLookAndFeelChange();
1522 if (pipCreatorWindow != nullptr) pipCreatorWindow->sendLookAndFeelChange();
1523
1524 auto* mcm = ModalComponentManager::getInstance();
1525 for (auto i = 0; i < mcm->getNumModalComponents(); ++i)
1526 mcm->getModalComponent (i)->sendLookAndFeelChange();
1527
1528 if (saveSetting)
1529 {
1530 auto& properties = getGlobalProperties();
1531 properties.setValue ("COLOUR SCHEME", index);
1532 }
1533
1534 selectedColourSchemeIndex = index;
1535
1536 getCommandManager().commandStatusChanged();
1537 }
1538
setEditorColourScheme(int index,bool saveSetting)1539 void ProjucerApplication::setEditorColourScheme (int index, bool saveSetting)
1540 {
1541 auto& appearanceSettings = getAppSettings().appearance;
1542 auto schemes = appearanceSettings.getPresetSchemes();
1543
1544 index = jmin (index, schemes.size() - 1);
1545
1546 appearanceSettings.selectPresetScheme (index);
1547
1548 if (saveSetting)
1549 {
1550 auto& properties = getGlobalProperties();
1551 properties.setValue ("EDITOR COLOUR SCHEME", index);
1552 }
1553
1554 selectedEditorColourSchemeIndex = index;
1555
1556 getCommandManager().commandStatusChanged();
1557 }
1558
isEditorColourSchemeADefaultScheme(const StringArray & schemes,int editorColourSchemeIndex)1559 static bool isEditorColourSchemeADefaultScheme (const StringArray& schemes, int editorColourSchemeIndex)
1560 {
1561 auto& schemeName = schemes[editorColourSchemeIndex];
1562 return (schemeName == "Default (Dark)" || schemeName == "Default (Light)");
1563 }
1564
getEditorColourSchemeForGUIColourScheme(const StringArray & schemes,int guiColourSchemeIndex)1565 static int getEditorColourSchemeForGUIColourScheme (const StringArray& schemes, int guiColourSchemeIndex)
1566 {
1567 auto defaultDarkEditorIndex = schemes.indexOf ("Default (Dark)");
1568 auto defaultLightEditorIndex = schemes.indexOf ("Default (Light)");
1569
1570 // Can't find default code editor colour schemes!
1571 jassert (defaultDarkEditorIndex != -1 && defaultLightEditorIndex != -1);
1572
1573 return (guiColourSchemeIndex == 2 ? defaultLightEditorIndex : defaultDarkEditorIndex);
1574 }
1575
updateEditorColourSchemeIfNeeded()1576 void ProjucerApplication::updateEditorColourSchemeIfNeeded()
1577 {
1578 auto& appearanceSettings = getAppSettings().appearance;
1579 auto schemes = appearanceSettings.getPresetSchemes();
1580
1581 if (isEditorColourSchemeADefaultScheme (schemes, selectedEditorColourSchemeIndex))
1582 setEditorColourScheme (getEditorColourSchemeForGUIColourScheme (schemes, selectedColourSchemeIndex), true);
1583 }
1584