| @@ -29,7 +29,6 @@ | |||
| #include "../jucer_Headers.h" | |||
| #include "jucer_MainWindow.h" | |||
| #include "jucer_JuceUpdater.h" | |||
| #include "../Project/jucer_NewProjectWizard.h" | |||
| #include "jucer_CommandLine.h" | |||
| @@ -38,10 +37,7 @@ class JucerApplication : public JUCEApplication | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| JucerApplication() | |||
| { | |||
| } | |||
| JucerApplication() {} | |||
| ~JucerApplication() {} | |||
| //============================================================================== | |||
| @@ -80,6 +76,8 @@ public: | |||
| openFile (projects.getReference(i)); | |||
| } | |||
| makeSureUserHasSelectedModuleFolder(); | |||
| if (mainWindows.size() == 0) | |||
| createNewMainWindow()->makeVisible(); | |||
| @@ -127,10 +125,10 @@ public: | |||
| jassert (mainWindows.contains (w)); | |||
| mainWindows.removeObject (w); | |||
| #if ! JUCE_MAC | |||
| #if ! JUCE_MAC | |||
| if (mainWindows.size() == 0) | |||
| systemRequestedQuit(); | |||
| #endif | |||
| #endif | |||
| updateRecentProjectList(); | |||
| } | |||
| @@ -162,6 +160,11 @@ public: | |||
| virtual void doExtraInitialisation() {} | |||
| static JucerApplication* getApp() | |||
| { | |||
| return dynamic_cast<JucerApplication*> (JUCEApplication::getInstance()); | |||
| } | |||
| //============================================================================== | |||
| class MainMenuModel : public MenuBarModel | |||
| { | |||
| @@ -173,7 +176,7 @@ public: | |||
| const StringArray getMenuBarNames() | |||
| { | |||
| const char* const names[] = { "File", "Edit", "View", "Window", "Update", 0 }; | |||
| const char* const names[] = { "File", "Edit", "View", "Window", "Tools", 0 }; | |||
| return StringArray ((const char**) names); | |||
| } | |||
| @@ -264,9 +267,10 @@ public: | |||
| menu.addSeparator(); | |||
| menu.addCommandItem (commandManager, CommandIDs::closeAllDocuments); | |||
| } | |||
| else if (topLevelMenuIndex == 4) // "Juce" menu | |||
| else if (topLevelMenuIndex == 4) // "Tools" menu | |||
| { | |||
| menu.addCommandItem (commandManager, CommandIDs::showJuceVersion); | |||
| menu.addCommandItem (commandManager, CommandIDs::updateModules); | |||
| menu.addCommandItem (commandManager, CommandIDs::showUTF8Tool); | |||
| } | |||
| return menu; | |||
| @@ -288,14 +292,9 @@ public: | |||
| MainWindow* w = getApp()->getOrCreateFrontmostWindow(); | |||
| w->makeVisible(); | |||
| w->getProjectContentComponent()->showDocument (doc); | |||
| getApp()->avoidSuperimposedWindows (w); | |||
| } | |||
| } | |||
| private: | |||
| JucerApplication* getApp() const | |||
| { | |||
| return static_cast<JucerApplication*> (JUCEApplication::getInstance()); | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| @@ -308,7 +307,8 @@ public: | |||
| CommandIDs::showPrefs, | |||
| CommandIDs::closeAllDocuments, | |||
| CommandIDs::saveAll, | |||
| CommandIDs::showJuceVersion }; | |||
| CommandIDs::updateModules, | |||
| CommandIDs::showUTF8Tool }; | |||
| commands.addArray (ids, numElementsInArray (ids)); | |||
| } | |||
| @@ -342,8 +342,12 @@ public: | |||
| result.setActive (OpenDocumentManager::getInstance()->anyFilesNeedSaving()); | |||
| break; | |||
| case CommandIDs::showJuceVersion: | |||
| result.setInfo ("Download the latest JUCE version", "Checks online for any Juce updates", CommandCategories::general, 0); | |||
| case CommandIDs::updateModules: | |||
| result.setInfo ("Download the latest JUCE modules", "Checks online for any JUCE modules updates and installs them", CommandCategories::general, 0); | |||
| break; | |||
| case CommandIDs::showUTF8Tool: | |||
| result.setInfo ("UTF-8 String-Literal Helper", "Shows the UTF-8 string literal utility", CommandCategories::general, 0); | |||
| break; | |||
| default: | |||
| @@ -361,12 +365,9 @@ public: | |||
| case CommandIDs::showPrefs: showPrefsPanel(); break; | |||
| case CommandIDs::saveAll: OpenDocumentManager::getInstance()->saveAll(); break; | |||
| case CommandIDs::closeAllDocuments: closeAllDocuments (true); break; | |||
| case CommandIDs::showJuceVersion: | |||
| { | |||
| ModuleList list (ModuleList::getDefaultModulesFolder (nullptr)); | |||
| JuceUpdater::show (list, mainWindows[0]); | |||
| break; | |||
| } | |||
| case CommandIDs::showUTF8Tool: showUTF8ToolWindow(); break; | |||
| case CommandIDs::updateModules: runModuleUpdate (String::empty); break; | |||
| default: return JUCEApplication::perform (info); | |||
| } | |||
| @@ -381,17 +382,11 @@ public: | |||
| void createNewProject() | |||
| { | |||
| MainWindow* mw = createNewMainWindow(); | |||
| ScopedPointer <Project> newProj (NewProjectWizard::runNewProjectWizard (mw)); | |||
| if (newProj != nullptr) | |||
| { | |||
| mw->setProject (newProj.release()); | |||
| mw->makeVisible(); | |||
| } | |||
| else | |||
| if (makeSureUserHasSelectedModuleFolder()) | |||
| { | |||
| closeWindow (mw); | |||
| MainWindow* mw = getOrCreateEmptyWindow(); | |||
| mw->showNewProjectWizard(); | |||
| avoidSuperimposedWindows (mw); | |||
| } | |||
| } | |||
| @@ -424,6 +419,7 @@ public: | |||
| MainWindow* w = getOrCreateEmptyWindow(); | |||
| w->setProject (newDoc.release()); | |||
| w->makeVisible(); | |||
| avoidSuperimposedWindows (w); | |||
| return true; | |||
| } | |||
| } | |||
| @@ -433,6 +429,7 @@ public: | |||
| const bool ok = w->openFile (file); | |||
| w->makeVisible(); | |||
| avoidSuperimposedWindows (w); | |||
| return ok; | |||
| } | |||
| @@ -470,6 +467,35 @@ public: | |||
| StoredSettings::getInstance()->setLastProjects (projects); | |||
| } | |||
| bool makeSureUserHasSelectedModuleFolder() | |||
| { | |||
| if (! ModuleList::isLocalModulesFolderValid()) | |||
| { | |||
| if (! runModuleUpdate ("Please select a location to store your local set of JUCE modules,\n" | |||
| "and download the ones that you'd like to use!")) | |||
| { | |||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, | |||
| "Introjucer", | |||
| "Unless you create a local JUCE folder containing some modules, you'll be unable to save any projects correctly!\n\n" | |||
| "Use the option on the 'Tools' menu to set this up!"); | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| bool runModuleUpdate (const String& message) | |||
| { | |||
| ModuleList list; | |||
| list.rescan (ModuleList::getDefaultModulesFolder (nullptr)); | |||
| JuceUpdater::show (list, mainWindows[0], message); | |||
| ModuleList::setLocalModulesFolder (list.getModulesFolder()); | |||
| return ModuleList::isJuceOrModulesFolder (list.getModulesFolder()); | |||
| } | |||
| ScopedPointer<MainMenuModel> menuModel; | |||
| private: | |||
| @@ -478,13 +504,9 @@ private: | |||
| MainWindow* createNewMainWindow() | |||
| { | |||
| MainWindow* mw = new MainWindow(); | |||
| for (int i = mainWindows.size(); --i >= 0;) | |||
| if (mw->getBounds() == mainWindows.getUnchecked(i)->getBounds()) | |||
| mw->setBounds (mw->getBounds().translated (20, 20)); | |||
| mainWindows.add (mw); | |||
| mw->restoreWindowPosition(); | |||
| avoidSuperimposedWindows (mw); | |||
| return mw; | |||
| } | |||
| @@ -518,6 +540,31 @@ private: | |||
| return createNewMainWindow(); | |||
| } | |||
| void avoidSuperimposedWindows (MainWindow* const mw) | |||
| { | |||
| for (int i = mainWindows.size(); --i >= 0;) | |||
| { | |||
| MainWindow* const other = mainWindows.getUnchecked(i); | |||
| const Rectangle<int> b1 (mw->getBounds()); | |||
| const Rectangle<int> b2 (other->getBounds()); | |||
| if (mw != other | |||
| && std::abs (b1.getX() - b2.getX()) < 3 | |||
| && std::abs (b1.getY() - b2.getY()) < 3 | |||
| && std::abs (b1.getRight() - b2.getRight()) < 3 | |||
| && std::abs (b1.getBottom() - b2.getBottom()) < 3) | |||
| { | |||
| int dx = 40, dy = 30; | |||
| if (b1.getCentreX() >= mw->getScreenBounds().getCentreX()) dx = -dx; | |||
| if (b1.getCentreY() >= mw->getScreenBounds().getCentreY()) dy = -dy; | |||
| mw->setBounds (b1.translated (dx, dy)); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| static bool cancelAnyModalComponents() | |||
| { | |||
| @@ -540,8 +587,8 @@ private: | |||
| stopTimer(); | |||
| delete this; | |||
| if (JUCEApplication::getInstance() != nullptr) | |||
| JUCEApplication::getInstance()->systemRequestedQuit(); | |||
| if (getApp() != nullptr) | |||
| getApp()->systemRequestedQuit(); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE (AsyncQuitRetrier); | |||
| @@ -40,7 +40,8 @@ namespace CommandIDs | |||
| static const int openInIDE = 0x200072; | |||
| static const int saveAndOpenInIDE = 0x200073; | |||
| static const int showProjectSettings = 0x200074; | |||
| static const int showJuceVersion = 0x200075; | |||
| static const int updateModules = 0x200075; | |||
| static const int showUTF8Tool = 0x200076; | |||
| static const int saveAll = 0x200080; | |||
| static const int undo = 0x200090; | |||
| @@ -192,7 +192,7 @@ namespace | |||
| int listModules() | |||
| { | |||
| std::cout << "Downloading list of available modules..." << std::endl; | |||
| ModuleList list (File::nonexistent); | |||
| ModuleList list; | |||
| list.loadFromWebsite(); | |||
| for (int i = 0; i < list.modules.size(); ++i) | |||
| @@ -29,9 +29,9 @@ | |||
| //============================================================================== | |||
| JuceUpdater::JuceUpdater (ModuleList& moduleList_) | |||
| JuceUpdater::JuceUpdater (ModuleList& moduleList_, const String& message) | |||
| : moduleList (moduleList_), | |||
| latestList (File::nonexistent), | |||
| messageLabel (String::empty, message), | |||
| filenameComp ("Juce Folder", ModuleList::getLocalModulesFolder (nullptr), | |||
| true, true, false, "*", String::empty, "Select your Juce folder"), | |||
| checkNowButton ("Check for available updates on the JUCE website...", | |||
| @@ -39,6 +39,9 @@ JuceUpdater::JuceUpdater (ModuleList& moduleList_) | |||
| installButton ("Download and install selected modules..."), | |||
| selectAllButton ("Select/Deselect All") | |||
| { | |||
| messageLabel.setJustificationType (Justification::centred); | |||
| addAndMakeVisible (&messageLabel); | |||
| addAndMakeVisible (&label); | |||
| addAndMakeVisible (¤tVersionLabel); | |||
| addAndMakeVisible (&filenameComp); | |||
| @@ -80,6 +83,7 @@ public: | |||
| : DialogWindow ("JUCE Module Updater", | |||
| Colours::lightgrey, true, true) | |||
| { | |||
| setUsingNativeTitleBar (true); | |||
| setContentOwned (updater, true); | |||
| centreAroundComponent (componentToCentreAround, getWidth(), getHeight()); | |||
| setResizable (true, true); | |||
| @@ -94,15 +98,17 @@ private: | |||
| JUCE_DECLARE_NON_COPYABLE (UpdateDialogWindow); | |||
| }; | |||
| void JuceUpdater::show (ModuleList& moduleList, Component* mainWindow) | |||
| void JuceUpdater::show (ModuleList& moduleList, Component* mainWindow, const String& message) | |||
| { | |||
| UpdateDialogWindow w (new JuceUpdater (moduleList), mainWindow); | |||
| UpdateDialogWindow w (new JuceUpdater (moduleList, message), mainWindow); | |||
| w.runModalLoop(); | |||
| } | |||
| void JuceUpdater::resized() | |||
| { | |||
| filenameComp.setBounds (20, 40, getWidth() - 40, 22); | |||
| messageLabel.setBounds (20, 10, getWidth() - 40, messageLabel.getText().isEmpty() ? 0 : 30); | |||
| filenameComp.setBounds (20, messageLabel.getBottom() + 20, getWidth() - 40, 22); | |||
| label.setBounds (filenameComp.getX(), filenameComp.getY() - 18, filenameComp.getWidth(), 18); | |||
| currentVersionLabel.setBounds (filenameComp.getX(), filenameComp.getBottom(), filenameComp.getWidth(), 25); | |||
| checkNowButton.changeWidthToFitText (22); | |||
| @@ -162,7 +168,7 @@ public: | |||
| else | |||
| AlertWindow::showMessageBox (AlertWindow::InfoIcon, | |||
| "Module Update", | |||
| "Couldn't connect to the website!"); | |||
| "Couldn't connect to the JUCE webserver!"); | |||
| } | |||
| void handleAsyncUpdate() | |||
| @@ -224,7 +230,7 @@ void JuceUpdater::filenameComponentChanged (FilenameComponent*) | |||
| moduleList.rescan (filenameComp.getCurrentFile()); | |||
| filenameComp.setCurrentFile (moduleList.getModulesFolder(), true, false); | |||
| if (! FileHelpers::isModulesFolder (moduleList.getModulesFolder())) | |||
| if (! ModuleList::isModulesFolder (moduleList.getModulesFolder())) | |||
| currentVersionLabel.setText ("(Not a Juce folder)", false); | |||
| else | |||
| currentVersionLabel.setText (String::empty, false); | |||
| @@ -296,7 +302,7 @@ Component* JuceUpdater::refreshComponentForRow (int rowNumber, bool isRowSelecte | |||
| } | |||
| else | |||
| { | |||
| status << " (latest version already installed)"; | |||
| status << " (latest version already installed: " << existingModule->version << ")"; | |||
| toggle.setEnabled (false); | |||
| } | |||
| } | |||
| @@ -37,10 +37,10 @@ class JuceUpdater : public Component, | |||
| private ValueTree::Listener | |||
| { | |||
| public: | |||
| JuceUpdater (ModuleList& moduleList); | |||
| JuceUpdater (ModuleList& moduleList, const String& message); | |||
| ~JuceUpdater(); | |||
| static void show (ModuleList& moduleList, Component* mainWindow); | |||
| static void show (ModuleList& moduleList, Component* mainWindow, const String& message); | |||
| //============================================================================== | |||
| void resized(); | |||
| @@ -58,7 +58,7 @@ private: | |||
| ModuleList& moduleList; | |||
| ModuleList latestList; | |||
| Label label, currentVersionLabel; | |||
| Label messageLabel, label, currentVersionLabel; | |||
| FilenameComponent filenameComp; | |||
| TextButton checkNowButton; | |||
| ListBox availableVersionsList; | |||
| @@ -35,43 +35,42 @@ ScopedPointer<ApplicationCommandManager> commandManager; | |||
| //============================================================================== | |||
| MainWindow::MainWindow() | |||
| : DocumentWindow (JUCEApplication::getInstance()->getApplicationName(), | |||
| : DocumentWindow (JucerApplication::getApp()->getApplicationName(), | |||
| Colour::greyLevel (0.6f), | |||
| DocumentWindow::allButtons, | |||
| false) | |||
| { | |||
| setUsingNativeTitleBar (true); | |||
| setContentOwned (new ProjectContentComponent(), false); | |||
| createProjectContentCompIfNeeded(); | |||
| #if ! JUCE_MAC | |||
| JucerApplication* app = static_cast<JucerApplication*> (JUCEApplication::getInstance()); | |||
| setMenuBar (app->menuModel); | |||
| setMenuBar (JucerApplication::getApp()->menuModel); | |||
| #endif | |||
| setResizable (true, false); | |||
| centreWithSize (700, 600); | |||
| centreWithSize (800, 600); | |||
| // Register all the app commands.. | |||
| { | |||
| commandManager->registerAllCommandsForTarget (this); | |||
| commandManager->registerAllCommandsForTarget (getProjectContentComponent()); | |||
| // use some temporary objects to harvest their commands.. | |||
| DocumentEditorComponent dec (nullptr); | |||
| commandManager->registerAllCommandsForTarget (&dec); | |||
| ProjectContentComponent pcc; | |||
| commandManager->registerAllCommandsForTarget (&pcc); | |||
| } | |||
| commandManager->getKeyMappings()->resetToDefaultMappings(); | |||
| // update key mappings.. | |||
| { | |||
| commandManager->getKeyMappings()->resetToDefaultMappings(); | |||
| ScopedPointer <XmlElement> keys (StoredSettings::getInstance()->getProps().getXmlValue ("keyMappings")); | |||
| ScopedPointer <XmlElement> keys (StoredSettings::getInstance()->getProps().getXmlValue ("keyMappings")); | |||
| if (keys != nullptr) | |||
| commandManager->getKeyMappings()->restoreFromXml (*keys); | |||
| if (keys != nullptr) | |||
| commandManager->getKeyMappings()->restoreFromXml (*keys); | |||
| addKeyListener (commandManager->getKeyMappings()); | |||
| addKeyListener (commandManager->getKeyMappings()); | |||
| } | |||
| // don't want the window to take focus when the title-bar is clicked.. | |||
| setWantsKeyboardFocus (false); | |||
| @@ -96,6 +95,15 @@ MainWindow::~MainWindow() | |||
| currentProject = nullptr; | |||
| } | |||
| void MainWindow::createProjectContentCompIfNeeded() | |||
| { | |||
| if (getProjectContentComponent() == nullptr) | |||
| { | |||
| clearContentComponent(); | |||
| setContentOwned (new ProjectContentComponent(), false); | |||
| } | |||
| } | |||
| void MainWindow::makeVisible() | |||
| { | |||
| setVisible (true); | |||
| @@ -115,8 +123,7 @@ void MainWindow::closeButtonPressed() | |||
| if (! closeCurrentProject()) | |||
| return; | |||
| JucerApplication* jucer = static_cast<JucerApplication*> (JUCEApplication::getInstance()); | |||
| jucer->closeWindow (this); | |||
| JucerApplication::getApp()->closeWindow (this); | |||
| } | |||
| bool MainWindow::closeProject (Project* project) | |||
| @@ -150,6 +157,7 @@ bool MainWindow::closeCurrentProject() | |||
| void MainWindow::setProject (Project* newProject) | |||
| { | |||
| createProjectContentCompIfNeeded(); | |||
| getProjectContentComponent()->setProject (newProject); | |||
| currentProject = newProject; | |||
| commandManager->commandStatusChanged(); | |||
| @@ -157,7 +165,7 @@ void MainWindow::setProject (Project* newProject) | |||
| // (mustn't do this when the project is 0, because that'll happen on shutdown, | |||
| // which will erase the list of recent projects) | |||
| if (newProject != nullptr) | |||
| static_cast<JucerApplication*> (JUCEApplication::getInstance())->updateRecentProjectList(); | |||
| JucerApplication::getApp()->updateRecentProjectList(); | |||
| } | |||
| void MainWindow::restoreWindowPosition() | |||
| @@ -181,6 +189,8 @@ bool MainWindow::canOpenFile (const File& file) const | |||
| bool MainWindow::openFile (const File& file) | |||
| { | |||
| createProjectContentCompIfNeeded(); | |||
| if (file.hasFileExtension (Project::projectFileExtension)) | |||
| { | |||
| ScopedPointer <Project> newDoc (new Project (file)); | |||
| @@ -232,7 +242,7 @@ void MainWindow::activeWindowStatusChanged() | |||
| void MainWindow::updateTitle (const String& documentName) | |||
| { | |||
| String name (JUCEApplication::getInstance()->getApplicationName()); | |||
| String name (JucerApplication::getApp()->getApplicationName()); | |||
| if (currentProject != nullptr) | |||
| name = currentProject->getDocumentTitle() + " - " + name; | |||
| @@ -243,6 +253,12 @@ void MainWindow::updateTitle (const String& documentName) | |||
| setName (name); | |||
| } | |||
| void MainWindow::showNewProjectWizard() | |||
| { | |||
| jassert (currentProject == nullptr); | |||
| setContentOwned (NewProjectWizard::createComponent(), true); | |||
| makeVisible(); | |||
| } | |||
| //============================================================================== | |||
| ApplicationCommandTarget* MainWindow::getNextCommandTarget() | |||
| @@ -57,6 +57,8 @@ public: | |||
| bool closeProject (Project* project); | |||
| bool closeCurrentProject(); | |||
| void showNewProjectWizard(); | |||
| bool isInterestedInFileDrag (const StringArray& files); | |||
| void filesDropped (const StringArray& filenames, int mouseX, int mouseY); | |||
| @@ -84,6 +86,8 @@ private: | |||
| return "projectWindowPos_" + currentProject->getProjectUID(); | |||
| } | |||
| void createProjectContentCompIfNeeded(); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainWindow); | |||
| }; | |||
| @@ -209,7 +209,8 @@ void ProjectExporter::createPropertyEditors (Array <PropertyComponent*>& props) | |||
| props.getLast()->setTooltip ("The location of the Juce library folder that the " + name + " project will use to when compiling. This can be an absolute path, or relative to the jucer project folder, but it must be valid on the filesystem of the machine you use to actually do the compiling."); | |||
| OwnedArray<LibraryModule> modules; | |||
| ModuleList moduleList (ModuleList::getDefaultModulesFolder (&project)); | |||
| ModuleList moduleList; | |||
| moduleList.rescan (ModuleList::getDefaultModulesFolder (&project)); | |||
| project.createRequiredModules (moduleList, modules); | |||
| for (int i = 0; i < modules.size(); ++i) | |||
| modules.getUnchecked(i)->createPropertyEditors (*this, props); | |||
| @@ -61,7 +61,8 @@ public: | |||
| OwnedArray<LibraryModule> modules; | |||
| { | |||
| ModuleList moduleList (ModuleList::getDefaultModulesFolder (&project)); | |||
| ModuleList moduleList; | |||
| moduleList.rescan (ModuleList::getDefaultModulesFolder (&project)); | |||
| project.createRequiredModules (moduleList, modules); | |||
| } | |||
| @@ -31,9 +31,8 @@ | |||
| //============================================================================== | |||
| ModuleList::ModuleList (const File& modulesFolder_) | |||
| ModuleList::ModuleList() | |||
| { | |||
| rescan (modulesFolder_); | |||
| } | |||
| ModuleList::ModuleList (const ModuleList& other) | |||
| @@ -68,6 +67,29 @@ bool ModuleList::operator== (const ModuleList& other) const | |||
| return true; | |||
| } | |||
| bool ModuleList::isLocalModulesFolderValid() | |||
| { | |||
| return isModulesFolder (getModulesFolderForJuceOrModulesFolder (getLocalModulesFolder (nullptr))); | |||
| } | |||
| bool ModuleList::isJuceFolder (const File& folder) | |||
| { | |||
| return folder.getFileName().containsIgnoreCase ("juce") | |||
| && isModulesFolder (folder.getChildFile ("modules")); | |||
| } | |||
| bool ModuleList::isModulesFolder (const File& folder) | |||
| { | |||
| return folder.getFileName().equalsIgnoreCase ("modules") | |||
| && folder.isDirectory(); | |||
| } | |||
| bool ModuleList::isJuceOrModulesFolder (const File& folder) | |||
| { | |||
| return isJuceFolder (folder) || isModulesFolder (folder); | |||
| } | |||
| File ModuleList::getModulesFolderForJuceOrModulesFolder (const File& f) | |||
| { | |||
| if (f.getFileName() != "modules" && f.isDirectory() && f.getChildFile ("modules").isDirectory()) | |||
| @@ -87,12 +109,16 @@ File ModuleList::getDefaultModulesFolder (Project* project) | |||
| File f (project->resolveFilename (exp->getJuceFolder().toString())); | |||
| f = getModulesFolderForJuceOrModulesFolder (f); | |||
| if (FileHelpers::isModulesFolder (f)) | |||
| if (ModuleList::isModulesFolder (f)) | |||
| return f; | |||
| } | |||
| } | |||
| #if JUCE_WINDOWS | |||
| return File::getSpecialLocation (File::userDocumentsDirectory) | |||
| #else | |||
| return File::getSpecialLocation (File::userHomeDirectory) | |||
| #endif | |||
| .getChildFile ("juce") | |||
| .getChildFile ("modules"); | |||
| } | |||
| @@ -104,7 +130,7 @@ File ModuleList::getLocalModulesFolder (Project* project) | |||
| File f (StoredSettings::getInstance()->getProps().getValue ("lastJuceFolder", defaultJuceFolder.getFullPathName())); | |||
| f = getModulesFolderForJuceOrModulesFolder (f); | |||
| if ((! FileHelpers::isModulesFolder (f)) && FileHelpers::isModulesFolder (defaultJuceFolder)) | |||
| if ((! ModuleList::isModulesFolder (f)) && ModuleList::isModulesFolder (defaultJuceFolder)) | |||
| f = defaultJuceFolder; | |||
| return f; | |||
| @@ -263,17 +289,20 @@ void ModuleList::getDependencies (const String& moduleID, StringArray& dependenc | |||
| const var depsArray (m->moduleInfo ["dependencies"]); | |||
| const Array<var>* const deps = depsArray.getArray(); | |||
| for (int i = 0; i < deps->size(); ++i) | |||
| if (deps != nullptr) | |||
| { | |||
| const var& d = deps->getReference(i); | |||
| for (int i = 0; i < deps->size(); ++i) | |||
| { | |||
| const var& d = deps->getReference(i); | |||
| String uid (d ["id"].toString()); | |||
| String version (d ["version"].toString()); | |||
| String uid (d ["id"].toString()); | |||
| String version (d ["version"].toString()); | |||
| if (! dependencies.contains (uid, true)) | |||
| { | |||
| dependencies.add (uid); | |||
| getDependencies (uid, dependencies); | |||
| if (! dependencies.contains (uid, true)) | |||
| { | |||
| dependencies.add (uid); | |||
| getDependencies (uid, dependencies); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -302,6 +331,18 @@ void ModuleList::createDependencies (const String& moduleID, OwnedArray<LibraryM | |||
| } | |||
| } | |||
| StringArray ModuleList::getExtraDependenciesNeeded (Project& project, const ModuleList::Module& m) | |||
| { | |||
| StringArray dependencies, extraDepsNeeded; | |||
| getDependencies (m.uid, dependencies); | |||
| for (int i = 0; i < dependencies.size(); ++i) | |||
| if ((! project.isModuleEnabled (dependencies[i])) && dependencies[i] != m.uid) | |||
| extraDepsNeeded.add (dependencies[i]); | |||
| return extraDepsNeeded; | |||
| } | |||
| //============================================================================== | |||
| LibraryModule::LibraryModule (const File& file) | |||
| : moduleInfo (JSON::parse (file)), | |||
| @@ -334,9 +375,11 @@ File LibraryModule::getInclude (const File& folder) const | |||
| RelativePath LibraryModule::getModuleRelativeToProject (ProjectExporter& exporter) const | |||
| { | |||
| return RelativePath (exporter.getJuceFolder().toString(), RelativePath::projectFolder) | |||
| .getChildFile ("modules") | |||
| .getChildFile (getID()); | |||
| RelativePath p (exporter.getJuceFolder().toString(), RelativePath::projectFolder); | |||
| if (p.getFileName() != "modules") | |||
| p = p.getChildFile ("modules"); | |||
| return p.getChildFile (getID()); | |||
| } | |||
| //============================================================================== | |||
| @@ -86,7 +86,7 @@ private: | |||
| class ModuleList | |||
| { | |||
| public: | |||
| ModuleList (const File& modulesFolder); | |||
| ModuleList(); | |||
| ModuleList (const ModuleList&); | |||
| ModuleList& operator= (const ModuleList&); | |||
| @@ -121,13 +121,20 @@ public: | |||
| bool operator== (const ModuleList&) const; | |||
| //============================================================================== | |||
| static bool isJuceFolder (const File& folder); | |||
| static bool isModulesFolder (const File& folder); | |||
| static bool isJuceOrModulesFolder (const File& folder); | |||
| static File getDefaultModulesFolder (Project* project); | |||
| static bool isLocalModulesFolderValid(); | |||
| static File getLocalModulesFolder (Project* project); | |||
| static void setLocalModulesFolder (const File& newFile); | |||
| static File getModulesFolderForJuceOrModulesFolder (const File& f); | |||
| StringArray getExtraDependenciesNeeded (Project& project, const Module& m); | |||
| //============================================================================== | |||
| OwnedArray<Module> modules; | |||
| @@ -26,6 +26,31 @@ | |||
| #include "jucer_NewProjectWizard.h" | |||
| #include "jucer_ProjectType.h" | |||
| #include "jucer_Module.h" | |||
| #include "../Application/jucer_Application.h" | |||
| #include "../Application/jucer_MainWindow.h" | |||
| static void createFileCreationOptionComboBox (Component& setupComp, | |||
| OwnedArray<Component>& itemsCreated, | |||
| const char** types) | |||
| { | |||
| ComboBox* c = new ComboBox(); | |||
| c->setComponentID ("filesToCreate"); | |||
| itemsCreated.add (c); | |||
| setupComp.addAndMakeVisible (c); | |||
| const char* fileOptions[] = { "Create a Main.cpp file", | |||
| "Create a Main.cpp file and a basic window", | |||
| "Don't create any files", 0 }; | |||
| c->addItemList (StringArray (fileOptions), 1); | |||
| c->setSelectedId (1, false); | |||
| Label* l = new Label (String::empty, "Files to Auto-Generate:"); | |||
| l->attachToComponent (c, true); | |||
| itemsCreated.add (l); | |||
| c->setBounds ("parent.width / 2 + 160, 10, parent.width - 10, top + 22"); | |||
| } | |||
| //============================================================================== | |||
| class GUIAppWizard : public NewProjectWizard | |||
| @@ -36,20 +61,22 @@ public: | |||
| String getName() { return "GUI Application"; } | |||
| String getDescription() { return "Creates a standard application"; } | |||
| void addItemsToAlertWindow (AlertWindow& aw) | |||
| void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated) | |||
| { | |||
| const char* fileOptions[] = { "Create a Main.cpp file", | |||
| "Create a Main.cpp file and a basic window", | |||
| "Don't create any files", 0 }; | |||
| aw.addComboBox ("files", StringArray (fileOptions), "Files to Auto-Generate"); | |||
| createFileCreationOptionComboBox (setupComp, itemsCreated, fileOptions); | |||
| } | |||
| String processResultsFromAlertWindow (AlertWindow& aw) | |||
| Result processResultsFromSetupItems (Component& setupComp) | |||
| { | |||
| ComboBox* cb = dynamic_cast<ComboBox*> (setupComp.findChildWithID ("filesToCreate")); | |||
| jassert (cb != nullptr); | |||
| createMainCpp = createWindow = false; | |||
| switch (aw.getComboBoxComponent ("files")->getSelectedItemIndex()) | |||
| switch (cb->getSelectedItemIndex()) | |||
| { | |||
| case 0: createMainCpp = true; break; | |||
| case 1: createMainCpp = createWindow = true; break; | |||
| @@ -57,7 +84,7 @@ public: | |||
| default: jassertfalse; break; | |||
| } | |||
| return String::empty; | |||
| return Result::ok(); | |||
| } | |||
| bool initialiseProject (Project& project) | |||
| @@ -142,26 +169,29 @@ public: | |||
| String getName() { return "Console Application"; } | |||
| String getDescription() { return "Creates a command-line application with no GUI features"; } | |||
| void addItemsToAlertWindow (AlertWindow& aw) | |||
| void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated) | |||
| { | |||
| const char* fileOptions[] = { "Create a Main.cpp file", | |||
| "Don't create any files", 0 }; | |||
| aw.addComboBox ("files", StringArray (fileOptions), "Files to Auto-Generate"); | |||
| createFileCreationOptionComboBox (setupComp, itemsCreated, fileOptions); | |||
| } | |||
| String processResultsFromAlertWindow (AlertWindow& aw) | |||
| Result processResultsFromSetupItems (Component& setupComp) | |||
| { | |||
| ComboBox* cb = dynamic_cast<ComboBox*> (setupComp.findChildWithID ("filesToCreate")); | |||
| jassert (cb != nullptr); | |||
| createMainCpp = false; | |||
| switch (aw.getComboBoxComponent ("files")->getSelectedItemIndex()) | |||
| switch (cb->getSelectedItemIndex()) | |||
| { | |||
| case 0: createMainCpp = true; break; | |||
| case 1: break; | |||
| default: jassertfalse; break; | |||
| } | |||
| return String::empty; | |||
| return Result::ok(); | |||
| } | |||
| bool initialiseProject (Project& project) | |||
| @@ -207,13 +237,13 @@ public: | |||
| String getName() { return "Audio Plug-In"; } | |||
| String getDescription() { return "Creates an audio plugin project"; } | |||
| void addItemsToAlertWindow (AlertWindow& aw) | |||
| void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated) | |||
| { | |||
| } | |||
| String processResultsFromAlertWindow (AlertWindow& aw) | |||
| Result processResultsFromSetupItems (Component& setupComp) | |||
| { | |||
| return String::empty; | |||
| return Result::ok(); | |||
| } | |||
| bool initialiseProject (Project& project) | |||
| @@ -231,7 +261,7 @@ public: | |||
| File editorHFile = editorCppFile.withFileExtension (".h"); | |||
| project.getProjectTypeValue() = ProjectType::getAudioPluginTypeName(); | |||
| project.addModule ("juce_audio_plugin_client"); | |||
| project.addModule ("juce_audio_plugin_client", true); | |||
| Project::Item sourceGroup (project.getMainGroup().addNewSubGroup ("Source", 0)); | |||
| project.getConfigFlag ("JUCE_QUICKTIME") = Project::configFlagDisabled; // disabled because it interferes with RTAS build on PC | |||
| @@ -285,31 +315,6 @@ public: | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /*class BrowserPluginAppWizard : public NewProjectWizard | |||
| { | |||
| public: | |||
| BrowserPluginAppWizard() {} | |||
| ~BrowserPluginAppWizard() {} | |||
| String getName() { return "Browser Plug-In"; } | |||
| String getDescription() { return "Creates an audio plugin project"; } | |||
| void addItemsToAlertWindow (AlertWindow& aw) | |||
| { | |||
| } | |||
| String processResultsFromAlertWindow (AlertWindow& aw) | |||
| { | |||
| return String::empty; | |||
| } | |||
| bool initialiseProject (Project& project) | |||
| { | |||
| return true; | |||
| } | |||
| };*/ | |||
| //============================================================================== | |||
| //============================================================================== | |||
| NewProjectWizard::NewProjectWizard() {} | |||
| @@ -347,71 +352,42 @@ NewProjectWizard* NewProjectWizard::createWizard (int index) | |||
| return 0; | |||
| } | |||
| File& NewProjectWizard::getLastWizardFolder() | |||
| { | |||
| #if JUCE_WINDOWS | |||
| static File lastFolder (File::getSpecialLocation (File::userDocumentsDirectory)); | |||
| #else | |||
| static File lastFolder (File::getSpecialLocation (File::userHomeDirectory)); | |||
| #endif | |||
| return lastFolder; | |||
| } | |||
| //============================================================================== | |||
| Project* NewProjectWizard::runWizard (Component* ownerWindow_) | |||
| Project* NewProjectWizard::runWizard (Component* ownerWindow_, | |||
| const String& projectName, | |||
| const File& targetFolder_) | |||
| { | |||
| ownerWindow = ownerWindow_; | |||
| appTitle = projectName; | |||
| targetFolder = targetFolder_; | |||
| if (! targetFolder.exists()) | |||
| { | |||
| static File newProjectFolder; | |||
| FileChooser fc ("New Juce Project", newProjectFolder, "*"); | |||
| if (! fc.browseForDirectory()) | |||
| return 0; | |||
| targetFolder = newProjectFolder = fc.getResult(); | |||
| if (! newProjectFolder.exists()) | |||
| { | |||
| if (! newProjectFolder.createDirectory()) | |||
| failedFiles.add (newProjectFolder.getFullPathName()); | |||
| } | |||
| if (FileHelpers::containsAnyNonHiddenFiles (newProjectFolder)) | |||
| { | |||
| if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, "New Juce Project", | |||
| "The folder you chose isn't empty - are you sure you want to create the project there?\n\nAny existing files with the same names may be overwritten by the new files.")) | |||
| return 0; | |||
| } | |||
| if (! targetFolder.createDirectory()) | |||
| failedFiles.add (targetFolder.getFullPathName()); | |||
| } | |||
| if (failedFiles.size() == 0) | |||
| else if (FileHelpers::containsAnyNonHiddenFiles (targetFolder)) | |||
| { | |||
| AlertWindow aw ("New " + getName(), | |||
| "Please choose some basic project options...", | |||
| AlertWindow::NoIcon, ownerWindow); | |||
| aw.addTextEditor ("name", "", "Project Name", false); | |||
| addItemsToAlertWindow (aw); | |||
| aw.addButton ("Create Project", 1, KeyPress (KeyPress::returnKey)); | |||
| aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey)); | |||
| for (;;) | |||
| { | |||
| if (aw.runModalLoop() == 0) | |||
| return 0; | |||
| appTitle = aw.getTextEditorContents ("name").trim(); | |||
| String error (processResultsFromAlertWindow (aw)); | |||
| if (error.isEmpty() && appTitle.isEmpty()) | |||
| error = "Please enter a sensible project title!"; | |||
| if (error.isEmpty()) | |||
| break; | |||
| aw.setColour (AlertWindow::textColourId, Colours::red); | |||
| aw.setMessage (error); | |||
| } | |||
| if (! AlertWindow::showOkCancelBox (AlertWindow::InfoIcon, "New Juce Project", | |||
| "The folder you chose isn't empty - are you sure you want to create the project there?\n\nAny existing files with the same names may be overwritten by the new files.")) | |||
| return nullptr; | |||
| } | |||
| projectFile = targetFolder.getChildFile (File::createLegalFileName (appTitle)) | |||
| .withFileExtension (Project::projectFileExtension); | |||
| ScopedPointer<Project> project (new Project (projectFile)); | |||
| project->addDefaultModules (true); | |||
| if (failedFiles.size() == 0) | |||
| { | |||
| @@ -420,10 +396,10 @@ Project* NewProjectWizard::runWizard (Component* ownerWindow_) | |||
| project->setBundleIdentifierToDefault(); | |||
| if (! initialiseProject (*project)) | |||
| return 0; | |||
| return nullptr; | |||
| if (project->save (false, true) != FileBasedDocument::savedOk) | |||
| return 0; | |||
| return nullptr; | |||
| project->setChangedFlag (false); | |||
| } | |||
| @@ -434,48 +410,160 @@ Project* NewProjectWizard::runWizard (Component* ownerWindow_) | |||
| "Errors in Creating Project!", | |||
| "The following files couldn't be written:\n\n" | |||
| + failedFiles.joinIntoString ("\n", 0, 10)); | |||
| return 0; | |||
| return nullptr; | |||
| } | |||
| return project.release(); | |||
| } | |||
| Project* NewProjectWizard::runNewProjectWizard (Component* ownerWindow) | |||
| //============================================================================== | |||
| class NewProjectWizard::WizardComp : public Component, | |||
| private ButtonListener, | |||
| private ComboBoxListener, | |||
| private TextEditorListener | |||
| { | |||
| ScopedPointer <NewProjectWizard> wizard; | |||
| public: | |||
| WizardComp() | |||
| : projectName ("Project name"), | |||
| nameLabel (String::empty, "Project Name:"), | |||
| typeLabel (String::empty, "Project Type:"), | |||
| fileBrowser (FileBrowserComponent::saveMode | FileBrowserComponent::canSelectDirectories, | |||
| getLastWizardFolder(), nullptr, nullptr), | |||
| fileOutline (String::empty, "Project Folder:"), | |||
| createButton ("Create..."), | |||
| cancelButton ("Cancel") | |||
| { | |||
| setOpaque (true); | |||
| setSize (600, 500); | |||
| projectName.setComponentID ("projectName"); | |||
| projectName.setText ("NewProject"); | |||
| projectName.setBounds ("100, 14, parent.width / 2 - 10, top + 22"); | |||
| addAndMakeVisible (&projectName); | |||
| nameLabel.attachToComponent (&projectName, true); | |||
| projectName.addListener (this); | |||
| projectType.setComponentID ("projectType"); | |||
| projectType.addItemList (getWizards(), 1); | |||
| projectType.setSelectedId (1, true); | |||
| projectType.setBounds ("100, projectName.bottom + 4, projectName.right, top + 22"); | |||
| addAndMakeVisible (&projectType); | |||
| typeLabel.attachToComponent (&projectType, true); | |||
| projectType.addListener (this); | |||
| fileOutline.setComponentID ("fileOutline"); | |||
| fileOutline.setColour (GroupComponent::outlineColourId, Colours::black.withAlpha (0.2f)); | |||
| fileOutline.setTextLabelPosition (Justification::centred); | |||
| addAndMakeVisible (&fileOutline); | |||
| fileOutline.setBounds ("10, projectType.bottom + 20, projectType.right, parent.height - 10"); | |||
| fileBrowser.setComponentID ("fileBrowser"); | |||
| fileBrowser.setBounds ("fileOutline.left + 10, fileOutline.top + 20, fileOutline.right - 10, fileOutline.bottom - 12"); | |||
| fileBrowser.setFilenameBoxLabel ("Folder:"); | |||
| addAndMakeVisible (&fileBrowser); | |||
| createButton.setComponentID ("createButton"); | |||
| createButton.setBounds ("right - 140, bottom - 24, parent.width - 10, parent.height - 10"); | |||
| addAndMakeVisible (&createButton); | |||
| createButton.addListener (this); | |||
| cancelButton.setComponentID ("cancelButton"); | |||
| cancelButton.setBounds ("right - 140, createButton.top, createButton.left - 10, createButton.bottom"); | |||
| addAndMakeVisible (&cancelButton); | |||
| cancelButton.addListener (this); | |||
| updateCustomItems(); | |||
| updateCreateButton(); | |||
| } | |||
| void paint (Graphics& g) | |||
| { | |||
| AlertWindow aw ("New Juce Project", | |||
| "Select the type of project to create, and the location of your Juce folder", | |||
| AlertWindow::NoIcon, | |||
| ownerWindow); | |||
| g.fillAll (Colour::greyLevel (0.93f)); | |||
| } | |||
| aw.addComboBox ("type", getWizards(), "Project Type"); | |||
| void buttonClicked (Button* b) | |||
| { | |||
| if (b == &createButton) | |||
| { | |||
| createProject(); | |||
| } | |||
| else | |||
| { | |||
| MainWindow* mw = dynamic_cast<MainWindow*> (getTopLevelComponent()); | |||
| jassert (mw != nullptr); | |||
| FilenameComponent juceFolderSelector ("Juce Library Location", ModuleList::getLocalModulesFolder (nullptr), | |||
| true, true, false, "*", String::empty, "(Please select the folder containing Juce!)"); | |||
| juceFolderSelector.setSize (350, 22); | |||
| JucerApplication::getApp()->closeWindow (mw); | |||
| } | |||
| } | |||
| aw.addCustomComponent (&juceFolderSelector); | |||
| void createProject() | |||
| { | |||
| MainWindow* mw = Component::findParentComponentOfClass<MainWindow>(); | |||
| jassert (mw != nullptr); | |||
| aw.addButton ("Next", 1, KeyPress (KeyPress::returnKey)); | |||
| aw.addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey)); | |||
| ScopedPointer <NewProjectWizard> wizard (createWizard()); | |||
| for (;;) | |||
| if (wizard != nullptr) | |||
| { | |||
| if (aw.runModalLoop() == 0) | |||
| return 0; | |||
| Result result (wizard->processResultsFromSetupItems (*this)); | |||
| if (FileHelpers::isJuceFolder (juceFolderSelector.getCurrentFile())) | |||
| if (result.failed()) | |||
| { | |||
| wizard = createWizard (aw.getComboBoxComponent ("type")->getSelectedItemIndex()); | |||
| break; | |||
| AlertWindow::showMessageBox (AlertWindow::WarningIcon, "Create Project", result.getErrorMessage()); | |||
| return; | |||
| } | |||
| aw.setColour (AlertWindow::textColourId, Colours::red); | |||
| aw.setMessage ("Please select a valid Juce folder for the project to use!"); | |||
| ScopedPointer<Project> project (wizard->runWizard (mw, projectName.getText(), | |||
| fileBrowser.getSelectedFile (0))); | |||
| if (project != nullptr) | |||
| mw->setProject (project.release()); | |||
| } | |||
| } | |||
| return wizard != nullptr ? wizard->runWizard (ownerWindow) : 0; | |||
| void updateCustomItems() | |||
| { | |||
| customItems.clear(); | |||
| ScopedPointer <NewProjectWizard> wizard (createWizard()); | |||
| if (wizard != nullptr) | |||
| wizard->addSetupItems (*this, customItems); | |||
| } | |||
| void comboBoxChanged (ComboBox*) | |||
| { | |||
| updateCustomItems(); | |||
| } | |||
| void textEditorTextChanged (TextEditor&) | |||
| { | |||
| updateCreateButton(); | |||
| fileBrowser.setFileName (File::createLegalFileName (projectName.getText())); | |||
| } | |||
| private: | |||
| ComboBox projectType; | |||
| TextEditor projectName; | |||
| Label nameLabel, typeLabel; | |||
| FileBrowserComponent fileBrowser; | |||
| GroupComponent fileOutline; | |||
| TextButton createButton, cancelButton; | |||
| OwnedArray<Component> customItems; | |||
| NewProjectWizard* createWizard() | |||
| { | |||
| return NewProjectWizard::createWizard (projectType.getSelectedItemIndex()); | |||
| } | |||
| void updateCreateButton() | |||
| { | |||
| createButton.setEnabled (projectName.getText().trim().isNotEmpty()); | |||
| } | |||
| }; | |||
| Component* NewProjectWizard::createComponent() | |||
| { | |||
| return new WizardComp(); | |||
| } | |||
| @@ -41,14 +41,14 @@ public: | |||
| static int getNumWizards(); | |||
| static NewProjectWizard* createWizard (int index); | |||
| static Project* runNewProjectWizard (Component* ownerWindow); | |||
| static Component* createComponent(); | |||
| //============================================================================== | |||
| virtual String getName() = 0; | |||
| virtual String getDescription() = 0; | |||
| virtual void addItemsToAlertWindow (AlertWindow& aw) = 0; | |||
| virtual String processResultsFromAlertWindow (AlertWindow& aw) = 0; | |||
| virtual void addSetupItems (Component& setupComp, OwnedArray<Component>& itemsCreated) = 0; | |||
| virtual Result processResultsFromSetupItems (Component& setupComp) = 0; | |||
| virtual bool initialiseProject (Project& project) = 0; | |||
| protected: | |||
| @@ -59,9 +59,15 @@ protected: | |||
| //============================================================================== | |||
| NewProjectWizard(); | |||
| Project* runWizard (Component* ownerWindow); | |||
| Project* runWizard (Component* ownerWindow, | |||
| const String& projectName, | |||
| const File& targetFolder); | |||
| class WizardComp; | |||
| friend class WizardComp; | |||
| File getSourceFilesFolder() const { return projectFile.getSiblingFile ("Source"); } | |||
| static File& getLastWizardFolder(); | |||
| }; | |||
| @@ -128,25 +128,28 @@ void Project::setMissingDefaultValues() | |||
| setBundleIdentifierToDefault(); | |||
| if (! projectRoot.getChildWithName (Tags::modulesGroup).isValid()) | |||
| { | |||
| addModule ("juce_core"); | |||
| addDefaultModules (false); | |||
| } | |||
| if (! isConfigFlagEnabled ("JUCE_ONLY_BUILD_CORE_LIBRARY")) | |||
| { | |||
| addModule ("juce_events"); | |||
| addModule ("juce_graphics"); | |||
| addModule ("juce_data_structures"); | |||
| addModule ("juce_gui_basics"); | |||
| addModule ("juce_gui_extra"); | |||
| addModule ("juce_gui_audio"); | |||
| addModule ("juce_cryptography"); | |||
| addModule ("juce_video"); | |||
| addModule ("juce_opengl"); | |||
| addModule ("juce_audio_basics"); | |||
| addModule ("juce_audio_devices"); | |||
| addModule ("juce_audio_formats"); | |||
| addModule ("juce_audio_processors"); | |||
| } | |||
| void Project::addDefaultModules (bool shouldCopyFilesLocally) | |||
| { | |||
| addModule ("juce_core", shouldCopyFilesLocally); | |||
| if (! isConfigFlagEnabled ("JUCE_ONLY_BUILD_CORE_LIBRARY")) | |||
| { | |||
| addModule ("juce_events", shouldCopyFilesLocally); | |||
| addModule ("juce_graphics", shouldCopyFilesLocally); | |||
| addModule ("juce_data_structures", shouldCopyFilesLocally); | |||
| addModule ("juce_gui_basics", shouldCopyFilesLocally); | |||
| addModule ("juce_gui_extra", shouldCopyFilesLocally); | |||
| addModule ("juce_gui_audio", shouldCopyFilesLocally); | |||
| addModule ("juce_cryptography", shouldCopyFilesLocally); | |||
| addModule ("juce_video", shouldCopyFilesLocally); | |||
| addModule ("juce_opengl", shouldCopyFilesLocally); | |||
| addModule ("juce_audio_basics", shouldCopyFilesLocally); | |||
| addModule ("juce_audio_devices", shouldCopyFilesLocally); | |||
| addModule ("juce_audio_formats", shouldCopyFilesLocally); | |||
| addModule ("juce_audio_processors", shouldCopyFilesLocally); | |||
| } | |||
| } | |||
| @@ -835,7 +838,7 @@ Value Project::shouldCopyModuleFilesLocally (const String& moduleID) | |||
| .getPropertyAsValue (Ids::useLocalCopy, getUndoManagerFor (getModulesNode())); | |||
| } | |||
| void Project::addModule (const String& moduleID) | |||
| void Project::addModule (const String& moduleID, bool shouldCopyFilesLocally) | |||
| { | |||
| if (! isModuleEnabled (moduleID)) | |||
| { | |||
| @@ -847,6 +850,9 @@ void Project::addModule (const String& moduleID) | |||
| shouldShowAllModuleFilesInProject (moduleID) = true; | |||
| } | |||
| if (shouldCopyFilesLocally) | |||
| shouldCopyModuleFilesLocally (moduleID) = true; | |||
| } | |||
| void Project::removeModule (const String& moduleID) | |||
| @@ -273,10 +273,11 @@ public: | |||
| Value shouldShowAllModuleFilesInProject (const String& moduleID); | |||
| Value shouldCopyModuleFilesLocally (const String& moduleID); | |||
| void addModule (const String& moduleID); | |||
| void addModule (const String& moduleID, bool shouldCopyFilesLocally); | |||
| void removeModule (const String& moduleID); | |||
| int getNumModules() const; | |||
| String getModuleID (int index) const; | |||
| void addDefaultModules (bool shouldCopyFilesLocally); | |||
| void createRequiredModules (const ModuleList& availableModules, OwnedArray<LibraryModule>& modules) const; | |||
| @@ -107,19 +107,6 @@ private: | |||
| int exporterIndex; | |||
| }; | |||
| //============================================================================== | |||
| static StringArray getExtraDependenciesNeeded (Project& project, ModuleList& moduleList, const ModuleList::Module& m) | |||
| { | |||
| StringArray dependencies, extraDepsNeeded; | |||
| moduleList.getDependencies (m.uid, dependencies); | |||
| for (int i = 0; i < dependencies.size(); ++i) | |||
| if ((! project.isModuleEnabled (dependencies[i])) && dependencies[i] != m.uid) | |||
| extraDepsNeeded.add (dependencies[i]); | |||
| return extraDepsNeeded; | |||
| } | |||
| //============================================================================== | |||
| class ModuleSettingsPanel : public PanelBase | |||
| { | |||
| @@ -141,7 +128,7 @@ public: | |||
| if (project.isModuleEnabled (moduleID)) | |||
| { | |||
| const ModuleList::Module* m = moduleList.findModuleInfo (moduleID); | |||
| if (m != nullptr && getExtraDependenciesNeeded (project, moduleList, *m).size() > 0) | |||
| if (m != nullptr && moduleList.getExtraDependenciesNeeded (project, *m).size() > 0) | |||
| props.add (new MissingDependenciesComponent (project, moduleList, moduleID)); | |||
| } | |||
| @@ -239,7 +226,7 @@ private: | |||
| const ModuleList::Module* module = moduleList.findModuleInfo (moduleID); | |||
| if (module != nullptr) | |||
| missingDependencies = getExtraDependenciesNeeded (project, moduleList, *module); | |||
| missingDependencies = moduleList.getExtraDependenciesNeeded (project, *module); | |||
| addAndMakeVisible (&fixButton); | |||
| fixButton.setColour (TextButton::buttonColourId, Colours::red); | |||
| @@ -277,42 +264,26 @@ private: | |||
| }; | |||
| }; | |||
| //============================================================================== | |||
| class ModulesPanel : public Component, | |||
| public ListBoxModel, | |||
| public FilenameComponentListener, | |||
| public ButtonListener | |||
| class ModuleSelectionListBox : public ListBox, | |||
| public ListBoxModel | |||
| { | |||
| public: | |||
| ModulesPanel (Project& project_) | |||
| : project (project_), | |||
| moduleList (ModuleList::getLocalModulesFolder (&project)), | |||
| modulesLocation ("modules", moduleList.getModulesFolder(), | |||
| true, true, false, "*", String::empty, | |||
| "Select a folder containing your JUCE modules..."), | |||
| modulesLabel (String::empty, "Module source folder:"), | |||
| updateModulesButton ("Check for module updates...") | |||
| ModuleSelectionListBox (ModuleList& list_) | |||
| : list (list_), handler (nullptr) | |||
| { | |||
| addAndMakeVisible (&modulesLocation); | |||
| modulesLocation.setBounds ("150, 3, parent.width - 180, 28"); | |||
| modulesLocation.addListener (this); | |||
| modulesLabel.attachToComponent (&modulesLocation, true); | |||
| addAndMakeVisible (&updateModulesButton); | |||
| updateModulesButton.setBounds ("parent.width - 175, 3, parent.width - 4, 28"); | |||
| updateModulesButton.addListener (this); | |||
| setColour (ListBox::backgroundColourId, Colours::white.withAlpha (0.4f)); | |||
| } | |||
| modulesList.setModel (this); | |||
| modulesList.setColour (ListBox::backgroundColourId, Colours::white.withAlpha (0.4f)); | |||
| addAndMakeVisible (&modulesList); | |||
| modulesList.setBounds ("4, 31, parent.width / 2 - 4, parent.height - 3"); | |||
| void refresh() | |||
| { | |||
| updateContent(); | |||
| repaint(); | |||
| } | |||
| int getNumRows() | |||
| { | |||
| return moduleList.modules.size(); | |||
| return list.modules.size(); | |||
| } | |||
| void paintListBoxItem (int rowNumber, Graphics& g, int width, int height, bool rowIsSelected) | |||
| @@ -320,16 +291,16 @@ public: | |||
| if (rowIsSelected) | |||
| g.fillAll (findColour (TextEditor::highlightColourId)); | |||
| const ModuleList::Module* const m = moduleList.modules [rowNumber]; | |||
| const ModuleList::Module* const m = list.modules [rowNumber]; | |||
| if (m != nullptr) | |||
| { | |||
| const float tickSize = height * 0.7f; | |||
| getLookAndFeel().drawTickBox (g, *this, (height - tickSize) / 2, (height - tickSize) / 2, tickSize, tickSize, | |||
| project.isModuleEnabled (m->uid), true, false, false); | |||
| handler->isEnabled (m), true, false, false); | |||
| if (project.isModuleEnabled (m->uid) && getExtraDependenciesNeeded (project, moduleList, *m).size() > 0) | |||
| if (handler->isEnabled (m) && handler->areDependenciesMissing (m)) | |||
| g.setColour (Colours::red); | |||
| else | |||
| g.setColour (Colours::black); | |||
| @@ -342,19 +313,91 @@ public: | |||
| } | |||
| } | |||
| void listBoxItemClicked (int row, const MouseEvent& e) | |||
| { | |||
| if (e.x < getRowHeight()) | |||
| flipRow (row); | |||
| } | |||
| void listBoxItemDoubleClicked (int row, const MouseEvent& e) | |||
| { | |||
| flipRow (row); | |||
| } | |||
| void returnKeyPressed (int row) | |||
| { | |||
| flipRow (row); | |||
| } | |||
| void selectedRowsChanged (int lastRowSelected) | |||
| { | |||
| handler->selectionChanged (list.modules [lastRowSelected]); | |||
| } | |||
| void flipRow (int row) | |||
| { | |||
| const ModuleList::Module* const m = moduleList.modules [row]; | |||
| const ModuleList::Module* const m = list.modules [row]; | |||
| if (m != nullptr) | |||
| { | |||
| if (project.isModuleEnabled (m->uid)) | |||
| project.removeModule (m->uid); | |||
| else | |||
| project.addModule (m->uid); | |||
| } | |||
| handler->setEnabled (m, ! handler->isEnabled (m)); | |||
| } | |||
| refresh(); | |||
| //============================================================================== | |||
| class Handler | |||
| { | |||
| public: | |||
| Handler() {} | |||
| virtual ~Handler() {}; | |||
| virtual bool isEnabled (const ModuleList::Module* m) const = 0; | |||
| virtual void setEnabled (const ModuleList::Module* m, bool enable) = 0; | |||
| virtual bool areDependenciesMissing (const ModuleList::Module* m) = 0; | |||
| virtual void selectionChanged (const ModuleList::Module* selectedModule) = 0; | |||
| }; | |||
| void setHandler (Handler* h) | |||
| { | |||
| handler = h; | |||
| setModel (this); | |||
| } | |||
| private: | |||
| ModuleList& list; | |||
| Handler* handler; | |||
| }; | |||
| //============================================================================== | |||
| class ModulesPanel : public Component, | |||
| public ModuleSelectionListBox::Handler, | |||
| public FilenameComponentListener, | |||
| public ButtonListener | |||
| { | |||
| public: | |||
| ModulesPanel (Project& project_) | |||
| : project (project_), | |||
| modulesLocation ("modules", ModuleList::getLocalModulesFolder (&project), | |||
| true, true, false, "*", String::empty, | |||
| "Select a folder containing your JUCE modules..."), | |||
| modulesLabel (String::empty, "Module source folder:"), | |||
| updateModulesButton ("Check for module updates..."), | |||
| moduleListBox (moduleList) | |||
| { | |||
| moduleList.rescan (ModuleList::getLocalModulesFolder (&project)); | |||
| addAndMakeVisible (&modulesLocation); | |||
| modulesLocation.setBounds ("150, 3, parent.width - 180, 28"); | |||
| modulesLocation.addListener (this); | |||
| modulesLabel.attachToComponent (&modulesLocation, true); | |||
| addAndMakeVisible (&updateModulesButton); | |||
| updateModulesButton.setBounds ("parent.width - 175, 3, parent.width - 4, 28"); | |||
| updateModulesButton.addListener (this); | |||
| moduleListBox.setHandler (this); | |||
| addAndMakeVisible (&moduleListBox); | |||
| moduleListBox.setBounds ("4, 31, parent.width / 2 - 4, parent.height - 3"); | |||
| } | |||
| void filenameComponentChanged (FilenameComponent*) | |||
| @@ -362,45 +405,47 @@ public: | |||
| moduleList.rescan (modulesLocation.getCurrentFile()); | |||
| modulesLocation.setCurrentFile (moduleList.getModulesFolder(), false, false); | |||
| ModuleList::setLocalModulesFolder (moduleList.getModulesFolder()); | |||
| modulesList.updateContent(); | |||
| moduleListBox.refresh(); | |||
| } | |||
| void buttonClicked (Button*) | |||
| { | |||
| JuceUpdater::show (moduleList, getTopLevelComponent()); | |||
| JuceUpdater::show (moduleList, getTopLevelComponent(), ""); | |||
| filenameComponentChanged (nullptr); | |||
| } | |||
| void listBoxItemClicked (int row, const MouseEvent& e) | |||
| bool isEnabled (const ModuleList::Module* m) const | |||
| { | |||
| if (e.x < modulesList.getRowHeight()) | |||
| flipRow (row); | |||
| return project.isModuleEnabled (m->uid); | |||
| } | |||
| void listBoxItemDoubleClicked (int row, const MouseEvent& e) | |||
| void setEnabled (const ModuleList::Module* m, bool enable) | |||
| { | |||
| flipRow (row); | |||
| if (enable) | |||
| project.addModule (m->uid, true); | |||
| else | |||
| project.removeModule (m->uid); | |||
| refresh(); | |||
| } | |||
| void returnKeyPressed (int row) | |||
| bool areDependenciesMissing (const ModuleList::Module* m) | |||
| { | |||
| flipRow (row); | |||
| return moduleList.getExtraDependenciesNeeded (project, *m).size() > 0; | |||
| } | |||
| void selectedRowsChanged (int lastRowSelected) | |||
| void selectionChanged (const ModuleList::Module* selectedModule) | |||
| { | |||
| const ModuleList::Module* const m = moduleList.modules [lastRowSelected]; | |||
| settings = nullptr; | |||
| if (m != nullptr) | |||
| addAndMakeVisible (settings = new ModuleSettingsPanel (project, moduleList, m->uid)); | |||
| if (selectedModule != nullptr) | |||
| addAndMakeVisible (settings = new ModuleSettingsPanel (project, moduleList, selectedModule->uid)); | |||
| } | |||
| void refresh() | |||
| { | |||
| modulesList.repaint(); | |||
| moduleListBox.refresh(); | |||
| if (settings != nullptr) | |||
| settings->refreshAll(); | |||
| @@ -412,14 +457,16 @@ private: | |||
| FilenameComponent modulesLocation; | |||
| Label modulesLabel; | |||
| TextButton updateModulesButton; | |||
| ListBox modulesList; | |||
| ModuleSelectionListBox moduleListBox; | |||
| ScopedPointer<ModuleSettingsPanel> settings; | |||
| }; | |||
| void ModuleSettingsPanel::MissingDependenciesComponent::buttonClicked (Button*) | |||
| { | |||
| bool isModuleCopiedLocally = project.shouldCopyModuleFilesLocally (moduleID).getValue(); | |||
| for (int i = missingDependencies.size(); --i >= 0;) | |||
| project.addModule (missingDependencies[i]); | |||
| project.addModule (missingDependencies[i], isModuleCopiedLocally); | |||
| ModulesPanel* mp = findParentComponentOfClass ((ModulesPanel*) 0); | |||
| if (mp != nullptr) | |||
| @@ -145,47 +145,4 @@ namespace FileHelpers | |||
| return path1.substring (0, commonBitLength).removeCharacters ("/:").isNotEmpty(); | |||
| } | |||
| //============================================================================== | |||
| bool isJuceFolder (const File& folder) | |||
| { | |||
| return folder.getFileName().containsIgnoreCase ("juce") | |||
| && isModulesFolder (folder.getChildFile ("modules")); | |||
| } | |||
| bool isModulesFolder (const File& folder) | |||
| { | |||
| return folder.getFileName().equalsIgnoreCase ("modules") | |||
| && folder.isDirectory(); | |||
| } | |||
| File lookInFolderForJuceFolder (const File& folder) | |||
| { | |||
| for (DirectoryIterator di (folder, false, "*juce*", File::findDirectories); di.next();) | |||
| { | |||
| if (isJuceFolder (di.getFile())) | |||
| return di.getFile(); | |||
| } | |||
| return File::nonexistent; | |||
| } | |||
| File findParentJuceFolder (const File& file) | |||
| { | |||
| File f (file); | |||
| while (f.exists() && f.getParentDirectory() != f) | |||
| { | |||
| if (isJuceFolder (f)) | |||
| return f; | |||
| File found = lookInFolderForJuceFolder (f); | |||
| if (found.exists()) | |||
| return found; | |||
| f = f.getParentDirectory(); | |||
| } | |||
| return File::nonexistent; | |||
| } | |||
| } | |||
| @@ -43,12 +43,6 @@ namespace FileHelpers | |||
| String windowsStylePath (const String& path); | |||
| bool shouldPathsBeRelative (String path1, String path2); | |||
| //============================================================================== | |||
| bool isJuceFolder (const File& folder); | |||
| bool isModulesFolder (const File& folder); | |||
| File findParentJuceFolder (const File& file); | |||
| File lookInFolderForJuceFolder (const File& folder); | |||
| } | |||
| //============================================================================== | |||
| @@ -27,18 +27,10 @@ | |||
| //============================================================================== | |||
| int64 hashCode64 (const String& s) | |||
| { | |||
| MD5 md5 (s.toUTF8()); | |||
| const uint64* const m = reinterpret_cast <const uint64*> (md5.getChecksumDataArray()); | |||
| return (int64) ByteOrder::swapIfBigEndian (m[0] ^ m[1]); | |||
| } | |||
| String createAlphaNumericUID() | |||
| { | |||
| String uid; | |||
| static const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||
| const char chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; | |||
| Random r; | |||
| uid << chars [r.nextInt (52)]; // make sure the first character is always a letter | |||
| @@ -46,7 +38,7 @@ String createAlphaNumericUID() | |||
| for (int i = 5; --i >= 0;) | |||
| { | |||
| r.setSeedRandomly(); | |||
| uid << chars [r.nextInt (numElementsInArray (chars))]; | |||
| uid << chars [r.nextInt (62)]; | |||
| } | |||
| return uid; | |||
| @@ -65,7 +57,7 @@ String createGUID (const String& seed) | |||
| + "-" + hex.substring (8, 12) | |||
| + "-" + hex.substring (12, 16) | |||
| + "-" + hex.substring (16, 20) | |||
| + "-" + hex.substring (20, 32) | |||
| + "-" + hex.substring (20, 32) | |||
| + "}"; | |||
| } | |||
| @@ -24,8 +24,6 @@ | |||
| */ | |||
| //============================================================================== | |||
| // String::hashCode64 actually hit some dupes, so this is a more powerful version. | |||
| int64 hashCode64 (const String& s); | |||
| String hexString8Digits (int value); | |||
| String createAlphaNumericUID(); | |||
| @@ -73,7 +73,7 @@ void StoredSettings::flush() | |||
| // These settings are used in defining the properties file's location. | |||
| PropertiesFile::Options options; | |||
| options.applicationName = "Introjucer"; | |||
| options.folderName = "Introjucer"; | |||
| options.folderName = "Introjucerxx"; | |||
| options.filenameSuffix = "settings"; | |||
| options.osxLibrarySubFolder = "Application Support"; | |||
| @@ -81,7 +81,7 @@ void StoredSettings::flush() | |||
| // Because older versions of the introjucer saved their settings under a different | |||
| // name, this code is an example of how to migrate your old settings files... | |||
| if (! props->getFile().exists()) | |||
| /* if (! props->getFile().exists()) | |||
| { | |||
| PropertiesFile::Options oldOptions; | |||
| oldOptions.applicationName = "Jucer2"; | |||
| @@ -93,7 +93,7 @@ void StoredSettings::flush() | |||
| if (oldProps.getFile().exists()) | |||
| props->addAllPropertiesFrom (oldProps); | |||
| } | |||
| } | |||
| */ } | |||
| // recent files... | |||
| recentFiles.restoreFromString (props->getValue ("recentFiles")); | |||