1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Introjucer/Source/Project/jucer_ProjectContentComponent.cpp
2012-07-07 15:05:25 +01:00

516 lines
17 KiB
C++

/*
==============================================================================
This file is part of the JUCE library - "Jules' Utility Class Extensions"
Copyright 2004-11 by Raw Material Software Ltd.
------------------------------------------------------------------------------
JUCE can be redistributed and/or modified under the terms of the GNU General
Public License (Version 2), as published by the Free Software Foundation.
A copy of the license is included in the JUCE distribution, or can be found
online 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.rawmaterialsoftware.com/juce for more information.
==============================================================================
*/
#include "jucer_ProjectContentComponent.h"
#include "../Application/jucer_MainWindow.h"
#include "../Application/jucer_Application.h"
#include "../Code Editor/jucer_SourceCodeEditor.h"
#include "jucer_ConfigPage.h"
#include "jucer_TreeViewTypes.h"
#include "../Project Saving/jucer_ProjectExporter.h"
//==============================================================================
class FileTreeTab : public TreePanelBase
{
public:
FileTreeTab (Project& project)
: TreePanelBase ("treeViewState_" + project.getProjectUID())
{
tree.setMultiSelectEnabled (true);
setRoot (new GroupTreeViewItem (project.getMainGroup()));
}
};
//==============================================================================
class ConfigTreeTab : public TreePanelBase
{
public:
ConfigTreeTab (Project& project)
: TreePanelBase ("settingsTreeViewState_" + project.getProjectUID())
{
tree.setMultiSelectEnabled (false);
setRoot (createProjectConfigTreeViewRoot (project));
if (tree.getNumSelectedItems() == 0)
tree.getRootItem()->setSelected (true, true);
#if JUCE_MAC || JUCE_WINDOWS
addAndMakeVisible (&openProjectButton);
openProjectButton.setCommandToTrigger (commandManager, CommandIDs::openInIDE, true);
openProjectButton.setButtonText (commandManager->getNameOfCommand (CommandIDs::openInIDE));
addAndMakeVisible (&saveAndOpenButton);
saveAndOpenButton.setCommandToTrigger (commandManager, CommandIDs::saveAndOpenInIDE, true);
saveAndOpenButton.setButtonText (commandManager->getNameOfCommand (CommandIDs::saveAndOpenInIDE));
#endif
}
void resized()
{
Rectangle<int> r (getLocalBounds());
r.removeFromBottom (6);
if (saveAndOpenButton.isVisible())
saveAndOpenButton.setBounds (r.removeFromBottom (28).reduced (20, 3));
if (openProjectButton.isVisible())
openProjectButton.setBounds (r.removeFromBottom (28).reduced (20, 3));
tree.setBounds (r);
}
TextButton openProjectButton, saveAndOpenButton;
};
//==============================================================================
ProjectContentComponent::ProjectContentComponent()
: project (nullptr),
currentDocument (nullptr),
treeViewTabs (TabbedButtonBar::TabsAtTop)
{
setOpaque (true);
setWantsKeyboardFocus (true);
treeSizeConstrainer.setMinimumWidth (100);
treeSizeConstrainer.setMaximumWidth (500);
}
ProjectContentComponent::~ProjectContentComponent()
{
setProject (nullptr);
contentView = nullptr;
jassert (getNumChildComponents() <= 1);
}
void ProjectContentComponent::paint (Graphics& g)
{
g.fillAll (Colour::greyLevel (0.8f));
}
void ProjectContentComponent::resized()
{
Rectangle<int> r (getLocalBounds());
treeViewTabs.setBounds (r.removeFromLeft (treeViewTabs.getWidth()));
if (resizerBar != nullptr)
resizerBar->setBounds (r.removeFromLeft (4));
if (contentView != nullptr)
contentView->setBounds (r);
}
void ProjectContentComponent::childBoundsChanged (Component* child)
{
if (child == &treeViewTabs)
resized();
}
void ProjectContentComponent::setProject (Project* newProject)
{
if (project != newProject)
{
PropertiesFile& settings = getAppProperties();
if (project != nullptr)
project->removeChangeListener (this);
contentView = nullptr;
resizerBar = nullptr;
if (project != nullptr && treeViewTabs.isShowing())
{
if (treeViewTabs.getWidth() > 0)
settings.setValue ("projectTreeviewWidth_" + project->getProjectUID(), treeViewTabs.getWidth());
settings.setValue ("lastTab_" + project->getProjectUID(), treeViewTabs.getCurrentTabName());
}
treeViewTabs.clearTabs();
project = newProject;
if (project != nullptr)
{
addAndMakeVisible (&treeViewTabs);
createProjectTabs();
const String lastTabName (settings.getValue ("lastTab_" + project->getProjectUID()));
int lastTabIndex = treeViewTabs.getTabNames().indexOf (lastTabName);
if (lastTabIndex < 0 || lastTabIndex > treeViewTabs.getNumTabs())
lastTabIndex = 1;
treeViewTabs.setCurrentTabIndex (lastTabIndex);
int lastTreeWidth = settings.getValue ("projectTreeviewWidth_" + project->getProjectUID()).getIntValue();
if (lastTreeWidth < 150)
lastTreeWidth = 250;
treeViewTabs.setBounds (0, 0, lastTreeWidth, getHeight());
addAndMakeVisible (resizerBar = new ResizableEdgeComponent (&treeViewTabs, &treeSizeConstrainer,
ResizableEdgeComponent::rightEdge));
project->addChangeListener (this);
updateMissingFileStatuses();
resized();
}
else
{
treeViewTabs.setVisible (false);
}
}
}
void ProjectContentComponent::createProjectTabs()
{
jassert (project != nullptr);
treeViewTabs.addTab ("Files", Colour::greyLevel (0.93f), new FileTreeTab (*project), true);
treeViewTabs.addTab ("Config", Colour::greyLevel (0.93f), new ConfigTreeTab (*project), true);
}
TreeView* ProjectContentComponent::getFilesTreeView() const
{
FileTreeTab* ft = dynamic_cast<FileTreeTab*> (treeViewTabs.getTabContentComponent (0));
return ft != nullptr ? &(ft->tree) : nullptr;
}
ProjectTreeViewBase* ProjectContentComponent::getFilesTreeRoot() const
{
TreeView* tv = getFilesTreeView();
return tv != nullptr ? dynamic_cast <ProjectTreeViewBase*> (tv->getRootItem()) : nullptr;
}
void ProjectContentComponent::saveTreeViewState()
{
for (int i = treeViewTabs.getNumTabs(); --i >= 0;)
{
TreePanelBase* t = dynamic_cast<TreePanelBase*> (treeViewTabs.getTabContentComponent (i));
if (t != nullptr)
t->saveOpenness();
}
}
void ProjectContentComponent::changeListenerCallback (ChangeBroadcaster*)
{
updateMissingFileStatuses();
}
void ProjectContentComponent::updateMissingFileStatuses()
{
ProjectTreeViewBase* p = getFilesTreeRoot();
if (p != nullptr)
p->checkFileStatus();
}
bool ProjectContentComponent::showEditorForFile (const File& f)
{
return getCurrentFile() == f
|| showDocument (JucerApplication::getApp()->openDocumentManager.openFile (project, f));
}
bool ProjectContentComponent::showDocument (OpenDocumentManager::Document* doc)
{
if (doc == nullptr)
return false;
JucerApplication::getApp()->openDocumentManager.moveDocumentToTopOfStack (doc);
if (doc->hasFileBeenModifiedExternally())
doc->reloadFromFile();
return setEditorComponent (doc->createEditor(), doc);
}
void ProjectContentComponent::hideEditor()
{
currentDocument = nullptr;
contentView = nullptr;
updateMainWindowTitle();
commandManager->commandStatusChanged();
}
void ProjectContentComponent::hideDocument (OpenDocumentManager::Document* doc)
{
if (doc == currentDocument)
hideEditor();
}
bool ProjectContentComponent::setEditorComponent (Component* editor, OpenDocumentManager::Document* doc)
{
if (editor != nullptr)
{
contentView = editor;
currentDocument = doc;
addAndMakeVisible (editor);
resized();
updateMainWindowTitle();
commandManager->commandStatusChanged();
return true;
}
updateMainWindowTitle();
return false;
}
void ProjectContentComponent::updateMainWindowTitle()
{
MainWindow* mw = findParentComponentOfClass<MainWindow>();
if (mw != nullptr)
mw->updateTitle (currentDocument != nullptr ? currentDocument->getName() : String::empty);
}
bool ProjectContentComponent::canProjectBeLaunched() const
{
if (project != nullptr)
{
ScopedPointer <ProjectExporter> launcher (ProjectExporter::createPlatformDefaultExporter (*project));
return launcher != nullptr;
}
return false;
}
ApplicationCommandTarget* ProjectContentComponent::getNextCommandTarget()
{
return findFirstTargetParentComponent();
}
void ProjectContentComponent::getAllCommands (Array <CommandID>& commands)
{
const CommandID ids[] = { CommandIDs::saveDocument,
CommandIDs::closeDocument,
CommandIDs::saveProject,
CommandIDs::closeProject,
CommandIDs::openInIDE,
CommandIDs::saveAndOpenInIDE,
CommandIDs::showProjectSettings,
StandardApplicationCommandIDs::del };
commands.addArray (ids, numElementsInArray (ids));
}
void ProjectContentComponent::getCommandInfo (const CommandID commandID, ApplicationCommandInfo& result)
{
String documentName;
if (currentDocument != nullptr)
documentName = " '" + currentDocument->getName().substring (0, 32) + "'";
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::closeDocument:
result.setInfo ("Close" + documentName,
"Closes the current document",
CommandCategories::general, 0);
result.setActive (currentDocument != nullptr);
#if JUCE_MAC
result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier | ModifierKeys::ctrlModifier, 0));
#else
result.defaultKeypresses.add (KeyPress ('w', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
#endif
break;
case CommandIDs::openInIDE:
#if JUCE_MAC
result.setInfo ("Open in XCode...",
#elif JUCE_WINDOWS
result.setInfo ("Open in Visual Studio...",
#else
result.setInfo ("Open as a Makefile...",
#endif
"Launches the project in an external IDE",
CommandCategories::general, 0);
result.setActive (canProjectBeLaunched());
break;
case CommandIDs::saveAndOpenInIDE:
#if JUCE_MAC
result.setInfo ("Save Project and Open in XCode...",
#elif JUCE_WINDOWS
result.setInfo ("Save Project and Open in Visual Studio...",
#else
result.setInfo ("Save Project and Open as a Makefile...",
#endif
"Saves the project and launches it in an external IDE",
CommandCategories::general, 0);
result.setActive (canProjectBeLaunched());
result.defaultKeypresses.add (KeyPress ('l', ModifierKeys::commandModifier, 0));
break;
case CommandIDs::showProjectSettings:
result.setInfo ("Show Project Build Settings",
"Shows the build options for the project",
CommandCategories::general, 0);
result.setActive (project != nullptr);
result.defaultKeypresses.add (KeyPress ('i', ModifierKeys::commandModifier | ModifierKeys::shiftModifier, 0));
break;
case StandardApplicationCommandIDs::del:
result.setInfo ("Delete", String::empty, 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;
default:
break;
}
}
bool ProjectContentComponent::isCommandActive (const CommandID commandID)
{
return project != nullptr;
}
bool ProjectContentComponent::perform (const InvocationInfo& info)
{
switch (info.commandID)
{
case CommandIDs::saveProject:
if (project != nullptr && ! reinvokeCommandAfterClosingPropertyEditors (info))
project->save (true, true);
break;
case CommandIDs::closeProject:
{
MainWindow* const mw = findParentComponentOfClass<MainWindow>();
if (mw != nullptr && ! reinvokeCommandAfterClosingPropertyEditors (info))
mw->closeCurrentProject();
}
break;
case CommandIDs::saveDocument:
if (! reinvokeCommandAfterClosingPropertyEditors (info))
{
if (currentDocument != nullptr)
currentDocument->save();
else if (project != nullptr)
project->save (true, true);
}
break;
case CommandIDs::closeDocument:
if (currentDocument != nullptr)
JucerApplication::getApp()->openDocumentManager.closeDocument (currentDocument, true);
break;
case CommandIDs::openInIDE:
if (project != nullptr)
{
ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
if (exporter != nullptr)
exporter->launchProject();
}
break;
case CommandIDs::saveAndOpenInIDE:
if (project != nullptr)
{
if (! reinvokeCommandAfterClosingPropertyEditors (info))
{
if (project->save (true, true) == FileBasedDocument::savedOk)
{
ScopedPointer <ProjectExporter> exporter (ProjectExporter::createPlatformDefaultExporter (*project));
if (exporter != nullptr)
exporter->launchProject();
}
}
}
break;
case CommandIDs::showProjectSettings:
treeViewTabs.setCurrentTabIndex (1);
break;
case StandardApplicationCommandIDs::del:
{
TreePanelBase* const tree = dynamic_cast<TreePanelBase*> (treeViewTabs.getCurrentContentComponent());
if (tree != nullptr)
tree->deleteSelectedItems();
}
break;
default:
return false;
}
return true;
}
bool ProjectContentComponent::reinvokeCommandAfterClosingPropertyEditors (const InvocationInfo& info)
{
if (reinvokeCommandAfterCancellingModalComps (info))
{
grabKeyboardFocus(); // to force any open labels to close their text editors
return true;
}
return false;
}
void ProjectContentComponent::showBubbleMessage (const Point<int>& pos, const String& text)
{
addChildComponent (&bubbleMessage);
bubbleMessage.setAlwaysOnTop (true);
bubbleMessage.showAt (pos, AttributedString (text), 3000, true, false);
}