mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
1605 lines
56 KiB
C++
1605 lines
56 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE library.
|
|
Copyright (c) 2015 - ROLI Ltd.
|
|
|
|
Permission is granted to use this software under the terms of either:
|
|
a) the GPL v2 (or any later version)
|
|
b) the Affero GPL v3
|
|
|
|
Details of these licenses can be found at: www.gnu.org/licenses
|
|
|
|
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
------------------------------------------------------------------------------
|
|
|
|
To release a closed-source product which uses JUCE, commercial licenses are
|
|
available: visit www.juce.com for more information.
|
|
|
|
==============================================================================
|
|
*/
|
|
|
|
#include "../jucer_Headers.h"
|
|
#include "jucer_ProjectContentComponent.h"
|
|
#include "jucer_Module.h"
|
|
#include "../Application/jucer_MainWindow.h"
|
|
#include "../Application/jucer_Application.h"
|
|
#include "../Application/jucer_DownloadCompileEngineThread.h"
|
|
#include "../Code Editor/jucer_SourceCodeEditor.h"
|
|
#include "../Utility/jucer_FilePathPropertyComponent.h"
|
|
#include "jucer_TreeItemTypes.h"
|
|
|
|
|
|
//==============================================================================
|
|
class FileTreePanel : public TreePanelBase
|
|
{
|
|
public:
|
|
FileTreePanel (Project& p)
|
|
: TreePanelBase (&p, "fileTreeState")
|
|
{
|
|
tree.setMultiSelectEnabled (true);
|
|
setRoot (new FileTreeItemTypes::GroupItem (p.getMainGroup()));
|
|
}
|
|
|
|
void updateMissingFileStatuses()
|
|
{
|
|
if (FileTreeItemTypes::ProjectTreeItemBase* p = dynamic_cast<FileTreeItemTypes::ProjectTreeItemBase*> (rootItem.get()))
|
|
p->checkFileStatus();
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
class ConfigTreePanel : public TreePanelBase
|
|
{
|
|
public:
|
|
ConfigTreePanel (Project& p)
|
|
: TreePanelBase (&p, "settingsTreeState")
|
|
{
|
|
tree.setMultiSelectEnabled (false);
|
|
setRoot (new ConfigTreeItemTypes::RootItem (p));
|
|
|
|
if (tree.getNumSelectedItems() == 0)
|
|
tree.getRootItem()->setSelected (true, true);
|
|
|
|
#if JUCE_MAC || JUCE_WINDOWS
|
|
ApplicationCommandManager& commandManager = ProjucerApplication::getCommandManager();
|
|
|
|
addAndMakeVisible (createExporterButton);
|
|
createExporterButton.setCommandToTrigger (&commandManager, CommandIDs::createNewExporter, true);
|
|
createExporterButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::createNewExporter));
|
|
createExporterButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
|
|
|
|
addAndMakeVisible (openProjectButton);
|
|
openProjectButton.setCommandToTrigger (&commandManager, CommandIDs::openInIDE, true);
|
|
openProjectButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::openInIDE));
|
|
openProjectButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
|
|
|
|
addAndMakeVisible (saveAndOpenButton);
|
|
saveAndOpenButton.setCommandToTrigger (&commandManager, CommandIDs::saveAndOpenInIDE, true);
|
|
saveAndOpenButton.setButtonText (commandManager.getNameOfCommand (CommandIDs::saveAndOpenInIDE));
|
|
saveAndOpenButton.setColour (TextButton::buttonColourId, Colours::white.withAlpha (0.5f));
|
|
#endif
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
Rectangle<int> r (getAvailableBounds());
|
|
r.removeFromBottom (6);
|
|
|
|
if (saveAndOpenButton.isVisible())
|
|
saveAndOpenButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
|
|
|
|
if (openProjectButton.isVisible())
|
|
openProjectButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
|
|
|
|
if (createExporterButton.isVisible())
|
|
{
|
|
r.removeFromBottom (10);
|
|
createExporterButton.setBounds (r.removeFromBottom (30).reduced (16, 4));
|
|
}
|
|
|
|
tree.setBounds (r);
|
|
}
|
|
|
|
static void reselect (TreeViewItem& item)
|
|
{
|
|
item.setSelected (false, true);
|
|
item.setSelected (true, true);
|
|
}
|
|
|
|
void showProjectSettings()
|
|
{
|
|
if (ConfigTreeItemTypes::ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (rootItem.get()))
|
|
if (root->isProjectSettings())
|
|
reselect (*root);
|
|
}
|
|
|
|
void showModules()
|
|
{
|
|
if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = getModulesItem())
|
|
reselect (*mods);
|
|
}
|
|
|
|
void showModule (const String& moduleID)
|
|
{
|
|
if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = getModulesItem())
|
|
{
|
|
mods->setOpen (true);
|
|
|
|
for (int i = mods->getNumSubItems(); --i >= 0;)
|
|
if (ConfigTreeItemTypes::ModuleItem* m = dynamic_cast<ConfigTreeItemTypes::ModuleItem*> (mods->getSubItem (i)))
|
|
if (m->moduleID == moduleID)
|
|
reselect (*m);
|
|
}
|
|
}
|
|
|
|
TextButton createExporterButton, openProjectButton, saveAndOpenButton;
|
|
|
|
private:
|
|
ConfigTreeItemTypes::ConfigTreeItemBase* getModulesItem()
|
|
{
|
|
if (ConfigTreeItemTypes::ConfigTreeItemBase* root = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (rootItem.get()))
|
|
if (root->isProjectSettings())
|
|
if (ConfigTreeItemTypes::ConfigTreeItemBase* mods = dynamic_cast<ConfigTreeItemTypes::ConfigTreeItemBase*> (root->getSubItem (0)))
|
|
if (mods->isModulesList())
|
|
return mods;
|
|
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
//==============================================================================
|
|
struct LogoComponent : public Component
|
|
{
|
|
LogoComponent()
|
|
{
|
|
ScopedPointer<XmlElement> svg (XmlDocument::parse (BinaryData::background_logo_svg));
|
|
logo = Drawable::createFromSVG (*svg);
|
|
}
|
|
|
|
void paint (Graphics& g) override
|
|
{
|
|
g.setColour (findColour (mainBackgroundColourId).contrasting (0.3f));
|
|
|
|
Rectangle<int> r (getLocalBounds());
|
|
|
|
g.setFont (15.0f);
|
|
g.drawFittedText (getVersionInfo(), r.removeFromBottom (50), Justification::centredBottom, 3);
|
|
|
|
logo->drawWithin (g, r.withTrimmedBottom (r.getHeight() / 4).toFloat(),
|
|
RectanglePlacement (RectanglePlacement::centred), 1.0f);
|
|
}
|
|
|
|
static String getVersionInfo()
|
|
{
|
|
return SystemStats::getJUCEVersion()
|
|
+ newLine
|
|
+ ProjucerApplication::getApp().getVersionDescription();
|
|
}
|
|
|
|
ScopedPointer<Drawable> logo;
|
|
};
|
|
|
|
//==============================================================================
|
|
ProjectContentComponent::ProjectContentComponent()
|
|
: project (nullptr),
|
|
currentDocument (nullptr),
|
|
treeViewTabs (TabbedButtonBar::TabsAtTop)
|
|
{
|
|
setOpaque (true);
|
|
setWantsKeyboardFocus (true);
|
|
|
|
addAndMakeVisible (logo = new LogoComponent());
|
|
|
|
treeSizeConstrainer.setMinimumWidth (200);
|
|
treeSizeConstrainer.setMaximumWidth (500);
|
|
|
|
treeViewTabs.setOutline (0);
|
|
treeViewTabs.getTabbedButtonBar().setMinimumTabScaleFactor (0.3);
|
|
|
|
ProjucerApplication::getApp().openDocumentManager.addListener (this);
|
|
|
|
Desktop::getInstance().addFocusChangeListener (this);
|
|
startTimer (1600);
|
|
}
|
|
|
|
ProjectContentComponent::~ProjectContentComponent()
|
|
{
|
|
Desktop::getInstance().removeFocusChangeListener (this);
|
|
killChildProcess();
|
|
|
|
ProjucerApplication::getApp().openDocumentManager.removeListener (this);
|
|
|
|
logo = nullptr;
|
|
setProject (nullptr);
|
|
contentView = nullptr;
|
|
removeChildComponent (&bubbleMessage);
|
|
jassert (getNumChildComponents() <= 1);
|
|
}
|
|
|
|
void ProjectContentComponent::paint (Graphics& g)
|
|
{
|
|
ProjucerLookAndFeel::fillWithBackgroundTexture (*this, g);
|
|
}
|
|
|
|
void ProjectContentComponent::paintOverChildren (Graphics& g)
|
|
{
|
|
if (resizerBar != nullptr)
|
|
{
|
|
const int shadowSize = 15;
|
|
const int x = resizerBar->getX();
|
|
|
|
ColourGradient cg (Colours::black.withAlpha (0.25f), (float) x, 0,
|
|
Colours::transparentBlack, (float) (x - shadowSize), 0, false);
|
|
cg.addColour (0.4, Colours::black.withAlpha (0.07f));
|
|
cg.addColour (0.6, Colours::black.withAlpha (0.02f));
|
|
|
|
g.setGradientFill (cg);
|
|
g.fillRect (x - shadowSize, 0, shadowSize, getHeight());
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::resized()
|
|
{
|
|
Rectangle<int> r (getLocalBounds());
|
|
|
|
if (treeViewTabs.isVisible())
|
|
treeViewTabs.setBounds (r.removeFromLeft (treeViewTabs.getWidth()));
|
|
|
|
if (resizerBar != nullptr)
|
|
resizerBar->setBounds (r.withWidth (4));
|
|
|
|
if (contentView != nullptr)
|
|
contentView->setBounds (r);
|
|
|
|
if (logo != nullptr)
|
|
logo->setBounds (r.reduced (r.getWidth() / 4, r.getHeight() / 4));
|
|
}
|
|
|
|
void ProjectContentComponent::lookAndFeelChanged()
|
|
{
|
|
repaint();
|
|
}
|
|
|
|
void ProjectContentComponent::childBoundsChanged (Component* child)
|
|
{
|
|
if (child == &treeViewTabs)
|
|
resized();
|
|
}
|
|
|
|
void ProjectContentComponent::setProject (Project* newProject)
|
|
{
|
|
if (project != newProject)
|
|
{
|
|
lastCrashMessage = String();
|
|
killChildProcess();
|
|
|
|
if (project != nullptr)
|
|
project->removeChangeListener (this);
|
|
|
|
contentView = nullptr;
|
|
resizerBar = nullptr;
|
|
|
|
deleteProjectTabs();
|
|
project = newProject;
|
|
rebuildProjectTabs();
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::rebuildProjectTabs()
|
|
{
|
|
deleteProjectTabs();
|
|
|
|
if (project != nullptr)
|
|
{
|
|
addAndMakeVisible (treeViewTabs);
|
|
|
|
createProjectTabs();
|
|
|
|
PropertiesFile& settings = project->getStoredProperties();
|
|
|
|
const String lastTabName (settings.getValue ("lastTab"));
|
|
int lastTabIndex = treeViewTabs.getTabNames().indexOf (lastTabName);
|
|
|
|
if (lastTabIndex < 0 || lastTabIndex > treeViewTabs.getNumTabs())
|
|
lastTabIndex = 1;
|
|
|
|
treeViewTabs.setCurrentTabIndex (lastTabIndex);
|
|
|
|
int lastTreeWidth = settings.getValue ("projectPanelWidth").getIntValue();
|
|
if (lastTreeWidth < 150)
|
|
lastTreeWidth = 240;
|
|
|
|
treeViewTabs.setBounds (0, 0, lastTreeWidth, getHeight());
|
|
|
|
addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&treeViewTabs, &treeSizeConstrainer,
|
|
ResizableEdgeComponent::rightEdge));
|
|
resizerBar->setAlwaysOnTop (true);
|
|
|
|
project->addChangeListener (this);
|
|
|
|
updateMissingFileStatuses();
|
|
}
|
|
else
|
|
{
|
|
treeViewTabs.setVisible (false);
|
|
}
|
|
|
|
resized();
|
|
}
|
|
|
|
|
|
//==============================================================================
|
|
struct BuildTabComponent : public ConcertinaPanel
|
|
{
|
|
BuildTabComponent (CompileEngineChildProcess* child, ProjucerAppClasses::ErrorListComp* errorList)
|
|
: errorListComp (errorList)
|
|
{
|
|
CurrentActivitiesComp* activities = new CurrentActivitiesComp (child->activityList);
|
|
ComponentListComp* comps = new ComponentListComp (*child);
|
|
|
|
addPanel (-1, errorList, true);
|
|
addPanel (-1, comps, true);
|
|
addPanel (-1, activities, true);
|
|
|
|
setMaximumPanelSize (activities, CurrentActivitiesComp::getMaxPanelHeight());
|
|
setPanelSize (errorList, 200, false);
|
|
setPanelSize (comps, 300, false);
|
|
}
|
|
|
|
Component::SafePointer<ProjucerAppClasses::ErrorListComp> errorListComp;
|
|
};
|
|
|
|
struct ProjucerDisabledComp : public Component,
|
|
private Button::Listener
|
|
{
|
|
ProjucerDisabledComp (String message, bool loggedIn, bool showSubscribeButton,
|
|
bool showSignInButton, bool showSwitchAccountButton,
|
|
bool showDownloadButton)
|
|
: isLoggedIn (loggedIn)
|
|
{
|
|
infoLabel.setColour (Label::textColourId, findColour (mainBackgroundColourId).contrasting (0.7f));
|
|
infoLabel.setJustificationType (Justification::centred);
|
|
infoLabel.setText (message, dontSendNotification);
|
|
addAndMakeVisible (infoLabel);
|
|
|
|
if (showSubscribeButton)
|
|
{
|
|
subscribeButton = new TextButton (String ( "Subscribe..."));
|
|
addAndMakeVisible (*subscribeButton);
|
|
subscribeButton->addListener (this);
|
|
}
|
|
|
|
if (showSignInButton)
|
|
{
|
|
signInButton = new TextButton (String ( "Sign in..."));
|
|
addAndMakeVisible (*signInButton);
|
|
signInButton->addListener (this);
|
|
}
|
|
|
|
if (showSwitchAccountButton)
|
|
{
|
|
switchAccountButton = new TextButton (String ("Switch account..."));
|
|
addAndMakeVisible (*switchAccountButton);
|
|
switchAccountButton->addListener (this);
|
|
}
|
|
|
|
if (showDownloadButton)
|
|
{
|
|
downloadButton = new TextButton (String ("Download live-build engine"));
|
|
addAndMakeVisible (*downloadButton);
|
|
downloadButton->addListener (this);
|
|
}
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
int infoWidth = proportionOfWidth (0.9f);
|
|
int infoHeight = 100;
|
|
|
|
infoLabel.centreWithSize (infoWidth, infoHeight);
|
|
|
|
int buttonWidth = jmin (getWidth() - 10, 150);
|
|
int buttonHeight = 22;
|
|
int itemDistance = 10;
|
|
|
|
int buttonCenterX = infoLabel.getBounds().getCentreX();
|
|
int buttonCenterY = infoLabel.getBottom() + itemDistance + buttonHeight / 2;
|
|
|
|
if (subscribeButton.get() != nullptr)
|
|
{
|
|
subscribeButton->setSize (buttonWidth, buttonHeight);
|
|
subscribeButton->setCentrePosition (buttonCenterX, buttonCenterY);
|
|
buttonCenterY += itemDistance + buttonHeight;
|
|
}
|
|
|
|
if (signInButton.get() != nullptr)
|
|
{
|
|
signInButton->setSize (buttonWidth, buttonHeight);
|
|
signInButton->setCentrePosition (buttonCenterX, buttonCenterY);
|
|
buttonCenterY += itemDistance + buttonHeight;
|
|
}
|
|
|
|
if (switchAccountButton.get() != nullptr)
|
|
{
|
|
switchAccountButton->setSize (buttonWidth, buttonHeight);
|
|
switchAccountButton->setCentrePosition (buttonCenterX, buttonCenterY);
|
|
buttonCenterY += itemDistance + buttonHeight;
|
|
}
|
|
|
|
if (downloadButton.get() != nullptr)
|
|
{
|
|
downloadButton->setSize (buttonWidth, buttonHeight);
|
|
downloadButton->setCentrePosition (buttonCenterX, buttonCenterY);
|
|
}
|
|
}
|
|
|
|
void buttonClicked (Button* btn) override
|
|
{
|
|
if (btn == subscribeButton.get())
|
|
{
|
|
URL ("http://www.juce.com/get-juce#indie").launchInDefaultBrowser();
|
|
}
|
|
else if (btn == signInButton.get())
|
|
{
|
|
ProjucerApplication::getApp().showLoginForm();
|
|
}
|
|
else if (btn == switchAccountButton.get())
|
|
{
|
|
ProjucerApplication::getApp().showLoginForm();
|
|
}
|
|
else if (btn == downloadButton.get())
|
|
{
|
|
if (DownloadCompileEngineThread::downloadAndInstall())
|
|
{
|
|
if (! ProjucerLicenses::getInstance()->retryLoadDll())
|
|
{
|
|
AlertWindow::showMessageBox(AlertWindow::WarningIcon,
|
|
"Download and install",
|
|
"Loading the live-build engine failed");
|
|
return;
|
|
}
|
|
|
|
// async!
|
|
ProjucerApplication::getApp().showLoginForm();
|
|
|
|
// if sign in successful project tabs update, otherwise they were not
|
|
auto parent = findParentComponentOfClass<ProjectContentComponent>();
|
|
parent->rebuildProjectTabs();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isLoggedIn;
|
|
|
|
private:
|
|
Label infoLabel { "info", String() };
|
|
ScopedPointer<TextButton> subscribeButton;
|
|
ScopedPointer<TextButton> signInButton;
|
|
ScopedPointer<TextButton> switchAccountButton;
|
|
ScopedPointer<TextButton> downloadButton;
|
|
};
|
|
|
|
struct EnableBuildComp : public Component
|
|
{
|
|
EnableBuildComp()
|
|
{
|
|
addAndMakeVisible (&enableButton);
|
|
enableButton.setCommandToTrigger (&ProjucerApplication::getCommandManager(), CommandIDs::enableBuild, true);
|
|
}
|
|
|
|
void resized() override
|
|
{
|
|
enableButton.centreWithSize (jmin (getWidth() - 10, 150), 22);
|
|
}
|
|
|
|
void paint (Graphics& g) override
|
|
{
|
|
if (ProjectContentComponent* ppc = findParentComponentOfClass<ProjectContentComponent>())
|
|
{
|
|
g.setColour (findColour (mainBackgroundColourId).contrasting (0.7f));
|
|
g.setFont (13.0f);
|
|
g.drawFittedText (ppc->lastCrashMessage,
|
|
getLocalBounds().reduced (8).withBottom (enableButton.getY() - 20),
|
|
Justification::centredBottom, 10);
|
|
}
|
|
}
|
|
|
|
TextButton enableButton { "Restart Compiler" };
|
|
};
|
|
|
|
//==============================================================================
|
|
Component* ProjectContentComponent::createBuildTab (CompileEngineChildProcess* child)
|
|
{
|
|
if (child != nullptr)
|
|
{
|
|
child->crashHandler = [this] (const String& m) { this->handleCrash (m); };
|
|
|
|
return new BuildTabComponent (child, new ProjucerAppClasses::ErrorListComp (child->errorList));
|
|
}
|
|
|
|
jassert (project != nullptr);
|
|
|
|
const auto osType = SystemStats::getOperatingSystemType();
|
|
const bool isMac = (osType & SystemStats::MacOSX) != 0;
|
|
const bool isWin = (osType & SystemStats::Windows) != 0;
|
|
const bool isLinux = (osType & SystemStats::Linux) != 0;
|
|
|
|
if (! isMac && ! isWin && ! isLinux)
|
|
return createDisabledBuildTabInfoOnly (
|
|
"Live-build features are not supported on your system.\n\n"
|
|
"Please check supported platforms at www.juce.com!");
|
|
|
|
if (isLinux)
|
|
return createDisabledBuildTabInfoOnly (
|
|
"Live-build features for Linux are under development.\n\n"
|
|
"Please check for updates at www.juce.com!");
|
|
|
|
if (isMac)
|
|
if (osType < SystemStats::MacOSX_10_9)
|
|
return createDisabledBuildTabInfoOnly (
|
|
"Live-build features are available only on MacOSX 10.9 or higher.");
|
|
|
|
if (isWin)
|
|
if (! SystemStats::isOperatingSystem64Bit() || osType < SystemStats::Windows8_0)
|
|
return createDisabledBuildTabInfoOnly (
|
|
"Live-build features are available only on 64-Bit Windows 8 or higher.");
|
|
|
|
const auto& unlockStatus = *ProjucerLicenses::getInstance();
|
|
|
|
if (! unlockStatus.isLoggedIn())
|
|
return createDisabledBuildTabSubscribe ("Sign in with your ROLI account",
|
|
false, unlockStatus.isDLLPresent());
|
|
|
|
if (! unlockStatus.hasLiveCodingLicence())
|
|
return createDisabledBuildTabSubscribe ("Subscribe to JUCE Pro or Indie",
|
|
true, unlockStatus.isDLLPresent());
|
|
|
|
jassert (unlockStatus.isLoggedIn());
|
|
jassert (unlockStatus.isDLLPresent());
|
|
return new EnableBuildComp();
|
|
}
|
|
|
|
Component* ProjectContentComponent::createDisabledBuildTabSubscribe (String textPrefix,
|
|
bool loggedIn, bool dllPresent)
|
|
{
|
|
bool showSubscribeButton = true;
|
|
bool showSignInButton = dllPresent && ! loggedIn;
|
|
bool showSwitchAccountButton = dllPresent && loggedIn;
|
|
bool showDownloadButton = ! dllPresent;
|
|
|
|
return new ProjucerDisabledComp (
|
|
textPrefix + " to use the Projucer's live-build features:",
|
|
loggedIn, showSubscribeButton, showSignInButton, showSwitchAccountButton, showDownloadButton);
|
|
}
|
|
|
|
Component* ProjectContentComponent::createDisabledBuildTabInfoOnly(const char* message)
|
|
{
|
|
return new ProjucerDisabledComp (message, false, false, false, false, false);
|
|
}
|
|
|
|
//==============================================================================
|
|
BuildTabComponent* findBuildTab (const TabbedComponent& tabs)
|
|
{
|
|
return dynamic_cast<BuildTabComponent*> (tabs.getTabContentComponent (2));
|
|
}
|
|
|
|
bool ProjectContentComponent::isBuildTabEnabled() const
|
|
{
|
|
return findBuildTab (treeViewTabs) != nullptr;
|
|
}
|
|
|
|
bool ProjectContentComponent::isBuildTabSuitableForLoggedInUser() const
|
|
{
|
|
return isBuildTabEnabled()
|
|
|| isBuildTabLoggedInWithoutLicense()
|
|
|| dynamic_cast<EnableBuildComp*> (treeViewTabs.getTabContentComponent (2)) != nullptr;
|
|
}
|
|
|
|
bool ProjectContentComponent::isBuildTabLoggedInWithoutLicense() const
|
|
{
|
|
if (auto* c = dynamic_cast<ProjucerDisabledComp*> (treeViewTabs.getTabContentComponent (2)))
|
|
return c->isLoggedIn;
|
|
|
|
return false;
|
|
}
|
|
|
|
void ProjectContentComponent::createProjectTabs()
|
|
{
|
|
jassert (project != nullptr);
|
|
const Colour tabColour (Colours::transparentBlack);
|
|
|
|
treeViewTabs.addTab ("Files", tabColour, new FileTreePanel (*project), true);
|
|
treeViewTabs.addTab ("Config", tabColour, new ConfigTreePanel (*project), true);
|
|
|
|
const CompileEngineChildProcess::Ptr childProc (getChildProcess());
|
|
|
|
treeViewTabs.addTab ("Build", Colours::transparentBlack, createBuildTab (childProc), true);
|
|
|
|
if (childProc != nullptr)
|
|
treeViewTabs.getTabbedButtonBar().getTabButton (2)
|
|
->setExtraComponent (new BuildStatusTabComp (childProc->errorList,
|
|
childProc->activityList),
|
|
TabBarButton::afterText);
|
|
}
|
|
|
|
void ProjectContentComponent::deleteProjectTabs()
|
|
{
|
|
if (project != nullptr && treeViewTabs.isShowing())
|
|
{
|
|
PropertiesFile& settings = project->getStoredProperties();
|
|
|
|
if (treeViewTabs.getWidth() > 0)
|
|
settings.setValue ("projectPanelWidth", treeViewTabs.getWidth());
|
|
|
|
if (treeViewTabs.getNumTabs() > 0)
|
|
settings.setValue ("lastTab", treeViewTabs.getCurrentTabName());
|
|
}
|
|
|
|
treeViewTabs.clearTabs();
|
|
}
|
|
|
|
void ProjectContentComponent::saveTreeViewState()
|
|
{
|
|
for (int i = treeViewTabs.getNumTabs(); --i >= 0;)
|
|
if (TreePanelBase* t = dynamic_cast<TreePanelBase*> (treeViewTabs.getTabContentComponent (i)))
|
|
t->saveOpenness();
|
|
}
|
|
|
|
void ProjectContentComponent::saveOpenDocumentList()
|
|
{
|
|
if (project != nullptr)
|
|
{
|
|
ScopedPointer<XmlElement> xml (recentDocumentList.createXML());
|
|
|
|
if (xml != nullptr)
|
|
project->getStoredProperties().setValue ("lastDocs", xml);
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::reloadLastOpenDocuments()
|
|
{
|
|
if (project != nullptr)
|
|
{
|
|
ScopedPointer<XmlElement> xml (project->getStoredProperties().getXmlValue ("lastDocs"));
|
|
|
|
if (xml != nullptr)
|
|
{
|
|
recentDocumentList.restoreFromXML (*project, *xml);
|
|
showDocument (recentDocumentList.getCurrentDocument(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ProjectContentComponent::documentAboutToClose (OpenDocumentManager::Document* document)
|
|
{
|
|
hideDocument (document);
|
|
return true;
|
|
}
|
|
|
|
void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
|
|
{
|
|
updateMissingFileStatuses();
|
|
}
|
|
|
|
void ProjectContentComponent::updateMissingFileStatuses()
|
|
{
|
|
if (FileTreePanel* tree = dynamic_cast<FileTreePanel*> (treeViewTabs.getTabContentComponent (0)))
|
|
tree->updateMissingFileStatuses();
|
|
}
|
|
|
|
bool ProjectContentComponent::showEditorForFile (const File& f, bool grabFocus)
|
|
{
|
|
return getCurrentFile() == f
|
|
|| showDocument (ProjucerApplication::getApp().openDocumentManager.openFile (project, f), grabFocus);
|
|
}
|
|
|
|
bool ProjectContentComponent::hasFileInRecentList (const File& f) const
|
|
{
|
|
return recentDocumentList.contains (f);
|
|
}
|
|
|
|
File ProjectContentComponent::getCurrentFile() const
|
|
{
|
|
return currentDocument != nullptr ? currentDocument->getFile()
|
|
: File();
|
|
}
|
|
|
|
bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc, bool grabFocus)
|
|
{
|
|
if (doc == nullptr)
|
|
return false;
|
|
|
|
if (doc->hasFileBeenModifiedExternally())
|
|
doc->reloadFromFile();
|
|
|
|
if (doc == getCurrentDocument() && contentView != nullptr)
|
|
{
|
|
if (grabFocus)
|
|
contentView->grabKeyboardFocus();
|
|
|
|
return true;
|
|
}
|
|
|
|
recentDocumentList.newDocumentOpened (doc);
|
|
|
|
bool opened = setEditorComponent (doc->createEditor(), doc);
|
|
|
|
if (opened && grabFocus)
|
|
contentView->grabKeyboardFocus();
|
|
|
|
return opened;
|
|
}
|
|
|
|
void ProjectContentComponent::hideEditor()
|
|
{
|
|
currentDocument = nullptr;
|
|
contentView = nullptr;
|
|
updateMainWindowTitle();
|
|
ProjucerApplication::getCommandManager().commandStatusChanged();
|
|
resized();
|
|
}
|
|
|
|
void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
|
|
{
|
|
if (doc == currentDocument)
|
|
{
|
|
if (OpenDocumentManager::Document* replacement = recentDocumentList.getClosestPreviousDocOtherThan (doc))
|
|
showDocument (replacement, true);
|
|
else
|
|
hideEditor();
|
|
}
|
|
}
|
|
|
|
bool ProjectContentComponent::setEditorComponent (Component* editor,
|
|
OpenDocumentManager::Document* doc)
|
|
{
|
|
if (editor != nullptr)
|
|
{
|
|
contentView = nullptr;
|
|
contentView = editor;
|
|
currentDocument = doc;
|
|
addAndMakeVisible (editor);
|
|
resized();
|
|
|
|
updateMainWindowTitle();
|
|
ProjucerApplication::getCommandManager().commandStatusChanged();
|
|
return true;
|
|
}
|
|
|
|
updateMainWindowTitle();
|
|
return false;
|
|
}
|
|
|
|
void ProjectContentComponent::closeDocument()
|
|
{
|
|
if (currentDocument != nullptr)
|
|
ProjucerApplication::getApp().openDocumentManager.closeDocument (currentDocument, true);
|
|
else if (contentView != nullptr)
|
|
if (! goToPreviousFile())
|
|
hideEditor();
|
|
}
|
|
|
|
static void showSaveWarning (OpenDocumentManager::Document* currentDocument)
|
|
{
|
|
AlertWindow::showMessageBox (AlertWindow::WarningIcon,
|
|
TRANS("Save failed!"),
|
|
TRANS("Couldn't save the file:")
|
|
+ "\n" + currentDocument->getFile().getFullPathName());
|
|
}
|
|
|
|
void ProjectContentComponent::saveDocument()
|
|
{
|
|
if (currentDocument != nullptr)
|
|
{
|
|
if (! currentDocument->save())
|
|
showSaveWarning (currentDocument);
|
|
}
|
|
else
|
|
saveProject();
|
|
|
|
updateMainWindowTitle();
|
|
}
|
|
|
|
void ProjectContentComponent::saveAs()
|
|
{
|
|
if (currentDocument != nullptr && ! currentDocument->saveAs())
|
|
showSaveWarning (currentDocument);
|
|
}
|
|
|
|
bool ProjectContentComponent::goToPreviousFile()
|
|
{
|
|
OpenDocumentManager::Document* doc = recentDocumentList.getCurrentDocument();
|
|
|
|
if (doc == nullptr || doc == getCurrentDocument())
|
|
doc = recentDocumentList.getPrevious();
|
|
|
|
return showDocument (doc, true);
|
|
}
|
|
|
|
bool ProjectContentComponent::goToNextFile()
|
|
{
|
|
return showDocument (recentDocumentList.getNext(), true);
|
|
}
|
|
|
|
bool ProjectContentComponent::canGoToCounterpart() const
|
|
{
|
|
return currentDocument != nullptr
|
|
&& currentDocument->getCounterpartFile().exists();
|
|
}
|
|
|
|
bool ProjectContentComponent::goToCounterpart()
|
|
{
|
|
if (currentDocument != nullptr)
|
|
{
|
|
const File file (currentDocument->getCounterpartFile());
|
|
|
|
if (file.exists())
|
|
return showEditorForFile (file, true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ProjectContentComponent::saveProject()
|
|
{
|
|
return project != nullptr
|
|
&& project->save (true, true) == FileBasedDocument::savedOk;
|
|
}
|
|
|
|
void ProjectContentComponent::closeProject()
|
|
{
|
|
if (MainWindow* const mw = findParentComponentOfClass<MainWindow>())
|
|
mw->closeCurrentProject();
|
|
}
|
|
|
|
void ProjectContentComponent::showFilesTab()
|
|
{
|
|
treeViewTabs.setCurrentTabIndex (0);
|
|
}
|
|
|
|
void ProjectContentComponent::showConfigTab()
|
|
{
|
|
treeViewTabs.setCurrentTabIndex (1);
|
|
}
|
|
|
|
void ProjectContentComponent::showProjectSettings()
|
|
{
|
|
showConfigTab();
|
|
|
|
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
|
tree->showProjectSettings();
|
|
}
|
|
|
|
void ProjectContentComponent::showModules()
|
|
{
|
|
showConfigTab();
|
|
|
|
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
|
tree->showModules();
|
|
}
|
|
|
|
void ProjectContentComponent::showModule (const String& moduleID)
|
|
{
|
|
showConfigTab();
|
|
|
|
if (ConfigTreePanel* const tree = dynamic_cast<ConfigTreePanel*> (treeViewTabs.getCurrentContentComponent()))
|
|
tree->showModule (moduleID);
|
|
}
|
|
|
|
StringArray ProjectContentComponent::getExportersWhichCanLaunch() const
|
|
{
|
|
StringArray s;
|
|
|
|
if (project != nullptr)
|
|
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
|
if (exporter->canLaunchProject())
|
|
s.add (exporter->getName());
|
|
|
|
return s;
|
|
}
|
|
|
|
void ProjectContentComponent::openInIDE (int exporterIndex, bool saveFirst)
|
|
{
|
|
if (saveFirst)
|
|
saveProject();
|
|
|
|
int i = 0;
|
|
|
|
if (project != nullptr)
|
|
for (Project::ExporterIterator exporter (*project); exporter.next();)
|
|
if (exporter->canLaunchProject())
|
|
if (i++ == exporterIndex && exporter->launchProject())
|
|
break;
|
|
}
|
|
|
|
static void openIDEMenuCallback (int result, ProjectContentComponent* comp, bool saveFirst)
|
|
{
|
|
if (comp != nullptr && result > 0)
|
|
comp->openInIDE (result - 1, saveFirst);
|
|
}
|
|
|
|
void ProjectContentComponent::openInIDE (bool saveFirst)
|
|
{
|
|
if (project != nullptr)
|
|
{
|
|
StringArray possibleExporters = getExportersWhichCanLaunch();
|
|
|
|
if (possibleExporters.size() > 1)
|
|
{
|
|
PopupMenu menu;
|
|
|
|
for (int i = 0; i < possibleExporters.size(); ++i)
|
|
menu.addItem (i + 1, possibleExporters[i]);
|
|
|
|
menu.showMenuAsync (PopupMenu::Options(),
|
|
ModalCallbackFunction::forComponent (openIDEMenuCallback, this, saveFirst));
|
|
}
|
|
else
|
|
{
|
|
openInIDE (0, saveFirst);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void newExporterMenuCallback (int result, ProjectContentComponent* comp)
|
|
{
|
|
if (comp != nullptr && result > 0)
|
|
{
|
|
if (Project* p = comp->getProject())
|
|
{
|
|
String exporterName (ProjectExporter::getExporterNames() [result - 1]);
|
|
|
|
if (exporterName.isNotEmpty())
|
|
p->addNewExporter (exporterName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::showNewExporterMenu()
|
|
{
|
|
if (project != nullptr)
|
|
{
|
|
PopupMenu menu;
|
|
|
|
menu.addSectionHeader ("Create a new export target:");
|
|
|
|
Array<ProjectExporter::ExporterTypeInfo> exporters (ProjectExporter::getExporterTypes());
|
|
|
|
for (int i = 0; i < exporters.size(); ++i)
|
|
{
|
|
const ProjectExporter::ExporterTypeInfo& type = exporters.getReference(i);
|
|
|
|
menu.addItem (i + 1, type.name, true, false, type.getIcon());
|
|
}
|
|
|
|
menu.showMenuAsync (PopupMenu::Options(),
|
|
ModalCallbackFunction::forComponent (newExporterMenuCallback, this));
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::deleteSelectedTreeItems()
|
|
{
|
|
if (TreePanelBase* const tree = dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()))
|
|
tree->deleteSelectedItems();
|
|
}
|
|
|
|
void ProjectContentComponent::updateMainWindowTitle()
|
|
{
|
|
if (MainWindow* mw = findParentComponentOfClass<MainWindow>())
|
|
{
|
|
String title;
|
|
File file;
|
|
bool edited = false;
|
|
|
|
if (currentDocument != nullptr)
|
|
{
|
|
title = currentDocument->getName();
|
|
edited = currentDocument->needsSaving();
|
|
file = currentDocument->getFile();
|
|
}
|
|
|
|
if (ComponentPeer* peer = mw->getPeer())
|
|
{
|
|
if (! peer->setDocumentEditedStatus (edited))
|
|
if (edited)
|
|
title << "*";
|
|
|
|
peer->setRepresentedFile (file);
|
|
}
|
|
|
|
mw->updateTitle (title);
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::showBubbleMessage (Rectangle<int> pos, const String& text)
|
|
{
|
|
addChildComponent (bubbleMessage);
|
|
bubbleMessage.setColour (BubbleComponent::backgroundColourId, Colours::white.withAlpha (0.7f));
|
|
bubbleMessage.setColour (BubbleComponent::outlineColourId, Colours::black.withAlpha (0.8f));
|
|
bubbleMessage.setAlwaysOnTop (true);
|
|
|
|
bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
|
|
}
|
|
|
|
//==============================================================================
|
|
void ProjectContentComponent::showTranslationTool()
|
|
{
|
|
if (translationTool != nullptr)
|
|
{
|
|
translationTool->toFront (true);
|
|
}
|
|
else if (project != nullptr)
|
|
{
|
|
new FloatingToolWindow ("Translation File Builder",
|
|
"transToolWindowPos",
|
|
new TranslationToolComponent(),
|
|
translationTool,
|
|
600, 700,
|
|
600, 400, 10000, 10000);
|
|
}
|
|
}
|
|
|
|
//==============================================================================
|
|
struct AsyncCommandRetrier : public Timer
|
|
{
|
|
AsyncCommandRetrier (const ApplicationCommandTarget::InvocationInfo& i) : info (i)
|
|
{
|
|
info.originatingComponent = nullptr;
|
|
startTimer (500);
|
|
}
|
|
|
|
void timerCallback() override
|
|
{
|
|
stopTimer();
|
|
ProjucerApplication::getCommandManager().invoke (info, true);
|
|
delete this;
|
|
}
|
|
|
|
ApplicationCommandTarget::InvocationInfo info;
|
|
|
|
JUCE_DECLARE_NON_COPYABLE (AsyncCommandRetrier)
|
|
};
|
|
|
|
bool reinvokeCommandAfterCancellingModalComps (const ApplicationCommandTarget::InvocationInfo& info)
|
|
{
|
|
if (ModalComponentManager::getInstance()->cancelAllModalComponents())
|
|
{
|
|
new AsyncCommandRetrier (info);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//==============================================================================
|
|
ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
|
|
{
|
|
return findFirstTargetParentComponent();
|
|
}
|
|
|
|
void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
|
|
{
|
|
const CommandID ids[] = { CommandIDs::saveDocument,
|
|
CommandIDs::saveDocumentAs,
|
|
CommandIDs::closeDocument,
|
|
CommandIDs::saveProject,
|
|
CommandIDs::closeProject,
|
|
CommandIDs::openInIDE,
|
|
CommandIDs::saveAndOpenInIDE,
|
|
CommandIDs::createNewExporter,
|
|
CommandIDs::showFilePanel,
|
|
CommandIDs::showConfigPanel,
|
|
CommandIDs::showProjectSettings,
|
|
CommandIDs::showProjectModules,
|
|
CommandIDs::goToPreviousDoc,
|
|
CommandIDs::goToNextDoc,
|
|
CommandIDs::goToCounterpart,
|
|
CommandIDs::deleteSelectedItem,
|
|
CommandIDs::showTranslationTool,
|
|
CommandIDs::showBuildTab,
|
|
CommandIDs::cleanAll,
|
|
CommandIDs::enableBuild,
|
|
CommandIDs::buildNow,
|
|
CommandIDs::toggleContinuousBuild,
|
|
CommandIDs::showWarnings,
|
|
CommandIDs::reinstantiateComp,
|
|
CommandIDs::launchApp,
|
|
CommandIDs::killApp,
|
|
CommandIDs::nextError,
|
|
CommandIDs::prevError };
|
|
|
|
commands.addArray (ids, numElementsInArray (ids));
|
|
}
|
|
|
|
void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
|
|
{
|
|
String documentName;
|
|
if (currentDocument != nullptr)
|
|
documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
|
|
|
|
#if JUCE_MAC
|
|
const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::commandModifier);
|
|
#else
|
|
const ModifierKeys cmdCtrl (ModifierKeys::ctrlModifier | ModifierKeys::altModifier);
|
|
#endif
|
|
|
|
switch (commandID)
|
|
{
|
|
case CommandIDs::saveProject:
|
|
result.setInfo ("Save Project",
|
|
"Saves the current project",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::closeProject:
|
|
result.setInfo ("Close Project",
|
|
"Closes the current project",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::saveDocument:
|
|
result.setInfo ("Save" + documentName,
|
|
"Saves the current document",
|
|
CommandCategories::general, 0);
|
|
result.setActive (currentDocument != nullptr || project != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::saveDocumentAs:
|
|
result.setInfo ("Save As...",
|
|
"Saves the current document to a new location",
|
|
CommandCategories::general, 0);
|
|
result.setActive (currentDocument != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::closeDocument:
|
|
result.setInfo ("Close" + documentName,
|
|
"Closes the current document",
|
|
CommandCategories::general, 0);
|
|
result.setActive (contentView != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('w', cmdCtrl, 0));
|
|
break;
|
|
|
|
case CommandIDs::goToPreviousDoc:
|
|
result.setInfo ("Previous Document", "Go to previous document", CommandCategories::general, 0);
|
|
result.setActive (recentDocumentList.canGoToPrevious());
|
|
result.defaultKeypresses.add (KeyPress (KeyPress::leftKey, cmdCtrl, 0));
|
|
break;
|
|
|
|
case CommandIDs::goToNextDoc:
|
|
result.setInfo ("Next Document", "Go to next document", CommandCategories::general, 0);
|
|
result.setActive (recentDocumentList.canGoToNext());
|
|
result.defaultKeypresses.add (KeyPress (KeyPress::rightKey, cmdCtrl, 0));
|
|
break;
|
|
|
|
case CommandIDs::goToCounterpart:
|
|
result.setInfo ("Open corresponding header or cpp file", "Open counterpart file", CommandCategories::general, 0);
|
|
result.setActive (canGoToCounterpart());
|
|
result.defaultKeypresses.add (KeyPress (KeyPress::upKey, cmdCtrl, 0));
|
|
break;
|
|
|
|
case CommandIDs::openInIDE:
|
|
result.setInfo ("Open in IDE...",
|
|
|
|
"Launches the project in an external IDE",
|
|
CommandCategories::general, 0);
|
|
result.setActive (ProjectExporter::canProjectBeLaunched (project));
|
|
break;
|
|
|
|
case CommandIDs::saveAndOpenInIDE:
|
|
result.setInfo ("Save Project and Open in IDE...",
|
|
|
|
"Saves the project and launches it in an external IDE",
|
|
CommandCategories::general, 0);
|
|
result.setActive (ProjectExporter::canProjectBeLaunched (project));
|
|
result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::createNewExporter:
|
|
result.setInfo ("Create New Exporter...",
|
|
"Creates a new exporter for a compiler type",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::showFilePanel:
|
|
result.setInfo ("Show File Panel",
|
|
"Shows the tree of files for this project",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('p', ModifierKeys::commandModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::showConfigPanel:
|
|
result.setInfo ("Show Config Panel",
|
|
"Shows the build options for the project",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::showProjectSettings:
|
|
result.setInfo ("Show Project Settings",
|
|
"Shows the main project options page",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::showProjectModules:
|
|
result.setInfo ("Show Project Modules",
|
|
"Shows the project's list of modules",
|
|
CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
result.defaultKeypresses.add (KeyPress ('m', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::deleteSelectedItem:
|
|
result.setInfo ("Delete Selected File", String(), CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress (KeyPress::deleteKey, 0, 0));
|
|
result.defaultKeypresses.add (KeyPress (KeyPress::backspaceKey, 0, 0));
|
|
result.setActive (dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent()) != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::showTranslationTool:
|
|
result.setInfo ("Translation File Builder", "Shows the translation file helper tool", CommandCategories::general, 0);
|
|
break;
|
|
|
|
case CommandIDs::showBuildTab:
|
|
result.setInfo ("Show Build Panel", "Shows the build panel", CommandCategories::general, 0);
|
|
//result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier, 0));
|
|
break;
|
|
|
|
case CommandIDs::cleanAll:
|
|
result.setInfo ("Clean All", "Cleans all intermediate files", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('k', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
result.setActive (project != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::enableBuild:
|
|
result.setInfo ("Enable Compilation", "Enables/disables the compiler", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
result.setActive (project != nullptr);
|
|
result.setTicked (childProcess != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::buildNow:
|
|
result.setInfo ("Build Now", "Recompiles any out-of-date files and updates the JIT engine", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('b', ModifierKeys::commandModifier, 0));
|
|
result.setActive (childProcess != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::toggleContinuousBuild:
|
|
result.setInfo ("Enable Continuous Recompiling", "Continuously recompiles any changes made in code editors", CommandCategories::general, 0);
|
|
result.setActive (childProcess != nullptr);
|
|
result.setTicked (isContinuousRebuildEnabled());
|
|
break;
|
|
|
|
case CommandIDs::showWarnings:
|
|
result.setInfo ("Show Warnings", "Shows or hides compilation warnings", CommandCategories::general, 0);
|
|
result.setActive (project != nullptr);
|
|
result.setTicked (areWarningsEnabled());
|
|
break;
|
|
|
|
case CommandIDs::launchApp:
|
|
result.setInfo ("Launch Application", "Invokes the app's main() function", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('r', ModifierKeys::commandModifier, 0));
|
|
result.setActive (childProcess != nullptr && childProcess->canLaunchApp());
|
|
break;
|
|
|
|
case CommandIDs::killApp:
|
|
result.setInfo ("Stop Application", "Kills the app if it's running", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('.', ModifierKeys::commandModifier, 0));
|
|
result.setActive (childProcess != nullptr && childProcess->canKillApp());
|
|
break;
|
|
|
|
case CommandIDs::reinstantiateComp:
|
|
result.setInfo ("Re-instantiate Components", "Re-loads any component editors that are open", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('r', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
result.setActive (childProcess != nullptr);
|
|
break;
|
|
|
|
case CommandIDs::nextError:
|
|
result.setInfo ("Highlight next error", "Jumps to the next error or warning", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier, 0));
|
|
result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty());
|
|
break;
|
|
|
|
case CommandIDs::prevError:
|
|
result.setInfo ("Highlight previous error", "Jumps to the last error or warning", CommandCategories::general, 0);
|
|
result.defaultKeypresses.add (KeyPress ('e', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
|
|
result.setActive (childProcess != nullptr && ! childProcess->errorList.isEmpty());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool ProjectContentComponent::perform (const InvocationInfo& info)
|
|
{
|
|
switch (info.commandID)
|
|
{
|
|
case CommandIDs::saveProject:
|
|
case CommandIDs::closeProject:
|
|
case CommandIDs::saveDocument:
|
|
case CommandIDs::saveDocumentAs:
|
|
case CommandIDs::closeDocument:
|
|
case CommandIDs::goToPreviousDoc:
|
|
case CommandIDs::goToNextDoc:
|
|
case CommandIDs::goToCounterpart:
|
|
case CommandIDs::saveAndOpenInIDE:
|
|
if (reinvokeCommandAfterCancellingModalComps (info))
|
|
{
|
|
grabKeyboardFocus(); // to force any open labels to close their text editors
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (isCurrentlyBlockedByAnotherModalComponent())
|
|
return false;
|
|
|
|
switch (info.commandID)
|
|
{
|
|
case CommandIDs::saveProject: saveProject(); break;
|
|
case CommandIDs::closeProject: closeProject(); break;
|
|
case CommandIDs::saveDocument: saveDocument(); break;
|
|
case CommandIDs::saveDocumentAs: saveAs(); break;
|
|
|
|
case CommandIDs::closeDocument: closeDocument(); break;
|
|
case CommandIDs::goToPreviousDoc: goToPreviousFile(); break;
|
|
case CommandIDs::goToNextDoc: goToNextFile(); break;
|
|
case CommandIDs::goToCounterpart: goToCounterpart(); break;
|
|
|
|
case CommandIDs::showFilePanel: showFilesTab(); break;
|
|
case CommandIDs::showConfigPanel: showConfigTab(); break;
|
|
case CommandIDs::showProjectSettings: showProjectSettings(); break;
|
|
case CommandIDs::showProjectModules: showModules(); break;
|
|
|
|
case CommandIDs::openInIDE: openInIDE (false); break;
|
|
case CommandIDs::saveAndOpenInIDE: openInIDE (true); break;
|
|
|
|
case CommandIDs::createNewExporter: showNewExporterMenu(); break;
|
|
|
|
case CommandIDs::deleteSelectedItem: deleteSelectedTreeItems(); break;
|
|
|
|
case CommandIDs::showTranslationTool: showTranslationTool(); break;
|
|
|
|
case CommandIDs::showBuildTab: showBuildTab(); break;
|
|
case CommandIDs::cleanAll: cleanAll(); break;
|
|
case CommandIDs::enableBuild: setBuildEnabled (! isBuildEnabled()); break;
|
|
case CommandIDs::buildNow: rebuildNow(); break;
|
|
case CommandIDs::toggleContinuousBuild: setContinuousRebuildEnabled (! isContinuousRebuildEnabled()); break;
|
|
case CommandIDs::launchApp: launchApp(); break;
|
|
case CommandIDs::killApp: killApp(); break;
|
|
case CommandIDs::reinstantiateComp: reinstantiateLivePreviewWindows(); break;
|
|
case CommandIDs::showWarnings: toggleWarnings(); break;
|
|
case CommandIDs::nextError: showNextError(); break;
|
|
case CommandIDs::prevError: showPreviousError(); break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void ProjectContentComponent::getSelectedProjectItemsBeingDragged (const DragAndDropTarget::SourceDetails& dragSourceDetails,
|
|
OwnedArray<Project::Item>& selectedNodes)
|
|
{
|
|
FileTreeItemTypes::ProjectTreeItemBase::getSelectedProjectItemsBeingDragged (dragSourceDetails, selectedNodes);
|
|
}
|
|
|
|
//==============================================================================
|
|
void ProjectContentComponent::killChildProcess()
|
|
{
|
|
if (childProcess != nullptr)
|
|
{
|
|
deleteProjectTabs();
|
|
childProcess = nullptr;
|
|
ProjucerApplication::getApp().childProcessCache->removeOrphans();
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::setBuildEnabled (bool b)
|
|
{
|
|
if (project != nullptr && b != isBuildEnabled())
|
|
{
|
|
LiveBuildProjectSettings::setBuildDisabled (*project, ! b);
|
|
killChildProcess();
|
|
refreshTabsIfBuildStatusChanged();
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::showBuildTab()
|
|
{
|
|
WeakReference<Component> currentFocus (Component::getCurrentlyFocusedComponent());
|
|
treeViewTabs.setCurrentTabIndex (2);
|
|
|
|
if (currentFocus != nullptr)
|
|
currentFocus->grabKeyboardFocus();
|
|
}
|
|
|
|
void ProjectContentComponent::cleanAll()
|
|
{
|
|
lastCrashMessage = String();
|
|
|
|
if (childProcess != nullptr)
|
|
childProcess->cleanAll();
|
|
else if (Project* p = getProject())
|
|
CompileEngineChildProcess::cleanAllCachedFilesForProject (*p);
|
|
}
|
|
|
|
void ProjectContentComponent::handleCrash (const String& message)
|
|
{
|
|
lastCrashMessage = message.isEmpty() ? TRANS("JIT process stopped responding!")
|
|
: (TRANS("JIT process crashed!") + ":\n\n" + message);
|
|
|
|
if (project != nullptr)
|
|
{
|
|
setBuildEnabled (false);
|
|
showBuildTab();
|
|
}
|
|
}
|
|
|
|
bool ProjectContentComponent::isBuildEnabled() const
|
|
{
|
|
return project != nullptr
|
|
&& ! LiveBuildProjectSettings::isBuildDisabled (*project)
|
|
&& ProjucerLicenses::getInstance()->hasLiveCodingLicence()
|
|
&& ProjucerLicenses::getInstance()->isLoggedIn();
|
|
}
|
|
|
|
void ProjectContentComponent::refreshTabsIfBuildStatusChanged()
|
|
{
|
|
if (project != nullptr
|
|
&& (treeViewTabs.getNumTabs() < 3
|
|
|| isBuildEnabled() != isBuildTabEnabled()
|
|
|| ProjucerLicenses::getInstance()->isLoggedIn() != isBuildTabSuitableForLoggedInUser()))
|
|
rebuildProjectTabs();
|
|
}
|
|
|
|
bool ProjectContentComponent::areWarningsEnabled() const
|
|
{
|
|
return project != nullptr && ! LiveBuildProjectSettings::areWarningsDisabled (*project);
|
|
}
|
|
|
|
void ProjectContentComponent::updateWarningState()
|
|
{
|
|
if (childProcess != nullptr)
|
|
childProcess->errorList.setWarningsEnabled (areWarningsEnabled());
|
|
}
|
|
|
|
void ProjectContentComponent::toggleWarnings()
|
|
{
|
|
if (project != nullptr)
|
|
{
|
|
LiveBuildProjectSettings::setWarningsDisabled (*project, areWarningsEnabled());
|
|
updateWarningState();
|
|
}
|
|
}
|
|
|
|
static ProjucerAppClasses::ErrorListComp* findErrorListComp (const TabbedComponent& tabs)
|
|
{
|
|
if (BuildTabComponent* bt = findBuildTab (tabs))
|
|
return bt->errorListComp;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ProjectContentComponent::showNextError()
|
|
{
|
|
if (ProjucerAppClasses::ErrorListComp* el = findErrorListComp (treeViewTabs))
|
|
{
|
|
showBuildTab();
|
|
el->showNext();
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::showPreviousError()
|
|
{
|
|
if (ProjucerAppClasses::ErrorListComp* el = findErrorListComp (treeViewTabs))
|
|
{
|
|
showBuildTab();
|
|
el->showPrevious();
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::reinstantiateLivePreviewWindows()
|
|
{
|
|
if (childProcess != nullptr)
|
|
childProcess->reinstantiatePreviews();
|
|
}
|
|
|
|
void ProjectContentComponent::launchApp()
|
|
{
|
|
if (childProcess != nullptr)
|
|
childProcess->launchApp();
|
|
}
|
|
|
|
void ProjectContentComponent::killApp()
|
|
{
|
|
if (childProcess != nullptr)
|
|
childProcess->killApp();
|
|
}
|
|
|
|
void ProjectContentComponent::rebuildNow()
|
|
{
|
|
if (childProcess != nullptr)
|
|
childProcess->flushEditorChanges();
|
|
}
|
|
|
|
void ProjectContentComponent::globalFocusChanged (Component* focusedComponent)
|
|
{
|
|
const bool nowForeground = (Process::isForegroundProcess()
|
|
&& (focusedComponent == this || isParentOf (focusedComponent)));
|
|
|
|
if (nowForeground != isForeground)
|
|
{
|
|
isForeground = nowForeground;
|
|
|
|
if (childProcess != nullptr)
|
|
childProcess->processActivationChanged (isForeground);
|
|
}
|
|
}
|
|
|
|
void ProjectContentComponent::timerCallback()
|
|
{
|
|
if (! isBuildEnabled())
|
|
killChildProcess();
|
|
|
|
refreshTabsIfBuildStatusChanged();
|
|
}
|
|
|
|
ReferenceCountedObjectPtr<CompileEngineChildProcess> ProjectContentComponent::getChildProcess()
|
|
{
|
|
if (childProcess == nullptr && isBuildEnabled())
|
|
{
|
|
childProcess = ProjucerApplication::getApp().childProcessCache->getOrCreate (*project);
|
|
|
|
if (childProcess != nullptr)
|
|
childProcess->setContinuousRebuild (isContinuousRebuildEnabled());
|
|
}
|
|
|
|
return childProcess;
|
|
}
|
|
|
|
void ProjectContentComponent::handleMissingSystemHeaders()
|
|
{
|
|
#if JUCE_MAC
|
|
const String tabMessage = "Compiler not available due to missing system headers\nPlease install a recent version of Xcode";
|
|
const String alertWindowMessage = "Missing system headers\nPlease install a recent version of Xcode";
|
|
#elif JUCE_WINDOWS
|
|
const String tabMessage = "Compiler not available due to missing system headers\nPlease install a recent version of Visual Studio and the Windows Desktop SDK";
|
|
const String alertWindowMessage = "Missing system headers\nPlease install a recent version of Visual Studio and the Windows Desktop SDK";
|
|
#elif JUCE_LINUX
|
|
const String tabMessage = "Compiler not available due to missing system headers\nPlease do a sudo apt-get install ...";
|
|
const String alertWindowMessage = "Missing system headers\nPlease do sudo apt-get install ...";
|
|
#endif
|
|
|
|
setBuildEnabled (false);
|
|
|
|
deleteProjectTabs();
|
|
createProjectTabs();
|
|
|
|
bool isLoggedIn = ProjucerLicenses::getInstance()->isLoggedIn();
|
|
ProjucerDisabledComp* buildTab = new ProjucerDisabledComp (tabMessage, isLoggedIn, false, false, false, false);
|
|
|
|
treeViewTabs.addTab ("Build", Colours::transparentBlack, buildTab, true);
|
|
showBuildTab();
|
|
|
|
AlertWindow::showMessageBox (AlertWindow::AlertIconType::WarningIcon,
|
|
"Missing system headers", alertWindowMessage);
|
|
}
|